|
// 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;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Security;
using System.Text;
using System.Threading;
namespace System.Xml.Serialization
{
internal sealed class TempAssembly
{
internal const string GeneratedAssemblyNamespace = "Microsoft.Xml.Serialization.GeneratedAssembly";
private readonly Assembly? _assembly;
private XmlSerializerImplementation? _contract;
private Hashtable? _writerMethods;
private Hashtable? _readerMethods;
private TempMethodDictionary? _methods;
internal sealed class TempMethod
{
internal MethodInfo? writeMethod;
internal MethodInfo? readMethod;
internal string? name;
internal string? ns;
internal bool isSoap;
internal string? methodKey;
}
private TempAssembly()
{
}
internal TempAssembly(XmlMapping[] xmlMappings, Assembly assembly, XmlSerializerImplementation? contract)
{
_assembly = assembly;
InitAssemblyMethods(xmlMappings);
_contract = contract;
}
[RequiresUnreferencedCode("calls GenerateRefEmitAssembly")]
[RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
internal TempAssembly(XmlMapping[] xmlMappings, Type?[] types, string? defaultNamespace, string? location)
{
bool containsSoapMapping = false;
for (int i = 0; i < xmlMappings.Length; i++)
{
xmlMappings[i].CheckShallow();
if (xmlMappings[i].IsSoap)
{
containsSoapMapping = true;
}
}
if (!containsSoapMapping && !TempAssembly.UseLegacySerializerGeneration)
{
try
{
_assembly = GenerateRefEmitAssembly(xmlMappings, types);
}
// Only catch and handle known failures with RefEmit
catch (CodeGeneratorConversionException ex)
{
// There is no CSharp-generating/compiling fallback in .Net Core because compilers are not part of the runtime.
// Instead of throwing a PNSE as a result of trying this "fallback" which doesn't exist, lets just let the
// original error bubble up.
//fallbackToCSharpAssemblyGeneration = true;
throw new InvalidOperationException(ex.Message, ex);
}
// Add other known exceptions here...
//
}
else
{
throw new PlatformNotSupportedException(SR.CompilingScriptsNotSupported);
}
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (_assembly == null)
throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Failed to generate XmlSerializer assembly, but did not throw"));
#endif
InitAssemblyMethods(xmlMappings);
}
internal static bool UseLegacySerializerGeneration
{
get
{
return false;
}
}
internal XmlSerializerImplementation Contract
{
[RequiresUnreferencedCode("calls GetTypeFromAssembly")]
get => _contract ??= (XmlSerializerImplementation)Activator.CreateInstance(GetTypeFromAssembly(_assembly!, "XmlSerializerContract"))!;
}
internal void InitAssemblyMethods(XmlMapping[] xmlMappings)
{
_methods = new TempMethodDictionary();
for (int i = 0; i < xmlMappings.Length; i++)
{
TempMethod method = new TempMethod();
method.isSoap = xmlMappings[i].IsSoap;
method.methodKey = xmlMappings[i].Key;
XmlTypeMapping? xmlTypeMapping = xmlMappings[i] as XmlTypeMapping;
if (xmlTypeMapping != null)
{
method.name = xmlTypeMapping.ElementName;
method.ns = xmlTypeMapping.Namespace;
}
_methods.Add(xmlMappings[i].Key!, method);
}
}
/// <devdoc>
/// <para>
/// Attempts to load pre-generated serialization assembly.
/// First check for the [XmlSerializerAssembly] attribute
/// </para>
/// </devdoc>
// SxS: This method does not take any resource name and does not expose any resources to the caller.
// It's OK to suppress the SxS warning.
[RequiresUnreferencedCode("calls LoadFrom")]
[UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
Justification = "Annotating this as dangerous will make the core of the serializer to be marked as not safe, instead " +
"this pattern is only dangerous if using sgen only. See https://github.com/dotnet/runtime/issues/50820")]
internal static Assembly? LoadGeneratedAssembly(Type type, string? defaultNamespace, out XmlSerializerImplementation? contract)
{
Assembly? serializer = null;
contract = null;
string? serializerName;
using (AssemblyLoadContext.EnterContextualReflection(type.Assembly))
{
// 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.CultureInfo = CultureInfo.InvariantCulture;
try
{
serializer = Assembly.Load(name);
}
catch (Exception e)
{
if (e is OutOfMemoryException)
{
throw;
}
}
serializer ??= LoadAssemblyByPath(type, serializerName);
if (serializer == null)
{
if (XmlSerializer.Mode == SerializationMode.PreGenOnly)
{
throw new Exception(SR.Format(SR.FailLoadAssemblyUnderPregenMode, serializerName));
}
return null;
}
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"));
// 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;
}
return null;
}
[RequiresUnreferencedCode("calls LoadFile")]
[UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
Justification = "Annotating this as dangerous will make the core of the serializer to be marked as not safe, instead " +
"this pattern is only dangerous if using sgen only. See https://github.com/dotnet/runtime/issues/50820")]
private static Assembly? LoadAssemblyByPath(Type type, string assemblyName)
{
Assembly? assembly = null;
string? path = null;
try
{
if (!string.IsNullOrEmpty(type.Assembly.Location))
{
path = Path.Combine(Path.GetDirectoryName(type.Assembly.Location)!, $"{assemblyName}.dll");
}
if ((string.IsNullOrEmpty(path) || !File.Exists(path)) && !string.IsNullOrEmpty(Assembly.GetEntryAssembly()?.Location))
{
path = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, $"{assemblyName}.dll");
}
if ((string.IsNullOrEmpty(path) || !File.Exists(path)) && !string.IsNullOrEmpty(AppContext.BaseDirectory))
{
path = Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory)!, $"{assemblyName}.dll");
}
if (!string.IsNullOrEmpty(path))
{
assembly = Assembly.LoadFile(path);
}
}
catch (Exception e)
{
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException)
{
throw;
}
}
return assembly;
}
private static bool IsSerializerVersionMatch(Assembly serializer, Type type, string? defaultNamespace)
{
if (serializer == null)
return false;
object[] attrs = serializer.GetCustomAttributes(typeof(XmlSerializerVersionAttribute), false);
if (attrs.Length != 1)
return false;
XmlSerializerVersionAttribute assemblyInfo = (XmlSerializerVersionAttribute)attrs[0];
if (assemblyInfo.ParentAssemblyId == GenerateAssemblyId(type) && assemblyInfo.Namespace == defaultNamespace)
return true;
return false;
}
private static string GenerateAssemblyId(Type type)
{
Module[] modules = type.Assembly.GetModules();
var list = new ArrayList();
for (int i = 0; i < modules.Length; i++)
{
list.Add(modules[i].ModuleVersionId.ToString());
}
list.Sort();
var sb = new StringBuilder();
for (int i = 0; i < list.Count; i++)
{
sb.Append(list[i]!.ToString());
sb.Append(',');
}
return sb.ToString();
}
[RequiresUnreferencedCode("calls GenerateBegin")]
internal static bool GenerateSerializerToStream(XmlMapping[] xmlMappings, Type?[] types, string? defaultNamespace, Assembly? assembly, Hashtable assemblies, Stream stream)
{
var compiler = new Compiler();
var scopeTable = new Hashtable();
foreach (XmlMapping mapping in xmlMappings)
scopeTable[mapping.Scope!] = mapping;
var scopes = new TypeScope[scopeTable.Keys.Count];
scopeTable.Keys.CopyTo(scopes, 0);
assemblies.Clear();
var importedTypes = new Hashtable();
foreach (TypeScope scope in scopes)
{
foreach (Type t in scope.Types)
{
Compiler.AddImport(t, importedTypes);
Assembly a = t.Assembly;
string name = a.FullName!;
if (assemblies[name] != null)
{
continue;
}
assemblies[name] = a;
}
}
for (int i = 0; i < types.Length; i++)
{
Compiler.AddImport(types[i], importedTypes);
}
var writer = new IndentedWriter(compiler.Source, false);
writer.WriteLine("[assembly:System.Security.AllowPartiallyTrustedCallers()]");
writer.WriteLine("[assembly:System.Security.SecurityTransparent()]");
writer.WriteLine("[assembly:System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)]");
if (assembly != null && types.Length > 0)
{
for (int i = 0; i < types.Length; i++)
{
Type? type = types[i];
if (type == null)
{
continue;
}
if (DynamicAssemblies.IsTypeDynamic(type))
{
throw new InvalidOperationException(SR.Format(SR.XmlPregenTypeDynamic, types[i]!.FullName));
}
}
writer.Write("[assembly:");
writer.Write(typeof(XmlSerializerVersionAttribute).FullName);
writer.Write("(");
writer.Write("ParentAssemblyId=");
ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, GenerateAssemblyId(types[0]!));
writer.Write(", Version=");
ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, ThisAssembly.Version);
if (defaultNamespace != null)
{
writer.Write(", Namespace=");
ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, defaultNamespace);
}
writer.WriteLine(")]");
}
var 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)
{
suffix += "Array";
}
}
writer.WriteLine($"namespace {GeneratedAssemblyNamespace} {{");
writer.Indent++;
writer.WriteLine();
string writerClass = $"XmlSerializationWriter{suffix}";
writerClass = classes.AddUnique(writerClass, writerClass);
var writerCodeGen = new XmlSerializationWriterCodeGen(writer, scopes, "public", writerClass);
writerCodeGen.GenerateBegin();
string?[] writeMethodNames = new string[xmlMappings.Length];
for (int i = 0; i < xmlMappings.Length; i++)
{
writeMethodNames[i] = writerCodeGen.GenerateElement(xmlMappings[i]);
}
writerCodeGen.GenerateEnd();
writer.WriteLine();
string readerClass = $"XmlSerializationReader{suffix}";
readerClass = classes.AddUnique(readerClass, readerClass);
var readerCodeGen = new XmlSerializationReaderCodeGen(writer, scopes, "public", readerClass);
readerCodeGen.GenerateBegin();
string?[] readMethodNames = new string[xmlMappings.Length];
for (int i = 0; i < xmlMappings.Length; i++)
{
readMethodNames[i] = readerCodeGen.GenerateElement(xmlMappings[i])!;
}
readerCodeGen.GenerateEnd();
string baseSerializer = readerCodeGen.GenerateBaseSerializer("XmlSerializer1", readerClass, writerClass, classes);
var serializers = new Hashtable();
for (int i = 0; i < xmlMappings.Length; i++)
{
if (serializers[xmlMappings[i].Key!] == null)
{
serializers[xmlMappings[i].Key!] = readerCodeGen.GenerateTypedSerializer(readMethodNames[i], writeMethodNames[i], xmlMappings[i], classes, baseSerializer, readerClass, writerClass);
}
}
readerCodeGen.GenerateSerializerContract(xmlMappings, types!, readerClass, readMethodNames, writerClass, writeMethodNames, serializers);
writer.Indent--;
writer.WriteLine("}");
string codecontent = compiler.Source.ToString()!;
byte[] info = new UTF8Encoding(true).GetBytes(codecontent);
stream.Write(info, 0, info.Length);
stream.Flush();
return true;
}
[RequiresUnreferencedCode("calls GenerateElement")]
[RequiresDynamicCode(XmlSerializer.AotSerializationWarning)]
internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type?[] types)
{
var mainType = (types.Length > 0) ? types[0] : null;
Assembly? mainAssembly = mainType?.Assembly;
var scopeTable = new Dictionary<TypeScope, XmlMapping>();
foreach (XmlMapping mapping in xmlMappings)
scopeTable[mapping.Scope!] = mapping;
TypeScope[] scopes = new TypeScope[scopeTable.Keys.Count];
scopeTable.Keys.CopyTo(scopes, 0);
using (AssemblyLoadContext.EnterContextualReflection(mainAssembly))
{
// 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 = CodeIdentifier.MakeValid(mainType.Name);
if (mainType.IsArray)
{
suffix += "Array";
}
}
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;
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();
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.GenerateBegin();
string[] readMethodNames = new string[xmlMappings.Length];
for (int i = 0; i < xmlMappings.Length; i++)
{
readMethodNames[i] = readerCodeGen.GenerateElement(xmlMappings[i])!;
}
readerCodeGen.GenerateEnd();
string baseSerializer = readerCodeGen.GenerateBaseSerializer("XmlSerializer1", readerClass, writerClass, classes);
var serializers = new Dictionary<string, string>();
for (int i = 0; i < xmlMappings.Length; i++)
{
if (!serializers.ContainsKey(xmlMappings[i].Key!))
{
serializers[xmlMappings[i].Key!] = readerCodeGen.GenerateTypedSerializer(readMethodNames[i], writeMethodNames[i], xmlMappings[i], classes, baseSerializer, readerClass, writerClass);
}
}
readerCodeGen.GenerateSerializerContract(xmlMappings, types!, readerClass, readMethodNames, writerClass, writeMethodNames, serializers);
return writerType.Assembly;
}
}
private static MethodInfo GetMethodFromType(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string methodName)
{
MethodInfo? method = type.GetMethod(methodName);
if (method != null)
return method;
// Not support pregen. Workaround SecurityCritical required for assembly.CodeBase api.
MissingMethodException missingMethod = new MissingMethodException($"{type.FullName}::{methodName}");
throw missingMethod;
}
[RequiresUnreferencedCode("calls GetType")]
internal static Type GetTypeFromAssembly(Assembly assembly, string typeName)
{
typeName = $"{GeneratedAssemblyNamespace}.{typeName}";
Type? type = assembly.GetType(typeName);
if (type == null)
throw new InvalidOperationException(SR.Format(SR.XmlMissingType, typeName, assembly.FullName));
return type;
}
internal bool CanRead(XmlMapping mapping, XmlReader xmlReader)
{
if (mapping == null)
return false;
if (mapping.Accessor.Any)
{
return true;
}
TempMethod method = _methods![mapping.Key!];
return xmlReader.IsStartElement(method.name!, method.ns!);
}
[return: NotNullIfNotNull(nameof(encodingStyle))]
private string? ValidateEncodingStyle(string? encodingStyle, string methodKey)
{
if (encodingStyle != null && encodingStyle.Length > 0)
{
if (_methods![methodKey].isSoap)
{
if (encodingStyle != Soap.Encoding && encodingStyle != Soap12.Encoding)
{
throw new InvalidOperationException(SR.Format(SR.XmlInvalidEncoding3, encodingStyle, Soap.Encoding, Soap12.Encoding));
}
}
else
{
throw new InvalidOperationException(SR.Format(SR.XmlInvalidEncodingNotEncoded1, encodingStyle));
}
}
else
{
if (_methods![methodKey].isSoap)
{
encodingStyle = Soap.Encoding;
}
}
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)
{
XmlSerializationReader? reader;
try
{
encodingStyle = ValidateEncodingStyle(encodingStyle, mapping.Key!);
reader = Contract.Reader;
reader.Init(xmlReader, events, encodingStyle);
if (_methods![mapping.Key!].readMethod == null)
{
_readerMethods ??= Contract.ReadMethods;
string? methodName = (string?)_readerMethods[mapping.Key!];
if (methodName == null)
{
throw new InvalidOperationException(SR.Format(SR.XmlNotSerializable, mapping.Accessor.Name));
}
_methods[mapping.Key!].readMethod = GetMethodFromType(reader.GetType(), methodName);
}
return _methods[mapping.Key!].readMethod!.Invoke(reader, Array.Empty<object>());
}
catch (SecurityException e)
{
throw new InvalidOperationException(SR.XmlNoPartialTrust, e);
}
}
[RequiresUnreferencedCode("calls Contract")]
internal void InvokeWriter(XmlMapping mapping, XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? id)
{
XmlSerializationWriter? writer;
try
{
encodingStyle = ValidateEncodingStyle(encodingStyle, mapping.Key!);
writer = Contract.Writer;
writer.Init(xmlWriter, namespaces, encodingStyle, id);
if (_methods![mapping.Key!].writeMethod == null)
{
_writerMethods ??= Contract.WriteMethods;
string? methodName = (string?)_writerMethods[mapping.Key!];
if (methodName == null)
{
throw new InvalidOperationException(SR.Format(SR.XmlNotSerializable, mapping.Accessor.Name));
}
_methods[mapping.Key!].writeMethod = GetMethodFromType(writer.GetType(), methodName);
}
_methods[mapping.Key!].writeMethod!.Invoke(writer, new object?[] { o });
}
catch (SecurityException e)
{
throw new InvalidOperationException(SR.XmlNoPartialTrust, e);
}
}
internal sealed class TempMethodDictionary : Dictionary<string, TempMethod>
{
}
}
internal sealed class TempAssemblyCacheKey
{
private readonly string? _ns;
private readonly Type _type;
internal TempAssemblyCacheKey(string? ns, Type type)
{
_type = type;
_ns = ns;
}
public override bool Equals([NotNullWhen(true)] object? o)
{
TempAssemblyCacheKey? key = o as TempAssemblyCacheKey;
if (key == null)
return false;
return (key._type == _type && key._ns == _ns);
}
public override int GetHashCode()
{
return ((_ns != null ? _ns.GetHashCode() : 0) ^ (_type != null ? _type.GetHashCode() : 0));
}
}
internal sealed class TempAssemblyCache
{
private readonly ConditionalWeakTable<Assembly, Dictionary<TempAssemblyCacheKey, TempAssembly>> _collectibleCaches = new ConditionalWeakTable<Assembly, Dictionary<TempAssemblyCacheKey, TempAssembly>>();
private Dictionary<TempAssemblyCacheKey, TempAssembly> _fastCache = new Dictionary<TempAssemblyCacheKey, TempAssembly>();
internal TempAssembly? this[string? ns, Type t]
{
get
{
TempAssembly? 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, Type t, TempAssembly assembly)
{
lock (this)
{
TempAssembly? tempAssembly = this[ns, t];
if (tempAssembly == assembly)
return;
AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(t.Assembly);
TempAssemblyCacheKey key = new TempAssemblyCacheKey(ns, t);
Dictionary<TempAssemblyCacheKey, TempAssembly>? cache;
if (alc != null && alc.IsCollectible)
{
cache = _collectibleCaches.TryGetValue(t.Assembly, out var c) // Clone or create
? new Dictionary<TempAssemblyCacheKey, TempAssembly>(c)
: new Dictionary<TempAssemblyCacheKey, TempAssembly>();
cache[key] = assembly;
_collectibleCaches.AddOrUpdate(t.Assembly, cache);
}
else
{
cache = new Dictionary<TempAssemblyCacheKey, TempAssembly>(_fastCache); // Clone
cache[key] = assembly;
_fastCache = cache;
}
}
}
}
internal static class ThisAssembly
{
internal const string Version = "1.0.0.0";
internal const string InformationalVersion = "1.0.0.0";
}
}
|