diff --git a/src/libraries/Common/tests/System/Runtime/Serialization/Utils.cs b/src/libraries/Common/tests/System/Runtime/Serialization/Utils.cs
index ca01381a6dff1..2e2ac299c2c53 100644
--- a/src/libraries/Common/tests/System/Runtime/Serialization/Utils.cs
+++ b/src/libraries/Common/tests/System/Runtime/Serialization/Utils.cs
@@ -9,6 +9,8 @@
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
using Xunit;
internal static class Utils
@@ -351,3 +353,30 @@ private static bool IsPrefixedAttributeValue(string atrValue, out string localPr
return false;
}
}
+
+internal class TestAssemblyLoadContext : AssemblyLoadContext
+{
+ private AssemblyDependencyResolver _resolver;
+
+ public TestAssemblyLoadContext(string name, bool isCollectible, string mainAssemblyToLoadPath = null) : base(name, isCollectible)
+ {
+ if (!PlatformDetection.IsBrowser)
+ _resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath ?? Assembly.GetExecutingAssembly().Location);
+ }
+
+ protected override Assembly Load(AssemblyName name)
+ {
+ if (PlatformDetection.IsBrowser)
+ {
+ return base.Load(name);
+ }
+
+ string assemblyPath = _resolver.ResolveAssemblyToPath(name);
+ if (assemblyPath != null)
+ {
+ return LoadFromAssemblyPath(assemblyPath);
+ }
+
+ return null;
+ }
+}
diff --git a/src/libraries/System.Private.Xml/src/Resources/Strings.resx b/src/libraries/System.Private.Xml/src/Resources/Strings.resx
index d19dc6ec4eb31..112359dfecba8 100644
--- a/src/libraries/System.Private.Xml/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.Xml/src/Resources/Strings.resx
@@ -2787,6 +2787,9 @@
Type '{0}' is not serializable.
+
+ Type '{0}' is from an AssemblyLoadContext which is incompatible with that which contains this XmlSerializer.
+
Invalid XmlSerializerAssemblyAttribute usage. Please use {0} property or {1} property.
diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
index 33f89ad93a7d4..ad19c548e3202 100644
--- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
+++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
@@ -445,6 +445,7 @@
+
@@ -564,6 +565,7 @@
+
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs
index 8fdeed7b60b77..c3df77a5a2bc3 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs
@@ -11,6 +11,8 @@
using System.Globalization;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
namespace System.Xml.Serialization
{
@@ -149,79 +151,82 @@ internal void InitAssemblyMethods(XmlMapping[] xmlMappings)
contract = null;
string? serializerName = null;
- // check to see if we loading explicit pre-generated assembly
- object[] attrs = type.GetCustomAttributes(typeof(System.Xml.Serialization.XmlSerializerAssemblyAttribute), false);
- if (attrs.Length == 0)
+ using (AssemblyLoadContext.EnterContextualReflection(type.Assembly))
{
- // Guess serializer name: if parent assembly signed use strong name
- AssemblyName name = type.Assembly.GetName();
- serializerName = Compiler.GetTempAssemblyName(name, defaultNamespace);
- // use strong name
- name.Name = serializerName;
- name.CodeBase = null;
- name.CultureInfo = CultureInfo.InvariantCulture;
-
- try
- {
- serializer = Assembly.Load(name);
- }
- catch (Exception e)
- {
- if (e is OutOfMemoryException)
+ // check to see if we loading explicit pre-generated assembly
+ object[] attrs = type.GetCustomAttributes(typeof(System.Xml.Serialization.XmlSerializerAssemblyAttribute), false);
+ if (attrs.Length == 0)
+ {
+ // Guess serializer name: if parent assembly signed use strong name
+ AssemblyName name = type.Assembly.GetName();
+ serializerName = Compiler.GetTempAssemblyName(name, defaultNamespace);
+ // use strong name
+ name.Name = serializerName;
+ name.CodeBase = null;
+ name.CultureInfo = CultureInfo.InvariantCulture;
+
+ try
{
- throw;
+ serializer = Assembly.Load(name);
}
- }
-
- serializer ??= LoadAssemblyByPath(type, serializerName);
-
- if (serializer == null)
- {
- if (XmlSerializer.Mode == SerializationMode.PreGenOnly)
+ catch (Exception e)
{
- throw new Exception(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName));
+ if (e is OutOfMemoryException)
+ {
+ throw;
+ }
}
- return null;
- }
+ serializer ??= LoadAssemblyByPath(type, serializerName);
- if (!IsSerializerVersionMatch(serializer, type, defaultNamespace))
- {
- XmlSerializationEventSource.Log.XmlSerializerExpired(serializerName, type.FullName!);
- return null;
- }
- }
- else
- {
- System.Xml.Serialization.XmlSerializerAssemblyAttribute assemblyAttribute = (System.Xml.Serialization.XmlSerializerAssemblyAttribute)attrs[0];
- if (assemblyAttribute.AssemblyName != null && assemblyAttribute.CodeBase != null)
- throw new InvalidOperationException(SR.Format(SR.XmlPregenInvalidXmlSerializerAssemblyAttribute, "AssemblyName", "CodeBase"));
+ if (serializer == null)
+ {
+ if (XmlSerializer.Mode == SerializationMode.PreGenOnly)
+ {
+ throw new Exception(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName));
+ }
- // found XmlSerializerAssemblyAttribute attribute, it should have all needed information to load the pre-generated serializer
- if (assemblyAttribute.AssemblyName != null)
- {
- serializerName = assemblyAttribute.AssemblyName;
- serializer = Assembly.Load(serializerName); // LoadWithPartialName just does this in .Net Core; changing the obsolete call.
- }
- else if (assemblyAttribute.CodeBase != null && assemblyAttribute.CodeBase.Length > 0)
- {
- serializerName = assemblyAttribute.CodeBase;
- serializer = Assembly.LoadFrom(serializerName);
+ return null;
+ }
+
+ if (!IsSerializerVersionMatch(serializer, type, defaultNamespace))
+ {
+ XmlSerializationEventSource.Log.XmlSerializerExpired(serializerName, type.FullName!);
+ return null;
+ }
}
else
{
- serializerName = type.Assembly.FullName;
- serializer = type.Assembly;
- }
- if (serializer == null)
- {
- throw new FileNotFoundException(null, serializerName);
+ System.Xml.Serialization.XmlSerializerAssemblyAttribute assemblyAttribute = (System.Xml.Serialization.XmlSerializerAssemblyAttribute)attrs[0];
+ if (assemblyAttribute.AssemblyName != null && assemblyAttribute.CodeBase != null)
+ throw new InvalidOperationException(SR.Format(SR.XmlPregenInvalidXmlSerializerAssemblyAttribute, "AssemblyName", "CodeBase"));
+
+ // found XmlSerializerAssemblyAttribute attribute, it should have all needed information to load the pre-generated serializer
+ if (assemblyAttribute.AssemblyName != null)
+ {
+ serializerName = assemblyAttribute.AssemblyName;
+ serializer = Assembly.Load(serializerName); // LoadWithPartialName just does this in .Net Core; changing the obsolete call.
+ }
+ else if (assemblyAttribute.CodeBase != null && assemblyAttribute.CodeBase.Length > 0)
+ {
+ serializerName = assemblyAttribute.CodeBase;
+ serializer = Assembly.LoadFrom(serializerName);
+ }
+ else
+ {
+ serializerName = type.Assembly.FullName;
+ serializer = type.Assembly;
+ }
+ if (serializer == null)
+ {
+ throw new FileNotFoundException(null, serializerName);
+ }
}
+ Type contractType = GetTypeFromAssembly(serializer, "XmlSerializerContract");
+ contract = (XmlSerializerImplementation)Activator.CreateInstance(contractType)!;
+ if (contract.CanSerialize(type))
+ return serializer;
}
- Type contractType = GetTypeFromAssembly(serializer, "XmlSerializerContract");
- contract = (XmlSerializerImplementation)Activator.CreateInstance(contractType)!;
- if (contract.CanSerialize(type))
- return serializer;
return null;
}
@@ -449,81 +454,93 @@ internal static bool GenerateSerializerToStream(XmlMapping[] xmlMappings, Type?[
}
[RequiresUnreferencedCode("calls GenerateElement")]
- internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type?[]? types, string? defaultNamespace)
+ internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type?[] types, string? defaultNamespace)
{
+ var mainType = (types.Length > 0) ? types[0] : null;
+ Assembly? mainAssembly = mainType?.Assembly;
var scopeTable = new Dictionary();
foreach (XmlMapping mapping in xmlMappings)
scopeTable[mapping.Scope!] = mapping;
TypeScope[] scopes = new TypeScope[scopeTable.Keys.Count];
scopeTable.Keys.CopyTo(scopes, 0);
- string assemblyName = "Microsoft.GeneratedCode";
- AssemblyBuilder assemblyBuilder = CodeGenerator.CreateAssemblyBuilder(assemblyName);
- // Add AssemblyVersion attribute to match parent assembly version
- if (types != null && types.Length > 0 && types[0] != null)
+ using (AssemblyLoadContext.EnterContextualReflection(mainAssembly))
{
- ConstructorInfo AssemblyVersionAttribute_ctor = typeof(AssemblyVersionAttribute).GetConstructor(
- new Type[] { typeof(string) }
- )!;
- string assemblyVersion = types[0]!.Assembly.GetName().Version!.ToString();
- assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(AssemblyVersionAttribute_ctor, new object[] { assemblyVersion }));
- }
- CodeIdentifiers classes = new CodeIdentifiers();
- classes.AddUnique("XmlSerializationWriter", "XmlSerializationWriter");
- classes.AddUnique("XmlSerializationReader", "XmlSerializationReader");
- string? suffix = null;
- if (types != null && types.Length == 1 && types[0] != null)
- {
- suffix = CodeIdentifier.MakeValid(types[0]!.Name);
- if (types[0]!.IsArray)
+ // Before generating any IL, check each mapping and supported type to make sure
+ // they are compatible with the current ALC
+ for (int i = 0; i < types.Length; i++)
+ VerifyLoadContext(types[i], mainAssembly);
+ foreach (var mapping in xmlMappings)
+ VerifyLoadContext(mapping.Accessor.Mapping?.TypeDesc?.Type, mainAssembly);
+
+ string assemblyName = "Microsoft.GeneratedCode";
+ AssemblyBuilder assemblyBuilder = CodeGenerator.CreateAssemblyBuilder(assemblyName);
+ // Add AssemblyVersion attribute to match parent assembly version
+ if (mainType != null)
+ {
+ ConstructorInfo AssemblyVersionAttribute_ctor = typeof(AssemblyVersionAttribute).GetConstructor(
+ new Type[] { typeof(string) }
+ )!;
+ string assemblyVersion = mainType.Assembly.GetName().Version!.ToString();
+ assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(AssemblyVersionAttribute_ctor, new object[] { assemblyVersion }));
+ }
+ CodeIdentifiers classes = new CodeIdentifiers();
+ classes.AddUnique("XmlSerializationWriter", "XmlSerializationWriter");
+ classes.AddUnique("XmlSerializationReader", "XmlSerializationReader");
+ string? suffix = null;
+ if (mainType != null)
{
- suffix += "Array";
+ suffix = CodeIdentifier.MakeValid(mainType.Name);
+ if (mainType.IsArray)
+ {
+ suffix += "Array";
+ }
}
- }
- ModuleBuilder moduleBuilder = CodeGenerator.CreateModuleBuilder(assemblyBuilder, assemblyName);
+ ModuleBuilder moduleBuilder = CodeGenerator.CreateModuleBuilder(assemblyBuilder, assemblyName);
- string writerClass = "XmlSerializationWriter" + suffix;
- writerClass = classes.AddUnique(writerClass, writerClass);
- XmlSerializationWriterILGen writerCodeGen = new XmlSerializationWriterILGen(scopes, "public", writerClass);
- writerCodeGen.ModuleBuilder = moduleBuilder;
+ string writerClass = "XmlSerializationWriter" + suffix;
+ writerClass = classes.AddUnique(writerClass, writerClass);
+ XmlSerializationWriterILGen writerCodeGen = new XmlSerializationWriterILGen(scopes, "public", writerClass);
+ writerCodeGen.ModuleBuilder = moduleBuilder;
- writerCodeGen.GenerateBegin();
- string[] writeMethodNames = new string[xmlMappings.Length];
+ writerCodeGen.GenerateBegin();
+ string[] writeMethodNames = new string[xmlMappings.Length];
- for (int i = 0; i < xmlMappings.Length; i++)
- {
- writeMethodNames[i] = writerCodeGen.GenerateElement(xmlMappings[i])!;
- }
- Type writerType = writerCodeGen.GenerateEnd();
+ for (int i = 0; i < xmlMappings.Length; i++)
+ {
+ writeMethodNames[i] = writerCodeGen.GenerateElement(xmlMappings[i])!;
+ }
+ Type writerType = writerCodeGen.GenerateEnd();
- string readerClass = "XmlSerializationReader" + suffix;
- readerClass = classes.AddUnique(readerClass, readerClass);
- XmlSerializationReaderILGen readerCodeGen = new XmlSerializationReaderILGen(scopes, "public", readerClass);
+ string readerClass = "XmlSerializationReader" + suffix;
+ readerClass = classes.AddUnique(readerClass, readerClass);
+ XmlSerializationReaderILGen readerCodeGen = new XmlSerializationReaderILGen(scopes, "public", readerClass);
- readerCodeGen.ModuleBuilder = moduleBuilder;
- readerCodeGen.CreatedTypes.Add(writerType.Name, writerType);
+ readerCodeGen.ModuleBuilder = moduleBuilder;
+ readerCodeGen.CreatedTypes.Add(writerType.Name, writerType);
- readerCodeGen.GenerateBegin();
- string[] readMethodNames = new string[xmlMappings.Length];
- for (int i = 0; i < xmlMappings.Length; i++)
- {
- readMethodNames[i] = readerCodeGen.GenerateElement(xmlMappings[i])!;
- }
- readerCodeGen.GenerateEnd(readMethodNames, xmlMappings, types!);
+ readerCodeGen.GenerateBegin();
+ string[] readMethodNames = new string[xmlMappings.Length];
+ for (int i = 0; i < xmlMappings.Length; i++)
+ {
+ readMethodNames[i] = readerCodeGen.GenerateElement(xmlMappings[i])!;
+ }
+ readerCodeGen.GenerateEnd(readMethodNames, xmlMappings, types!);
- string baseSerializer = readerCodeGen.GenerateBaseSerializer("XmlSerializer1", readerClass, writerClass, classes);
- var serializers = new Dictionary();
- for (int i = 0; i < xmlMappings.Length; i++)
- {
- if (!serializers.ContainsKey(xmlMappings[i].Key!))
+ string baseSerializer = readerCodeGen.GenerateBaseSerializer("XmlSerializer1", readerClass, writerClass, classes);
+ var serializers = new Dictionary();
+ for (int i = 0; i < xmlMappings.Length; i++)
{
- serializers[xmlMappings[i].Key!] = readerCodeGen.GenerateTypedSerializer(readMethodNames[i], writeMethodNames[i], xmlMappings[i], classes, baseSerializer, readerClass, writerClass);
+ if (!serializers.ContainsKey(xmlMappings[i].Key!))
+ {
+ serializers[xmlMappings[i].Key!] = readerCodeGen.GenerateTypedSerializer(readMethodNames[i], writeMethodNames[i], xmlMappings[i], classes, baseSerializer, readerClass, writerClass);
+ }
}
- }
- readerCodeGen.GenerateSerializerContract("XmlSerializerContract", xmlMappings, types!, readerClass, readMethodNames, writerClass, writeMethodNames, serializers);
+ readerCodeGen.GenerateSerializerContract("XmlSerializerContract", xmlMappings, types!, readerClass, readMethodNames, writerClass, writeMethodNames, serializers);
- return writerType.Assembly;
+ return writerType.Assembly;
+ }
}
private static MethodInfo GetMethodFromType(
@@ -588,6 +605,23 @@ internal bool CanRead(XmlMapping mapping, XmlReader xmlReader)
return encodingStyle;
}
+ internal static void VerifyLoadContext(Type? t, Assembly? assembly)
+ {
+ // The quick case, t is null or in the same assembly
+ if (t == null || assembly == null || t.Assembly == assembly)
+ return;
+
+ // No worries if the type is not collectible
+ var typeALC = AssemblyLoadContext.GetLoadContext(t.Assembly);
+ if (typeALC == null || !typeALC.IsCollectible)
+ return;
+
+ // Collectible types should be in the same collectible context
+ var baseALC = AssemblyLoadContext.GetLoadContext(assembly) ?? AssemblyLoadContext.CurrentContextualReflectionContext;
+ if (typeALC != baseALC)
+ throw new InvalidOperationException(SR.Format(SR.XmlTypeInBadLoadContext, t.FullName));
+ }
+
[RequiresUnreferencedCode("calls Contract")]
internal object? InvokeReader(XmlMapping mapping, XmlReader xmlReader, XmlDeserializationEvents events, string? encodingStyle)
{
@@ -666,9 +700,9 @@ internal sealed class TempMethodDictionary : Dictionary
internal sealed class TempAssemblyCacheKey
{
private readonly string? _ns;
- private readonly object _type;
+ private readonly Type _type;
- internal TempAssemblyCacheKey(string? ns, object type)
+ internal TempAssemblyCacheKey(string? ns, Type type)
{
_type = type;
_ns = ns;
@@ -690,29 +724,52 @@ public override int GetHashCode()
internal sealed class TempAssemblyCache
{
- private Dictionary _cache = new Dictionary();
+ private Dictionary _fastCache = new Dictionary();
+ private ConditionalWeakTable> _collectibleCaches = new ConditionalWeakTable>();
- internal TempAssembly? this[string? ns, object o]
+ internal TempAssembly? this[string? ns, Type t]
{
get
{
TempAssembly? tempAssembly;
- _cache.TryGetValue(new TempAssemblyCacheKey(ns, o), out tempAssembly);
+ TempAssemblyCacheKey key = new TempAssemblyCacheKey(ns, t);
+
+ if (_fastCache.TryGetValue(key, out tempAssembly))
+ return tempAssembly;
+
+ if (_collectibleCaches.TryGetValue(t.Assembly, out var cCache))
+ cCache.TryGetValue(key, out tempAssembly);
+
return tempAssembly;
}
}
- internal void Add(string? ns, object o, TempAssembly assembly)
+ internal void Add(string? ns, Type t, TempAssembly assembly)
{
- TempAssemblyCacheKey key = new TempAssemblyCacheKey(ns, o);
lock (this)
{
- TempAssembly? tempAssembly;
- if (_cache.TryGetValue(key, out tempAssembly) && tempAssembly == assembly)
+ TempAssembly? tempAssembly = this[ns, t];
+ if (tempAssembly == assembly)
return;
- Dictionary _copy = new Dictionary(_cache); // clone
- _copy[key] = assembly;
- _cache = _copy;
+
+ AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(t.Assembly);
+ TempAssemblyCacheKey key = new TempAssemblyCacheKey(ns, t);
+ Dictionary? cache;
+
+ if (alc != null && alc.IsCollectible)
+ {
+ cache = _collectibleCaches.TryGetValue(t.Assembly, out var c) // Clone or create
+ ? new Dictionary(c)
+ : new Dictionary();
+ cache[key] = assembly;
+ _collectibleCaches.AddOrUpdate(t.Assembly, cache);
+ }
+ else
+ {
+ cache = new Dictionary(_fastCache); // Clone
+ cache[key] = assembly;
+ _fastCache = cache;
+ }
}
}
}
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ContextAwareTables.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ContextAwareTables.cs
new file mode 100644
index 0000000000000..64e09bbc063b2
--- /dev/null
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ContextAwareTables.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Xml.Serialization
+{
+ using System;
+ using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.Loader;
+
+ internal class ContextAwareTables<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T> where T : class?
+ {
+ private Hashtable _defaultTable;
+ private ConditionalWeakTable _collectibleTable;
+
+ public ContextAwareTables()
+ {
+ _defaultTable = new Hashtable();
+ _collectibleTable = new ConditionalWeakTable();
+ }
+
+ internal T GetOrCreateValue(Type t, Func f)
+ {
+ // The fast and most common default case
+ T? ret = (T?)_defaultTable[t];
+ if (ret != null)
+ return ret;
+
+ // Common case for collectible contexts
+ if (_collectibleTable.TryGetValue(t, out ret))
+ return ret;
+
+ // Not found. Do the slower work of creating the value in the correct collection.
+ AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(t.Assembly);
+
+ // Null and non-collectible load contexts use the default table
+ if (alc == null || !alc.IsCollectible)
+ {
+ lock (_defaultTable)
+ {
+ if ((ret = (T?)_defaultTable[t]) == null)
+ {
+ ret = f();
+ _defaultTable[t] = ret;
+ }
+ }
+ }
+
+ // Collectible load contexts should use the ConditionalWeakTable so they can be unloaded
+ else
+ {
+ lock (_collectibleTable)
+ {
+ if (!_collectibleTable.TryGetValue(t, out ret))
+ {
+ ret = f();
+ _collectibleTable.AddOrUpdate(t, ret);
+ }
+ }
+ }
+
+ return ret;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs
index 0c55638a27a66..2477f283b01f1 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs
@@ -627,40 +627,48 @@ private static void AddObjectsIntoTargetCollection(object targetCollection, List
}
}
- private static readonly ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate> s_setMemberValueDelegateCache = new ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>();
+ private static readonly ContextAwareTables s_setMemberValueDelegateCache = new ContextAwareTables();
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
private static ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate GetSetMemberValueDelegate(object o, string memberName)
{
Debug.Assert(o != null, "Object o should not be null");
Debug.Assert(!string.IsNullOrEmpty(memberName), "memberName must have a value");
- (Type, string) typeMemberNameTuple = (o.GetType(), memberName);
- if (!s_setMemberValueDelegateCache.TryGetValue(typeMemberNameTuple, out ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate? result))
+ Type type = o.GetType();
+ var delegateCacheForType = s_setMemberValueDelegateCache.GetOrCreateValue(type, () => new Hashtable());
+ var result = delegateCacheForType[memberName];
+ if (result == null)
{
- MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName);
- Debug.Assert(memberInfo != null, "memberInfo could not be retrieved");
- Type memberType;
- if (memberInfo is PropertyInfo propInfo)
- {
- memberType = propInfo.PropertyType;
- }
- else if (memberInfo is FieldInfo fieldInfo)
- {
- memberType = fieldInfo.FieldType;
- }
- else
+ lock (delegateCacheForType)
{
- throw new InvalidOperationException(SR.XmlInternalError);
- }
+ if ((result = delegateCacheForType[memberName]) == null)
+ {
+ MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName);
+ Debug.Assert(memberInfo != null, "memberInfo could not be retrieved");
+ Type memberType;
+ if (memberInfo is PropertyInfo propInfo)
+ {
+ memberType = propInfo.PropertyType;
+ }
+ else if (memberInfo is FieldInfo fieldInfo)
+ {
+ memberType = fieldInfo.FieldType;
+ }
+ else
+ {
+ throw new InvalidOperationException(SR.XmlInternalError);
+ }
- MethodInfo getSetMemberValueDelegateWithTypeGenericMi = typeof(ReflectionXmlSerializationReaderHelper).GetMethod("GetSetMemberValueDelegateWithType", BindingFlags.Static | BindingFlags.Public)!;
- MethodInfo getSetMemberValueDelegateWithTypeMi = getSetMemberValueDelegateWithTypeGenericMi.MakeGenericMethod(o.GetType(), memberType);
- var getSetMemberValueDelegateWithType = (Func)getSetMemberValueDelegateWithTypeMi.CreateDelegate(typeof(Func));
- result = getSetMemberValueDelegateWithType(memberInfo);
- s_setMemberValueDelegateCache.TryAdd(typeMemberNameTuple, result);
+ MethodInfo getSetMemberValueDelegateWithTypeGenericMi = typeof(ReflectionXmlSerializationReaderHelper).GetMethod("GetSetMemberValueDelegateWithType", BindingFlags.Static | BindingFlags.Public)!;
+ MethodInfo getSetMemberValueDelegateWithTypeMi = getSetMemberValueDelegateWithTypeGenericMi.MakeGenericMethod(o.GetType(), memberType);
+ var getSetMemberValueDelegateWithType = (Func)getSetMemberValueDelegateWithTypeMi.CreateDelegate(typeof(Func));
+ result = getSetMemberValueDelegateWithType(memberInfo);
+ delegateCacheForType[memberName] = result;
+ }
+ }
}
- return result;
+ return (ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate)result;
}
private object? GetMemberValue(object o, MemberInfo memberInfo)
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs
index 46877f900e902..7ebc4462dfb7a 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs
@@ -19,6 +19,7 @@ namespace System.Xml.Serialization
using System.Xml.Serialization;
using System.Xml;
using System.Diagnostics.CodeAnalysis;
+ using System.Runtime.CompilerServices;
///
public abstract class XmlSerializationWriter : XmlSerializationGeneratedCode
@@ -1465,14 +1466,13 @@ internal static class DynamicAssemblies
{
private static readonly Hashtable s_nameToAssemblyMap = new Hashtable();
private static readonly Hashtable s_assemblyToNameMap = new Hashtable();
- private static readonly Hashtable s_tableIsTypeDynamic = Hashtable.Synchronized(new Hashtable());
+ private static readonly ContextAwareTables