/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; namespace dnSpy.Debugger.DotNet.Metadata.Impl { sealed class DmdAppDomainImpl : DmdAppDomain { sealed private protected override void YouCantDeriveFromThisClass() => throw new InvalidOperationException(); public override DmdRuntime Runtime => runtime; public override int Id { get; } public override DmdAssembly? CorLib { get { lock (assembliesLockObj) { // Assume that the first assembly is always the corlib. This is documented in DmdAppDomain.CreateAssembly() var assemblies = this.assemblies; for (int i = 0; i < assemblies.Count; i++) { var asm = assemblies[i]; if (!asm.IsSynthetic) return asm; } return null; } } } internal AssemblyNameEqualityComparer AssemblyNameEqualityComparer => assemblyNameEqualityComparer; readonly AssemblyNameEqualityComparer assemblyNameEqualityComparer; // Assemblies lock fields readonly object assembliesLockObj; readonly List assemblies; readonly Dictionary simpleNameToAssembly; readonly Dictionary assemblyNameToAssembly; readonly List assemblyLoadedListeners; // Fully resolved type lock fields readonly object fullyResolvedTypesLockObj; readonly Dictionary fullyResolvedTypes; // Module type lock fields readonly object moduleTypeLockObj; readonly Dictionary> toModuleTypeDict; readonly Dictionary> toModuleTypeDictIgnoreCase; // Exported type lock fields readonly object exportedTypeLockObj; readonly Dictionary> toModuleExportedTypeDict; readonly Dictionary> toModuleExportedTypeDictIgnoreCase; // No locks required readonly DmdRuntimeImpl runtime; readonly WellKnownMemberResolver wellKnownMemberResolver; const DmdSigComparerOptions moduleTypeOptions = DmdSigComparerOptions.DontCompareTypeScope; static readonly DmdMemberInfoEqualityComparer moduleTypeDictComparer = new DmdMemberInfoEqualityComparer(moduleTypeOptions); static readonly DmdMemberInfoEqualityComparer moduleTypeDictComparerIgnoreCase = new DmdMemberInfoEqualityComparer(moduleTypeOptions | DmdSigComparerOptions.CaseInsensitiveMemberNames); readonly Func metadataReaderFactory; public DmdAppDomainImpl(DmdRuntimeImpl runtime, int id) { assembliesLockObj = new object(); fullyResolvedTypesLockObj = new object(); moduleTypeLockObj = new object(); exportedTypeLockObj = new object(); assemblies = new List(); simpleNameToAssembly = new Dictionary(StringComparer.OrdinalIgnoreCase); // Ignore PKT, see comment in DmdSigComparer: bool Equals(IDmdAssemblyName a, IDmdAssemblyName b) assemblyNameEqualityComparer = new AssemblyNameEqualityComparer(ignorePublicKeyToken: true); assemblyNameToAssembly = new Dictionary(assemblyNameEqualityComparer); fullyResolvedTypes = new Dictionary(new DmdMemberInfoEqualityComparer(DmdSigComparerOptions.CompareDeclaringType | DmdSigComparerOptions.CompareCustomModifiers | DmdSigComparerOptions.CompareGenericParameterDeclaringMember)); toModuleTypeDict = new Dictionary>(); toModuleTypeDictIgnoreCase = new Dictionary>(); toModuleExportedTypeDict = new Dictionary>(); toModuleExportedTypeDictIgnoreCase = new Dictionary>(); wellKnownMemberResolver = new WellKnownMemberResolver(this); assemblyLoadedListeners = new List(); this.runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); metadataReaderFactory = CreateMetadataReader; Id = id; } DmdMetadataReader CreateMetadataReader(DmdModuleImpl module, DmdLazyMetadataBytes lzmd) { if (module is null) throw new ArgumentNullException(nameof(module)); if (lzmd is null) throw new ArgumentNullException(nameof(lzmd)); try { switch (lzmd) { case DmdLazyMetadataBytesPtr lzmdPtr: return MD.DmdEcma335MetadataReader.Create(module, lzmdPtr.Address, lzmdPtr.Size, lzmdPtr.IsFileLayout); case DmdLazyMetadataBytesArray lzmdArray: return MD.DmdEcma335MetadataReader.Create(module, lzmdArray.Bytes, lzmdArray.IsFileLayout); case DmdLazyMetadataBytesFile lzmdFile: return MD.DmdEcma335MetadataReader.Create(module, lzmdFile.Filename, lzmdFile.IsFileLayout); case DmdLazyMetadataBytesCom lzmdCom: return new COMD.DmdComMetadataReader(module, lzmdCom.MetaDataImport, lzmdCom.DynamicModuleHelper, lzmdCom.Dispatcher); } } catch { Debug.Fail("Failed to create metadata"); return new DmdNullMetadataReader(module); } throw new NotSupportedException($"Unknown lazy metadata: {lzmd.GetType()}"); } public override DmdAssembly CreateAssembly(Func getMetadata, DmdCreateAssemblyInfo assemblyInfo) { if (getMetadata is null) throw new ArgumentNullException(nameof(getMetadata)); if (assemblyInfo.FullyQualifiedName is null || assemblyInfo.AssemblyLocation is null) throw new ArgumentException(); var metadataReader = new DmdLazyMetadataReader(getMetadata, metadataReaderFactory); var options = assemblyInfo.Options; var assembly = new DmdAssemblyImpl(this, metadataReader, assemblyInfo.AssemblyLocation, assemblyInfo.AssemblySimpleName, (options & DmdCreateAssemblyOptions.IsEXE) != 0); var module = new DmdModuleImpl(assembly, metadataReader, (options & DmdCreateAssemblyOptions.InMemory) != 0, (options & DmdCreateAssemblyOptions.Dynamic) != 0, (options & DmdCreateAssemblyOptions.Synthetic) != 0, assemblyInfo.FullyQualifiedName); metadataReader.SetModule(module); assembly.Add(module); if ((options & DmdCreateAssemblyOptions.DontAddAssembly) == 0) Add(assembly); return assembly; } public override DmdModule CreateModule(DmdAssembly assembly, Func getMetadata, bool isInMemory, bool isDynamic, string fullyQualifiedName) { if (assembly is null) throw new ArgumentNullException(nameof(assembly)); if (getMetadata is null) throw new ArgumentNullException(nameof(getMetadata)); if (fullyQualifiedName is null) throw new ArgumentNullException(nameof(fullyQualifiedName)); var assemblyImpl = assembly as DmdAssemblyImpl; if (assemblyImpl is null) throw new ArgumentException(); var metadataReader = new DmdLazyMetadataReader(getMetadata, metadataReaderFactory); var module = new DmdModuleImpl(assemblyImpl, metadataReader, isInMemory, isDynamic, assemblyImpl.IsSynthetic, fullyQualifiedName); metadataReader.SetModule(module); assemblyImpl.Add(module); return module; } public override void Add(DmdAssembly assembly) { if (assembly is null) throw new ArgumentNullException(nameof(assembly)); if (assembly.AppDomain != this) throw new InvalidOperationException(); var assemblyImpl = assembly as DmdAssemblyImpl; if (assemblyImpl is null) throw new InvalidOperationException(); AssemblyLoadedListener[] listeners; lock (assembliesLockObj) { Debug.Assert(!assemblies.Contains(assemblyImpl)); assemblies.Add(assemblyImpl); assemblyImpl.IsLoadedInternal = true; listeners = assemblyLoadedListeners.Count == 0 ? Array.Empty() : assemblyLoadedListeners.ToArray(); } foreach (var listener in listeners) listener.AssemblyLoaded(assemblyImpl); } public override void Remove(DmdAssembly assembly) { if (assembly is null) throw new ArgumentNullException(nameof(assembly)); if (assembly.AppDomain != this) throw new InvalidOperationException(); var assemblyImpl = assembly as DmdAssemblyImpl; if (assemblyImpl is null) throw new InvalidOperationException(); lock (assembliesLockObj) { bool b = assemblies.Remove(assemblyImpl); Debug.Assert(b); assemblyImpl.IsLoadedInternal = false; simpleNameToAssembly.Remove(assemblyImpl.GetName().Name!); assemblyNameToAssembly.Remove(assemblyImpl.GetName()); } var modules = assemblyImpl.GetModules(); lock (moduleTypeLockObj) { foreach (var module in modules) { toModuleTypeDict.Remove(module); toModuleTypeDictIgnoreCase.Remove(module); } } lock (exportedTypeLockObj) { foreach (var module in modules) { toModuleExportedTypeDict.Remove(module); toModuleExportedTypeDictIgnoreCase.Remove(module); } } //TODO: Remove all its types from fullyResolvedTypes } internal bool GetIsLoaded(DmdAssemblyImpl assembly) { lock (assembliesLockObj) return assembly.IsLoadedInternal; } public override DmdAssembly[] GetAssemblies(bool includeSyntheticAssemblies) { lock (assembliesLockObj) { if (includeSyntheticAssemblies) return assemblies.ToArray(); int count = 0; foreach (var asm in assemblies) { if (!asm.IsSynthetic) count++; } var asms = new DmdAssemblyImpl[count]; int w = 0; foreach (var asm in assemblies) { if (!asm.IsSynthetic) asms[w++] = asm; } if (w != count) throw new InvalidOperationException(); return asms; } } public override DmdAssembly? GetAssembly(string simpleName) { if (simpleName is null) throw new ArgumentNullException(nameof(simpleName)); return GetAssemblyCore(simpleName, null); } public override DmdAssembly? GetAssembly(IDmdAssemblyName name) { if (name is null) throw new ArgumentNullException(nameof(name)); return GetAssemblyCore(name.Name ?? string.Empty, name); } static readonly Version zeroVersion = new Version(0, 0, 0, 0); DmdAssembly? GetAssemblyCore(string simpleName, IDmdAssemblyName? name) { bool onlySimpleName = name is null || ((name.Version is null || name.Version == zeroVersion) && string.IsNullOrEmpty(name.CultureName) && (name.GetPublicKeyToken() ?? Array.Empty()).Length == 0); if (onlySimpleName) name = null; DmdAssemblyImpl[] assembliesCopy; lock (assembliesLockObj) { if (name is not null) { if (assemblyNameToAssembly.TryGetValue(name, out var cached)) return cached; } else { if (simpleNameToAssembly.TryGetValue(simpleName, out var cached)) return cached; } assembliesCopy = assemblies.ToArray(); } var assembly = GetAssemblySlowCore(assembliesCopy, simpleName, name); if (assembly is not null) { lock (assembliesLockObj) { if (name is not null) { if (assemblyNameToAssembly.TryGetValue(name, out var cached)) return cached; assemblyNameToAssembly[name.AsReadOnly()] = assembly; } else { if (simpleNameToAssembly.TryGetValue(simpleName, out var cached)) return cached; simpleNameToAssembly[simpleName] = assembly; } } } else if (CanResolveToCorLib(name)) { // .NET hack. We don't resolve assemblies, but some attributes used by the // debugger reference types in an assembly that just forwards the type to the corlib. // This assembly isn't normally loaded at runtime. // We could resolve assemblies but then it's possible that we'll resolve the wrong // assembly. This is the only known case where we must resolve an assembly so, for now, // use this hack by just returning the corlib. return CorLib; } return assembly; } static bool CanResolveToCorLib(IDmdAssemblyName? name) { if (name is null) return false; if (!string.IsNullOrEmpty(name.CultureName)) return false; if (Equals(name.GetPublicKeyToken(), pkt)) { if (StringComparer.OrdinalIgnoreCase.Equals(name.Name, "System.Diagnostics.Debug")) return true; return false; } return false; } static readonly byte[] pkt = new byte[8] { 0xB0, 0x3F, 0x5F, 0x7F, 0x11, 0xD5, 0x0A, 0x3A }; static bool Equals(byte[]? a, byte[]? b) { if (a == b) return true; if (a is null || b is null) return false; if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } DmdAssemblyImpl? GetAssemblySlowCore(DmdAssemblyImpl[] assemblies, string simpleName, IDmdAssemblyName? name) { // Try to avoid reading the metadata in case we're debugging a program with lots of assemblies. // We first loop over all disk file assemblies since we can check simpleName without accessing metadata. foreach (var assembly in assemblies) { if (assembly.IsInMemory || assembly.IsDynamic) continue; if (!StringComparer.OrdinalIgnoreCase.Equals(simpleName, assembly.AssemblySimpleName)) continue; // Access metadata (when calling GetName()) if (name is null || AssemblyNameEqualityComparer.Equals(assembly.GetName(), name)) return assembly; } // Check all in-memory and dynamic assemblies. We need to read their metadata. foreach (var assembly in assemblies) { if (!(assembly.IsInMemory || assembly.IsDynamic || assembly.AssemblySimpleName == string.Empty)) continue; if (name is null) { if (StringComparer.OrdinalIgnoreCase.Equals(simpleName, assembly.GetName().Name)) return assembly; } else if (AssemblyNameEqualityComparer.Equals(assembly.GetName(), name)) return assembly; } return null; } DmdAssembly? GetAssemblyByPath(string path) { if (path is null) throw new ArgumentNullException(nameof(path)); lock (assembliesLockObj) { // We don't check synthetic assemblies, caller wants real assemblies foreach (var assembly in assemblies) { if (assembly.IsDynamic || assembly.IsInMemory || assembly.IsSynthetic) continue; if (StringComparer.OrdinalIgnoreCase.Equals(assembly.Location, path)) return assembly; } } return null; } sealed class AssemblyLoadedListener : IDisposable { readonly DmdAppDomainImpl owner; public List LoadedAssemblies { get; } public AssemblyLoadedListener(DmdAppDomainImpl owner) { this.owner = owner ?? throw new ArgumentNullException(nameof(owner)); LoadedAssemblies = new List(); owner.AddAssemblyLoadedListener(this); } public void AssemblyLoaded(DmdAssembly assembly) => LoadedAssemblies.Add(assembly); public void Dispose() => owner.RemoveAssemblyLoadedListener(this); } void AddAssemblyLoadedListener(AssemblyLoadedListener listener) { lock (assembliesLockObj) assemblyLoadedListeners.Add(listener); } void RemoveAssemblyLoadedListener(AssemblyLoadedListener listener) { lock (assembliesLockObj) assemblyLoadedListeners.Remove(listener); } public override DmdAssembly? Load(object? context, IDmdAssemblyName name) { if (name is null) throw new ArgumentNullException(nameof(name)); var asm = GetAssembly(name); if (asm is not null) return asm; var assemblyType = GetWellKnownType(DmdWellKnownType.System_Reflection_Assembly); var method = assemblyType.GetMethod("Load", assemblyType, new[] { System_String }, throwOnError: true)!; // Load an assembly and then try to figure out which one of the 0 or more loaded assemblies // is the one we want. This isn't guaranteed to succeed. using (var listener = new AssemblyLoadedListener(this)) { method.Invoke(context, null, new[] { name.ToString() }); // Dispose it so we can access its LoadedAssemblies prop listener.Dispose(); var asms = listener.LoadedAssemblies.Where(a => !a.IsSynthetic && StringComparer.OrdinalIgnoreCase.Equals(a.GetName().Name, name.Name)).ToArray(); if (asms.Length != 0) { if (asms.Length != 1) { foreach (var a in asms) { if (DmdMemberInfoEqualityComparer.DefaultOther.Equals(a.GetName(), name)) return a; } } return asms[0]; } // It probably failed to load return null; } } public override DmdAssembly? LoadFile(object? context, string path) { if (path is null) throw new ArgumentNullException(nameof(path)); var asm = GetAssemblyByPath(path); if (asm is not null) return asm; var assemblyType = GetWellKnownType(DmdWellKnownType.System_Reflection_Assembly); var method = assemblyType.GetMethod("LoadFile", assemblyType, new[] { System_String }, throwOnError: true)!; method.Invoke(context, null, new[] { path }); return GetAssemblyByPath(path); } public override DmdAssembly? LoadFrom(object? context, string assemblyFile) { if (assemblyFile is null) throw new ArgumentNullException(nameof(assemblyFile)); var name = new DmdReadOnlyAssemblyName(assemblyFile); var asm = GetAssembly(name) ?? GetAssemblyByPath(assemblyFile); if (asm is not null) return asm; var assemblyType = GetWellKnownType(DmdWellKnownType.System_Reflection_Assembly); var method = assemblyType.GetMethod("LoadFrom", assemblyType, new[] { System_String }, throwOnError: true)!; method.Invoke(context, null, new[] { name.ToString() }); return GetAssembly(name) ?? GetAssemblyByPath(assemblyFile); } internal override DmdType? GetWellKnownType(DmdWellKnownType wellKnownType, bool isOptional, bool onlyCorLib) { var type = wellKnownMemberResolver.GetWellKnownType(wellKnownType, onlyCorLib); if (type is null && !isOptional) throw new ResolveException("Couldn't resolve well known type: " + wellKnownType); return type; } public override DmdType Intern(DmdType type, DmdMakeTypeOptions options) { if (type is null) throw new ArgumentNullException(nameof(type)); if (type.AppDomain != this) throw new InvalidOperationException(); var res = type as DmdTypeBase ?? throw new ArgumentException(); if ((options & DmdMakeTypeOptions.NoResolve) == 0) res = res.FullResolve() ?? res; lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakePointerType(DmdType elementType, IList? customModifiers, DmdMakeTypeOptions options) { if (elementType is null) throw new ArgumentNullException(nameof(elementType)); if (elementType.AppDomain != this) throw new InvalidOperationException(); var et = elementType as DmdTypeBase; if (et is null) throw new ArgumentException(); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } if ((options & DmdMakeTypeOptions.NoResolve) == 0) et = et.FullResolve() ?? et; var res = new DmdPointerType(et, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeByRefType(DmdType elementType, IList? customModifiers, DmdMakeTypeOptions options) { if (elementType is null) throw new ArgumentNullException(nameof(elementType)); if (elementType.AppDomain != this) throw new InvalidOperationException(); var et = elementType as DmdTypeBase; if (et is null) throw new ArgumentException(); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } if ((options & DmdMakeTypeOptions.NoResolve) == 0) et = et.FullResolve() ?? et; var res = new DmdByRefType(et, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeArrayType(DmdType elementType, IList? customModifiers, DmdMakeTypeOptions options) { if (elementType is null) throw new ArgumentNullException(nameof(elementType)); if (elementType.AppDomain != this) throw new InvalidOperationException(); var et = elementType as DmdTypeBase; if (et is null) throw new ArgumentException(); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } if ((options & DmdMakeTypeOptions.NoResolve) == 0) et = et.FullResolve() ?? et; var res = new DmdSZArrayType(et, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeArrayType(DmdType elementType, int rank, IList sizes, IList lowerBounds, IList? customModifiers, DmdMakeTypeOptions options) { // Allow 0, it's allowed in the MD if (rank < 0) throw new ArgumentOutOfRangeException(nameof(rank)); if (elementType is null) throw new ArgumentNullException(nameof(elementType)); if (elementType.AppDomain != this) throw new InvalidOperationException(); if (sizes is null) throw new ArgumentNullException(nameof(sizes)); if (lowerBounds is null) throw new ArgumentNullException(nameof(lowerBounds)); var et = elementType as DmdTypeBase; if (et is null) throw new ArgumentException(); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } if ((options & DmdMakeTypeOptions.NoResolve) == 0) et = et.FullResolve() ?? et; var res = new DmdMDArrayType(et, rank, sizes, lowerBounds, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeGenericType(DmdType genericTypeDefinition, IList typeArguments, IList? customModifiers, DmdMakeTypeOptions options) { if (genericTypeDefinition is null) throw new ArgumentNullException(nameof(genericTypeDefinition)); if (genericTypeDefinition.AppDomain != this) throw new InvalidOperationException(); if (typeArguments is null) throw new ArgumentNullException(nameof(typeArguments)); for (int i = 0; i < typeArguments.Count; i++) { if (typeArguments[i].AppDomain != this) throw new InvalidOperationException(); } if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } DmdTypeBase res; DmdTypeDef? gtDef; bool resolve = (options & DmdMakeTypeOptions.NoResolve) == 0; if (resolve) gtDef = genericTypeDefinition.Resolve() as DmdTypeDef; else gtDef = genericTypeDefinition as DmdTypeDef; if (gtDef is null) { var gtRef = genericTypeDefinition as DmdTypeRef; if (gtRef is null) throw new ArgumentException(); if (resolve) typeArguments = DmdTypeUtilities.FullResolve(typeArguments) ?? typeArguments; res = new DmdGenericInstanceTypeRef(gtRef, typeArguments, customModifiers); } else { if (resolve) gtDef = (DmdTypeDef?)gtDef.FullResolve() ?? gtDef; if (!gtDef.IsGenericTypeDefinition) throw new ArgumentException(); if (gtDef.GetGenericArguments().Count != typeArguments.Count) throw new ArgumentException(); if (resolve) typeArguments = DmdTypeUtilities.FullResolve(typeArguments) ?? typeArguments; res = new DmdGenericInstanceType(gtDef, typeArguments, customModifiers); } lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdMethodInfo MakeGenericMethod(DmdMethodInfo genericMethodDefinition, IList typeArguments, DmdMakeTypeOptions options) { if (genericMethodDefinition is null) throw new ArgumentNullException(nameof(genericMethodDefinition)); if (genericMethodDefinition.AppDomain != this) throw new ArgumentException(); if (!genericMethodDefinition.IsGenericMethodDefinition) throw new ArgumentException(); if (typeArguments is null) throw new ArgumentNullException(nameof(typeArguments)); if (typeArguments.Count == 0) throw new ArgumentException(); for (int i = 0; i < typeArguments.Count; i++) { if (typeArguments[i].AppDomain != this) throw new InvalidOperationException(); } var sig = genericMethodDefinition.GetMethodSignature(); if (sig.GenericParameterCount != typeArguments.Count) throw new ArgumentException(); DmdMethodInfoBase res; DmdMethodDef? gmDef; bool resolve = (options & DmdMakeTypeOptions.NoResolve) == 0; if (resolve) gmDef = genericMethodDefinition.Resolve() as DmdMethodDef; else gmDef = genericMethodDefinition as DmdMethodDef; if (gmDef is null) { var gmRef = genericMethodDefinition as DmdMethodRef; if (gmRef is null) throw new ArgumentException(); if (resolve) typeArguments = DmdTypeUtilities.FullResolve(typeArguments) ?? typeArguments; res = new DmdMethodSpecRef(gmRef, typeArguments); } else { if (gmDef.GetGenericArguments().Count != typeArguments.Count) throw new ArgumentException(); if (resolve) typeArguments = DmdTypeUtilities.FullResolve(typeArguments) ?? typeArguments; res = new DmdMethodSpec(gmDef, typeArguments); } return res; } public override DmdType MakeFunctionPointerType(DmdMethodSignature methodSignature, IList? customModifiers, DmdMakeTypeOptions options) { if (methodSignature is null) throw new ArgumentNullException(nameof(methodSignature)); if (methodSignature.ReturnType.AppDomain != this) throw new ArgumentException(); var parameterTypes = methodSignature.GetParameterTypes(); for (int i = 0; i < parameterTypes.Count; i++) { if (parameterTypes[i].AppDomain != this) throw new ArgumentException(); } var varArgsParameterTypes = methodSignature.GetVarArgsParameterTypes(); for (int i = 0; i < varArgsParameterTypes.Count; i++) { if (varArgsParameterTypes[i].AppDomain != this) throw new ArgumentException(); } if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } var res = new DmdFunctionPointerType(this, methodSignature, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeFunctionPointerType(DmdSignatureCallingConvention flags, int genericParameterCount, DmdType returnType, IList parameterTypes, IList varArgsParameterTypes, IList? customModifiers, DmdMakeTypeOptions options) { if (genericParameterCount < 0) throw new ArgumentOutOfRangeException(nameof(genericParameterCount)); if (returnType is null) throw new ArgumentNullException(nameof(returnType)); if (parameterTypes is null) throw new ArgumentNullException(nameof(parameterTypes)); if (varArgsParameterTypes is null) throw new ArgumentNullException(nameof(varArgsParameterTypes)); if (returnType.AppDomain != this) throw new ArgumentException(); for (int i = 0; i < parameterTypes.Count; i++) { if (parameterTypes[i].AppDomain != this) throw new ArgumentException(); } for (int i = 0; i < varArgsParameterTypes.Count; i++) { if (varArgsParameterTypes[i].AppDomain != this) throw new ArgumentException(); } if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } if ((options & DmdMakeTypeOptions.NoResolve) == 0) { returnType = ((DmdTypeBase)returnType).FullResolve() ?? returnType; parameterTypes = DmdTypeUtilities.FullResolve(parameterTypes) ?? parameterTypes; varArgsParameterTypes = DmdTypeUtilities.FullResolve(varArgsParameterTypes) ?? varArgsParameterTypes; } var methodSignature = new DmdMethodSignature(flags, genericParameterCount, returnType, parameterTypes, varArgsParameterTypes); var res = new DmdFunctionPointerType(this, methodSignature, customModifiers); lock (fullyResolvedTypesLockObj) { if (fullyResolvedTypes.TryGetValue(res, out var cachedType)) return cachedType; if (res.IsFullyResolved) fullyResolvedTypes.Add(res, res); } return res; } public override DmdType MakeGenericTypeParameter(int position, DmdType declaringType, string name, DmdGenericParameterAttributes attributes, IList? customModifiers, DmdMakeTypeOptions options) { if (position < 0) throw new ArgumentOutOfRangeException(nameof(position)); if (declaringType is null) throw new ArgumentNullException(nameof(declaringType)); if (name is null) throw new ArgumentNullException(nameof(name)); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } var declTypeBase = declaringType as DmdTypeBase; if (declTypeBase is null) throw new ArgumentException(); return new DmdGenericParameterTypeImpl(this, declTypeBase, name, position, attributes, customModifiers); } public override DmdType MakeGenericMethodParameter(int position, DmdMethodBase declaringMethod, string name, DmdGenericParameterAttributes attributes, IList? customModifiers, DmdMakeTypeOptions options) { if (position < 0) throw new ArgumentOutOfRangeException(nameof(position)); if (declaringMethod is null) throw new ArgumentNullException(nameof(declaringMethod)); if (name is null) throw new ArgumentNullException(nameof(name)); if (customModifiers is not null) { for (int i = 0; i < customModifiers.Count; i++) { if (customModifiers[i].Type.AppDomain != this) throw new ArgumentException(); } } return new DmdGenericParameterTypeImpl(this, declaringMethod, name, position, attributes, customModifiers); } sealed class TypeDefResolver : ITypeDefResolver { readonly DmdAppDomain appDomain; readonly bool ignoreCase; public TypeDefResolver(DmdAppDomain appDomain, bool ignoreCase) { this.appDomain = appDomain ?? throw new ArgumentNullException(nameof(appDomain)); this.ignoreCase = ignoreCase; } public DmdTypeDef? GetTypeDef(IDmdAssemblyName? assemblyName, List typeNames) { if (typeNames.Count == 0) return null; DmdTypeDef? type; DmdTypeUtilities.SplitFullName(typeNames[0], out var @namespace, out var name); if (assemblyName is not null) { var assembly = (DmdAssemblyImpl?)appDomain.GetAssembly(assemblyName); if (!(assembly?.ManifestModule is DmdModule module)) return null; var typeRef = new DmdParsedTypeRef(module, null, DmdTypeScope.Invalid, @namespace, name, null); type = assembly.GetType(typeRef, ignoreCase); } else { type = null; foreach (DmdAssemblyImpl assembly in appDomain.GetAssemblies()) { var module = assembly.ManifestModule; if (module is null) continue; var typeRef = new DmdParsedTypeRef(module, null, DmdTypeScope.Invalid, @namespace, name, null); type = assembly.GetType(typeRef, ignoreCase); if (type is not null) break; } } if (type is null) return null; for (int i = 1; i < typeNames.Count; i++) { var flags = DmdBindingFlags.Public | DmdBindingFlags.NonPublic; if (ignoreCase) flags |= DmdBindingFlags.IgnoreCase; type = (DmdTypeDef?)type.GetNestedType(typeNames[i], flags); if (type is null) return null; } return type; } } public override DmdType? GetType(string typeName, DmdGetTypeOptions options) { if (typeName is null) throw new ArgumentNullException(nameof(typeName)); var resolver = new TypeDefResolver(this, (options & DmdGetTypeOptions.IgnoreCase) != 0); var type = DmdTypeNameParser.Parse(resolver, typeName); if (type is not null) return Intern(type, DmdMakeTypeOptions.NoResolve); if ((options & DmdGetTypeOptions.ThrowOnError) != 0) throw new TypeNotFoundException(typeName); return null; } internal DmdTypeDef? Resolve(DmdTypeRef typeRef, bool throwOnError, bool ignoreCase) { if (typeRef is null) throw new ArgumentNullException(nameof(typeRef)); var type = ResolveCore(typeRef, ignoreCase); if (type is not null) return type; if (throwOnError) throw new TypeResolveException(typeRef); return null; } DmdTypeDef? ResolveCore(DmdTypeRef typeRef, bool ignoreCase) { var nonNestedTypeRef = DmdTypeUtilities.GetNonNestedType(typeRef); if (nonNestedTypeRef is null) return null; DmdModule? module; DmdAssembly? assembly; var typeScope = nonNestedTypeRef.TypeScope; switch (typeScope.Kind) { case DmdTypeScopeKind.Invalid: Debug.Fail("Shouldn't be here"); return null; case DmdTypeScopeKind.Module: module = (DmdModule)typeScope.Data!; return Lookup(module, typeRef, ignoreCase) ?? ResolveExportedType(new[] { module }, typeRef, ignoreCase); case DmdTypeScopeKind.ModuleRef: assembly = GetAssembly((IDmdAssemblyName)typeScope.Data2!); if (assembly is null) return null; module = assembly.GetModule((string)typeScope.Data!); if (module is null) return null; return Lookup(module, typeRef, ignoreCase) ?? ResolveExportedType(new[] { module }, typeRef, ignoreCase); case DmdTypeScopeKind.AssemblyRef: assembly = GetAssembly((IDmdAssemblyName)typeScope.Data!); if (assembly is null) return null; return Lookup(assembly, typeRef, ignoreCase) ?? ResolveExportedType(assembly.GetModules(), typeRef, ignoreCase); default: throw new InvalidOperationException(); } } DmdTypeDef? ResolveExportedType(DmdModule[] modules, DmdTypeRef typeRef, bool ignoreCase) { for (int i = 0; i < 30; i++) { var exportedType = FindExportedType(modules, typeRef, ignoreCase); if (exportedType is null) return null; var nonNested = DmdTypeUtilities.GetNonNestedType(exportedType); if (nonNested is null) return null; var typeScope = nonNested.TypeScope; if (typeScope.Kind != DmdTypeScopeKind.AssemblyRef) return null; var etAsm = GetAssembly((IDmdAssemblyName)typeScope.Data!); if (etAsm is null) return null; var td = Lookup(etAsm, typeRef, ignoreCase); if (td is not null) return td; modules = etAsm.GetModules(); } return null; } DmdTypeRef? FindExportedType(IList modules, DmdTypeRef typeRef, bool ignoreCase) { foreach (var module in modules) { Dictionary? dict; do { lock (exportedTypeLockObj) { if (ignoreCase) { if (toModuleExportedTypeDictIgnoreCase.TryGetValue(module, out dict)) break; dict = new Dictionary(moduleTypeDictComparerIgnoreCase); toModuleExportedTypeDictIgnoreCase[module] = dict; } else { if (toModuleExportedTypeDict.TryGetValue(module, out dict)) break; dict = new Dictionary(moduleTypeDictComparer); toModuleExportedTypeDict[module] = dict; } } var types = (DmdTypeRef[])module.GetExportedTypes(); lock (exportedTypeLockObj) { foreach (var type in types) dict[type] = type; } } while (false); lock (exportedTypeLockObj) { if (dict.TryGetValue(typeRef, out var exportedType)) return exportedType; } } return null; } internal DmdTypeDef? TryLookup(DmdAssemblyImpl assembly, DmdTypeRef typeRef, bool ignoreCase) => Lookup(assembly, typeRef, ignoreCase) ?? ResolveExportedType(assembly.GetModules(), typeRef, ignoreCase); internal DmdTypeDef? TryLookup(DmdModuleImpl module, DmdTypeRef typeRef, bool ignoreCase) => Lookup(module, typeRef, ignoreCase) ?? ResolveExportedType(new[] { module }, typeRef, ignoreCase); DmdTypeDef? Lookup(DmdAssembly assembly, DmdTypeRef typeRef, bool ignoreCase) { // Most likely it's in the manifest module so we don't have to alloc an array (GetModules()) var manifestModule = assembly.ManifestModule; if (manifestModule is null) return null; var type = Lookup(manifestModule, typeRef, ignoreCase); if (type is not null) return type; foreach (var module in assembly.GetModules()) { if (manifestModule == module) continue; type = Lookup(module, typeRef, ignoreCase); if (type is not null) return type; } return null; } void DmdMetadataReader_TypesUpdated(DmdModule module, DmdTypesUpdatedEventArgs e) { var types = new DmdTypeDef?[e.Tokens.Length]; for (int i = 0; i < types.Length; i++) types[i] = module.ResolveType((int)e.Tokens[i], DmdResolveOptions.None) as DmdTypeDef; lock (moduleTypeLockObj) { ((DmdModuleImpl)module).DynamicModuleVersionInternal++; Dictionary? dict1 = null, dict2 = null; toModuleTypeDictIgnoreCase?.TryGetValue(module, out dict1); toModuleTypeDict?.TryGetValue(module, out dict2); Debug2.Assert(dict1 is not null || dict2 is not null); foreach (var type in types) { if (type is null) continue; if (dict1 is not null) dict1[type] = type; if (dict2 is not null) dict2[type] = type; } } } DmdTypeDef? Lookup(DmdModule module, DmdTypeRef typeRef, bool ignoreCase) { Dictionary? dict; do { lock (moduleTypeLockObj) { if (ignoreCase) { if (toModuleTypeDictIgnoreCase.TryGetValue(module, out dict)) break; dict = new Dictionary(moduleTypeDictComparerIgnoreCase); toModuleTypeDictIgnoreCase[module] = dict; } else { if (toModuleTypeDict.TryGetValue(module, out dict)) break; dict = new Dictionary(moduleTypeDictComparer); toModuleTypeDict[module] = dict; } // Only dynamic modules can add more types at runtime if (module.IsDynamic) { // If it's the first time this code gets executed with this module if (toModuleTypeDictIgnoreCase.ContainsKey(module) != toModuleTypeDict.ContainsKey(module)) { var moduleImpl = (DmdModuleImpl)module; moduleImpl.MetadataReader.TypesUpdated += (s, e) => DmdMetadataReader_TypesUpdated(module, e); } } } var types = (DmdTypeDef[])module.GetTypes(); lock (moduleTypeLockObj) { foreach (var type in types) dict[type] = type; } } while (false); lock (moduleTypeLockObj) { if (dict.TryGetValue(typeRef, out var typeDef)) return typeDef; } return null; } internal DmdType[] GetSZArrayInterfaces(DmdType elementType) { var ifaces = defaultExistingWellKnownSZArrayInterfaces; if (ifaces is null) { Debug2.Assert(CorLib is not null, "CorLib hasn't been loaded yet!"); if (CorLib is null) return Array.Empty(); List? list = ObjectPools.AllocListOfType(); foreach (var wellKnownType in possibleWellKnownSZArrayInterfaces) { // These interfaces should only be in corlib since the CLR needs them. // They're not always present so if we fail to find a type in the corlib, we don't // want to search the remaining assemblies (could be hundreds of assemblies). var iface = GetWellKnownType(wellKnownType, isOptional: true, onlyCorLib: true); if (iface is not null) list.Add(iface); } Interlocked.CompareExchange(ref defaultExistingWellKnownSZArrayInterfaces, ObjectPools.FreeAndToArray(ref list), null); ifaces = defaultExistingWellKnownSZArrayInterfaces!; } var res = new DmdType[ifaces.Length]; var typeArguments = new[] { elementType }; for (int i = 0; i < res.Length; i++) res[i] = MakeGenericType(ifaces[i], typeArguments, null, DmdMakeTypeOptions.None); return res; } volatile DmdType[]? defaultExistingWellKnownSZArrayInterfaces; static readonly DmdWellKnownType[] possibleWellKnownSZArrayInterfaces = new DmdWellKnownType[] { // Available since .NET Framework 2.0 DmdWellKnownType.System_Collections_Generic_IList_T, // Available since .NET Framework 4.5 DmdWellKnownType.System_Collections_Generic_IReadOnlyList_T, // Available since .NET Framework 4.5 DmdWellKnownType.System_Collections_Generic_IReadOnlyCollection_T, }; public override object? CreateInstance(object? context, DmdConstructorInfo ctor, object?[] parameters) { if (ctor is null) throw new ArgumentNullException(nameof(ctor)); if (ctor.IsStatic) throw new ArgumentException(); if (ctor.AppDomain != this) throw new ArgumentException(); return runtime.Evaluator.CreateInstance(context, ctor, parameters ?? Array.Empty()); } public override object? Invoke(object? context, DmdMethodBase method, object? obj, object?[]? parameters) { if (method is null) throw new ArgumentNullException(nameof(method)); if (method.IsStatic != (obj is null)) throw new ArgumentException(); if (method.AppDomain != this) throw new ArgumentException(); return runtime.Evaluator.Invoke(context, method, obj, parameters ?? Array.Empty()); } public override object? LoadField(object? context, DmdFieldInfo field, object? obj) { if (field is null) throw new ArgumentNullException(nameof(field)); if (field.IsStatic != (obj is null)) throw new ArgumentException(); if (field.AppDomain != this) throw new ArgumentException(); return runtime.Evaluator.LoadField(context, field, obj); } public override void StoreField(object? context, DmdFieldInfo field, object? obj, object? value) { if (field is null) throw new ArgumentNullException(nameof(field)); if (field.IsStatic != (obj is null)) throw new ArgumentException(); if (field.AppDomain != this) throw new ArgumentException(); runtime.Evaluator.StoreField(context, field, obj, value); } } }