// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.Reflection.Runtime.General; using System.Threading; using Internal.Metadata.NativeFormat; using Internal.NativeFormat; using Internal.Runtime.CompilerServices; using Internal.TypeSystem; namespace Internal.Runtime.TypeLoader { public sealed partial class TypeLoaderEnvironment { internal class GenericMethodEntry { private int? _hashCode; public bool _isRegisteredSuccessfully; public bool _isAsyncVariant; public bool _isReturnDroppingAsyncThunk; public IntPtr _methodDictionary; public RuntimeTypeHandle _declaringTypeHandle; public MethodNameAndSignature _methodNameAndSignature; public RuntimeTypeHandle[] _genericMethodArgumentHandles; public override int GetHashCode() { if (!_hashCode.HasValue) { _hashCode = _declaringTypeHandle.GetHashCode() ^ VersionResilientHashCode.GenericInstanceHashCode(VersionResilientHashCode.NameHashCode(_methodNameAndSignature.Name), _genericMethodArgumentHandles); } return _hashCode.Value; } public override bool Equals(object obj) { // There are no scenarios where we call .Equals to check the componentized equality (we explicitly use IsEqualToEntryByComponentsComparison for that), // so making sure we revert to the reference equality in case someone calls here. return base.Equals(obj); } public virtual bool IsEqualToEntryByComponentsComparison(GenericMethodEntry other) { if (!other._declaringTypeHandle.Equals(_declaringTypeHandle)) return false; if (_isAsyncVariant != other._isAsyncVariant) return false; if (_isReturnDroppingAsyncThunk != other._isReturnDroppingAsyncThunk) return false; if (!other._methodNameAndSignature.Equals(_methodNameAndSignature)) return false; if (other._genericMethodArgumentHandles == null) return false; if (other._genericMethodArgumentHandles.Length != _genericMethodArgumentHandles.Length) return false; for (int i = 0; i < _genericMethodArgumentHandles.Length; i++) if (!other._genericMethodArgumentHandles[i].Equals(_genericMethodArgumentHandles[i])) return false; return true; } } internal class DynamicGenericMethodsHashtable : LockFreeReaderHashtable<GenericMethodLookupData, GenericMethodEntry> { protected override int GetKeyHashCode(GenericMethodLookupData key) { return key.LookupHashCode(); } protected override bool CompareKeyToValue(GenericMethodLookupData key, GenericMethodEntry value) { return key.MatchGenericMethodEntry(value); } protected override int GetValueHashCode(GenericMethodEntry value) { return value.GetHashCode(); } protected override bool CompareValueToValue(GenericMethodEntry value1, GenericMethodEntry value2) { // Comparisons should *only* be done using the generic method components return value1.IsEqualToEntryByComponentsComparison(value2); } protected override GenericMethodEntry CreateValueFromKey(GenericMethodLookupData key) { // Feature not used by the TypeBuilder throw NotImplemented.ByDesign; } } internal class DynamicGenericMethodComponentsHashtable : LockFreeReaderHashtable<IntPtr, GenericMethodEntry> { protected override int GetKeyHashCode(IntPtr key) { return key.GetHashCode(); } protected override bool CompareKeyToValue(IntPtr key, GenericMethodEntry value) { return key.Equals(value._methodDictionary); } protected override int GetValueHashCode(GenericMethodEntry value) { Debug.Assert(value._methodDictionary != IntPtr.Zero); return value._methodDictionary.GetHashCode(); } protected override bool CompareValueToValue(GenericMethodEntry value1, GenericMethodEntry value2) { // Comparisons should *only* be done using the generic method components return value1.IsEqualToEntryByComponentsComparison(value2); } protected override GenericMethodEntry CreateValueFromKey(IntPtr key) { // Feature not used by the TypeBuilder throw NotImplemented.ByDesign; } } internal abstract class GenericMethodLookupData { internal abstract int LookupHashCode(); internal abstract bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle); internal abstract bool MatchGenericMethodEntry(GenericMethodEntry entry); } internal class MethodDescBasedGenericMethodLookup : GenericMethodLookupData { protected InstantiatedMethod _methodToLookup; internal MethodDescBasedGenericMethodLookup(InstantiatedMethod methodToLookup) { _methodToLookup = methodToLookup; } internal override int LookupHashCode() { return _methodToLookup.GetHashCode(); } internal override bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle) { // // Entries read from the hashtable are loaded as GenericMethodDescs, and compared to the input. // This lookup is slower than the lookups using RuntimeTypeHandles, but can handle cases where we don't have // RuntimeTypeHandle values for all of the components of the input GenericMethodDesc, but still need to look it up in case the // method dictionary statically really exists // TypeSystemContext context = _methodToLookup.Context; RuntimeTypeHandle parsedDeclaringTypeHandle = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); TypeDesc parsedDeclaringType = context.ResolveRuntimeTypeHandle(parsedDeclaringTypeHandle); if (_methodToLookup.OwningType != parsedDeclaringType) return false; int flagsAndToken = (int)entryParser.GetUnsigned(); bool isAsyncVariant = (flagsAndToken & GenericMethodsHashtableConstants.IsAsyncVariant) != 0; bool isReturnDroppingAsyncThunk = (flagsAndToken & GenericMethodsHashtableConstants.IsReturnDroppingAsyncThunk) != 0; if (_methodToLookup.AsyncVariant != isAsyncVariant) return false; if (_methodToLookup.ReturnDroppingAsyncThunk != isReturnDroppingAsyncThunk) return false; int token = ((int)HandleType.Method << 25) | (flagsAndToken & ~(GenericMethodsHashtableConstants.IsAsyncVariant | GenericMethodsHashtableConstants.IsReturnDroppingAsyncThunk)); MethodNameAndSignature nameAndSignature = TypeLoaderEnvironment.GetMethodNameAndSignatureFromToken(moduleHandle, (uint)token); if (!_methodToLookup.NameAndSignature.Equals(nameAndSignature)) return false; RuntimeTypeHandle[] parsedArgsHandles = GetTypeSequence(ref externalReferencesLookup, ref entryParser); if (parsedArgsHandles.Length != _methodToLookup.Instantiation.Length) return false; for (int i = 0; i < _methodToLookup.Instantiation.Length; i++) { TypeDesc leftType = context.ResolveRuntimeTypeHandle(parsedArgsHandles[i]); TypeDesc rightType = _methodToLookup.Instantiation[i]; if (leftType != rightType) return false; } return true; } internal override bool MatchGenericMethodEntry(GenericMethodEntry entry) { TypeSystemContext context = _methodToLookup.Context; TypeDesc parsedDeclaringType = context.ResolveRuntimeTypeHandle(entry._declaringTypeHandle); if (_methodToLookup.OwningType != parsedDeclaringType) return false; if (_methodToLookup.AsyncVariant != entry._isAsyncVariant) return false; if (_methodToLookup.ReturnDroppingAsyncThunk != entry._isReturnDroppingAsyncThunk) return false; if (!_methodToLookup.NameAndSignature.Equals(entry._methodNameAndSignature)) return false; if (entry._genericMethodArgumentHandles.Length != _methodToLookup.Instantiation.Length) return false; for (int i = 0; i < _methodToLookup.Instantiation.Length; i++) { TypeDesc leftType = context.ResolveRuntimeTypeHandle(entry._genericMethodArgumentHandles[i]); TypeDesc rightType = _methodToLookup.Instantiation[i]; if (leftType != rightType) return false; } return true; } } private DynamicGenericMethodsHashtable _dynamicGenericMethods = new DynamicGenericMethodsHashtable(); private DynamicGenericMethodComponentsHashtable _dynamicGenericMethodComponents = new DynamicGenericMethodComponentsHashtable(); internal bool TryLookupGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result) { if (!TryGetStaticGenericMethodDictionary(lookupData, out result)) if (!TryGetDynamicGenericMethodDictionary(lookupData, out result)) return false; return true; } public bool TryGetGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles, out bool isAsyncVariant) { if (!TryGetDynamicGenericMethodComponents(methodDictionary, out declaringType, out nameAndSignature, out genericMethodArgumentHandles, out isAsyncVariant)) { if (!TryGetStaticGenericMethodComponents(methodDictionary, out declaringType, out nameAndSignature, out genericMethodArgumentHandles, out isAsyncVariant)) return false; } return true; } public static bool TryGetGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out RuntimeTypeHandle[] genericMethodArgumentHandles) { TypeLoaderEnvironment instance = TypeLoaderEnvironment.InstanceOrNull; if (instance == null || !instance.TryGetDynamicGenericMethodComponents(methodDictionary, out declaringType, out _, out genericMethodArgumentHandles, out _)) if (!TryGetStaticGenericMethodComponents(methodDictionary, out declaringType, out _, out genericMethodArgumentHandles, out _)) return false; return true; } public bool TryLookupExactMethodPointer(InstantiatedMethod method, out IntPtr result) { int lookupHashcode = method.OwningType.GetHashCode(); NativeHashtable hashtable; ExternalReferencesTable externalReferencesLookup; MethodDescBasedGenericMethodLookup lookupData = new MethodDescBasedGenericMethodLookup(method); foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { if (!GetHashtableFromBlob(module, ReflectionMapBlob.ExactMethodInstantiationsHashtable, out hashtable, out externalReferencesLookup)) continue; var enumerator = hashtable.Lookup(lookupHashcode); NativeParser entryParser; while (!(entryParser = enumerator.GetNext()).IsNull) { if (!lookupData.MatchParsedEntry(ref entryParser, ref externalReferencesLookup, module.Handle)) continue; // We found a match result = externalReferencesLookup.GetIntPtrFromIndex(entryParser.GetUnsigned()); return true; } } result = IntPtr.Zero; return false; } // This method computes the method pointer and dictionary pointer for a GVM. // Inputs: // - method: the GVM whose pointer and dictionary to retrieve // Outputs: // - methodPointer: pointer to the GVM's implementation // - dictionaryPointer: (if applicable) pointer to the dictionary to be used with the GVM call public bool TryGetGenericVirtualMethodPointer(InstantiatedMethod method, out IntPtr methodPointer, out IntPtr dictionaryPointer) { if (!method.CanShareNormalGenericCode()) { if (TryLookupExactMethodPointer(method, out methodPointer)) { Debug.Assert(methodPointer != IntPtr.Zero); dictionaryPointer = IntPtr.Zero; return true; } } InstantiatedMethod nonTemplateMethod = method; // Templates are always unboxing stubs for valuetype instance methods if (!method.UnboxingStub && method.OwningType.IsValueType && !IsStaticMethodSignature(method.NameAndSignature)) { // Make it an unboxing stub, note the first parameter which is true nonTemplateMethod = (InstantiatedMethod)method.Context.ResolveGenericMethodInstantiation(true, method.AsyncVariant, method.ReturnDroppingAsyncThunk, (DefType)method.OwningType, method.NameAndSignature, method.Instantiation); } // If we cannot find an exact method entry point, look for an equivalent template and compute the generic dictionary InstantiatedMethod templateMethod = TemplateLocator.TryGetGenericMethodTemplate(nonTemplateMethod, out _, out _); if (templateMethod == null) { methodPointer = default; dictionaryPointer = default; return false; } methodPointer = templateMethod.FunctionPointer; if (!TryLookupGenericMethodDictionary(new MethodDescBasedGenericMethodLookup(method), out dictionaryPointer)) { using (_typeLoaderLock.EnterScope()) { // Now that we hold the lock, we may find that existing types can now find // their associated RuntimeTypeHandle. Flush the type builder states as a way // to force the reresolution of RuntimeTypeHandles which couldn't be found before. method.Context.FlushTypeBuilderStates(); if (!TypeBuilder.TryBuildGenericMethod(method, out dictionaryPointer)) { return false; } } } Debug.Assert(methodPointer != IntPtr.Zero && dictionaryPointer != IntPtr.Zero); return true; } #region Privates private bool TryGetDynamicGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result) { result = IntPtr.Zero; using (_dynamicGenericsLock.EnterScope()) { GenericMethodEntry entry; if (!_dynamicGenericMethods.TryGetValue(lookupData, out entry)) return false; if (!entry._isRegisteredSuccessfully) return false; result = entry._methodDictionary; return true; } } private static bool TryGetStaticGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result) { // Search the hashtable for a generic instantiation match ExternalReferencesTable externalReferencesLookup; NativeHashtable genericMethodsHashtable; foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericMethodsHashtable, out genericMethodsHashtable, out externalReferencesLookup)) continue; int lookupHashcode = lookupData.LookupHashCode(); var enumerator = genericMethodsHashtable.Lookup(lookupHashcode); NativeParser entryParser; while (!(entryParser = enumerator.GetNext()).IsNull) { uint dictionaryIndex = entryParser.GetUnsigned(); if (!lookupData.MatchParsedEntry(ref entryParser, ref externalReferencesLookup, module.Handle)) continue; // Current entry matched all inputs, return success result = externalReferencesLookup.GetIntPtrFromIndex(dictionaryIndex); return true; } } result = IntPtr.Zero; return false; } private bool TryGetDynamicGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature methodNameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles, out bool isAsyncVariant) { declaringType = default(RuntimeTypeHandle); methodNameAndSignature = null; genericMethodArgumentHandles = null; isAsyncVariant = false; using (_dynamicGenericsLock.EnterScope()) { GenericMethodEntry entry; if (!_dynamicGenericMethodComponents.TryGetValue(methodDictionary, out entry)) return false; if (!entry._isRegisteredSuccessfully) return false; declaringType = entry._declaringTypeHandle; methodNameAndSignature = entry._methodNameAndSignature; genericMethodArgumentHandles = entry._genericMethodArgumentHandles; isAsyncVariant = entry._isAsyncVariant; return true; } } private static unsafe bool TryGetStaticGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles, out bool isAsyncVariant) { // Generic method dictionaries have a header that has the hash code in it. Locate the header IntPtr dictionaryHeader = IntPtr.Subtract(methodDictionary, IntPtr.Size); int lookupHashcode = *(int*)dictionaryHeader; ExternalReferencesTable externalReferencesLookup; NativeHashtable genericMethodsHashtable; foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { if (!GetHashtableFromBlob(module, ReflectionMapBlob.GenericMethodsHashtable, out genericMethodsHashtable, out externalReferencesLookup)) continue; var enumerator = genericMethodsHashtable.Lookup(lookupHashcode); NativeParser entryParser; while (!(entryParser = enumerator.GetNext()).IsNull) { // Is this entry the dictionary we are looking for? uint dictionaryIndex = entryParser.GetUnsigned(); IntPtr parsedMethodDictionary = externalReferencesLookup.GetIntPtrFromIndex(dictionaryIndex); if (parsedMethodDictionary != methodDictionary) continue; // We have a match - fill in the results declaringType = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); int flagsAndToken = (int)entryParser.GetUnsigned(); isAsyncVariant = (flagsAndToken & GenericMethodsHashtableConstants.IsAsyncVariant) != 0; int token = ((int)HandleType.Method << 25) | (flagsAndToken & ~(GenericMethodsHashtableConstants.IsAsyncVariant | GenericMethodsHashtableConstants.IsReturnDroppingAsyncThunk)); nameAndSignature = new MethodNameAndSignature(module.MetadataReader, token.AsHandle().ToMethodHandle(module.MetadataReader)); uint arity = entryParser.GetSequenceCount(); genericMethodArgumentHandles = new RuntimeTypeHandle[arity]; for (int i = 0; i < arity; i++) { genericMethodArgumentHandles[i] = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); } return true; } } declaringType = default(RuntimeTypeHandle); nameAndSignature = null; genericMethodArgumentHandles = null; isAsyncVariant = false; return false; } #endregion } } |