Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Downgrade to building on Mono 5.20 #2976

Merged
merged 1 commit into from
Jan 28, 2020

Conversation

HebaruSan
Copy link
Member

@HebaruSan HebaruSan commented Jan 28, 2020

Problem

The latest release (1.26.8) on Windows:

Unhandled Exception: System.ArgumentException: Object of type 'System.Globalization.CalendarId[]' cannot be converted to type 'System.Int32[]'.
   at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.RtFieldInfo.UnsafeSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
   at System.Runtime.Serialization.FormatterServices.SerializationSetValue(MemberInfo fi, Object target, Object value)
   at System.Runtime.Serialization.ObjectManager.CompleteObject(ObjectHolder holder, Boolean bObjectFullyComplete)
   at System.Runtime.Serialization.ObjectManager.DoNewlyRegisteredObjectFixups(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.RegisterObject(Object obj, Int64 objectID, SerializationInfo info, Int64 idOfContainingObj, MemberInfo member, Int32[] arrayIndex)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.RegisterObject(Object obj, ParseRecord pr, ParseRecord objectPr, Boolean bIsString)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObjectEnd(ParseRecord pr)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord pr)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Resources.ResourceReader.DeserializeObject(Int32 typeIndex)
   at System.Resources.ResourceReader._LoadObjectV2(Int32 pos, ResourceTypeCode& typeCode)
   at System.Resources.ResourceReader.LoadObjectV2(Int32 pos, ResourceTypeCode& typeCode)
   at System.Resources.ResourceReader.LoadObject(Int32 pos)
   at System.Resources.ResourceReader.ResourceEnumerator.get_Entry()
   at System.Resources.ResourceReader.ResourceEnumerator.get_Current()
   at System.ComponentModel.ComponentResourceManager.FillResources(CultureInfo culture, ResourceSet& resourceSet)
   at System.ComponentModel.ComponentResourceManager.FillResources(CultureInfo culture, ResourceSet& resourceSet)
   at System.ComponentModel.ComponentResourceManager.FillResources(CultureInfo culture, ResourceSet& resourceSet)
   at System.ComponentModel.ComponentResourceManager.ApplyResources(Object value, String objectName, CultureInfo culture)
   at System.ComponentModel.ComponentResourceManager.ApplyResources(Object value, String objectName)
   at CKAN.Main.InitializeComponent()
   at CKAN.Main..ctor(String[] cmdlineArgs, KSPManager mgr, Boolean showConsole)
   at CKAN.GUI.Main_(String[] args, KSPManager manager, Boolean showConsole)
   at CKAN.CmdLine.MainClass.RunSimpleAction(Options cmdline, CommonOptions options, String[] args, IUser user, KSPManager manager)
   at CKAN.CmdLine.MainClass.Execute(KSPManager manager, CommonOptions opts, String[] args)
   at CKAN.CmdLine.MainClass.Main(String[] args)

The latest release on Ubuntu with Mono 5:

exception inside UnhandledException handler: Object reference not set to an instance of an object

[ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
  at System.Globalization.DateTimeFormatInfo.InitializeOverridableProperties (System.Globalization.CultureData cultureData, System.Int32 calendarID) [0x0007b] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Globalization.DateTimeFormatInfo.OnDeserialized (System.Runtime.Serialization.StreamingContext ctx) [0x000a7] in <2943701620b54f86b436d3ffad010412>:0 
  at (wrapper delegate-invoke) <Module>.invoke_void_StreamingContext(System.Runtime.Serialization.StreamingContext)
  at System.Runtime.Serialization.ObjectManager.RaiseDeserializationEvent () [0x00008] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize (System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Runtime.Serialization.Formatters.Binary.__BinaryParser serParser, System.Boolean fCheck, System.Boolean isCrossAppDomain, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x0010d] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Boolean isCrossAppDomain, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x000a2] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck) [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader.DeserializeObject (System.Int32 typeIndex) [0x00019] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader._LoadObjectV2 (System.Int32 pos, System.Resources.ResourceTypeCode& typeCode) [0x0035c] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader.LoadObjectV2 (System.Int32 pos, System.Resources.ResourceTypeCode& typeCode) [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader.LoadObject (System.Int32 pos) [0x00011] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader+ResourceEnumerator.get_Entry () [0x000d3] in <2943701620b54f86b436d3ffad010412>:0 
  at System.Resources.ResourceReader+ResourceEnumerator.get_Current () [0x00000] in <2943701620b54f86b436d3ffad010412>:0 
  at System.ComponentModel.ComponentResourceManager.FillResources (System.Globalization.CultureInfo culture, System.Resources.ResourceSet& resourceSet) [0x0006c] in <b3d41b23de534128a4f18a6e1312f79c>:0 
  at System.ComponentModel.ComponentResourceManager.FillResources (System.Globalization.CultureInfo culture, System.Resources.ResourceSet& resourceSet) [0x00024] in <b3d41b23de534128a4f18a6e1312f79c>:0 
  at System.ComponentModel.ComponentResourceManager.FillResources (System.Globalization.CultureInfo culture, System.Resources.ResourceSet& resourceSet) [0x00024] in <b3d41b23de534128a4f18a6e1312f79c>:0 
  at System.ComponentModel.ComponentResourceManager.ApplyResources (System.Object value, System.String objectName, System.Globalization.CultureInfo culture) [0x00039] in <b3d41b23de534128a4f18a6e1312f79c>:0 
  at System.ComponentModel.ComponentResourceManager.ApplyResources (System.Object value, System.String objectName) [0x00000] in <b3d41b23de534128a4f18a6e1312f79c>:0 
  at CKAN.Main.InitializeComponent () [0x00722] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at CKAN.Main..ctor (System.String[] cmdlineArgs, CKAN.KSPManager mgr, System.Boolean showConsole) [0x00069] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at (wrapper remoting-invoke-with-check) CKAN.Main..ctor(string[],CKAN.KSPManager,bool)
  at CKAN.GUI.Main_ (System.String[] args, CKAN.KSPManager manager, System.Boolean showConsole) [0x0003b] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at CKAN.CmdLine.MainClass.Gui (CKAN.KSPManager manager, CKAN.CmdLine.GuiOptions options, System.String[] args) [0x00008] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at CKAN.CmdLine.MainClass.RunSimpleAction (CKAN.CmdLine.Options cmdline, CKAN.CmdLine.CommonOptions options, System.String[] args, CKAN.IUser user, CKAN.KSPManager manager) [0x002d1] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at CKAN.CmdLine.MainClass.Execute (CKAN.KSPManager manager, CKAN.CmdLine.CommonOptions opts, System.String[] args) [0x00251] in <5016fb43589a4af2af9aed3ff1a8d603>:0 
  at CKAN.CmdLine.MainClass.Main (System.String[] args) [0x00091] in <5016fb43589a4af2af9aed3ff1a8d603>:0

Neither of these appears to be due to anything CKAN itself is doing: note the exception is 20+ levels deep in the stack trace, and building on Windows produces a binary that works fine everywhere.

Cause

In #2964 we changed the Mono version for the net-core builds from latest to 6.6.0 to work around a problem with Cake on Mono 6.8.0.

We also updated versions of Mono and other packages for the regular builds since we hadn't done that in a while, since later versions of software are supposed to have fixes and improvements and we would like to take advantage of those. However, Mono 6.8.0 apparently is not the only release with problems; it looks like Mono 6 can't build an EXE that works on Windows or Mono 5.

Changes

Now we use the build from Mono 5.20.1. @DasSkelett has tested ckan.exe built with Mono 5.20 and confirmed that it works on Windows.

Fixes #2972.
Fixes #2974.

@HebaruSan HebaruSan added Bug Something is not working as intended Pull request Build Issues affecting the build system Windows Issues specific for Windows Linux Issues specific for Linux Mono Issues specific for Mono labels Jan 28, 2020
HebaruSan added a commit that referenced this pull request Jan 28, 2020
@HebaruSan HebaruSan merged commit 93de574 into KSP-CKAN:master Jan 28, 2020
@HebaruSan HebaruSan deleted the fix/mono-rollback branch January 28, 2020 18:49
@HebaruSan
Copy link
Member Author

(@DasSkelett, I'm crediting you with co-development of this in the changelog rather than just review because you took care of testing the 5.20 builds and replacing them in the release assets.)

@DasSkelett DasSkelett mentioned this pull request Feb 4, 2020
@HebaruSan HebaruSan mentioned this pull request Oct 13, 2020
@HebaruSan
Copy link
Member Author

We probably can't build on Mono 5 forever, so it would be good to get Mono 6 builds working. Documenting further investigation here because it's the only place where the problems for both Windows and Mono are presented, and I'm still not completely sure that we can solve this ourselves...

  Built on Windows Built on Mono 5 Built on Mono 6 Summary
Run on Windows Works Works Fails, #2972 Confused by Mono 6's EXE somehow
Run on Mono 5 Works Works Fails, #2974 Confused by Mono 6's EXE somehow
Run on Mono 6 Works Works Works Able to handle its own EXE somehow
Summary Always works Always works Only works on Mono 6

Interesting discoveries:

  • Both call stacks go through System.ComponentModel.ComponentResourceManager.ApplyResources, but the object instances in question are of our custom type SingleAssemblyComponentResourceManager, which inherits from that class. So it's plausible that our child class is doing something the parent class doesn't expect. We can test whether commenting out our override of InternalGetResourceSet affects the crashes.
  • Our code relies on protected Hashtable ResourceManager.ResourceSets, but Mono's code shows this not being used at all!
    https://github.com/mono/mono/blob/master/mcs/class/referencesource/mscorlib/system/resources/resourcemanager.cs
    And in fact we already get compiler warnings for that!
    "/__w/CKAN/CKAN/GUI/CKAN-GUI.csproj" (default target) (6:2) ->
      SingleAssemblyResourceManager.cs(20,43): warning CS0618: 'ResourceManager.ResourceSets' is obsolete: 'call InternalGetResourceSet instead' [/__w/CKAN/CKAN/GUI/CKAN-GUI.csproj]
      SingleAssemblyResourceManager.cs(44,36): warning CS0618: 'ResourceManager.ResourceSets' is obsolete: 'call InternalGetResourceSet instead' [/__w/CKAN/CKAN/GUI/CKAN-GUI.csproj]
      SingleAssemblyComponentResourceManager.cs(22,43): warning CS0618: 'ResourceManager.ResourceSets' is obsolete: 'call InternalGetResourceSet instead' [/__w/CKAN/CKAN/GUI/CKAN-GUI.csproj]
      SingleAssemblyComponentResourceManager.cs(46,36): warning CS0618: 'ResourceManager.ResourceSets' is obsolete: 'call InternalGetResourceSet instead' [/__w/CKAN/CKAN/GUI/CKAN-GUI.csproj]
    
    This probably doesn't create functional problems for us; we would still be able to use it as a cache, it just wouldn't be shared with the parent class anymore. But if that's our intent, we might as well declare our own private cache to make it clear that it's separate. Although maybe the rest of ResourceManager is assuming that InternalGetResourceSet will put it somewhere else, and we don't (because we can't, because the new destination is private)?
  • private ResourceManager.AddResourceSet is copied into our child classes, but the code isn't the same as what's in Mono currently. Copying over the lastest changes may make sense.
    https://github.com/mono/mono/blob/master/mcs/class/referencesource/mscorlib/system/resources/resourcemanager.cs#L858-L882
  • ResourceReader._LoadObjectV2 only calls DeserializeObject if the type of the serialized object isn't recognized, which means it's not a string, char, int, etc. We have a few <data> elements in Main.resx, of type System.Drawing.Point or System.Globalization.CultureInfo, so the crash looks to be happening while one of those is being processed, and we can test whether removing these elements temporarily affects the crashes.
    https://github.com/mono/mono/blob/master/mcs/class/referencesource/mscorlib/system/resources/resourcereader.cs#L808
    The next call in both call stacks is BinaryFormatter.Deserialize, and from there they diverge.
  • Our other <data> elements lack a type attribute, presumably falling back to string. Maybe that's not allowed anymore, and we have to specify type="string" explicitly?
  • That type check is based on (ResourceTypeCode) _store.Read7BitEncodedInt(), which suggests that it's processing a compiled form of the resx file rather than the raw XML (where the types are indicated by a type="..." attribute instead). Maybe there are differences from one compiler to the next in how this data is generated, and nobody else can read Mono 6's data?
  • I found somebody's blog post pointing out how the custom types allowed by resx files are a security problem, I assume because you can make your own custom class and then reference it in a resx and pass it as input to make another program run your code.
    https://www.nccgroup.com/uk/about-us/newsroom-and-events/blogs/2018/august/aspnet-resource-files-resx-and-deserialisation-issues/

@HebaruSan
Copy link
Member Author

Continuing some of the above thought process, this explanation seems to fit the observations thus far:

  • Windows's and Mono 5's resx compiler compiles <data> elements lacking a type attribute to have a type of string in the binary representation
  • Mono 6's resx compiler compiles <data> elements lacking a type attribute to have something else in the binary representation, like null
  • Windows's and Mono 5's runtimes don't like that, so they crash when running Mono 6 builds
  • Mono 6's runtime silently defaults in string as the type when it loads those elements from the binary representation, so it's able to run its own builds

I.e., in the change from Mono 5 to 6, the defaulting for a missing type attribute may have been moved from compile time to run time. Note that this is still speculation, we have not yet confirmed that the missing type attribute matters through any direct test.

Also it should be fine that our InternalGetResourceSet implementation doesn't populate a shared cache anymore, because it returns the value requested.

@HebaruSan
Copy link
Member Author

HebaruSan commented Dec 1, 2020

Inspecting Mono 6's _build/out/CKAN-GUI/Debug/CKAN.Main.resources, which appears to be the binary representation mentioned above, CalendarId[] is mentioned in what appears to be the serialization of the default System.Globalization.CultureInfo object. (That's the one that Windows complains about, as compared to Mono 5's null reference exception.)

image

Checking the same file on Windows, there is no mention of CalendarId, but the System.Globalization.CultureInfo object is there. So it appears the two runtimes do indeed disagree about how some of the resx data is (de)serialized.

Possibly useful info here:

@HebaruSan
Copy link
Member Author

HebaruSan commented Dec 1, 2020

Deleting these lines (in several files) makes a Mono 6 build run in Windows!

CKAN/GUI/Main/Main.resx

Lines 138 to 140 in 4f08f2a

<data name="$this.Language" type="System.Globalization.CultureInfo, mscorlib">
<value>(Default)</value>
</data>

Still need to test on Mono 5.
And figure out if that will cause other problems...

@HebaruSan
Copy link
Member Author

HebaruSan commented Dec 1, 2020

I can't even find the Language property in the documentation of Control or Form...

... it might be design-time only? For the GUI in Visual Studio to set which locale is currently being edited, so translators can work without editing XML files.

@DasSkelett
Copy link
Member

Found a mention in the Mono code:

https://github.com/mono/mono/blob/a383593b4e93c34f16851668118beefe70ed0d5d/mcs/class/System.Design/Documentation/en/System.ComponentModel.Design/LocalizationExtenderProvider.xml#L40

The default serializers that ship with Visual Studio are already capable of localizing components and controls, but they only do so if they locate support for the .NET Framework localization architecture. To detect the presence of localization support, the serialization system must locate a public property named "Localizable" on the root designer component. If a serializer finds this property, it searches for a property of type named "Language" to determine the current resource configuration. Default serializers use these properties to determine if it should localize any localizable resources of the component, and if so, what format the resource information should be saved in.

Not sure what to do with this though 😄

@HebaruSan
Copy link
Member Author

I think that's saying the same thing I said above regarding design-time and translators' work flow:

Default serializers use these properties to determine if it should localize any localizable resources of the component, and if so, what format the resource information should be saved in.

Serializing a control turns the in-memory representation of it into a .cs/.Designer.cs/.resx file ensemble, which I believe only happens when you save it in Visual Studio. Before that, the user would select the right language to edit in VS. But we maintain our XML files by hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is not working as intended Build Issues affecting the build system Linux Issues specific for Linux Mono Issues specific for Mono Windows Issues specific for Windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug] Unhandled Exception after launch on Linux [Bug] Builds with Mono >=6.6 don't run on Windows
2 participants