Skip to content

NativeAOT and XmlSerializer #106580

Open
Open
@danielo-unity3d

Description

@danielo-unity3d

Description

When publishing a test application which uses XML serialization, vi code generated via xscgen (see here) along with PublishAot, the application crashes because the serialization code relies heavily upon reflection.

The stack trace is, then:

bin/Debug/net8.0/linux-x64/publish/XmlSerializationTest
Unhandled Exception: System.InvalidOperationException: There is an error in the XML document.
 ---> System.InvalidOperationException: There was an error reflecting type 'ACME.Package.Xml.Schema.PackageACL'.
 ---> System.InvalidOperationException: You must implement a default accessor on System.Collections.ObjectModel.Collection`1[[ACME.Package.Xml.Schema.Package, XmlSerializationTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it inherits from ICollection.
   at System.Xml.Serialization.TypeScope.GetDefaultIndexer(Type, String) + 0x4e9
   at System.Xml.Serialization.TypeScope.GetCollectionElementType(Type, String) + 0x44
   at System.Xml.Serialization.TypeScope.ImportTypeDesc(Type, MemberInfo, Boolean) + 0x732
   at System.Xml.Serialization.TypeScope.GetTypeDesc(Type, MemberInfo, Boolean, Boolean) + 0x172
   at System.Xml.Serialization.StructModel.GetPropertyModel(PropertyInfo) + 0xf6
   at System.Xml.Serialization.StructModel.GetFieldModel(MemberInfo) + 0xb0
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping, StructModel, Boolean, String, RecursionLimiter) + 0x777
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel, String, Boolean, XmlAttributes, RecursionLimiter) + 0x57f
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel, String, XmlReflectionImporter.ImportContext, String, XmlAttributes, Boolean, Boolean, RecursionLimiter) + 0x90d
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel, String, XmlReflectionImporter.ImportContext, String, XmlAttributes, Boolean, Boolean, RecursionLimiter) + 0xb9f
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel, String, XmlReflectionImporter.ImportContext, String, XmlAttributes, RecursionLimiter) + 0x57
   at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel, XmlRootAttribute, String, RecursionLimiter) + 0x15e
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type, XmlRootAttribute, String) + 0xa5
   at System.Xml.Serialization.XmlSerializer.GenerateXmlTypeMapping(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, String defaultNamespace) + 0xba
   at System.Xml.Serialization.XmlSerializer.GetMapping() + 0x5e
   at System.Xml.Serialization.XmlSerializer.DeserializeUsingReflection(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0x33
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0x1e9
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0x4ed
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle) + 0x83
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader) + 0x27
   at ACME.Xml.XmlExtensions.<ParseTag>d__0`1.MoveNext() + 0x222
   at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1, Boolean&) + 0x16d
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1) + 0x4c
   at ACME.XmlSerializationTest.Program.Main(String[] args) + 0x125
   at XmlSerializationTest!<BaseAddress>+0x9e6cc7
   at XmlSerializationTest!<BaseAddress>+0x9e6d4d
[1]    500757 IOT instruction (core dumped)  bin/Debug/net8.0/linux-x64/publish/XmlSerializationTest

However, I have (in the real application) also seen the following stack:

 ---> System.NotSupportedException: 'System.Xml.Serialization.ReflectionXmlSerializationReaderHelper.GetSetMemberValueDelegateWithType[ACME.Package.Xml.Schema.PackageACL,System.DateTime](System.Reflection.MemberInfo)' is missing native code. MethodInfo.MakeGenericMethod() is not compatible with AOT compilation. Inspect and fix AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
   at System.Reflection.Runtime.MethodInfos.RuntimeNamedMethodInfo`1.GetUncachedMethodInvoker(RuntimeTypeInfo[], MemberInfo) + 0x61
   at System.Reflection.Runtime.MethodInfos.RuntimeConstructedGenericMethodInfo.get_UncachedMethodInvoker() + 0x3b
   at System.Reflection.Runtime.MethodInfos.RuntimeMethodInfo.get_MethodInvoker() + 0x60
   at System.Reflection.Runtime.MethodInfos.RuntimeNamedMethodInfo`1.MakeGenericMethod(Type[]) + 0x363
   at System.Xml.Serialization.ReflectionXmlSerializationReader.GetSetMemberValueDelegate(Object, String) + 0x40a
   at System.Xml.Serialization.ReflectionXmlSerializationReader.SetOrAddValueToMember(Object, Object, MemberInfo) + 0xc5
   at System.Xml.Serialization.ReflectionXmlSerializationReader.<>c__DisplayClass53_1.<WriteLiteralStructMethod>g__Wrapper|1(Object value) + 0x61
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteAttribute(ReflectionXmlSerializationReader.Member, Object) + 0x4be
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteAttributes(ReflectionXmlSerializationReader.Member[], ReflectionXmlSerializationReader.Member, UnknownNodeAction, Object&) + 0x35b
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteLiteralStructMethod(StructMapping, Boolean, Boolean, String) + 0x13dc
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteStructMethod(StructMapping, Boolean, Boolean, String) + 0x72
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteElement(ElementAccessor, Boolean, Boolean, String, Int32, XmlSerializationReader.Fixup, ReflectionXmlSerializationReader.Member) + 0xc34
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteMemberElementsIf(ReflectionXmlSerializationReader.Member[], ReflectionXmlSerializationReader.Member, UnknownNodeAction, XmlSerializationReader.Fixup, ReflectionXmlSerializationReader.CheckTypeSource) + 0x718
   at System.Xml.Serialization.ReflectionXmlSerializationReader.WriteMemberElements(ReflectionXmlSerializationReader.Member[], UnknownNodeAction, UnknownNodeAction, ReflectionXmlSerializationReader.Member, ReflectionXmlSerializationReader.Member, XmlSerializationReader.Fixup, List`1) + 0x116
   at System.Xml.Serialization.ReflectionXmlSerializationReader.GenerateTypeElement(XmlTypeMapping) + 0x3f7
   at System.Xml.Serialization.ReflectionXmlSerializationReader.ReadObject() + 0xd4
   at System.Xml.Serialization.XmlSerializer.DeserializeUsingReflection(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0xc6
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0x1e9
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) + 0x4ed
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle) + 0x83
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader) + 0x27

The test project, which I have tried to prune as much as possible, can be found here: XmlSerializationTest.zip

  • Can the linker be forced to instantiate the pruned functions? (private/internal classes and methods, with generic types)
  • In the test project. does it matter what flags are used for xscgen, and what attributes are finally used to mark the generated classes and fields, and
    • what collections are eventually used (default is System.Collections.ObjectModel.Collection`1)
  • Are there any plans to support this scenario? (I could only find issue Annotate remaining runtime libraries for NativeAOT #75480)
  • How can System.Private.Xml be modified (to support this scenario)?
    • Is this just a question of addressing all linker warnings (IL3050) that are printed during the link phase (when TrimmerSingleWarn is true), or are other warnings/issues (perhaps being masked)?
    • Occurrences of IL3050 are:
      • src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs: 25 hits
      • src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs: 1 hit
      • src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs: 6 hits
      • src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs: 1 hit
      • src/libraries/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs: 1 hit
  • Would the support ever be available in .Net 8, or just .Net 9? (could the solution be back-ported if we get to that point?)

Reproduction Steps

Compile and run attached project:

dotnet publish -c Debug
bin/Debug/net8.0/linux-x64/publish/XmlSerializationTest

Expected behavior

The test program should successfully serialize and deserialize a basic data structure to and from XML.

Actual behavior

See stack trace of crash in the description.

Regression?

No.

Known Workarounds

No response

Configuration

dotnet --info
.NET SDK:
 Version:           8.0.303
 Commit:            29ab8e3268
 Workload version:  8.0.300-manifests.c915c39d
 MSBuild version:   17.10.4+10fbfbf2e

Runtime Environment:
 OS Name:     ubuntu
 OS Version:  24.04
 OS Platform: Linux
 RID:         linux-x64
 Base Path:   /home/user/.dotnet/sdk/8.0.303/

.NET workloads installed:
There are no installed workloads to display.

Host:
  Version:      8.0.7
  Architecture: x64
  Commit:       2aade6beb0

.NET SDKs installed:
  6.0.424 [/home/user/.dotnet/sdk]
  7.0.410 [/home/user/.dotnet/sdk]
  8.0.303 [/home/user/.dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.32 [/home/user/.dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.20 [/home/user/.dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 8.0.7 [/home/user/.dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.32 [/home/user/.dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.20 [/home/user/.dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.7 [/home/user/.dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  DOTNET_ROOT       [/home/user/.dotnet]

global.json file:
  /home/user/Unity/licensing.entitlement4/global.json

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

Other information

See also this issue in xscgen

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions