Skip to content

Commit 27eb94b

Browse files
committed
Merge in 'release/6.0' changes
2 parents f528972 + 04a5836 commit 27eb94b

File tree

13 files changed

+418
-195
lines changed

13 files changed

+418
-195
lines changed

src/coreclr/vm/appdomain.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -3533,8 +3533,8 @@ DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions
35333533
!pManifestFile->IsResource() &&
35343534
pManifestFile->Equals(pFile))
35353535
{
3536-
// Caller already has PEAssembly, so we can give DomainAssembly away freely without AddRef
3537-
return pDomainAssembly.Extract();
3536+
// Caller already has PEAssembly, so we can give DomainAssembly away freely without added reference
3537+
return pDomainAssembly.GetValue();
35383538
}
35393539
}
35403540
return NULL;

src/libraries/Common/tests/System/Runtime/Serialization/Utils.cs

+29
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using System.Threading.Tasks;
1010
using System.Xml.Linq;
1111
using System.Linq;
12+
using System.Reflection;
13+
using System.Runtime.Loader;
1214
using Xunit;
1315

1416
internal static class Utils
@@ -351,3 +353,30 @@ private static bool IsPrefixedAttributeValue(string atrValue, out string localPr
351353
return false;
352354
}
353355
}
356+
357+
internal class TestAssemblyLoadContext : AssemblyLoadContext
358+
{
359+
private AssemblyDependencyResolver _resolver;
360+
361+
public TestAssemblyLoadContext(string name, bool isCollectible, string mainAssemblyToLoadPath = null) : base(name, isCollectible)
362+
{
363+
if (!PlatformDetection.IsBrowser)
364+
_resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath ?? Assembly.GetExecutingAssembly().Location);
365+
}
366+
367+
protected override Assembly Load(AssemblyName name)
368+
{
369+
if (PlatformDetection.IsBrowser)
370+
{
371+
return base.Load(name);
372+
}
373+
374+
string assemblyPath = _resolver.ResolveAssemblyToPath(name);
375+
if (assemblyPath != null)
376+
{
377+
return LoadFromAssemblyPath(assemblyPath);
378+
}
379+
380+
return null;
381+
}
382+
}

src/libraries/System.Private.Xml/src/Resources/Strings.resx

+3
Original file line numberDiff line numberDiff line change
@@ -2787,6 +2787,9 @@
27872787
<data name="XmlNotSerializable" xml:space="preserve">
27882788
<value>Type '{0}' is not serializable.</value>
27892789
</data>
2790+
<data name="XmlTypeInBadLoadContext" xml:space="preserve">
2791+
<value>Type '{0}' is from an AssemblyLoadContext which is incompatible with that which contains this XmlSerializer.</value>
2792+
</data>
27902793
<data name="XmlPregenInvalidXmlSerializerAssemblyAttribute" xml:space="preserve">
27912794
<value>Invalid XmlSerializerAssemblyAttribute usage. Please use {0} property or {1} property.</value>
27922795
</data>

src/libraries/System.Private.Xml/src/System.Private.Xml.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@
446446
<Compile Include="System\Xml\Serialization\CodeIdentifiers.cs" />
447447
<Compile Include="System\Xml\Serialization\Compilation.cs" />
448448
<Compile Include="System\Xml\Serialization\Compiler.cs" />
449+
<Compile Include="System\Xml\Serialization\ContextAwareTables.cs" />
449450
<Compile Include="System\Xml\Serialization\ImportContext.cs" />
450451
<Compile Include="System\Xml\Serialization\indentedWriter.cs" />
451452
<Compile Include="System\Xml\Serialization\IXmlSerializable.cs" />
@@ -565,6 +566,7 @@
565566
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
566567
<Reference Include="System.Runtime.Extensions" />
567568
<Reference Include="System.Runtime.InteropServices" />
569+
<Reference Include="System.Runtime.Loader" />
568570
<Reference Include="System.Security.Cryptography.Algorithms" />
569571
<Reference Include="System.Security.Cryptography.Primitives" />
570572
<Reference Include="System.Text.Encoding.Extensions" />

src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs

+183-126
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Xml.Serialization
5+
{
6+
using System;
7+
using System.Collections;
8+
using System.Diagnostics.CodeAnalysis;
9+
using System.Runtime.CompilerServices;
10+
using System.Runtime.Loader;
11+
12+
internal class ContextAwareTables<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T> where T : class?
13+
{
14+
private Hashtable _defaultTable;
15+
private ConditionalWeakTable<Type, T> _collectibleTable;
16+
17+
public ContextAwareTables()
18+
{
19+
_defaultTable = new Hashtable();
20+
_collectibleTable = new ConditionalWeakTable<Type, T>();
21+
}
22+
23+
internal T GetOrCreateValue(Type t, Func<T> f)
24+
{
25+
// The fast and most common default case
26+
T? ret = (T?)_defaultTable[t];
27+
if (ret != null)
28+
return ret;
29+
30+
// Common case for collectible contexts
31+
if (_collectibleTable.TryGetValue(t, out ret))
32+
return ret;
33+
34+
// Not found. Do the slower work of creating the value in the correct collection.
35+
AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(t.Assembly);
36+
37+
// Null and non-collectible load contexts use the default table
38+
if (alc == null || !alc.IsCollectible)
39+
{
40+
lock (_defaultTable)
41+
{
42+
if ((ret = (T?)_defaultTable[t]) == null)
43+
{
44+
ret = f();
45+
_defaultTable[t] = ret;
46+
}
47+
}
48+
}
49+
50+
// Collectible load contexts should use the ConditionalWeakTable so they can be unloaded
51+
else
52+
{
53+
lock (_collectibleTable)
54+
{
55+
if (!_collectibleTable.TryGetValue(t, out ret))
56+
{
57+
ret = f();
58+
_collectibleTable.AddOrUpdate(t, ret);
59+
}
60+
}
61+
}
62+
63+
return ret;
64+
}
65+
}
66+
}

src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs

+31-23
Original file line numberDiff line numberDiff line change
@@ -627,40 +627,48 @@ private static void AddObjectsIntoTargetCollection(object targetCollection, List
627627
}
628628
}
629629

630-
private static readonly ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate> s_setMemberValueDelegateCache = new ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>();
630+
private static readonly ContextAwareTables<Hashtable> s_setMemberValueDelegateCache = new ContextAwareTables<Hashtable>();
631631

632632
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
633633
private static ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate GetSetMemberValueDelegate(object o, string memberName)
634634
{
635635
Debug.Assert(o != null, "Object o should not be null");
636636
Debug.Assert(!string.IsNullOrEmpty(memberName), "memberName must have a value");
637-
(Type, string) typeMemberNameTuple = (o.GetType(), memberName);
638-
if (!s_setMemberValueDelegateCache.TryGetValue(typeMemberNameTuple, out ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate? result))
637+
Type type = o.GetType();
638+
var delegateCacheForType = s_setMemberValueDelegateCache.GetOrCreateValue(type, () => new Hashtable());
639+
var result = delegateCacheForType[memberName];
640+
if (result == null)
639641
{
640-
MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName);
641-
Debug.Assert(memberInfo != null, "memberInfo could not be retrieved");
642-
Type memberType;
643-
if (memberInfo is PropertyInfo propInfo)
644-
{
645-
memberType = propInfo.PropertyType;
646-
}
647-
else if (memberInfo is FieldInfo fieldInfo)
648-
{
649-
memberType = fieldInfo.FieldType;
650-
}
651-
else
642+
lock (delegateCacheForType)
652643
{
653-
throw new InvalidOperationException(SR.XmlInternalError);
654-
}
644+
if ((result = delegateCacheForType[memberName]) == null)
645+
{
646+
MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetEffectiveSetInfo(o.GetType(), memberName);
647+
Debug.Assert(memberInfo != null, "memberInfo could not be retrieved");
648+
Type memberType;
649+
if (memberInfo is PropertyInfo propInfo)
650+
{
651+
memberType = propInfo.PropertyType;
652+
}
653+
else if (memberInfo is FieldInfo fieldInfo)
654+
{
655+
memberType = fieldInfo.FieldType;
656+
}
657+
else
658+
{
659+
throw new InvalidOperationException(SR.XmlInternalError);
660+
}
655661

656-
MethodInfo getSetMemberValueDelegateWithTypeGenericMi = typeof(ReflectionXmlSerializationReaderHelper).GetMethod("GetSetMemberValueDelegateWithType", BindingFlags.Static | BindingFlags.Public)!;
657-
MethodInfo getSetMemberValueDelegateWithTypeMi = getSetMemberValueDelegateWithTypeGenericMi.MakeGenericMethod(o.GetType(), memberType);
658-
var getSetMemberValueDelegateWithType = (Func<MemberInfo, ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>)getSetMemberValueDelegateWithTypeMi.CreateDelegate(typeof(Func<MemberInfo, ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>));
659-
result = getSetMemberValueDelegateWithType(memberInfo);
660-
s_setMemberValueDelegateCache.TryAdd(typeMemberNameTuple, result);
662+
MethodInfo getSetMemberValueDelegateWithTypeGenericMi = typeof(ReflectionXmlSerializationReaderHelper).GetMethod("GetSetMemberValueDelegateWithType", BindingFlags.Static | BindingFlags.Public)!;
663+
MethodInfo getSetMemberValueDelegateWithTypeMi = getSetMemberValueDelegateWithTypeGenericMi.MakeGenericMethod(o.GetType(), memberType);
664+
var getSetMemberValueDelegateWithType = (Func<MemberInfo, ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>)getSetMemberValueDelegateWithTypeMi.CreateDelegate(typeof(Func<MemberInfo, ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>));
665+
result = getSetMemberValueDelegateWithType(memberInfo);
666+
delegateCacheForType[memberName] = result;
667+
}
668+
}
661669
}
662670

663-
return result;
671+
return (ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate)result;
664672
}
665673

666674
private object? GetMemberValue(object o, MemberInfo memberInfo)

src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace System.Xml.Serialization
1919
using System.Xml.Serialization;
2020
using System.Xml;
2121
using System.Diagnostics.CodeAnalysis;
22+
using System.Runtime.CompilerServices;
2223

2324
///<internalonly/>
2425
public abstract class XmlSerializationWriter : XmlSerializationGeneratedCode
@@ -1465,14 +1466,13 @@ internal static class DynamicAssemblies
14651466
{
14661467
private static readonly Hashtable s_nameToAssemblyMap = new Hashtable();
14671468
private static readonly Hashtable s_assemblyToNameMap = new Hashtable();
1468-
private static readonly Hashtable s_tableIsTypeDynamic = Hashtable.Synchronized(new Hashtable());
1469+
private static readonly ContextAwareTables<object> s_tableIsTypeDynamic = new ContextAwareTables<object>();
14691470

14701471
// SxS: This method does not take any resource name and does not expose any resources to the caller.
14711472
// It's OK to suppress the SxS warning.
14721473
internal static bool IsTypeDynamic(Type type)
14731474
{
1474-
object? oIsTypeDynamic = s_tableIsTypeDynamic[type];
1475-
if (oIsTypeDynamic == null)
1475+
object oIsTypeDynamic = s_tableIsTypeDynamic.GetOrCreateValue(type, () =>
14761476
{
14771477
Assembly assembly = type.Assembly;
14781478
bool isTypeDynamic = assembly.IsDynamic /*|| string.IsNullOrEmpty(assembly.Location)*/;
@@ -1500,8 +1500,8 @@ internal static bool IsTypeDynamic(Type type)
15001500
}
15011501
}
15021502
}
1503-
s_tableIsTypeDynamic[type] = oIsTypeDynamic = isTypeDynamic;
1504-
}
1503+
return isTypeDynamic;
1504+
});
15051505
return (bool)oIsTypeDynamic;
15061506
}
15071507

src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs

+28-32
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.IO;
1111
using System.Reflection;
1212
using System.Runtime.CompilerServices;
13+
using System.Runtime.Loader;
1314
using System.Runtime.Versioning;
1415
using System.Security;
1516
using System.Text;
@@ -161,8 +162,7 @@ private static XmlSerializerNamespaces DefaultNamespaces
161162
internal const string TrimSerializationWarning = "Members from serialized types may be trimmed if not referenced directly";
162163
private const string TrimDeserializationWarning = "Members from deserialized types may be trimmed if not referenced directly";
163164

164-
private static readonly Dictionary<Type, Dictionary<XmlSerializerMappingKey, XmlSerializer>> s_xmlSerializerTable = new Dictionary<Type, Dictionary<XmlSerializerMappingKey, XmlSerializer>>();
165-
165+
private static readonly ContextAwareTables<Dictionary<XmlSerializerMappingKey, XmlSerializer>> s_xmlSerializerTable = new ContextAwareTables<Dictionary<XmlSerializerMappingKey, XmlSerializer>>();
166166
protected XmlSerializer()
167167
{
168168
}
@@ -235,30 +235,28 @@ public XmlSerializer(Type type, string? defaultNamespace)
235235
_tempAssembly = s_cache[defaultNamespace, type];
236236
if (_tempAssembly == null)
237237
{
238+
XmlSerializerImplementation? contract = null;
239+
Assembly? assembly = TempAssembly.LoadGeneratedAssembly(type, defaultNamespace, out contract);
240+
if (assembly == null)
238241
{
239-
XmlSerializerImplementation? contract = null;
240-
Assembly? assembly = TempAssembly.LoadGeneratedAssembly(type, defaultNamespace, out contract);
241-
if (assembly == null)
242-
{
243-
if (Mode == SerializationMode.PreGenOnly)
244-
{
245-
AssemblyName name = type.Assembly.GetName();
246-
var serializerName = Compiler.GetTempAssemblyName(name, defaultNamespace);
247-
throw new FileLoadException(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName));
248-
}
249-
250-
// need to reflect and generate new serialization assembly
251-
XmlReflectionImporter importer = new XmlReflectionImporter(defaultNamespace);
252-
_mapping = importer.ImportTypeMapping(type, null, defaultNamespace);
253-
_tempAssembly = GenerateTempAssembly(_mapping, type, defaultNamespace)!;
254-
}
255-
else
242+
if (Mode == SerializationMode.PreGenOnly)
256243
{
257-
// we found the pre-generated assembly, now make sure that the assembly has the right serializer
258-
// try to avoid the reflection step, need to get ElementName, namespace and the Key form the type
259-
_mapping = XmlReflectionImporter.GetTopLevelMapping(type, defaultNamespace);
260-
_tempAssembly = new TempAssembly(new XmlMapping[] { _mapping }, assembly, contract);
244+
AssemblyName name = type.Assembly.GetName();
245+
var serializerName = Compiler.GetTempAssemblyName(name, defaultNamespace);
246+
throw new FileLoadException(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName));
261247
}
248+
249+
// need to reflect and generate new serialization assembly
250+
XmlReflectionImporter importer = new XmlReflectionImporter(defaultNamespace);
251+
_mapping = importer.ImportTypeMapping(type, null, defaultNamespace);
252+
_tempAssembly = GenerateTempAssembly(_mapping, type, defaultNamespace)!;
253+
}
254+
else
255+
{
256+
// we found the pre-generated assembly, now make sure that the assembly has the right serializer
257+
// try to avoid the reflection step, need to get ElementName, namespace and the Key form the type
258+
_mapping = XmlReflectionImporter.GetTopLevelMapping(type, defaultNamespace);
259+
_tempAssembly = new TempAssembly(new XmlMapping[] { _mapping }, assembly, contract);
262260
}
263261
}
264262
s_cache.Add(defaultNamespace, type, _tempAssembly);
@@ -403,7 +401,9 @@ public void Serialize(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? n
403401
}
404402
}
405403
else
404+
{
406405
_tempAssembly.InvokeWriter(_mapping, xmlWriter, o, namespaces == null || namespaces.Count == 0 ? DefaultNamespaces : namespaces, encodingStyle, id);
406+
}
407407
}
408408
catch (Exception? e)
409409
{
@@ -629,7 +629,10 @@ public static XmlSerializer[] FromMappings(XmlMapping[]? mappings, Type? type)
629629
{
630630
XmlSerializer[] serializers = new XmlSerializer[mappings.Length];
631631
for (int i = 0; i < serializers.Length; i++)
632+
{
632633
serializers[i] = (XmlSerializer)contract!.TypedSerializers[mappings[i].Key!]!;
634+
TempAssembly.VerifyLoadContext(serializers[i]._rootType, type!.Assembly);
635+
}
633636
return serializers;
634637
}
635638
}
@@ -696,16 +699,9 @@ internal static bool GenerateSerializer(Type[]? types, XmlMapping[] mappings, St
696699
private static XmlSerializer[] GetSerializersFromCache(XmlMapping[] mappings, Type type)
697700
{
698701
XmlSerializer?[] serializers = new XmlSerializer?[mappings.Length];
699-
700702
Dictionary<XmlSerializerMappingKey, XmlSerializer>? typedMappingTable = null;
701-
lock (s_xmlSerializerTable)
702-
{
703-
if (!s_xmlSerializerTable.TryGetValue(type, out typedMappingTable))
704-
{
705-
typedMappingTable = new Dictionary<XmlSerializerMappingKey, XmlSerializer>();
706-
s_xmlSerializerTable[type] = typedMappingTable;
707-
}
708-
}
703+
704+
typedMappingTable = s_xmlSerializerTable.GetOrCreateValue(type, () => new Dictionary<XmlSerializerMappingKey, XmlSerializer>());
709705

710706
lock (typedMappingTable)
711707
{

src/libraries/System.Private.Xml/tests/XmlSerializer/ReflectionOnly/System.Xml.XmlSerializer.ReflectionOnly.Tests.csproj

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
<DefineConstants>$(DefineConstants);ReflectionOnly</DefineConstants>
44
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
55
</PropertyGroup>
6+
<ItemGroup>
7+
<ProjectReference Include="..\..\..\..\Microsoft.XmlSerializer.Generator\tests\SerializableAssembly.csproj" />
8+
<TrimmerRootAssembly Include="SerializableAssembly" />
9+
</ItemGroup>
610
<ItemGroup>
711
<Compile Include="$(CommonTestPath)System\Runtime\Serialization\Utils.cs" />
8-
<Compile Include="$(TestSourceFolder)..\..\..\..\System.Runtime.Serialization.Xml\tests\SerializationTypes.cs" />
12+
<None Include="$(TestSourceFolder)..\..\..\..\System.Runtime.Serialization.Xml\tests\SerializationTypes.cs" />
913
<Compile Include="$(TestSourceFolder)..\..\..\..\System.Runtime.Serialization.Xml\tests\SerializationTypes.RuntimeOnly.cs" />
1014
<Compile Include="$(TestSourceFolder)..\XmlSerializerTests.cs" />
1115
<Compile Include="$(TestSourceFolder)..\XmlSerializerTests.RuntimeOnly.cs" />

0 commit comments

Comments
 (0)