From 7673f7032c71e73c167d112810f34455cfafc156 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Wed, 11 Nov 2020 18:03:03 -0600 Subject: [PATCH 1/3] XmlSerializer.Serialize doesn't work when using TrimMode=link Make XmlSerializer work correctly with ILLinker trimming. 1. Use constant BindingFlags to work around https://github.com/mono/linker/issues/1617 2. Annotate CodeGenerator.CreateTypeBuilder correctly to preserve base class members Fix #41389 --- .../System/Xml/Serialization/CodeGenerator.cs | 14 ++- .../System.Xml.TrimmingTests.proj | 10 ++ .../XmlSerializer.Deserialize.cs | 86 +++++++++++++++++ .../TrimmingTests/XmlSerializer.Serialize.cs | 95 +++++++++++++++++++ 4 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 src/libraries/System.Private.Xml/tests/TrimmingTests/System.Xml.TrimmingTests.proj create mode 100644 src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Deserialize.cs create mode 100644 src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Serialize.cs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index 2ae83d3c2022c..5e94cf8bf856a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -23,9 +23,9 @@ namespace System.Xml.Serialization { internal class CodeGenerator { - internal static BindingFlags InstancePublicBindingFlags = BindingFlags.Instance | BindingFlags.Public; - internal static BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - internal static BindingFlags StaticBindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + internal const BindingFlags InstancePublicBindingFlags = BindingFlags.Instance | BindingFlags.Public; + internal const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + internal const BindingFlags StaticBindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal static MethodAttributes PublicMethodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig; internal static MethodAttributes PublicOverrideMethodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig; internal static MethodAttributes ProtectedOverrideMethodAttributes = MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig; @@ -1242,7 +1242,13 @@ internal static ModuleBuilder CreateModuleBuilder(AssemblyBuilder assemblyBuilde { return assemblyBuilder.DefineDynamicModule(name); } - internal static TypeBuilder CreateTypeBuilder(ModuleBuilder moduleBuilder, string name, TypeAttributes attributes, Type parent, Type[] interfaces) + + internal static TypeBuilder CreateTypeBuilder( + ModuleBuilder moduleBuilder, + string name, + TypeAttributes attributes, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type parent, + Type[] interfaces) { // parent is nullable if no base class return moduleBuilder.DefineType(TempAssembly.GeneratedAssemblyNamespace + "." + name, diff --git a/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Xml.TrimmingTests.proj b/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Xml.TrimmingTests.proj new file mode 100644 index 0000000000000..a30ad093ba40e --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Xml.TrimmingTests.proj @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Deserialize.cs b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Deserialize.cs new file mode 100644 index 0000000000000..608e16b62dbea --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Deserialize.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Xml.Schema; + +namespace System.Xml.Serialization.TrimmingTests +{ + internal class Program + { + // Preserve these types until XmlSerializer is fully trim-safe. + // see https://github.com/dotnet/runtime/issues/44768 + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Response))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(DataUpdates))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(DataUpdatesDataUpdateInfo))] + public static int Main() + { + using StringReader stringReader = new StringReader(@" + + + + + + + "); + + Response obj = (Response)new XmlSerializer(typeof(Response)).Deserialize(stringReader); + if (obj.DataUpdates.DataUpdateInfo.Count == 3 && + obj.DataUpdates.DataUpdateInfo.All(i => i.DataDate.Year == 2009 && i.LastUpdatedDate.Year == 2010)) + { + return 100; + } + + return -1; + } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class Response + { + public Response() + { + this.DataUpdates = new DataUpdates(); + } + + [XmlElement(Order = 0)] + public DataUpdates DataUpdates { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class DataUpdates + { + public DataUpdates() + { + this.DataUpdateInfo = new List(); + } + + [XmlElement("DataUpdateInfo", Form = XmlSchemaForm.Unqualified, Order = 0)] + public List DataUpdateInfo { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true)] + public class DataUpdatesDataUpdateInfo + { + public DataUpdatesDataUpdateInfo() + { + } + + [XmlAttribute] + public DateTime DataDate { get; set; } + + [XmlAttribute] + public string DataType { get; set; } + + [XmlAttribute] + public DateTime LastUpdatedDate { get; set; } + } +} diff --git a/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Serialize.cs b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Serialize.cs new file mode 100644 index 0000000000000..1e127dd7893c9 --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSerializer.Serialize.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Xml.Schema; + +namespace System.Xml.Serialization.TrimmingTests +{ + internal class Program + { + // Preserve these types until XmlSerializer is fully trim-safe. + // see https://github.com/dotnet/runtime/issues/44768 + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Response))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(DataUpdates))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(DataUpdatesDataUpdateInfo))] + public static int Main() + { + Response obj = new Response(); + obj.DataUpdates.DataUpdateInfo.Add(new DataUpdatesDataUpdateInfo() + { + DataDate = new DateTime(2009, 4, 13), + DataType = "Data", + LastUpdatedDate = new DateTime(2010, 12, 12) + }); + obj.DataUpdates.DataUpdateInfo.Add(new DataUpdatesDataUpdateInfo() + { + DataDate = new DateTime(2009, 4, 14), + DataType = "Data", + LastUpdatedDate = new DateTime(2010, 12, 12) + }); + + using StringWriter writer = new StringWriter(); + new XmlSerializer(typeof(Response)).Serialize(writer, obj); + string serialized = writer.ToString(); + + if (serialized.Contains("") && + serialized.Contains(@"") && + serialized.Contains(@"")) + { + return 100; + } + + return -1; + } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class Response + { + public Response() + { + this.DataUpdates = new DataUpdates(); + } + + [XmlElement(Order = 0)] + public DataUpdates DataUpdates { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class DataUpdates + { + public DataUpdates() + { + this.DataUpdateInfo = new List(); + } + + [XmlElement("DataUpdateInfo", Form = XmlSchemaForm.Unqualified, Order = 0)] + public List DataUpdateInfo { get; set; } + } + + [Serializable] + [XmlType(AnonymousType = true)] + public class DataUpdatesDataUpdateInfo + { + public DataUpdatesDataUpdateInfo() + { + } + + [XmlAttribute] + public DateTime DataDate { get; set; } + + [XmlAttribute] + public string DataType { get; set; } + + [XmlAttribute] + public DateTime LastUpdatedDate { get; set; } + } +} From 2278b22600860dd330c9b3f7b18c1163ee36712c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 16 Nov 2020 18:09:57 -0600 Subject: [PATCH 2/3] Update ILLinker suppressions file. --- .../src/ILLink/ILLink.Suppressions.xml | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml index 95df6776ce7ab..882bb1394065f 100644 --- a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml @@ -39,9 +39,15 @@ ILLink - IL2067 + IL2070 member - M:System.Xml.Serialization.CodeGenerator.CreateTypeBuilder(System.Reflection.Emit.ModuleBuilder,System.String,System.Reflection.TypeAttributes,System.Type,System.Type[]) + M:System.Xml.Serialization.Compiler.AddImport(System.Type,System.Collections.Hashtable) + + + ILLink + IL2070 + member + M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMappingInfo(System.Xml.Serialization.TypeMapping,System.String,System.Type) ILLink @@ -49,6 +55,12 @@ member M:System.Xml.Serialization.ReflectionXmlSerializationReader.ReflectionCreateObject(System.Type) + + ILLink + IL2070 + member + M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMemberInfo(System.Type,System.String,System.String,System.String) + ILLink IL2070 @@ -193,6 +205,12 @@ member M:System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(System.Type,System.String,System.Xml.Serialization.XmlSerializerImplementation@) + + ILLink + IL2072 + member + M:System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializer(System.String,System.String,System.Xml.Serialization.XmlMapping,System.Xml.Serialization.CodeIdentifiers,System.String,System.String,System.String) + ILLink IL2072 From a5d416caf0f777f567ff158925377a5264efb69f Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 17 Nov 2020 08:25:28 -0600 Subject: [PATCH 3/3] PR feedback. Fix ILLinker suppresions. --- .../src/ILLink/ILLink.Suppressions.xml | 16 +++++++++++----- .../System/Xml/Serialization/CodeGenerator.cs | 8 ++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml index 882bb1394065f..3256637c95633 100644 --- a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml @@ -25,6 +25,12 @@ member M:System.Xml.Serialization.XmlAttributes.get_IgnoreAttribute + + ILLink + IL2057 + member + M:System.Xml.Serialization.XmlSerializationWriterCodeGen.WriteCheckDefault(System.Xml.Serialization.TypeMapping,System.String,System.Object,System.Boolean) + ILLink IL2057 @@ -39,21 +45,21 @@ ILLink - IL2070 + IL2067 member - M:System.Xml.Serialization.Compiler.AddImport(System.Type,System.Collections.Hashtable) + M:System.Xml.Serialization.ReflectionXmlSerializationReader.ReflectionCreateObject(System.Type) ILLink IL2070 member - M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMappingInfo(System.Xml.Serialization.TypeMapping,System.String,System.Type) + M:System.Xml.Serialization.Compiler.AddImport(System.Type,System.Collections.Hashtable) ILLink - IL2067 + IL2070 member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.ReflectionCreateObject(System.Type) + M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMappingInfo(System.Xml.Serialization.TypeMapping,System.String,System.Type) ILLink diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index 5e94cf8bf856a..5b17707b8d841 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -26,10 +26,10 @@ internal class CodeGenerator internal const BindingFlags InstancePublicBindingFlags = BindingFlags.Instance | BindingFlags.Public; internal const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; internal const BindingFlags StaticBindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - internal static MethodAttributes PublicMethodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig; - internal static MethodAttributes PublicOverrideMethodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig; - internal static MethodAttributes ProtectedOverrideMethodAttributes = MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig; - internal static MethodAttributes PrivateMethodAttributes = MethodAttributes.Private | MethodAttributes.HideBySig; + internal const MethodAttributes PublicMethodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig; + internal const MethodAttributes PublicOverrideMethodAttributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig; + internal const MethodAttributes ProtectedOverrideMethodAttributes = MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig; + internal const MethodAttributes PrivateMethodAttributes = MethodAttributes.Private | MethodAttributes.HideBySig; private readonly TypeBuilder _typeBuilder; private MethodBuilder? _methodBuilder;