diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs index e4eb2775977bf2..c7fee5d3cc92a7 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -49,12 +49,6 @@ public uint NumStandardEntries { return _standardEntryCount; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - _standardEntryCount = checked((ushort)value); - } -#endif } public uint NumDefaultEntries @@ -63,12 +57,6 @@ public uint NumDefaultEntries { return _defaultEntryCount; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - _defaultEntryCount = checked((ushort)value); - } -#endif } public uint NumStandardStaticEntries @@ -77,12 +65,6 @@ public uint NumStandardStaticEntries { return _standardStaticEntryCount; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - _standardStaticEntryCount = checked((ushort)value); - } -#endif } public uint NumDefaultStaticEntries @@ -91,12 +73,6 @@ public uint NumDefaultStaticEntries { return _defaultStaticEntryCount; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - _defaultStaticEntryCount = checked((ushort)value); - } -#endif } public int Size @@ -182,21 +158,6 @@ internal bool HasComponentSize // return (_uFlags & (uint)EETypeFlags.HasComponentSizeFlag) != 0; return (int)_uFlags < 0; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - if (value) - { - Debug.Assert(ExtendedFlags == 0); - _uFlags |= (uint)EETypeFlags.HasComponentSizeFlag; - } - else - { - // we should not be un-setting this bit. - Debug.Assert(!HasComponentSize); - } - } -#endif } internal ushort ComponentSize @@ -209,7 +170,7 @@ internal ushort ComponentSize set { Debug.Assert(HasComponentSize); - _uFlags |= (uint)value; + _uFlags = (_uFlags & ~(uint)ushort.MaxValue) | value; } #endif } @@ -221,13 +182,6 @@ internal ushort GenericParameterCount Debug.Assert(IsGenericTypeDefinition); return (ushort)_uBaseSize; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - Debug.Assert(IsGenericTypeDefinition); - _uBaseSize = value; - } -#endif } internal uint Flags @@ -251,14 +205,6 @@ internal ushort ExtendedFlags { return HasComponentSize ? (ushort)0 : (ushort)_uFlags; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - Debug.Assert(!HasComponentSize); - Debug.Assert(ExtendedFlags == 0); - _uFlags |= (uint)value; - } -#endif } internal uint RawBaseSize @@ -282,12 +228,6 @@ internal uint BaseSize Debug.Assert(IsCanonical || IsArray); return _uBaseSize; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - _uBaseSize = value; - } -#endif } internal ushort NumVtableSlots @@ -643,13 +583,6 @@ internal uint NumFunctionPointerParameters Debug.Assert(IsFunctionPointer); return _uBaseSize & ~FunctionPointerFlags.FlagsMask; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - Debug.Assert(IsFunctionPointer); - _uBaseSize = value | (_uBaseSize & FunctionPointerFlags.FlagsMask); - } -#endif } internal bool IsUnmanagedFunctionPointer @@ -659,16 +592,6 @@ internal bool IsUnmanagedFunctionPointer Debug.Assert(IsFunctionPointer); return (_uBaseSize & FunctionPointerFlags.IsUnmanaged) != 0; } -#if TYPE_LOADER_IMPLEMENTATION - set - { - Debug.Assert(IsFunctionPointer); - if (value) - _uBaseSize |= FunctionPointerFlags.IsUnmanaged; - else - _uBaseSize &= ~FunctionPointerFlags.IsUnmanaged; - } -#endif } internal MethodTableList FunctionPointerParameters diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs index c75c2007535aef..2b38ccb531fb36 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeUnifier.cs @@ -109,18 +109,13 @@ private static RuntimeTypeHandle GetRuntimeTypeHandleIfAny(RuntimeTypeInfo eleme { Debug.Assert(multiDim || rank == 1); + if (elementType.IsByRef || elementType.IsVoid || elementType.IsByRefLike) + throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); + RuntimeTypeHandle elementTypeHandle = elementType.InternalTypeHandleIfAvailable; if (elementTypeHandle.IsNull()) return default(RuntimeTypeHandle); - // The check is here on purpose - one of the implementations of IsByRefLike contains a custom attribute - // search and those are very expensive from size on disk footprint perspective. We purposefully - // place this call in a path that won't be part of the executable image unless more advanced reflection services - // are also needed ("pay for play"). We really don't want a typeof() to push the app into requiring the full reflection - // stack to be compiled into the final executable. - if (elementType.IsByRefLike) - throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, elementType)); - RuntimeTypeHandle typeHandle; if (!multiDim) { @@ -140,8 +135,6 @@ private sealed class ArrayTypeTable : ConcurrentUnifierWKeyed 0); Debug.Assert(rtth.ToEETypePtr()->IsArray || rtth.ToEETypePtr()->IsString); - rtth.ToEETypePtr()->HasComponentSize = true; rtth.ToEETypePtr()->ComponentSize = componentSize; } } @@ -145,46 +143,24 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo pTemplateEEType = pTemplateEEType->DynamicTemplateType; } - int baseSize = 0; - - bool isValueType; - bool hasDispatchMap; - bool hasFinalizer; - bool isNullable; - bool isArray; - bool isGeneric; - bool hasSealedVTable; - uint flags; - ushort runtimeInterfacesLength = 0; - IntPtr typeManager = IntPtr.Zero; - - if (state.RuntimeInterfaces != null) - { - runtimeInterfacesLength = checked((ushort)state.RuntimeInterfaces.Length); - } - - baseSize = (int)pTemplateEEType->RawBaseSize; - isValueType = pTemplateEEType->IsValueType; - hasFinalizer = pTemplateEEType->IsFinalizable; - hasDispatchMap = pTemplateEEType->HasDispatchMap; - isNullable = pTemplateEEType->IsNullable; - flags = pTemplateEEType->Flags; - isArray = pTemplateEEType->IsArray; - isGeneric = pTemplateEEType->IsGeneric; - hasSealedVTable = pTemplateEEType->HasSealedVTableEntries; - typeManager = pTemplateEEType->PointerToTypeManager; - Debug.Assert(pTemplateEEType->NumInterfaces == runtimeInterfacesLength); - - flags |= (uint)EETypeFlags.IsDynamicTypeFlag; + int baseSize = (int)pTemplateEEType->RawBaseSize; + bool hasFinalizer = pTemplateEEType->IsFinalizable; + bool hasDispatchMap = pTemplateEEType->HasDispatchMap; + bool isGeneric = pTemplateEEType->IsGeneric; + bool hasSealedVTable = pTemplateEEType->HasSealedVTableEntries; + ushort runtimeInterfacesLength = pTemplateEEType->NumInterfaces; + Debug.Assert(runtimeInterfacesLength == state.TypeBeingBuilt.RuntimeInterfaces.Length); + uint flags = pTemplateEEType->Flags | (uint)EETypeFlags.IsDynamicTypeFlag; + bool isMdArray = state.TypeBeingBuilt.IsMdArray; int numFunctionPointerTypeParameters = 0; - if (state.TypeBeingBuilt.IsMdArray) + if (isMdArray) { // If we're building an MDArray, the template is object[,] and we // need to recompute the base size. baseSize = IntPtr.Size + // sync block 2 * IntPtr.Size + // EETypePtr + Length - state.ArrayRank.Value * sizeof(int) * 2; // 2 ints per rank for bounds + ((ArrayType)state.TypeBeingBuilt).Rank * sizeof(int) * 2; // 2 ints per rank for bounds } else if (state.TypeBeingBuilt.IsFunctionPointer) { @@ -210,23 +186,17 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo if (allocatedNonGCDataSize != 0) dynamicTypeFlags |= DynamicTypeFlags.HasNonGCStatics; - if (state.GcDataSize != 0) + if (state.GcStaticDesc != IntPtr.Zero) dynamicTypeFlags |= DynamicTypeFlags.HasGCStatics; - if (state.ThreadDataSize != 0) + if (state.ThreadStaticDesc != IntPtr.Zero) dynamicTypeFlags |= DynamicTypeFlags.HasThreadStatics; - ushort numVtableSlots = state.NumVTableSlots; + ushort numVtableSlots = pTemplateEEType->NumVtableSlots; // Compute the MethodTable size and allocate it MethodTable* pEEType; { - // In order to get the size of the MethodTable to allocate we need the following information - // 1) The number of VTable slots (from the TypeBuilderState) - // 2) The number of Interfaces (from the template) - // 3) Whether or not there is a finalizer (from the template) - // 4) Optional fields size - // 5) Whether or not the type has sealed virtuals (from the TypeBuilderState) int cbEEType = (int)MethodTable.GetSizeofEEType( numVtableSlots, runtimeInterfacesLength, @@ -236,13 +206,16 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo isGeneric, numFunctionPointerTypeParameters, allocatedNonGCDataSize != 0, - state.GcDataSize != 0, - state.ThreadDataSize != 0); + state.GcStaticDesc != IntPtr.Zero, + state.ThreadStaticDesc != IntPtr.Zero); // Dynamic types have an extra pointer-sized field that contains a pointer to their template type cbEEType += IntPtr.Size; - int cbGCDesc = GetInstanceGCDescSize(state, pTemplateEEType, isValueType, isArray); + MethodTable* elementEEType = null; + int cbGCDesc = isMdArray + ? GetMdArrayGCDescSize((ArrayType)state.TypeBeingBuilt, out elementEEType) + : RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle()); int cbGCDescAligned = MemoryHelpers.AlignUp(cbGCDesc, IntPtr.Size); // Allocate enough space for the MethodTable + gcDescSize @@ -258,13 +231,18 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo pEEType->NumVtableSlots = numVtableSlots; pEEType->NumInterfaces = runtimeInterfacesLength; pEEType->HashCode = hashCodeOfNewType; - pEEType->PointerToTypeManager = typeManager; + pEEType->PointerToTypeManager = pTemplateEEType->PointerToTypeManager; - // Write the GCDesc - bool isSzArray = isArray ? state.ArrayRank < 1 : false; - int arrayRank = isArray ? state.ArrayRank.Value : 0; - CreateInstanceGCDesc(state, pTemplateEEType, pEEType, baseSize, cbGCDesc, isValueType, isArray, isSzArray, arrayRank); - Debug.Assert(pEEType->ContainsGCPointers == (cbGCDesc != 0)); + if (isMdArray) + { + CreateMdArrayGCDesc(elementEEType, pEEType, cbGCDesc); + } + else + { + // Specific-canonical templates have the same instance layout, including SZ-array elements. + Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc); + } + Debug.Assert(RuntimeAugments.GetGCDescSize(pEEType->ToRuntimeTypeHandle()) == cbGCDesc); // Copy VTable entries from template type IntPtr* pVtable = (IntPtr*)((byte*)pEEType + sizeof(MethodTable)); @@ -299,15 +277,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo pEEType->DynamicTemplateType = pTemplateEEType; pEEType->DynamicTypeFlags = dynamicTypeFlags; - int nonGCStaticDataOffset = 0; - - if (!isArray) - { - nonGCStaticDataOffset = state.HasStaticConstructor ? -TypeBuilder.ClassConstructorOffset : 0; - - // If we have a class constructor, our NonGcDataSize MUST be non-zero - Debug.Assert(!state.HasStaticConstructor || (allocatedNonGCDataSize != 0)); - } + int nonGCStaticDataOffset = state.HasStaticConstructor ? -TypeBuilder.ClassConstructorOffset : 0; if (isGeneric) { @@ -326,7 +296,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo } } - if (state.ThreadDataSize != 0) + if (state.ThreadStaticDesc != IntPtr.Zero) { state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(pEEType->TypeManager); @@ -336,7 +306,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo pEEType->DynamicThreadStaticsIndex = (IntPtr)threadStaticIndex; } - if (state.GcDataSize != 0) + if (state.GcStaticDesc != IntPtr.Zero) { // Statics are allocated on GC heap object obj = RuntimeAugments.RawNewObject(((MethodTable*)state.GcStaticDesc)->ToRuntimeTypeHandle()); @@ -371,168 +341,74 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo } } - private static void CreateInstanceGCDesc(TypeBuilderState state, MethodTable* pTemplateEEType, MethodTable* pEEType, int baseSize, int cbGCDesc, bool isValueType, bool isArray, bool isSzArray, int arrayRank) - { - var gcBitfield = state.InstanceGCLayout; - if (isArray) - { - if (cbGCDesc != 0) - { - pEEType->ContainsGCPointers = true; - if (state.IsArrayOfReferenceTypes || IsAllGCPointers(gcBitfield)) - { - IntPtr* gcDescStart = (IntPtr*)((byte*)pEEType - cbGCDesc); - // Series size - gcDescStart[0] = new IntPtr(-baseSize); - // Series offset - gcDescStart[1] = new IntPtr(baseSize - sizeof(IntPtr)); - // NumSeries - gcDescStart[2] = new IntPtr(1); - } - else - { - CreateArrayGCDesc(gcBitfield, arrayRank, isSzArray, ((void**)pEEType) - 1); - } - } - else - { - pEEType->ContainsGCPointers = false; - } - } - else - { - Debug.Assert(gcBitfield == null); - - if (pTemplateEEType != null) - { - Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc); - pEEType->ContainsGCPointers = pTemplateEEType->ContainsGCPointers; - } - else - { - pEEType->ContainsGCPointers = false; - } - } - } - - private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTable* pTemplateEEType, bool isValueType, bool isArray) - { - var gcBitfield = state.InstanceGCLayout; - if (isArray) - { - if (state.IsArrayOfReferenceTypes || - (gcBitfield != null && IsAllGCPointers(gcBitfield))) - { - // For efficiency this is special cased and encoded as one serie - return 3 * sizeof(IntPtr); - } - else - { - int series = 0; - if (gcBitfield != null) - series = CreateArrayGCDesc(gcBitfield, 1, true, null); - - return series > 0 ? (series + 2) * IntPtr.Size : 0; - } - } - else - { - Debug.Assert(gcBitfield == null); - - if (pTemplateEEType != null) - { - return RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle()); - } - else - { - return 0; - } - } - } - - private static bool IsAllGCPointers(bool[] bitfield) + private static void CreateMdArrayGCDesc(MethodTable* elementEEType, MethodTable* pEEType, int cbGCDesc) { - int count = bitfield.Length; - Debug.Assert(count > 0); - - for (int i = 0; i < count; i++) + pEEType->ContainsGCPointers = cbGCDesc != 0; + if (cbGCDesc == 0) + return; + + int baseSize = (int)pEEType->BaseSize; + nint* gcDesc = (nint*)pEEType; + nint* elementGCDesc = (nint*)elementEEType; + // A series spanning the entire boxed payload has only the two header words subtracted. + if (elementEEType == null || elementGCDesc[-3] == -2 * sizeof(nint)) { - if (!bitfield[i]) - return false; + Debug.Assert(cbGCDesc == 3 * sizeof(nint)); + gcDesc[-3] = -baseSize; + gcDesc[-2] = baseSize - sizeof(nint); + gcDesc[-1] = 1; + return; } - return true; - } - - private static unsafe int CreateArrayGCDesc(bool[] bitfield, int rank, bool isSzArray, void* gcdesc) - { - if (bitfield == null) - return 0; - - void** baseOffsetPtr = (void**)gcdesc - 1; + int elementBaseSize = (int)elementEEType->BaseSize; + int series = (int)elementGCDesc[-1]; + int firstOffset = (int)elementGCDesc[-2]; + gcDesc[-1] = -series; + gcDesc[-2] = baseSize - 2 * sizeof(nint) + firstOffset; + elementGCDesc -= 2; #if TARGET_64BIT - int* ptr = (int*)baseOffsetPtr - 1; + uint* ptr = (uint*)(gcDesc - 2) - 1; #else - short* ptr = (short*)baseOffsetPtr - 1; + ushort* ptr = (ushort*)(gcDesc - 2) - 1; #endif - int baseOffset = 2; - if (!isSzArray) + for (int i = 0; i < series; i++) { - baseOffset += 2 * rank / (sizeof(IntPtr) / sizeof(int)); + int offset = (int)*elementGCDesc--; + int length = (int)*elementGCDesc-- + elementBaseSize; + // The last skip wraps to the first GC pointer in the next unboxed element. + int nextOffset = i + 1 < series + ? (int)*elementGCDesc + : firstOffset + elementBaseSize - 2 * sizeof(nint); + Debug.Assert(length > 0 && nextOffset >= offset + length); + *ptr-- = (ushort)(nextOffset - offset - length); + *ptr-- = (ushort)(length / sizeof(nint)); } + Debug.Assert(cbGCDesc == (byte*)gcDesc - (byte*)(ptr + 1)); + } - int numSeries = 0; - int i = 0; - - int first = -1; - int last = 0; - short numPtrs = 0; - while (i < bitfield.Length) + private static int GetMdArrayGCDescSize(ArrayType arrayType, out MethodTable* elementEEType) + { + Debug.Assert(arrayType.IsMdArray); + elementEEType = null; + TypeDesc elementType = arrayType.ElementType; + if (!elementType.IsValueType) { - if (bitfield[i]) - { - if (first == -1) - { - first = i; - baseOffset += first; - } - else if (gcdesc != null) - { - *ptr-- = (short)((i - last) * IntPtr.Size); - *ptr-- = numPtrs; - } - - numSeries++; - numPtrs = 0; - - while ((i < bitfield.Length) && (bitfield[i])) - { - numPtrs++; - i++; - } - - last = i; - } - else - { - i++; - } + Debug.Assert(!elementType.IsByRef); + return elementType.IsPointer || elementType.IsFunctionPointer ? 0 : 3 * sizeof(nint); } - if (gcdesc != null) - { - if (numSeries > 0) - { - *ptr-- = (short)((first + bitfield.Length - last) * IntPtr.Size); - *ptr-- = numPtrs; + RuntimeTypeHandle elementHandle = elementType.GetRuntimeTypeHandle(); + if (elementHandle.IsNull()) + elementHandle = elementType.ComputeTemplate().RuntimeTypeHandle; - *(void**)gcdesc = (void*)-numSeries; - *baseOffsetPtr = (void*)(baseOffset * IntPtr.Size); - } - } + elementEEType = elementHandle.ToEETypePtr(); + if (!elementEEType->ContainsGCPointers) + return 0; - return numSeries; + int series = (int)((nint*)elementEEType)[-1]; + Debug.Assert(series > 0); + return (series + 2) * sizeof(nint); } public static RuntimeTypeHandle CreateFunctionPointerEEType(uint hashCodeOfNewType, RuntimeTypeHandle returnTypeHandle, RuntimeTypeHandle[] parameterHandles, FunctionPointerType functionPointerType) @@ -599,7 +475,6 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta { Debug.Assert(0 == state.NonGcDataSize); Debug.Assert(!state.HasStaticConstructor); - Debug.Assert(0 == state.GcDataSize); Debug.Assert(0 == state.ThreadStaticOffset); Debug.Assert(IntPtr.Zero == state.GcStaticDesc); Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc); @@ -660,7 +535,15 @@ public static int GetDictionarySlotInVTable(TypeDesc type) return -1; // Dictionary slot is the first slot in the vtable after the base type's vtable entries - return type.BaseType != null ? type.BaseType.GetOrCreateTypeBuilderState().NumVTableSlots : 0; + DefType baseType = type.BaseType; + if (baseType is null) + return 0; + + RuntimeTypeHandle baseTypeHandle = baseType.GetRuntimeTypeHandle(); + if (baseTypeHandle.IsNull()) + baseTypeHandle = baseType.ComputeTemplate().RuntimeTypeHandle; + + return baseTypeHandle.ToEETypePtr()->NumVtableSlots; } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs index 7d2ead3b32aea2..61ec20a36ecd4b 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs @@ -30,7 +30,7 @@ public void Finish(TypeBuilder typeBuilder) IntPtr* realCells = (IntPtr*)_addressOfFirstCellSlot; for (int i = 0; i < _cells.Length; i++) { - _cells[i].WriteCellIntoDictionary(typeBuilder, realCells, i); + realCells[i] = _cells[i].Create(typeBuilder); } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index 422e725157c6c7..0d6c1c7124cbc4 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -19,11 +19,6 @@ public abstract class GenericDictionaryCell { internal abstract void Prepare(TypeBuilder builder); internal abstract IntPtr Create(TypeBuilder builder); - internal virtual unsafe void WriteCellIntoDictionary(TypeBuilder typeBuilder, IntPtr* pDictionary, int slotIndex) - { - pDictionary[slotIndex] = Create(typeBuilder); - } - internal virtual IntPtr CreateLazyLookupCell(TypeBuilder builder, out IntPtr auxResult) { auxResult = IntPtr.Zero; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs index 8dcc0721421946..e4e002af172696 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/MetadataNameExtensions.cs @@ -1,11 +1,6 @@ // 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.Collections.Generic; -using System.Reflection; -using System.Text; - using global::Internal.Metadata.NativeFormat; using Debug = System.Diagnostics.Debug; @@ -14,104 +9,15 @@ namespace Internal.Runtime.TypeLoader { internal static class MetadataNameExtensions { - public static string GetFullName(this Handle handle, MetadataReader reader) - { - switch (handle.HandleType) - { - case HandleType.TypeDefinition: - return handle.ToTypeDefinitionHandle(reader).GetFullName(reader); - case HandleType.TypeReference: - return handle.ToTypeReferenceHandle(reader).GetFullName(reader); - - case HandleType.NamespaceDefinition: - return handle.ToNamespaceDefinitionHandle(reader).GetFullName(reader); - case HandleType.NamespaceReference: - return handle.ToNamespaceReferenceHandle(reader).GetFullName(reader); - - case HandleType.TypeSpecification: - return handle.ToTypeSpecificationHandle(reader).GetFullName(reader); - case HandleType.TypeInstantiationSignature: - return handle.ToTypeInstantiationSignatureHandle(reader).GetFullName(reader); - - case HandleType.ArraySignature: - return handle.ToArraySignatureHandle(reader).GetFullName(reader); - case HandleType.SZArraySignature: - return handle.ToSZArraySignatureHandle(reader).GetFullName(reader); - - case HandleType.PointerSignature: - return handle.ToPointerSignatureHandle(reader).GetFullName(reader); - case HandleType.ByReferenceSignature: - return handle.ToByReferenceSignatureHandle(reader).GetFullName(reader); - - case HandleType.ScopeDefinition: - return handle.ToScopeDefinitionHandle(reader).GetFullName(reader); - case HandleType.ScopeReference: - return handle.ToScopeReferenceHandle(reader).GetFullName(reader); - } - return null; - } - - public static string GetFullName(this ByReferenceSignatureHandle handle, MetadataReader reader) - { - var result = handle.GetByReferenceSignature(reader).Type.GetFullName(reader); - if (result == null) return null; - return result + "&"; - } - - public static string GetFullName(this PointerSignatureHandle handle, MetadataReader reader) - { - var result = handle.GetPointerSignature(reader).Type.GetFullName(reader); - if (result == null) return null; - return result + "*"; - } - - public static string GetFullName(this ArraySignatureHandle handle, MetadataReader reader) - { - ArraySignature array = handle.GetArraySignature(reader); - var result = array.ElementType.GetFullName(reader); - if (result == null) return null; - return result + "[" + (new string(',', array.Rank - 1)) + "]"; - } - - public static string GetFullName(this SZArraySignatureHandle handle, MetadataReader reader) - { - var result = handle.GetSZArraySignature(reader).ElementType.GetFullName(reader); - if (result == null) return null; - return result + "[]"; - } - - public static string GetFullName(this TypeSpecificationHandle typeSpecHandle, MetadataReader reader) + private static string GetFullName(this Handle handle, MetadataReader reader) { - var typeSpec = typeSpecHandle.GetTypeSpecification(reader); - - if (typeSpec.Signature.IsNil) - return null; - - return typeSpec.Signature.GetFullName(reader); - } - - public static string GetFullName(this TypeInstantiationSignatureHandle typeInstSigHandle, MetadataReader reader) - { - var typeInstSig = typeInstSigHandle.GetTypeInstantiationSignature(reader); - - if (typeInstSig.GenericType.IsNil) - return null; - - var name = typeInstSig.GenericType.GetFullName(reader); - if (name == null) - return null; - - var index = 0; - string argsString = null; - foreach (var argHandle in typeInstSig.GenericTypeArguments) + return handle.HandleType switch { - if (index > 0) argsString += ","; - var argName = argHandle.GetFullName(reader); - if (argName == null) return name; - argsString += argName; - index++; - } - return name + "<" + argsString + ">"; + HandleType.TypeDefinition => handle.ToTypeDefinitionHandle(reader).GetFullName(reader), + HandleType.NamespaceDefinition => handle.ToNamespaceDefinitionHandle(reader).GetFullName(reader), + HandleType.ScopeDefinition => handle.ToScopeDefinitionHandle(reader).GetFullName(reader), + _ => null, + }; } public static void GetFullName(this TypeDefinitionHandle typeDefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) @@ -132,9 +38,9 @@ public static string GetFullName(this TypeDefinitionHandle typeDefHandle, Metada string nspace; typeDefHandle.GetFullName(reader, out name, out enclosing, out nspace); - if (enclosing != null && name != null) + if (enclosing is not null) return enclosing + "+" + name; - else if (nspace != null && name != null) + else if (nspace is not null) return nspace + "." + name; return name; @@ -170,78 +76,10 @@ public static string GetContainingModuleName(this TypeDefinitionHandle typeDefHa return "?"; } - public static string GetFullName(this NamespaceDefinitionHandle namespaceHandle, MetadataReader reader) - { - var nspace = namespaceHandle.GetNamespaceDefinition(reader); - - if (nspace.Name.IsNil) - return null; - - var name = nspace.Name.GetConstantStringValue(reader).Value; - var containingNamespace = nspace.ParentScopeOrNamespace.IsNil ? null : nspace.ParentScopeOrNamespace.GetFullName(reader); - - if (containingNamespace != null) - return containingNamespace + "." + name; - - return name; - } - - public static void GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader, out string name, out string enclosing, out string nspace) - { - var typeRef = typeRefHandle.GetTypeReference(reader); - - Debug.Assert(!typeRef.TypeName.IsNil); - - name = typeRef.TypeName.GetConstantStringValue(reader).Value; - enclosing = typeRef.ParentNamespaceOrType.HandleType == HandleType.TypeReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; - nspace = typeRef.ParentNamespaceOrType.HandleType == HandleType.NamespaceReference ? typeRef.ParentNamespaceOrType.GetFullName(reader) : null; - } - - public static string GetFullName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) - { - string name; - string enclosing; - string nspace; - typeRefHandle.GetFullName(reader, out name, out enclosing, out nspace); - - if (enclosing != null && name != null) - return enclosing + "+" + name; - else if (nspace != null && name != null) - return nspace + "." + name; - - return name; - } - - public static string GetContainingModuleName(this TypeReferenceHandle typeRefHandle, MetadataReader reader) - { - var typeRef = typeRefHandle.GetTypeReference(reader); - - Handle currentHandle = typeRef.ParentNamespaceOrType; - Debug.Assert(!currentHandle.IsNil); - - while (!currentHandle.IsNil) - { - switch (currentHandle.HandleType) - { - case HandleType.TypeReference: - case HandleType.NamespaceReference: - currentHandle = typeRef.ParentNamespaceOrType; - break; - - case HandleType.ScopeReference: - return currentHandle.GetFullName(reader); - - default: - return "?"; - } - } - - return "?"; - } - public static string GetFullName(this NamespaceReferenceHandle namespaceHandle, MetadataReader reader) + private static string GetFullName(this NamespaceDefinitionHandle namespaceHandle, MetadataReader reader) { - var nspace = namespaceHandle.GetNamespaceReference(reader); + var nspace = namespaceHandle.GetNamespaceDefinition(reader); if (nspace.Name.IsNil) return null; @@ -249,7 +87,7 @@ public static string GetFullName(this NamespaceReferenceHandle namespaceHandle, var name = nspace.Name.GetConstantStringValue(reader).Value; var containingNamespace = nspace.ParentScopeOrNamespace.IsNil ? null : nspace.ParentScopeOrNamespace.GetFullName(reader); - if (containingNamespace != null) + if (containingNamespace is not null) return containingNamespace + "." + name; return name; @@ -260,11 +98,5 @@ private static string GetFullName(this ScopeDefinitionHandle scopeDefHandle, Met var scopeDef = scopeDefHandle.GetScopeDefinition(reader); return scopeDef.Name.GetConstantStringValue(reader).Value; } - - private static string GetFullName(this ScopeReferenceHandle scopeRefHandle, MetadataReader reader) - { - var scopeRef = scopeRefHandle.GetScopeReference(reader); - return scopeRef.Name.GetConstantStringValue(reader).Value; - } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs index f4353f9db8f54b..4590ecdb747bdd 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/ModuleList.cs @@ -53,15 +53,7 @@ internal NativeFormatModuleInfo(TypeManagerHandle moduleHandle, IntPtr pBlob, in internal unsafe bool TryFindBlob(ReflectionMapBlob blobId, out byte* pBlob, out uint cbBlob) { - pBlob = null; - cbBlob = 0; - fixed (byte** ppBlob = &pBlob) - { - fixed (uint* pcbBlob = &cbBlob) - { - return RuntimeAugments.FindBlob(Handle, (int)blobId, new IntPtr(ppBlob), new IntPtr(pcbBlob)); - } - } + return TryFindBlob((int)blobId, out pBlob, out cbBlob); } public unsafe bool TryFindBlob(int blobId, out byte* pBlob, out uint cbBlob) @@ -79,10 +71,7 @@ public unsafe bool TryFindBlob(int blobId, out byte* pBlob, out uint cbBlob) } /// - /// This class represents a linear module list and a dictionary mapping module handles - /// to its indices. When a new module is registered, a new instance of this class gets - /// constructed and atomically updates the _loadedModuleMap so that at any point in time - /// all threads see the map as consistent. + /// The initially loaded modules and a mapping from module handles to their indices. /// internal sealed class ModuleMap { @@ -239,7 +228,7 @@ public NativeFormatModuleInfo Current /// public sealed class ModuleList { - private ModuleMap _loadedModuleMap; + private readonly ModuleMap _loadedModuleMap; /// /// Module list is a process-wide singleton that physically lives in the TypeLoaderEnvironment instance. @@ -362,18 +351,7 @@ public NativeFormatModuleInfo GetModuleInfoForMetadataReader(MetadataReader read /// Module handle of the module containing the given reader public TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader) { - foreach (ModuleInfo moduleInfo in _loadedModuleMap.Modules) - { - NativeFormatModuleInfo nativeFormatModuleInfo = moduleInfo as NativeFormatModuleInfo; - if (nativeFormatModuleInfo != null && nativeFormatModuleInfo.MetadataReader == reader) - { - return moduleInfo.Handle; - } - } - - // We should never have a reader that is not associated with a module (where does it come from?!) - Debug.Assert(false); - return default(TypeManagerHandle); + return GetModuleInfoForMetadataReader(reader)?.Handle ?? default; } /// diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs index 9e7cf7f40fa423..258818fa562b9b 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInterfacesAlgorithm.cs @@ -1,11 +1,7 @@ // 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 Internal.NativeFormat; -using Internal.Runtime.Augments; using Internal.TypeSystem; namespace Internal.Runtime.TypeLoader @@ -18,77 +14,18 @@ internal class NativeLayoutInterfacesAlgorithm : RuntimeInterfacesAlgorithm public override DefType[] ComputeRuntimeInterfaces(TypeDesc type) { TypeBuilderState state = type.GetOrCreateTypeBuilderState(); - int totalInterfaces = RuntimeAugments.GetInterfaceCount(state.TemplateType.RuntimeTypeHandle); - - TypeLoaderLogger.WriteLine("Building runtime interfaces for type " + type.ToString() + " (total interfaces = " + totalInterfaces.LowLevelToString() + ") ..."); - - DefType[] interfaces = new DefType[totalInterfaces]; - int numInterfaces = 0; - - // - // Copy over all interfaces from base class - // - if (type.BaseType != null) - { - foreach (var baseInterface in type.BaseType.RuntimeInterfaces) - { - // There should be no duplicates - Debug.Assert(!InterfaceInSet(interfaces, numInterfaces, baseInterface)); - interfaces[numInterfaces++] = baseInterface; - TypeLoaderLogger.WriteLine(" -> Added basetype interface " + baseInterface.ToString() + " on type " + type.ToString()); - } - } - NativeParser typeInfoParser = state.GetParserForNativeLayoutInfo(); NativeParser interfaceParser = typeInfoParser.GetParserForBagElementKind(BagElementKind.ImplementedInterfaces); - TypeDesc[] implementedInterfaces; - if (!interfaceParser.IsNull) - implementedInterfaces = state.NativeLayoutInfo.LoadContext.GetTypeSequence(ref interfaceParser); - else - implementedInterfaces = TypeDesc.EmptyTypes; + uint count = interfaceParser.IsNull ? 0 : interfaceParser.GetSequenceCount(); - // Note that the order in which the interfaces are added to the list is same as the order in which the MDIL binder adds them. - // It is required for correctness - - foreach (TypeDesc interfaceType in implementedInterfaces) + // The compiler emits the complete slot-ordered list. Substitution can make distinct slots equal. + DefType[] interfaces = new DefType[count]; + for (int i = 0; i < interfaces.Length; i++) { - DefType interfaceTypeAsDefType = (DefType)interfaceType; - - // Skip duplicates - if (InterfaceInSet(interfaces, numInterfaces, interfaceTypeAsDefType)) - continue; - interfaces[numInterfaces++] = interfaceTypeAsDefType; - - TypeLoaderLogger.WriteLine(" -> Added interface " + interfaceTypeAsDefType.ToString() + " on type " + type.ToString()); - - foreach (var inheritedInterface in interfaceTypeAsDefType.RuntimeInterfaces) - { - // Skip duplicates - if (InterfaceInSet(interfaces, numInterfaces, inheritedInterface)) - continue; - interfaces[numInterfaces++] = inheritedInterface; - TypeLoaderLogger.WriteLine(" -> Added inherited interface " + inheritedInterface.ToString() + " on type " + type.ToString()); - } + interfaces[i] = (DefType)state.NativeLayoutInfo.LoadContext.GetType(ref interfaceParser); } - // TODO: Handle the screwy cases of generic interface folding - Debug.Assert(numInterfaces == totalInterfaces, "Unexpected number of interfaces"); - return interfaces; } - - /// - /// Checks if the interface exists in the list of interfaces - /// - private static bool InterfaceInSet(DefType[] interfaces, int numInterfaces, DefType interfaceType) - { - for (int i = 0; i < numInterfaces; i++) - { - if (interfaces[i].Equals(interfaceType)) - return true; - } - - return false; - } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs index 07c322552dc825..d11e415151210a 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TemplateLocator.cs @@ -10,7 +10,7 @@ namespace Internal.Runtime.TypeLoader { - internal struct TemplateLocator + internal static class TemplateLocator { private const uint BadTokenFixupValue = 0xFFFFFFFF; @@ -19,14 +19,9 @@ internal struct TemplateLocator // public static TypeDesc TryGetTypeTemplate(TypeDesc concreteType, ref NativeLayoutInfo nativeLayoutInfo) { - return TryGetTypeTemplate_Internal(concreteType, CanonicalFormKind.Specific, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset); - } - - private static TypeDesc TryGetTypeTemplate_Internal(TypeDesc concreteType, CanonicalFormKind kind, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) - { - nativeLayoutInfoModule = null; - nativeLayoutInfoToken = 0; - var canonForm = concreteType.ConvertToCanonForm(kind); + nativeLayoutInfo.Module = null; + nativeLayoutInfo.Offset = 0; + var canonForm = concreteType.ConvertToCanonForm(CanonicalFormKind.Specific); var hashCode = canonForm.GetHashCode(); foreach (NativeFormatModuleInfo moduleInfo in ModuleList.EnumerateModules()) @@ -45,18 +40,18 @@ private static TypeDesc TryGetTypeTemplate_Internal(TypeDesc concreteType, Canon RuntimeTypeHandle candidateTemplateTypeHandle = externalFixupsTable.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); TypeDesc candidateTemplate = concreteType.Context.ResolveRuntimeTypeHandle(candidateTemplateTypeHandle); - if (canonForm == candidateTemplate.ConvertToCanonForm(kind)) + if (canonForm == candidateTemplate.ConvertToCanonForm(CanonicalFormKind.Specific)) { TypeLoaderLogger.WriteLine("Found template for type " + concreteType.ToString() + ": " + candidateTemplate.ToString()); - nativeLayoutInfoToken = entryParser.GetUnsigned(); - if (nativeLayoutInfoToken == BadTokenFixupValue) + nativeLayoutInfo.Offset = entryParser.GetUnsigned(); + if (nativeLayoutInfo.Offset == BadTokenFixupValue) { // TODO: once multifile gets fixed up, make this throw a BadImageFormatException TypeLoaderLogger.WriteLine("ERROR: template not fixed up, skipping"); continue; } - nativeLayoutInfoModule = moduleInfo; + nativeLayoutInfo.Module = moduleInfo; return candidateTemplate; } } @@ -70,14 +65,10 @@ private static TypeDesc TryGetTypeTemplate_Internal(TypeDesc concreteType, Canon // Returns the template method for a generic method instantiation // public static InstantiatedMethod TryGetGenericMethodTemplate(InstantiatedMethod concreteMethod, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) - { - return TryGetGenericMethodTemplate_Internal(concreteMethod, CanonicalFormKind.Specific, out nativeLayoutInfoModule, out nativeLayoutInfoToken); - } - private static InstantiatedMethod TryGetGenericMethodTemplate_Internal(InstantiatedMethod concreteMethod, CanonicalFormKind kind, out NativeFormatModuleInfo nativeLayoutInfoModule, out uint nativeLayoutInfoToken) { nativeLayoutInfoModule = null; nativeLayoutInfoToken = 0; - var canonForm = concreteMethod.GetCanonMethodTarget(kind); + var canonForm = concreteMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); var hashCode = canonForm.GetHashCode(); foreach (NativeFormatModuleInfo moduleInfo in ModuleList.EnumerateModules()) @@ -110,7 +101,7 @@ private static InstantiatedMethod TryGetGenericMethodTemplate_Internal(Instantia var candidateTemplate = (InstantiatedMethod)context.GetMethod(ref methodSignatureParser); Debug.Assert(candidateTemplate.Instantiation.Length > 0); - if (canonForm == candidateTemplate.GetCanonMethodTarget(kind)) + if (canonForm == candidateTemplate.GetCanonMethodTarget(CanonicalFormKind.Specific)) { TypeLoaderLogger.WriteLine("Found template for generic method " + concreteMethod.ToString() + ": " + candidateTemplate.ToString()); nativeLayoutInfoModule = moduleInfo; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs index fbfd71f31e7b84..ce9ca9ce47d925 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -55,7 +55,7 @@ internal static bool RetrieveMethodDictionaryIfPossible(InstantiatedMethod metho IntPtr methodDictionary; - if (TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionary(new TypeLoaderEnvironment.MethodDescBasedGenericMethodLookup(method), out methodDictionary)) + if (TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionary(new TypeLoaderEnvironment.GenericMethodLookupData(method), out methodDictionary)) { TypeLoaderLogger.WriteLine("Found DICT = " + methodDictionary.LowLevelToString() + " for method " + method.ToString()); method.AssociateWithRuntimeMethodDictionary(methodDictionary); @@ -143,15 +143,12 @@ internal void PrepareType(TypeDesc type) TypeLoaderLogger.WriteLine("Preparing type " + type.ToString() + " ..."); TypeBuilderState state = type.GetTypeBuilderStateIfExist(); - bool hasTypeHandle = type.RetrieveRuntimeTypeHandleIfPossible(); - - // If this type has type handle, do nothing and return unless we should prepare even in the presence of a type handle - if (hasTypeHandle) + if (type.RetrieveRuntimeTypeHandleIfPossible()) return; state ??= type.GetOrCreateTypeBuilderState(); - // If this type was already prepared, do nothing unless we are re-preparing it for the purpose of loading the field layout + // Avoid recursively preparing the same type. if (state.HasBeenPrepared) { return; @@ -160,41 +157,17 @@ internal void PrepareType(TypeDesc type) state.HasBeenPrepared = true; state.NeedsTypeHandle = true; - if (!hasTypeHandle) - { - InsertIntoNeedsTypeHandleList(type); - } - - bool noExtraPreparation = false; // Set this to true for types which don't need other types to be prepared. I.e GenericTypeDefinitions + InsertIntoNeedsTypeHandleList(type); if (type is DefType typeAsDefType) { - if (typeAsDefType.HasInstantiation) - { - if (typeAsDefType.IsTypeDefinition) - { - noExtraPreparation = true; - } - else - { - // This call to ComputeTemplate will find the native layout info for the type, and the template - // For metadata loaded types, a template will not exist, but we may find the NativeLayout describing the generic dictionary - TypeDesc.ComputeTemplate(state, false); - - Debug.Assert(state.TemplateType == null || (state.TemplateType is DefType && !state.TemplateType.RuntimeTypeHandle.IsNull())); + Debug.Assert(typeAsDefType.HasInstantiation && !typeAsDefType.IsTypeDefinition); + TypeDesc.ComputeTemplate(state); - // Collect dependencies + foreach (var instArg in typeAsDefType.Instantiation) + RegisterForPreparation(instArg); - // We need the instantiation arguments to register a generic type - foreach (var instArg in typeAsDefType.Instantiation) - RegisterForPreparation(instArg); - - ParseNativeLayoutInfo(state, type); - } - } - - if (!noExtraPreparation) - state.PrepareStaticGCLayout(); + ParseNativeLayoutInfo(state, type); } else if (type is ParameterizedType) { @@ -226,14 +199,10 @@ internal void PrepareType(TypeDesc type) Debug.Assert(false); } - // Need to prepare the base type first since it is used to compute interfaces - if (!noExtraPreparation) - { - PrepareBaseTypeAndDictionaries(type); - PrepareRuntimeInterfaces(type); + PrepareBaseTypeAndDictionaries(type); + PrepareRuntimeInterfaces(type); - TypeLoaderLogger.WriteLine("Layout for type " + type.ToString() + " complete."); - } + TypeLoaderLogger.WriteLine("Layout for type " + type.ToString() + " complete."); } /// @@ -241,8 +210,6 @@ internal void PrepareType(TypeDesc type) /// private void PrepareRuntimeInterfaces(TypeDesc type) { - // Prepare all the interfaces that might be used. (This can be a superset of the - // interfaces explicitly in the NativeLayout.) foreach (DefType interfaceType in type.RuntimeInterfaces) { PrepareType(interfaceType); @@ -381,18 +348,6 @@ internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type) state.NonGcDataSize = checked((int)typeInfoParser.GetUnsigned()); break; - case BagElementKind.GcStaticDataSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDataSize"); - // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) - state.GcDataSize = checked((int)typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.ThreadStaticDataSize: - TypeLoaderLogger.WriteLine("Found BagElementKind.ThreadStaticDataSize"); - // Use checked typecast to int to ensure there aren't any overflows/truncations (size value used in allocation of memory later) - state.ThreadDataSize = checked((int)typeInfoParser.GetUnsigned()); - break; - case BagElementKind.GcStaticDesc: TypeLoaderLogger.WriteLine("Found BagElementKind.GcStaticDesc"); state.GcStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned()); @@ -424,87 +379,6 @@ internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type) type.ParseBaseType(context, baseTypeParser); } - /// - /// Wraps information about how a type is laid out into one package. Types may have been laid out by - /// TypeBuilder (which means they have a gc bitfield), or they could be types that were laid out by NUTC - /// (which means we only have a GCDesc for them). This struct wraps both of those possibilities into - /// one package to be able to write that layout to another bitfield we are constructing. (This is for - /// struct fields.) - /// - internal unsafe struct GCLayout - { - private bool[] _bitfield; - private unsafe void* _gcdesc; - private int _size; - - public static GCLayout None { get { return default(GCLayout); } } - public static GCLayout SingleReference { get; } = new GCLayout([true]); - - public bool IsNone { get { return _bitfield == null && _gcdesc == null; } } - - public GCLayout(bool[] bitfield) - { - Debug.Assert(bitfield != null); - - _bitfield = bitfield; - _gcdesc = null; - _size = 0; - } - - public GCLayout(RuntimeTypeHandle rtth) - { - MethodTable* MethodTable = rtth.ToEETypePtr(); - Debug.Assert(MethodTable != null); - - _bitfield = null; - _gcdesc = MethodTable->ContainsGCPointers ? (void**)MethodTable - 1 : null; - _size = (int)MethodTable->BaseSize; - } - - /// - /// Gets this layout in bitfield array. - /// - /// The layout in bitfield. - public bool[] AsBitfield() - { - // This method should only be called when not none. - Debug.Assert(!IsNone); - - // Ensure exactly one of these two are set. - Debug.Assert(_gcdesc != null ^ _bitfield != null); - - return _bitfield ?? WriteGCDescToBitfield(); - } - - private unsafe bool[] WriteGCDescToBitfield() - { - void** ptr = (void**)_gcdesc; - Debug.Assert(_gcdesc != null); - - // Number of series - int count = (int)*ptr-- - 1; - Debug.Assert(count >= 0); - - // Ensure capacity for the values we are about to write - int capacity = _size / IntPtr.Size - 2; - bool[] bitfield = new bool[capacity]; - - while (count-- >= 0) - { - int offs = (int)*ptr-- / IntPtr.Size - 1; - int len = ((int)*ptr-- + _size) / IntPtr.Size; - - Debug.Assert(len > 0); - Debug.Assert(offs >= 0); - - for (int i = 0; i < len; i++) - bitfield[offs + i] = true; - } - - return bitfield; - } - } - private unsafe void AllocateRuntimeType(TypeDesc type) { TypeBuilderState state = type.GetTypeBuilderState(); @@ -513,7 +387,7 @@ private unsafe void AllocateRuntimeType(TypeDesc type) RuntimeTypeHandle rtt = EETypeCreator.CreateEEType(type, state); - if (state.ThreadDataSize != 0) + if (state.ThreadStaticDesc != IntPtr.Zero) TypeLoaderEnvironment.Instance.RegisterDynamicThreadStaticsInfo(state.HalfBakedRuntimeTypeHandle, state.ThreadStaticOffset, state.ThreadStaticDesc); TypeLoaderLogger.WriteLine("Allocated new type " + type.ToString() + " with hashcode value = 0x" + type.GetHashCode().LowLevelToString() + " with MethodTable = " + rtt.ToIntPtr().LowLevelToString() + " of size " + rtt.ToEETypePtr()->RawBaseSize.LowLevelToString()); @@ -561,41 +435,12 @@ public RuntimeTypeHandle[] GetRuntimeTypeHandles(Instantiation types) return result; } - public static DefType GetBaseTypeUsingRuntimeTypeHandle(TypeDesc type) - { - type.RetrieveRuntimeTypeHandleIfPossible(); - unsafe - { - RuntimeTypeHandle thBaseTypeTemplate = type.RuntimeTypeHandle.ToEETypePtr()->BaseType->ToRuntimeTypeHandle(); - if (thBaseTypeTemplate.IsNull()) - return null; - - return (DefType)type.Context.ResolveRuntimeTypeHandle(thBaseTypeTemplate); - } - } - - public static DefType GetBaseTypeThatIsCorrectForMDArrays(TypeDesc type) - { - if (type.BaseType == type.Context.GetWellKnownType(WellKnownType.Array)) - { - // Use the type from the template, the metadata we have will be inaccurate for multidimensional - // arrays, as we hide the MDArray infrastructure from the metadata. - TypeDesc template = type.ComputeTemplate(false); - return GetBaseTypeUsingRuntimeTypeHandle(template ?? type); - } - - return type.BaseType; - } - private void FinishInterfaces(TypeBuilderState state) { - DefType[] interfaces = state.RuntimeInterfaces; - if (interfaces != null) + DefType[] interfaces = state.TypeBeingBuilt.RuntimeInterfaces; + for (int i = 0; i < interfaces.Length; i++) { - for (int i = 0; i < interfaces.Length; i++) - { - state.HalfBakedRuntimeTypeHandle.SetInterface(i, GetRuntimeTypeHandle(interfaces[i])); - } + state.HalfBakedRuntimeTypeHandle.SetInterface(i, GetRuntimeTypeHandle(interfaces[i])); } } @@ -651,33 +496,26 @@ private unsafe void FinishClassConstructor(TypeDesc type, TypeBuilderState state private void CopyDictionaryFromTypeToAppropriateSlotInDerivedType(DefType baseType, TypeBuilderState derivedTypeState) { - var baseTypeState = baseType.GetOrCreateTypeBuilderState(); + if (!baseType.CanShareNormalGenericCode()) + return; - if (baseTypeState.HasDictionaryInVTable) - { - RuntimeTypeHandle baseTypeHandle = GetRuntimeTypeHandle(baseType); - - // If the basetype is currently being created by the TypeBuilder, we need to get its dictionary pointer from the - // TypeBuilder state (at this point, the dictionary has not yet been set on the baseTypeHandle). If - // the basetype is not a dynamic type, or has previously been dynamically allocated in the past, the TypeBuilder - // state will have a null dictionary pointer, in which case we need to read it directly from the basetype's vtable - IntPtr dictionaryEntry = baseTypeState.HalfBakedDictionary; - if (dictionaryEntry == IntPtr.Zero) - dictionaryEntry = baseTypeHandle.GetDictionary(); - Debug.Assert(dictionaryEntry != IntPtr.Zero); - - // Compute the vtable slot for the dictionary entry to set - int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(baseType); - Debug.Assert(dictionarySlot >= 0); - - derivedTypeState.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, dictionaryEntry); - TypeLoaderLogger.WriteLine("Setting basetype " + baseType.ToString() + " dictionary on type " + derivedTypeState.TypeBeingBuilt.ToString()); - } + // An unpublished base may not have its base pointer or dictionary slot initialized yet. + RuntimeTypeHandle baseTypeHandle = baseType.RuntimeTypeHandle; + IntPtr dictionaryEntry = baseTypeHandle.IsNull() + ? baseType.GetTypeBuilderState().HalfBakedDictionary + : baseTypeHandle.GetDictionary(); + if (dictionaryEntry == IntPtr.Zero) + return; + + int dictionarySlot = EETypeCreator.GetDictionarySlotInVTable(baseType); + Debug.Assert(dictionarySlot >= 0); + derivedTypeState.HalfBakedRuntimeTypeHandle.SetDictionary(dictionarySlot, dictionaryEntry); + TypeLoaderLogger.WriteLine("Setting basetype " + baseType.ToString() + " dictionary on type " + derivedTypeState.TypeBeingBuilt.ToString()); } private void FinishBaseTypeAndDictionaries(TypeDesc type, TypeBuilderState state) { - DefType baseType = GetBaseTypeThatIsCorrectForMDArrays(type); + DefType baseType = type.BaseType; state.HalfBakedRuntimeTypeHandle.SetBaseType(baseType == null ? default(RuntimeTypeHandle) : GetRuntimeTypeHandle(baseType)); if (baseType == null) @@ -699,19 +537,11 @@ private void FinishRuntimeType(TypeDesc type) if (type is DefType typeAsDefType) { - if (type.HasInstantiation) - { - // Type definitions don't need any further finishing once created by the EETypeCreator - if (type.IsTypeDefinition) - return; - - state.HalfBakedRuntimeTypeHandle.SetGenericDefinition(GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition())); - Instantiation instantiation = typeAsDefType.Instantiation; - for (int argIndex = 0; argIndex < instantiation.Length; argIndex++) - { - state.HalfBakedRuntimeTypeHandle.SetGenericArgument(argIndex, GetRuntimeTypeHandle(instantiation[argIndex])); - } - } + Debug.Assert(type.HasInstantiation && !type.IsTypeDefinition); + state.HalfBakedRuntimeTypeHandle.SetGenericDefinition(GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition())); + Instantiation instantiation = typeAsDefType.Instantiation; + for (int argIndex = 0; argIndex < instantiation.Length; argIndex++) + state.HalfBakedRuntimeTypeHandle.SetGenericArgument(argIndex, GetRuntimeTypeHandle(instantiation[argIndex])); FinishBaseTypeAndDictionaries(type, state); @@ -726,13 +556,16 @@ private void FinishRuntimeType(TypeDesc type) RuntimeTypeHandle elementTypeHandle = GetRuntimeTypeHandle(typeAsSzArrayType.ElementType); state.HalfBakedRuntimeTypeHandle.SetRelatedParameterType(elementTypeHandle); - ushort componentSize = (ushort)IntPtr.Size; - unsafe + if (typeAsSzArrayType.IsMdArray) { - if (typeAsSzArrayType.ElementType.IsValueType) - componentSize = checked((ushort)elementTypeHandle.ToEETypePtr()->ValueTypeSize); + ushort componentSize = (ushort)IntPtr.Size; + unsafe + { + if (typeAsSzArrayType.ElementType.IsValueType) + componentSize = checked((ushort)elementTypeHandle.ToEETypePtr()->ValueTypeSize); + } + state.HalfBakedRuntimeTypeHandle.SetComponentSize(componentSize); } - state.HalfBakedRuntimeTypeHandle.SetComponentSize(componentSize); FinishInterfaces(state); } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs index f351a128572459..932cb1ffaafb87 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Diagnostics; using Internal.NativeFormat; @@ -47,7 +46,6 @@ public TypeBuilderState(TypeDesc typeBeingBuilt) public IntPtr HalfBakedDictionary; private bool _templateComputed; - private bool _nativeLayoutTokenComputed; private TypeDesc _templateType; public TypeDesc TemplateType @@ -61,32 +59,19 @@ public TypeDesc TemplateType if (TypeBeingBuilt.IsMdArray) { _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(object[,]).TypeHandle); - _templateTypeLoaderNativeLayout = false; - _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; - - return _templateType; } - - // Arrays of pointers don't implement generic interfaces and are special cases. They use - // typeof(char*[]) as their template. - if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType && + else if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType && (elementType.IsPointer || elementType.IsFunctionPointer)) { + // Pointer arrays don't implement generic interfaces. _templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(char*[]).TypeHandle); - _templateTypeLoaderNativeLayout = false; - _nativeLayoutComputed = _nativeLayoutTokenComputed = _templateComputed = true; - - return _templateType; } - - // Locate the template type and native layout info - _templateType = TemplateLocator.TryGetTypeTemplate(TypeBeingBuilt, ref _nativeLayoutInfo); - Debug.Assert(_templateType == null || !_templateType.RuntimeTypeHandle.IsNull()); - - _templateTypeLoaderNativeLayout = true; + else + { + _templateType = TemplateLocator.TryGetTypeTemplate(TypeBeingBuilt, ref _nativeLayoutInfo); + Debug.Assert(_templateType is null || !_templateType.RuntimeTypeHandle.IsNull()); + } _templateComputed = true; - if (_templateType != null) - _nativeLayoutTokenComputed = true; } return _templateType; @@ -94,7 +79,6 @@ public TypeDesc TemplateType } private bool _nativeLayoutComputed; - private bool _templateTypeLoaderNativeLayout; private NativeLayoutInfo _nativeLayoutInfo; @@ -102,17 +86,9 @@ private void EnsureNativeLayoutInfoComputed() { if (!_nativeLayoutComputed) { - if (!_nativeLayoutTokenComputed) - { - if (!_templateComputed) - { - // Attempt to compute native layout through as a non-ReadyToRun template - object _ = this.TemplateType; - } - _nativeLayoutTokenComputed = true; - } + _ = TemplateType; - if (_nativeLayoutInfo.Module != null) + if (_nativeLayoutInfo.Module is not null) { FinishInitNativeLayoutInfo(TypeBeingBuilt, ref _nativeLayoutInfo); } @@ -164,137 +140,15 @@ public NativeLayoutInfo NativeLayoutInfo public NativeParser GetParserForNativeLayoutInfo() { EnsureNativeLayoutInfoComputed(); - if (_templateTypeLoaderNativeLayout) + if (_nativeLayoutInfo.Reader is not null) return new NativeParser(_nativeLayoutInfo.Reader, _nativeLayoutInfo.Offset); else return default(NativeParser); } - // RuntimeInterfaces is the full list of interfaces that the type implements. It can include private internal implementation - // detail interfaces that nothing is known about. - public DefType[] RuntimeInterfaces - { - get - { - // Generic Type Definitions have no runtime interfaces - if (TypeBeingBuilt.IsGenericDefinition) - return null; - - return TypeBeingBuilt.RuntimeInterfaces; - } - } - - private bool? _hasDictionarySlotInVTable; - private bool ComputeHasDictionarySlotInVTable() - { - if (!TypeBeingBuilt.IsGeneric() && !(TypeBeingBuilt is ArrayType)) - return false; - - // Generic interfaces always have a dictionary slot - if (TypeBeingBuilt.IsInterface) - return true; - - return TypeBeingBuilt.CanShareNormalGenericCode(); - } - - public bool HasDictionarySlotInVTable - { - get - { - _hasDictionarySlotInVTable ??= ComputeHasDictionarySlotInVTable(); - return _hasDictionarySlotInVTable.Value; - } - } - - private bool? _hasDictionaryInVTable; - private bool ComputeHasDictionaryInVTable() - { - if (!HasDictionarySlotInVTable) - return false; - - if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) - { - // Type was already constructed - return TypeBeingBuilt.RuntimeTypeHandle.GetDictionary() != IntPtr.Zero; - } - else - { - // Type is being newly constructed - Debug.Assert(TemplateType != null); - NativeParser parser = GetParserForNativeLayoutInfo(); - // Template type loader case - var dictionaryLayoutParser = parser.GetParserForBagElementKind(BagElementKind.DictionaryLayout); - - return !dictionaryLayoutParser.IsNull; - } - } - - public bool HasDictionaryInVTable - { - get - { - _hasDictionaryInVTable ??= ComputeHasDictionaryInVTable(); - return _hasDictionaryInVTable.Value; - } - } - - private ushort? _numVTableSlots; - private ushort ComputeNumVTableSlots() - { - if (TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible()) - { - unsafe - { - return TypeBeingBuilt.RuntimeTypeHandle.ToEETypePtr()->NumVtableSlots; - } - } - else - { - // Template type loader case - unsafe - { - if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef || TypeBeingBuilt.IsFunctionPointer) - { - // Pointers and byrefs don't have vtable slots - return 0; - } - if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType - && (elementType.IsPointer || elementType.IsFunctionPointer))) - { - // MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from. - // They do not implement the generic interfaces that make this interesting for normal arrays. - return TypeBeingBuilt.BaseType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; - } - else - { - Debug.Assert(TypeBeingBuilt.IsTemplateCanonical()); - - TypeDesc templateType = TypeBeingBuilt.ComputeTemplate(false); - Debug.Assert(templateType != null); - - // Canonical template type loader case - return templateType.GetRuntimeTypeHandle().ToEETypePtr()->NumVtableSlots; - } - } - } - } - - public ushort NumVTableSlots - { - get - { - _numVTableSlots ??= ComputeNumVTableSlots(); - - return _numVTableSlots.Value; - } - } - - public GenericTypeDictionary Dictionary; public int NonGcDataSize; - public int GcDataSize; - public int ThreadDataSize; public bool HasStaticConstructor => ClassConstructorPointer.HasValue; @@ -302,181 +156,5 @@ public ushort NumVTableSlots public IntPtr GcStaticDesc; public IntPtr ThreadStaticDesc; public uint ThreadStaticOffset; - public GenericVariance[] GenericVarianceFlags; - - // Sentinel static to allow us to initialize _instanceLayout to something - // and then detect that InstanceGCLayout should return null - private static readonly bool[] s_emptyLayout = []; - - private bool[] _instanceGCLayout; - - /// - /// The instance gc layout of a dynamically laid out type. - /// null if one of the following is true - /// 1) For an array type: - /// - the type is a reference array - /// 2) For a generic type: - /// - the type has no GC instance fields - /// - the type already has a type handle - /// - the type has a non-universal canonical template - /// - the type has already been constructed - /// - /// If the type is a valuetype array, this is the layout of the valuetype held in the array if the type has GC reference fields - /// Otherwise, it is the layout of the fields in the type. - /// - public bool[] InstanceGCLayout - { - get - { - if (_instanceGCLayout == null) - { - if (TypeBeingBuilt is ArrayType) - { - if (!IsArrayOfReferenceTypes) - { - ArrayType arrayType = (ArrayType)TypeBeingBuilt; - TypeBuilder.GCLayout elementGcLayout = GetFieldGCLayout(arrayType.ElementType); - if (!elementGcLayout.IsNone) - { - _instanceGCLayout = elementGcLayout.AsBitfield(); - } - } - else - { - // Array of reference type returns null - _instanceGCLayout = s_emptyLayout; - } - } - else - { - Debug.Assert(TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible() || - (TypeBeingBuilt is PointerType) || - (TypeBeingBuilt is ByRefType) || - (TypeBeingBuilt is FunctionPointerType) || - TypeBeingBuilt.IsTemplateCanonical()); - _instanceGCLayout = s_emptyLayout; - } - } - - if (_instanceGCLayout == s_emptyLayout) - return null; - else - return _instanceGCLayout; - } - } - - private bool _staticGCLayoutPrepared; - - /// - /// Prepare the StaticGCLayout/ThreadStaticGCLayout/GcStaticDesc/ThreadStaticDesc fields by - /// reading native layout or metadata as appropriate. This method should only be called for types which - /// are actually to be created. - /// - public void PrepareStaticGCLayout() - { - if (!_staticGCLayoutPrepared) - { - _staticGCLayoutPrepared = true; - DefType defType = TypeBeingBuilt as DefType; - - if (defType == null) - { - // Array/pointer types do not have static fields - } - else if (defType.IsTemplateCanonical()) - { - // Canonical templates get their layout directly from the NativeLayoutInfo. - // Parse it and pull that info out here. - - NativeParser typeInfoParser = GetParserForNativeLayoutInfo(); - - BagElementKind kind; - while ((kind = typeInfoParser.GetBagElementKind()) != BagElementKind.End) - { - switch (kind) - { - case BagElementKind.GcStaticDesc: - GcStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - - case BagElementKind.ThreadStaticDesc: - ThreadStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - - default: - typeInfoParser.SkipInteger(); - break; - } - } - } - else - { - // SUPPORTS_NATIVE_METADATA_TYPE_LOADING - // We land here instead of throwing MissingTemplateException. We really should throw. - } - } - } - - /// - /// Get the GC layout of a type when used as a field. - /// NOTE: if the fieldtype is a reference type, this function will return GCLayout.None - /// Consumers of the api must handle that special case. - /// - private static unsafe TypeBuilder.GCLayout GetFieldGCLayout(TypeDesc fieldType) - { - if (!fieldType.IsValueType) - { - Debug.Assert(!fieldType.IsByRef); - if (fieldType.IsPointer || fieldType.IsFunctionPointer) - return TypeBuilder.GCLayout.None; - else - return TypeBuilder.GCLayout.SingleReference; - } - - // Is this a type that already exists? If so, get its gclayout from the MethodTable directly - if (fieldType.RetrieveRuntimeTypeHandleIfPossible()) - { - return new TypeBuilder.GCLayout(fieldType.RuntimeTypeHandle); - } - - // The type of the field must be a valuetype that is dynamically being constructed - - Debug.Assert(fieldType.IsTemplateCanonical()); - // Pull the GC Desc from the canonical instantiation - TypeDesc templateType = fieldType.ComputeTemplate(); - bool success = templateType.RetrieveRuntimeTypeHandleIfPossible(); - Debug.Assert(success); - return new TypeBuilder.GCLayout(templateType.RuntimeTypeHandle); - } - - - public bool IsArrayOfReferenceTypes - { - get - { - ArrayType typeAsArrayType = TypeBeingBuilt as ArrayType; - if (typeAsArrayType != null) - return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer && !typeAsArrayType.ParameterType.IsFunctionPointer; - else - return false; - } - } - - // Rank for arrays, -1 is used for an SzArray, and a positive number for a multidimensional array. - public int? ArrayRank - { - get - { - if (!TypeBeingBuilt.IsArray) - return null; - else if (TypeBeingBuilt.IsSzArray) - return -1; - else - { - Debug.Assert(TypeBeingBuilt.IsMdArray); - return ((ArrayType)TypeBeingBuilt).Rank; - } - } - } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs index 4f3555016d4a0b..d571fd65e4edaa 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.ConstructedGenericMethodsLookup.cs @@ -16,7 +16,7 @@ namespace Internal.Runtime.TypeLoader { public sealed partial class TypeLoaderEnvironment { - internal class GenericMethodEntry + internal sealed class GenericMethodEntry { private int? _hashCode; public bool _isRegisteredSuccessfully; @@ -43,7 +43,7 @@ public override bool Equals(object obj) return base.Equals(obj); } - public virtual bool IsEqualToEntryByComponentsComparison(GenericMethodEntry other) + public bool IsEqualToEntryByComponentsComparison(GenericMethodEntry other) { if (!other._declaringTypeHandle.Equals(_declaringTypeHandle)) return false; @@ -130,21 +130,15 @@ protected override GenericMethodEntry CreateValueFromKey(IntPtr key) } } - internal abstract class GenericMethodLookupData + internal readonly struct 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; + private readonly InstantiatedMethod _methodToLookup; - internal MethodDescBasedGenericMethodLookup(InstantiatedMethod methodToLookup) { _methodToLookup = methodToLookup; } + internal GenericMethodLookupData(InstantiatedMethod methodToLookup) { _methodToLookup = methodToLookup; } - internal override int LookupHashCode() { return _methodToLookup.GetHashCode(); } + internal int LookupHashCode() { return _methodToLookup.GetHashCode(); } - internal override bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle) + internal bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle) { // // Entries read from the hashtable are loaded as GenericMethodDescs, and compared to the input. @@ -173,13 +167,13 @@ internal override bool MatchParsedEntry(ref NativeParser entryParser, ref Extern if (!_methodToLookup.NameAndSignature.Equals(nameAndSignature)) return false; - RuntimeTypeHandle[] parsedArgsHandles = GetTypeSequence(ref externalReferencesLookup, ref entryParser); - if (parsedArgsHandles.Length != _methodToLookup.Instantiation.Length) + if (entryParser.GetSequenceCount() != _methodToLookup.Instantiation.Length) return false; for (int i = 0; i < _methodToLookup.Instantiation.Length; i++) { - TypeDesc leftType = context.ResolveRuntimeTypeHandle(parsedArgsHandles[i]); + RuntimeTypeHandle parsedArgHandle = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + TypeDesc leftType = context.ResolveRuntimeTypeHandle(parsedArgHandle); TypeDesc rightType = _methodToLookup.Instantiation[i]; if (leftType != rightType) return false; @@ -188,7 +182,7 @@ internal override bool MatchParsedEntry(ref NativeParser entryParser, ref Extern return true; } - internal override bool MatchGenericMethodEntry(GenericMethodEntry entry) + internal bool MatchGenericMethodEntry(GenericMethodEntry entry) { TypeSystemContext context = _methodToLookup.Context; @@ -261,7 +255,7 @@ public bool TryLookupExactMethodPointer(InstantiatedMethod method, out IntPtr re NativeHashtable hashtable; ExternalReferencesTable externalReferencesLookup; - MethodDescBasedGenericMethodLookup lookupData = new MethodDescBasedGenericMethodLookup(method); + GenericMethodLookupData lookupData = new GenericMethodLookupData(method); foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules()) { @@ -324,7 +318,7 @@ public bool TryGetGenericVirtualMethodPointer(InstantiatedMethod method, out Int methodPointer = templateMethod.FunctionPointer; - if (!TryLookupGenericMethodDictionary(new MethodDescBasedGenericMethodLookup(method), out dictionaryPointer)) + if (!TryLookupGenericMethodDictionary(new GenericMethodLookupData(method), out dictionaryPointer)) { using (_typeLoaderLock.EnterScope()) { diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs index 06573d1beb03e9..36f94989082050 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.FieldAccess.cs @@ -18,11 +18,6 @@ namespace Internal.Runtime.TypeLoader /// public struct FieldAccessMetadata { - /// - /// Module containing the relevant metadata, null when not found - /// - public TypeManagerHandle MappingTableModule; - /// /// Cookie for field access. This field is set to IntPtr.Zero when the value is not available. /// @@ -108,7 +103,6 @@ public static unsafe bool TryGetFieldAccessMetadataFromFieldAccessMap( fieldOffset = (int)entryParser.GetUnsigned(); } - fieldAccessMetadata.MappingTableModule = mappingTableModule.Handle; fieldAccessMetadata.Cookie = fieldAddressCookie; fieldAccessMetadata.Flags = entryFlags; fieldAccessMetadata.Offset = fieldOffset; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs index 8a94c655e09e3b..1ef68244a3fec0 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs @@ -181,14 +181,6 @@ public static MethodNameAndSignature GetMethodNameAndSignatureFromToken(TypeMana return new MethodNameAndSignature(ModuleList.Instance.GetMetadataReaderForModule(moduleHandle), token.AsHandle().ToMethodHandle(null)); } - private static RuntimeTypeHandle GetTypeDefinition(RuntimeTypeHandle typeHandle) - { - if (RuntimeAugments.IsGenericType(typeHandle)) - return RuntimeAugments.GetGenericDefinition(typeHandle); - - return typeHandle; - } - private static InstantiatedMethod FindMatchingInterfaceSlot(NativeFormatModuleInfo module, NativeReader nativeLayoutReader, ref NativeParser entryParser, ref ExternalReferencesTable extRefs, InstantiatedMethod slotMethod, DefType targetType, bool variantDispatch, bool defaultMethods) { uint numTargetImplementations = entryParser.GetUnsigned(); @@ -250,7 +242,7 @@ private static InstantiatedMethod FindMatchingInterfaceSlot(NativeFormatModuleIn NativeParser ifaceSigParser = new NativeParser(nativeLayoutReader, entryParser.GetUnsigned()); NativeLayoutInfoLoadContext nativeLayoutContext = new NativeLayoutInfoLoadContext(); - nativeLayoutContext._module = ModuleList.Instance.GetModuleInfoByHandle(module.Handle); + nativeLayoutContext._module = module; nativeLayoutContext._typeSystemContext = context; nativeLayoutContext._typeArgumentHandles = targetType.Instantiation; @@ -484,10 +476,10 @@ private static InstantiatedMethod ResolveInterfaceGenericVirtualMethodSlot(DefTy private static InstantiatedMethod ResolveGenericVirtualMethodTarget(DefType targetType, InstantiatedMethod slotMethod) { // Get the open type definition of the containing type of the generic virtual method being resolved - RuntimeTypeHandle openCallingTypeHandle = GetTypeDefinition(slotMethod.OwningType.GetTypeDefinition().RuntimeTypeHandle); + RuntimeTypeHandle openCallingTypeHandle = slotMethod.OwningType.GetTypeDefinition().RuntimeTypeHandle; // Get the open type definition of the current type of the object instance on which the GVM is being resolved - RuntimeTypeHandle openTargetTypeHandle = GetTypeDefinition(targetType.GetTypeDefinition().RuntimeTypeHandle); + RuntimeTypeHandle openTargetTypeHandle = targetType.GetTypeDefinition().RuntimeTypeHandle; int hashCode = openCallingTypeHandle.GetHashCode(); hashCode = ((hashCode << 13) ^ hashCode) ^ openTargetTypeHandle.GetHashCode(); @@ -502,10 +494,6 @@ private static InstantiatedMethod ResolveGenericVirtualMethodTarget(DefType targ if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.GenericVirtualMethodTable, out gvmTableReader)) continue; - NativeReader nativeLayoutReader; - if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.NativeLayoutInfo, out nativeLayoutReader)) - continue; - NativeParser gvmTableParser = new NativeParser(gvmTableReader, 0); NativeHashtable gvmHashtable = new NativeHashtable(gvmTableParser); ExternalReferencesTable extRefs = default(ExternalReferencesTable); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs index e65b6230d845fa..96c4a88b14982a 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -31,16 +31,6 @@ public struct MethodInvokeMetadata /// public IntPtr MethodEntryPoint; - /// - /// Raw method entrypoint - /// - public IntPtr RawMethodEntryPoint; - - /// - /// Method dictionary for components - /// - public IntPtr DictionaryComponent; - /// /// Dynamic invoke cookie /// @@ -54,25 +44,6 @@ public struct MethodInvokeMetadata public sealed partial class TypeLoaderEnvironment { - /// - /// Compare two arrays sequentially. - /// - /// First array to compare - /// Second array to compare - /// - /// true = arrays have the same values and Equals holds for all pairs of elements - /// with the same indices - /// - private static bool SequenceEqual(T[] seq1, T[] seq2) - { - if (seq1.Length != seq2.Length) - return false; - for (int i = 0; i < seq1.Length; i++) - if (!seq1[i].Equals(seq2[i])) - return false; - return true; - } - /// /// Locate blob with given ID and create native reader on it. /// @@ -645,6 +616,10 @@ private static bool TryGetMethodInvokeMetadataFromInvokeMap( foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle))) { + // Metadata handles cannot be compared across modules. + if (module.Handle != methodHandleModule) + continue; + NativeReader invokeMapReader; if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.InvokeMap, out invokeMapReader)) { @@ -658,241 +633,67 @@ private static bool TryGetMethodInvokeMetadataFromInvokeMap( externalReferences.InitializeCommonFixupsTable(module); var lookup = invokeHashtable.Lookup(canonHelper.LookupHashCode); - var entryData = new InvokeMapEntryDataEnumerator( - new PreloadedTypeComparator(declaringTypeHandle, genericMethodTypeArgumentHandles), - module.Handle, - methodHandle, - methodHandleModule); - NativeParser entryParser; while (!(entryParser = lookup.GetNext()).IsNull) { - entryData.GetNext(ref entryParser, ref externalReferences, canonHelper); + InvokeTableFlags flags = (InvokeTableFlags)entryParser.GetUnsigned(); + Handle entryMethodHandle = (((uint)HandleType.Method << 25) | entryParser.GetUnsigned()).AsHandle(); + if (!methodHandle.Equals(entryMethodHandle)) + continue; - if (!entryData.IsMatchingOrCompatibleEntry()) + RuntimeTypeHandle entryType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); + if (!canonHelper.IsCanonicallyEquivalent(entryType)) continue; - if (entryData.GetMethodEntryPoint( - out methodInvokeMetadata.MethodEntryPoint, - out methodInvokeMetadata.DictionaryComponent, - out methodInvokeMetadata.RawMethodEntryPoint)) - { - methodInvokeMetadata.MappingTableModule = module; - methodInvokeMetadata.DynamicInvokeCookie = entryData._dynamicInvokeCookie; - methodInvokeMetadata.InvokeTableFlags = entryData._flags; + bool hasEntryPoint = (flags & InvokeTableFlags.HasEntrypoint) != 0; + IntPtr methodEntryPoint = hasEntryPoint + ? externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned()) + : IntPtr.Zero; + uint dynamicInvokeCookie = (flags & InvokeTableFlags.NeedsParameterInterpretation) == 0 + ? entryParser.GetUnsigned() + : uint.MaxValue; - return true; + bool isGenericMethod = (flags & InvokeTableFlags.IsGenericMethod) != 0; + if (isGenericMethod) + { + RuntimeTypeHandle[] methodInstantiation = GetTypeSequence(ref externalReferences, ref entryParser); + if (!TypeLoaderEnvironment.Instance.CanInstantiationsShareCode(methodInstantiation, genericMethodTypeArgumentHandles)) + continue; } - } - } - - methodInvokeMetadata = default(MethodInvokeMetadata); - return false; - } - - // Api surface for controlling invoke map enumeration. - private interface IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling - { - bool GetTypeDictionary(out TDictionaryComponentType dictionary); - bool GetMethodDictionary(MethodNameAndSignature nameAndSignature, out TDictionaryComponentType dictionary); - bool IsUninterestingDictionaryComponent(TDictionaryComponentType dictionary); - bool CompareMethodInstantiation(RuntimeTypeHandle[] methodInstantiation); - bool CanInstantiationsShareCode(RuntimeTypeHandle[] methodInstantiation, CanonicalFormKind canonFormKind); - IntPtr ProduceFatFunctionPointerMethodEntryPoint(IntPtr methodEntrypoint, TDictionaryComponentType dictionary); - } - - // Comparator for invoke map when used to find an invoke map entry and the search data is a set of - // pre-loaded types, and metadata handles. - private struct PreloadedTypeComparator : IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling - { - private readonly RuntimeTypeHandle _declaringTypeHandle; - private readonly RuntimeTypeHandle[] _genericMethodTypeArgumentHandles; - - public PreloadedTypeComparator(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) - { - _declaringTypeHandle = declaringTypeHandle; - _genericMethodTypeArgumentHandles = genericMethodTypeArgumentHandles; - } - - public bool GetTypeDictionary(out IntPtr dictionary) - { - dictionary = RuntimeAugments.GetPointerFromTypeHandle(_declaringTypeHandle); - Debug.Assert(dictionary != IntPtr.Zero); - return true; - } - - public bool GetMethodDictionary(MethodNameAndSignature nameAndSignature, out IntPtr dictionary) - { - return TypeLoaderEnvironment.Instance.TryGetGenericMethodDictionaryForComponents(_declaringTypeHandle, - _genericMethodTypeArgumentHandles, - nameAndSignature, - out dictionary); - } - - public bool IsUninterestingDictionaryComponent(IntPtr dictionary) - { - return dictionary == IntPtr.Zero; - } - - public IntPtr ProduceFatFunctionPointerMethodEntryPoint(IntPtr methodEntrypoint, IntPtr dictionary) - { - return FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntrypoint, dictionary); - } - - public bool CompareMethodInstantiation(RuntimeTypeHandle[] methodInstantiation) - { - return SequenceEqual(_genericMethodTypeArgumentHandles, methodInstantiation); - } - - public bool CanInstantiationsShareCode(RuntimeTypeHandle[] methodInstantiation, CanonicalFormKind canonFormKind) - { - return TypeLoaderEnvironment.Instance.CanInstantiationsShareCode(methodInstantiation, _genericMethodTypeArgumentHandles, canonFormKind); - } - } - - // Enumerator for discovering methods in the InvokeMap. This is generic to allow highly efficient - // searching of this table with multiple different input data formats. - private struct InvokeMapEntryDataEnumerator where TLookupMethodInfo : IInvokeMapEntryDataDeclaringTypeAndGenericMethodParameterHandling - { - // Read-only inputs - private TLookupMethodInfo _lookupMethodInfo; - private readonly TypeManagerHandle _moduleHandle; - private readonly TypeManagerHandle _moduleForMethodHandle; - private readonly MethodHandle _methodHandle; - - // Parsed data from entry in the hashtable - public InvokeTableFlags _flags; - public RuntimeTypeHandle _entryType; - public IntPtr _methodEntrypoint; - public uint _dynamicInvokeCookie; - public RuntimeTypeHandle[] _methodInstantiation; - - // Computed data - private bool _hasEntryPoint; - private bool _isMatchingMethodHandleAndDeclaringType; - - public InvokeMapEntryDataEnumerator( - TLookupMethodInfo lookupMethodInfo, - TypeManagerHandle moduleHandle, - MethodHandle methodHandle, - TypeManagerHandle moduleForMethodHandle) - { - _lookupMethodInfo = lookupMethodInfo; - _moduleHandle = moduleHandle; - _methodHandle = methodHandle; - _moduleForMethodHandle = moduleForMethodHandle; - - _flags = 0; - _entryType = default(RuntimeTypeHandle); - _methodEntrypoint = IntPtr.Zero; - _dynamicInvokeCookie = 0xffffffff; - _hasEntryPoint = false; - _isMatchingMethodHandleAndDeclaringType = false; - _methodInstantiation = null; - } - - public void GetNext( - ref NativeParser entryParser, - ref ExternalReferencesTable extRefTable, - CanonicallyEquivalentEntryLocator canonHelper) - { - // Read flags and reset members data - _flags = (InvokeTableFlags)entryParser.GetUnsigned(); - _hasEntryPoint = ((_flags & InvokeTableFlags.HasEntrypoint) != 0); - _isMatchingMethodHandleAndDeclaringType = false; - _entryType = default(RuntimeTypeHandle); - _methodEntrypoint = IntPtr.Zero; - _dynamicInvokeCookie = 0xffffffff; - _methodInstantiation = null; - - // Metadata handles are not known cross module, and cannot be compared across modules. - if (_moduleHandle != _moduleForMethodHandle) - return; - - Handle entryMethodHandle = (((uint)HandleType.Method << 25) | entryParser.GetUnsigned()).AsHandle(); - if (!_methodHandle.Equals(entryMethodHandle)) - return; - - _entryType = extRefTable.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned()); - if (!canonHelper.IsCanonicallyEquivalent(_entryType)) - return; - - // Method handle and entry type match at this point. Continue reading data from the entry... - _isMatchingMethodHandleAndDeclaringType = true; - - if (_hasEntryPoint) - _methodEntrypoint = extRefTable.GetFunctionPointerFromIndex(entryParser.GetUnsigned()); - - if ((_flags & InvokeTableFlags.NeedsParameterInterpretation) == 0) - _dynamicInvokeCookie = entryParser.GetUnsigned(); - - if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) - return; - _methodInstantiation = GetTypeSequence(ref extRefTable, ref entryParser); - } - - public bool IsMatchingOrCompatibleEntry() - { - // Check if method handle and entry type were matching or compatible - if (!_isMatchingMethodHandleAndDeclaringType) - return false; - - // Nothing special about non-generic methods. - if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) - return true; - - return _lookupMethodInfo.CanInstantiationsShareCode(_methodInstantiation, CanonicalFormKind.Specific); - } - - public bool GetMethodEntryPoint(out IntPtr methodEntrypoint, out TDictionaryComponentType dictionaryComponent, out IntPtr rawMethodEntrypoint) - { - // Debug-only sanity check before proceeding (IsMatchingOrCompatibleEntry is called from TryGetDynamicMethodInvokeInfo) - Debug.Assert(IsMatchingOrCompatibleEntry()); - - rawMethodEntrypoint = _methodEntrypoint; - methodEntrypoint = IntPtr.Zero; - - if (!GetDictionaryComponent(out dictionaryComponent) || !GetMethodEntryPointComponent(dictionaryComponent, out methodEntrypoint)) - return false; - - return true; - } + if (hasEntryPoint && (flags & InvokeTableFlags.RequiresInstArg) != 0) + { + IntPtr dictionary; + if (isGenericMethod) + { + if (!TypeLoaderEnvironment.Instance.TryGetGenericMethodDictionaryForComponents( + declaringTypeHandle, genericMethodTypeArgumentHandles, + new MethodNameAndSignature(metadataReader, methodHandle), out dictionary)) + continue; + } + else + { + dictionary = RuntimeAugments.GetPointerFromTypeHandle(declaringTypeHandle); + Debug.Assert(dictionary != IntPtr.Zero); + } - private bool GetDictionaryComponent(out TDictionaryComponentType dictionaryComponent) - { - dictionaryComponent = default(TDictionaryComponentType); + if (dictionary != IntPtr.Zero) + methodEntryPoint = FunctionPointerOps.GetGenericMethodFunctionPointer(methodEntryPoint, dictionary); + } - if (((_flags & InvokeTableFlags.RequiresInstArg) == 0) || !_hasEntryPoint) + methodInvokeMetadata = new MethodInvokeMetadata + { + MappingTableModule = module, + MethodEntryPoint = methodEntryPoint, + DynamicInvokeCookie = dynamicInvokeCookie, + InvokeTableFlags = flags, + }; return true; - - // Dictionary for non-generic method is the type handle of the declaring type - if ((_flags & InvokeTableFlags.IsGenericMethod) == 0) - { - return _lookupMethodInfo.GetTypeDictionary(out dictionaryComponent); } - - // Dictionary for generic method (either found statically or constructed dynamically) - return _lookupMethodInfo.GetMethodDictionary(new MethodNameAndSignature(ModuleList.Instance.GetMetadataReaderForModule(_moduleHandle), _methodHandle), out dictionaryComponent); } - private bool GetMethodEntryPointComponent(TDictionaryComponentType dictionaryComponent, out IntPtr methodEntrypoint) - { - methodEntrypoint = _methodEntrypoint; - - if (_lookupMethodInfo.IsUninterestingDictionaryComponent(dictionaryComponent)) - return true; - - methodEntrypoint = _lookupMethodInfo.ProduceFatFunctionPointerMethodEntryPoint(_methodEntrypoint, dictionaryComponent); - - return true; - } - } - - public bool TryGetMetadataForTypeMethodNameAndSignature(RuntimeTypeHandle declaringTypeHandle, MethodNameAndSignature nameAndSignature, out QMethodDefinition methodHandle) - { - methodHandle = new QMethodDefinition(nameAndSignature.Reader, nameAndSignature.Handle); - return true; + methodInvokeMetadata = default; + return false; } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index f935da40de8a2c..acc358a4a42f44 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -280,12 +280,6 @@ public bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, b using (_typeLoaderLock.EnterScope()) { - if (isMdArray && (rank < MDArray.MinRank) && (rank > MDArray.MaxRank)) - { - arrayTypeHandle = default(RuntimeTypeHandle); - return false; - } - if (TypeSystemContext.GetArrayTypesCache(isMdArray, rank).TryGetValue(elementTypeHandle, out arrayTypeHandle)) return true; @@ -296,12 +290,6 @@ public bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, b // Looks up an array RuntimeTypeHandle given an element's RuntimeTypeHandle and rank. A rank of -1 indicates SzArray internal static bool TryGetArrayTypeForElementType_LookupOnly(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle) { - if (isMdArray && (rank < MDArray.MinRank) && (rank > MDArray.MaxRank)) - { - arrayTypeHandle = default(RuntimeTypeHandle); - return false; - } - if (TypeSystemContext.GetArrayTypesCache(isMdArray, rank).TryGetValue(elementTypeHandle, out arrayTypeHandle)) return true; @@ -373,11 +361,11 @@ public static bool TryGetByRefTypeForTargetType_LookupOnly(RuntimeTypeHandle poi return false; } - public int GetCanonicalHashCode(RuntimeTypeHandle typeHandle, CanonicalFormKind kind) + public int GetCanonicalHashCode(RuntimeTypeHandle typeHandle) { TypeSystemContext context = TypeSystemContextFactory.Create(); TypeDesc type = context.ResolveRuntimeTypeHandle(typeHandle); - int hashCode = type.ConvertToCanonForm(kind).GetHashCode(); + int hashCode = type.ConvertToCanonForm(CanonicalFormKind.Specific).GetHashCode(); TypeSystemContextFactory.Recycle(context); return hashCode; @@ -390,7 +378,7 @@ public bool TryGetGenericMethodDictionaryForComponents(RuntimeTypeHandle declari DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); InstantiatedMethod methodBeingLoaded = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, asyncVariant: false, returnDroppingAsyncThunk: false, declaringType, nameAndSignature, context.ResolveRuntimeTypeHandles(genericMethodArgHandles)); - if (TryLookupGenericMethodDictionary(new MethodDescBasedGenericMethodLookup(methodBeingLoaded), out methodDictionary)) + if (TryLookupGenericMethodDictionary(new GenericMethodLookupData(methodBeingLoaded), out methodDictionary)) { TypeSystemContextFactory.Recycle(context); return true; @@ -408,7 +396,7 @@ public bool TryGetGenericMethodDictionaryForComponents(RuntimeTypeHandle declari } } - public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, RuntimeTypeHandle[] genericArgHandles2, CanonicalFormKind kind) + public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, RuntimeTypeHandle[] genericArgHandles2) { if (genericArgHandles1.Length != genericArgHandles2.Length) return false; @@ -422,7 +410,7 @@ public bool CanInstantiationsShareCode(RuntimeTypeHandle[] genericArgHandles1, R TypeDesc genericArg1 = context.ResolveRuntimeTypeHandle(genericArgHandles1[i]); TypeDesc genericArg2 = context.ResolveRuntimeTypeHandle(genericArgHandles2[i]); - if (context.ConvertToCanon(genericArg1, kind) != context.ConvertToCanon(genericArg2, kind)) + if (context.ConvertToCanon(genericArg1, CanonicalFormKind.Specific) != context.ConvertToCanon(genericArg2, CanonicalFormKind.Specific)) { match = false; break; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs index 00736f8178e9b9..37d407def34707 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderTypeSystemContext.cs @@ -29,11 +29,8 @@ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDe // its more efficient than loading from native layout or metadata. return s_noMetadataRuntimeInterfacesAlgorithm; } - else if (type.HasNativeLayout) - { - return s_nativeLayoutInterfacesAlgorithm; - } - return s_noMetadataRuntimeInterfacesAlgorithm; + + return s_nativeLayoutInterfacesAlgorithm; } protected internal sealed override bool IsIDynamicInterfaceCastableInterface(DefType type) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs index a50e1eba1bc41a..dce78cf371cfb1 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemExtensions.cs @@ -16,12 +16,6 @@ public static bool CanShareNormalGenericCode(this TypeDesc type) return (type != type.ConvertToCanonForm(CanonicalFormKind.Specific)); } - public static bool IsGeneric(this TypeDesc type) - { - DefType typeAsDefType = type as DefType; - return typeAsDefType != null && typeAsDefType.HasInstantiation; - } - public static bool IsWellKnownType(this TypeDesc type, WellKnownType wellKnownType) { return type == type.Context.GetWellKnownType(wellKnownType, false); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs deleted file mode 100644 index 7fef72e99ddab0..00000000000000 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/DefType.Runtime.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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.Collections.Generic; -using System.Diagnostics; - -using Internal.NativeFormat; -using Internal.Runtime.TypeLoader; - -namespace Internal.TypeSystem -{ - // Includes functionality for runtime type loading - public partial class DefType - { - internal static readonly LayoutInt MaximumAlignmentPossible = new LayoutInt(8); - - public virtual bool HasNativeLayout - { - get - { - // Attempt to compute the template, if there isn't one, then there isn't native layout - return ComputeTemplate(false) != null; - } - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs index d298dc6261ec98..3bc6a66a936f3b 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/InstantiatedMethod.Runtime.cs @@ -22,14 +22,6 @@ public override MethodNameAndSignature NameAndSignature } } - - protected override bool ComputeIsNonSharableMethod() - { - return !IsCanonicalMethod(CanonicalFormKind.Any) && - this == GetCanonMethodTarget(CanonicalFormKind.Specific); - } - - /// /// Does this method need a dictionary? /// diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs index 9d183b9ff48383..d3793bb40d6d29 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/MethodDesc.Runtime.cs @@ -33,25 +33,6 @@ public IntPtr FunctionPointer public abstract MethodNameAndSignature NameAndSignature { get; } - private bool? _isNonSharableCache; - public virtual bool IsNonSharableMethod - { - get - { - if (!_isNonSharableCache.HasValue) - { - _isNonSharableCache = ComputeIsNonSharableMethod(); - } - return _isNonSharableCache.Value; - } - } - - protected virtual bool ComputeIsNonSharableMethod() - { - return !OwningType.IsCanonicalSubtype(CanonicalFormKind.Any) && - OwningType == (OwningType.ConvertToCanonForm(CanonicalFormKind.Specific) as DefType); - } - public virtual bool UnboxingStub { get diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs deleted file mode 100644 index 4a09f0f91e999f..00000000000000 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/NoMetadataMethodDesc.cs +++ /dev/null @@ -1,17 +0,0 @@ -// 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 Internal.Runtime.CompilerServices; - -namespace Internal.TypeSystem.NoMetadata -{ - /// - /// Represents a method that does not have metadata - /// - internal abstract class NoMetadataMethodDesc : MethodDesc - { - } -} diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs index 29ea55ec5aa039..7d6783e958480e 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/RuntimeMethodDesc.cs @@ -13,7 +13,7 @@ namespace Internal.TypeSystem.NoMetadata /// /// Represents a method within the NativeAOT runtime /// - internal sealed partial class RuntimeMethodDesc : NoMetadataMethodDesc + internal sealed partial class RuntimeMethodDesc : MethodDesc { public RuntimeMethodDesc(bool unboxingStub, bool asyncVariant, bool returnDroppingAsyncThunk, DefType owningType, MethodNameAndSignature nameAndSignature, int hashcode) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs index 295d5b9a48012c..0c2d652dee239f 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs @@ -202,27 +202,21 @@ internal virtual void ParseBaseType(NativeLayoutInfoLoadContext nativeLayoutInfo return; } - internal TypeDesc ComputeTemplate(bool templateRequired = true) + internal TypeDesc ComputeTemplate() { - return ComputeTemplate(GetOrCreateTypeBuilderState(), templateRequired); + return ComputeTemplate(GetOrCreateTypeBuilderState()); } - internal static TypeDesc ComputeTemplate(TypeBuilderState state, bool templateRequired = true) + internal static TypeDesc ComputeTemplate(TypeBuilderState state) { TypeDesc templateType = state.TemplateType; - if (templateRequired && (templateType == null)) + if (templateType is null) { throw new TypeBuilder.MissingTemplateException(); } return templateType; } - - internal bool IsTemplateCanonical() - { - TypeDesc templateType = ComputeTemplate(false); - return templateType != null; - } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs index 2574e2d05d6548..a71fe034dc2eb8 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs @@ -21,8 +21,6 @@ namespace Internal.TypeSystem { public abstract partial class TypeSystemContext { - internal static TemplateLocator TemplateLookup => default; - internal class RuntimeTypeHandleToParameterTypeRuntimeTypeHandleHashtable : LockFreeReaderHashtableOfPointers { protected override bool CompareKeyToValue(RuntimeTypeHandle key, RuntimeTypeHandle value) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 5190bcf2d52ca4..ca850192b0e69a 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -17,7 +17,6 @@ - @@ -257,12 +256,10 @@ - - diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index dac96c08847b53..a2a8ad8266050e 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -122,16 +122,16 @@ public void EmitNaturalInt(int emit) } } - public void EmitHalfNaturalInt(short emit) + public void EmitHalfNaturalUInt(ushort emit) { if (_target.PointerSize == 8) { - EmitInt(emit); + EmitUInt(emit); } else { Debug.Assert(_target.PointerSize == 4); - EmitShort(emit); + EmitUShort(emit); } } diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs index 662cbc510dc98c..a76d24e9428500 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs @@ -44,9 +44,9 @@ enum BagElementKind : uint NonGcStaticData = 0x42, GcStaticData = 0x43, NonGcStaticDataSize = 0x44, - GcStaticDataSize = 0x45, + // unused = 0x45, GcStaticDesc = 0x46, - ThreadStaticDataSize = 0x47, + // unused = 0x47, ThreadStaticDesc = 0x48, ThreadStaticIndex = 0x49, ThreadStaticOffset = 0x4a, diff --git a/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs b/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs index 4481d98546ecc6..e0bbeb52ecd5a0 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/GCDescEncoder.cs @@ -198,10 +198,10 @@ private static void EncodeArrayGCDesc(ref T builder, GCPointerMap map, int of } Debug.Assert(pointerCount < 64 * 1024); - builder.EmitHalfNaturalInt((short)pointerCount); + builder.EmitHalfNaturalUInt((ushort)pointerCount); Debug.Assert(nonPointerCount * pointerSize < 64 * 1024); - builder.EmitHalfNaturalInt((short)(nonPointerCount * pointerSize)); + builder.EmitHalfNaturalUInt((ushort)(nonPointerCount * pointerSize)); nonPointerCount = 0; } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs b/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs index 58920e42ec17ae..2401f15029a452 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ITargetBinaryWriter.cs @@ -24,8 +24,8 @@ public interface ITargetBinaryWriter void EmitNaturalInt(int emit); /// - /// Emits an integer that has half of the natural size on the target platform (e.g. 32 bits on 64 bit platforms). + /// Emits an unsigned integer that has half of the natural size on the target platform (e.g. 32 bits on 64 bit platforms). /// - void EmitHalfNaturalInt(short emit); + void EmitHalfNaturalUInt(ushort emit); } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs b/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs index b85d3c48d0cd78..4897b7864c2818 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MappingTableFlags.cs @@ -5,12 +5,6 @@ namespace Internal.Runtime { - internal struct DynamicInvokeMapEntry - { - public const uint IsImportMethodFlag = 0x40000000; - public const uint InstantiationDetailIndexMask = 0x3FFFFFFF; - } - internal struct VirtualInvokeTableEntry { public const int GenericVirtualMethod = 1; @@ -22,19 +16,10 @@ public enum InvokeTableFlags : uint { HasVirtualInvoke = 0x00000001, IsGenericMethod = 0x00000002, - // = 0x00000004, - IsDefaultConstructor = 0x00000008, - RequiresInstArg = 0x00000010, - HasEntrypoint = 0x00000020, - // = 0x00000040, - NeedsParameterInterpretation = 0x00000080, - CallingConventionDefault = 0x00000000, - Cdecl = 0x00001000, - Winapi = 0x00002000, - StdCall = 0x00003000, - ThisCall = 0x00004000, - FastCall = 0x00005000, - CallingConventionMask = 0x00007000, + IsDefaultConstructor = 0x00000004, + RequiresInstArg = 0x00000008, + HasEntrypoint = 0x00000010, + NeedsParameterInterpretation = 0x00000020, } [Flags] @@ -47,10 +32,7 @@ public enum FieldTableFlags : uint StorageClass = 0x03, - // = 0x04, - // = 0x08, - FieldOffsetEncodedDirectly = 0x20, - IsAnyCanonicalEntry = 0x40, - IsInitOnly = 0x80 + FieldOffsetEncodedDirectly = 0x04, + IsInitOnly = 0x08 } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 4a41c8e78f7804..2a12a0bb18713e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -699,7 +699,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo // Emit VTable Debug.Assert(objData.CountBytes - ((ISymbolDefinitionNode)this).Offset == GetVTableOffset(objData.TargetPointerSize)); SlotCounter virtualSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData); - OutputVirtualSlots(factory, ref objData, _type, _type, _type, relocsOnly); + OutputVirtualSlots(factory, ref objData, _type, _type, relocsOnly); // Update slot count int numberOfVtableSlots = virtualSlotCounter.CountSlots(ref /* readonly */ objData); @@ -883,42 +883,19 @@ protected virtual void OutputRelatedType(NodeFactory factory, ref ObjectDataBuil } } - private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly) + private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, bool relocsOnly) { Debug.Assert(EmitVirtualSlots); declType = declType.GetClosestDefType(); - templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific); - var baseType = declType.BaseType; if (baseType != null) { - Debug.Assert(templateType.BaseType != null); - OutputVirtualSlots(factory, ref objData, implType, baseType, templateType.BaseType, relocsOnly); + OutputVirtualSlots(factory, ref objData, implType, baseType, relocsOnly); } - // - // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. - // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the - // hierarchy of the template type of the original input type to detect these cases. - // - // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: - // - // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot - // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial - // Universal canonical type, so we need to fully canonicalize it. - // - // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot - // // -> Basetype template is Base<__UniversalCanon> - // - // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. - // - // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need - // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. - // - // The generic dictionary pointer occupies the first slot of each type vtable slice - if (declType.HasGenericDictionarySlot() || templateType.HasGenericDictionarySlot()) + if (declType.HasGenericDictionarySlot()) { // All generic interface types have a dictionary slot, but only some of them have an actual dictionary. bool isInterfaceWithAnEmptySlot = declType.IsInterface && diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index e93b8c6a73f6a2..0004dfa6358bb5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -839,11 +839,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto yield return new DependencyListEntry(context.GenericDictionaryLayout(_method), "Dictionary layout"); } - private static int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) - { - return left.Key - right.Key; - } - public override Vertex WriteVertex(NodeFactory factory) { Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); @@ -997,11 +992,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override bool HasConditionalStaticDependencies => false; public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; - private static int CompareDictionaryEntries(KeyValuePair left, KeyValuePair right) - { - return left.Key - right.Key; - } - public override Vertex WriteVertex(NodeFactory factory) { Debug.Assert(Marked, "WriteVertex should only happen for marked vertices"); @@ -1056,7 +1046,6 @@ public override Vertex WriteVertex(NodeFactory factory) if (closestCanonDefType.GCStaticFieldSize.AsInt != 0) { - layoutInfo.AppendUnsigned(BagElementKind.GcStaticDataSize, checked((uint)closestCanonDefType.GCStaticFieldSize.AsInt)); BagElementKind staticDescBagType; ISymbolNode staticsDescSymbol = GetStaticsNode(factory, out staticDescBagType); uint gcStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(staticsDescSymbol); @@ -1065,7 +1054,6 @@ public override Vertex WriteVertex(NodeFactory factory) if (closestCanonDefType.ThreadGcStaticFieldSize.AsInt != 0) { - layoutInfo.AppendUnsigned(BagElementKind.ThreadStaticDataSize, checked((uint)closestCanonDefType.ThreadGcStaticFieldSize.AsInt)); BagElementKind threadStaticDescBagType; ISymbolNode threadStaticsDescSymbol = GetThreadStaticsNode(factory, out threadStaticDescBagType); uint threadStaticsSymbolIndex = factory.MetadataManager.NativeLayoutInfo.StaticsReferences.GetIndex(threadStaticsDescSymbol); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs index edad70d6b045b6..975d92d7ce713c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionFieldMapNode.cs @@ -74,9 +74,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) flags = FieldTableFlags.Instance | FieldTableFlags.FieldOffsetEncodedDirectly; } - if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) - flags |= FieldTableFlags.IsAnyCanonicalEntry; - if (field.IsInitOnly) flags |= FieldTableFlags.IsInitOnly; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs index 6a9f36af5c40ac..56f2d3b755d139 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/VirtualMethodCallHelper.cs @@ -123,8 +123,6 @@ private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType int baseSlots = 0; TypeDesc baseType = owningType.BaseType; - TypeDesc templateBaseType = owningType.ConvertToCanonForm(CanonicalFormKind.Specific).BaseType; - while (baseType != null) { // Normalize the base type. Necessary to make this work with the lazy vtable slot @@ -132,31 +130,9 @@ private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType // something like Base<__Canon, string>. We would get "0 slots used" for weird // base types like this. baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific); - templateBaseType = templateBaseType.ConvertToCanonForm(CanonicalFormKind.Specific); - - // - // In the universal canonical types case, we could have base types in the hierarchy that are partial universal canonical types. - // The presence of these types could cause incorrect vtable layouts, so we need to fully canonicalize them and walk the - // hierarchy of the template type of the original input type to detect these cases. - // - // Exmaple: we begin with Derived<__UniversalCanon> and walk the template hierarchy: - // - // class Derived : Middle { } // -> Template is Derived<__UniversalCanon> and needs a dictionary slot - // // -> Basetype tempalte is Middle<__UniversalCanon, MyStruct>. It's a partial - // Universal canonical type, so we need to fully canonicalize it. - // - // class Middle : Base { } // -> Template is Middle<__UniversalCanon, __UniversalCanon> and needs a dictionary slot - // // -> Basetype template is Base<__UniversalCanon> - // - // class Base { } // -> Template is Base<__UniversalCanon> and needs a dictionary slot. - // - // If we had not fully canonicalized the Middle class template, we would have ended up with Base, which does not need - // a dictionary slot, meaning we would have created a vtable layout that the runtime does not expect. - // - // For types that have a generic dictionary, the introduced virtual method slots are // prefixed with a pointer to the generic dictionary. - if ((baseType.HasGenericDictionarySlot() || templateBaseType.HasGenericDictionarySlot()) && countDictionarySlots) + if (baseType.HasGenericDictionarySlot() && countDictionarySlots) baseSlots++; IReadOnlyList baseVirtualSlots = factory.VTable(baseType).Slots; @@ -169,7 +145,6 @@ private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType } baseType = baseType.BaseType; - templateBaseType = templateBaseType.BaseType; } return baseSlots; diff --git a/src/tests/nativeaot/SmokeTests/DynamicGenerics/arrays.cs b/src/tests/nativeaot/SmokeTests/DynamicGenerics/arrays.cs index 7ae6279be552be..1be71030520fa9 100644 --- a/src/tests/nativeaot/SmokeTests/DynamicGenerics/arrays.cs +++ b/src/tests/nativeaot/SmokeTests/DynamicGenerics/arrays.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Reflection; using System.Linq; +using System.Runtime.InteropServices; #if INTERNAL_CONTRACTS using Internal.Runtime.Augments; #endif @@ -98,6 +99,16 @@ public struct GenStructImplementsIEquatable2 : IEquatable + { + public nint Leading; + public T First; + public nint Middle; + public T Second; + public nint Trailing; + } + static void TestForEach(object[] array) { foreach(var item1 in array) @@ -248,6 +259,47 @@ public static void TestDynamicArrays() [TestMethod] public static void TestMDArrays() { + string first = new string('a', 10); + string second = new string('b', 10); + var mixed = new MixedGCLayout<(string, string)> + { + Leading = 1, + First = (first, second), + Middle = 2, + Second = (second, first), + Trailing = 3, + }; + object[] values = + { + 42, + first, + new GenericStruct(first), + (first, second), + mixed, + new MixedGCLayout> { First = mixed, Second = mixed }, + }; + foreach (object value in values) + { + Assert.IsTrue(value.GetType().MakeArrayType(1).TypeHandle.Value != IntPtr.Zero); + foreach (int rank in new[] { 2, 3, 32 }) + { + int[] lengths = new int[rank]; + int[] indices = new int[rank]; + Array.Fill(lengths, 1); + lengths[rank - 1] = 2; + Array mdArray = Array.CreateInstance(value.GetType(), lengths); + mdArray.SetValue(value, indices); + indices[rank - 1] = 1; + mdArray.SetValue(value, indices); + + GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); + + Assert.AreEqual(value, mdArray.GetValue(indices)); + indices[rank - 1] = 0; + Assert.AreEqual(value, mdArray.GetValue(indices)); + } + } + #if UNIVERSAL_GENERICS int[,,] array = new int[1, 2, 3]; int value = 1; diff --git a/src/tests/nativeaot/SmokeTests/DynamicGenerics/interfaces.cs b/src/tests/nativeaot/SmokeTests/DynamicGenerics/interfaces.cs index 7aa6edb8fafbd9..b3695c5edb2fb8 100644 --- a/src/tests/nativeaot/SmokeTests/DynamicGenerics/interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/DynamicGenerics/interfaces.cs @@ -103,6 +103,16 @@ public static void TestGenericCollapsingInInterfaceMap() TypeOf.CommonType2); mi.Invoke(null, null); + + mi = typeof(InterfacesTests).GetTypeInfo().GetDeclaredMethod(nameof(TestGenericCollapsingInInterfaceMapHelper)).MakeGenericMethod( + TypeOf.CommonType1, + TypeOf.CommonType1); + mi.Invoke(null, null); + + mi = typeof(InterfacesTests).GetTypeInfo().GetDeclaredMethod(nameof(TestGenericCollapsingInInterfaceMapHelper)).MakeGenericMethod( + TypeOf.CommonType1, + TypeOf.String); + mi.Invoke(null, null); #endif } public static void TestGenericCollapsingInInterfaceMapHelper() diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs index 6febd238358128..4d25afbd5b5a59 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs @@ -80,6 +80,7 @@ internal static int Run() TestNativeLayoutGeneration.Run(); TestByRefLikeVTables.Run(); TestFunctionPointerLoading.Run(); + TestTemplateArrayLayout.Run(); return 100; } @@ -766,6 +767,43 @@ public T Cast(object v) } } + interface IHierarchy + { + Type GetBaseArgument(); + Type GetDerivedArgument(); + } + + struct Value { } + + class GenericBase + { + public virtual Type GetBaseArgument() => typeof(T); + } + + class Middle : GenericBase, IHierarchy + { + public virtual Type GetDerivedArgument() => typeof(T); + } + + class ReferenceDerived : Middle { } + class ValueDerived : Middle { } + class NestedValueDerived : Middle> { } + + class EmptyDictionaryBase : IHierarchy + { + public Type GetBaseArgument() => typeof(int); + public virtual Type GetDerivedArgument() => typeof(int); + } + + class EmptyDictionaryDerived : EmptyDictionaryBase + { + public override Type GetDerivedArgument() => typeof(T); + } + + class ReusedDictionaryDerived : EmptyDictionaryDerived { } + + private static Type s_argument = typeof(TestSlotsInHierarchy); + public static void Run() { var derived = new Derived(); @@ -776,6 +814,24 @@ public static void Run() if (derived.Cast("Hello") != "Hello") throw new Exception(); + + CheckHierarchy(new ReferenceDerived(), typeof(object), typeof(string)); + CheckHierarchy(new ValueDerived(), typeof(object), typeof(int)); + CheckHierarchy(new NestedValueDerived(), typeof(object), typeof(Value)); + CheckHierarchy(new EmptyDictionaryDerived(), typeof(object), typeof(int)); + CheckHierarchy(new ReusedDictionaryDerived(), typeof(object), typeof(int)); + + CheckHierarchy((IHierarchy)Activator.CreateInstance(typeof(ReferenceDerived<>).MakeGenericType(s_argument)), s_argument, typeof(string)); + CheckHierarchy((IHierarchy)Activator.CreateInstance(typeof(ValueDerived<>).MakeGenericType(s_argument)), s_argument, typeof(int)); + CheckHierarchy((IHierarchy)Activator.CreateInstance(typeof(NestedValueDerived<>).MakeGenericType(s_argument)), s_argument, typeof(Value<>).MakeGenericType(s_argument)); + CheckHierarchy((IHierarchy)Activator.CreateInstance(typeof(EmptyDictionaryDerived<>).MakeGenericType(s_argument)), s_argument, typeof(int)); + CheckHierarchy((IHierarchy)Activator.CreateInstance(typeof(ReusedDictionaryDerived<>).MakeGenericType(s_argument)), s_argument, typeof(int)); + } + + private static void CheckHierarchy(IHierarchy value, Type derivedArgument, Type baseArgument) + { + if (value.GetBaseArgument() != baseArgument || value.GetDerivedArgument() != derivedArgument) + throw new Exception("Unexpected hierarchy dictionary"); } } @@ -2656,6 +2712,128 @@ public static void Run() } } + class TestTemplateArrayLayout + { + class Payload + { + public Payload() { } + + public override string ToString() => "payload"; + } + + struct NoReferences + { + public long Number; + public byte Tail; + public override string ToString() => $"{Number}:{Tail}"; + } + + struct AllReferences + { + public T First; + public object Second; + public override string ToString() => $"{First}:{Second}"; + } + + struct MixedReferences + { + public byte Head; + public T First; + public long Number; + public object Second; + public byte Tail; + public override string ToString() => $"{Head}:{First}:{Number}:{Second}:{Tail}"; + } + + interface ITest + { + void Run(); + } + + class Test : ITest where T : new() + { + public void Run() + { + Check(() => new T(), new T().ToString()); + Check>( + () => new NoReferences { Number = 123456789, Tail = 42 }, + "123456789:42"); + Check>( + () => new AllReferences { First = new T(), Second = new string('a', 23) }, + $"{new T()}:{new string('a', 23)}"); + Check>( + () => new MixedReferences { Head = 17, First = new T(), Number = 123456789, Second = new string('b', 29), Tail = 42 }, + $"17:{new T()}:123456789:{new string('b', 29)}:42"); + Check?>( + () => new MixedReferences { Head = 17, First = new T(), Number = 123456789, Second = new string('c', 31), Tail = 42 }, + $"17:{new T()}:123456789:{new string('c', 31)}:42"); + } + + private static void Check(Func createValue, string expected) + { + Type rankOneType = typeof(TElement).MakeArrayType(1); + if (rankOneType.IsSZArray || rankOneType.GetArrayRank() != 1 || rankOneType.TypeHandle.Equals(typeof(TElement[]).TypeHandle)) + throw new Exception($"Unexpected rank-one MD array type: {rankOneType}"); + + Array[] arrays = + [ + new TElement[3], + new TElement[2, 3], + new TElement[2, 2, 3], + ]; + + for (int i = 0; i < arrays.Length; i++) + { + Array array = arrays[i]; + Type expectedType = i == 0 ? typeof(TElement[]) : typeof(TElement).MakeArrayType(i + 1); + if (array.GetType() != expectedType) + throw new Exception($"Unexpected array type: {array.GetType()}"); + + Fill(array, createValue); + GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); + } + + foreach (Array array in arrays) + { + foreach (object value in array) + { + if (value.ToString() != expected) + throw new Exception($"Unexpected array value: {value}"); + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Fill(Array array, Func createValue) + { + int[] indices = new int[array.Rank]; + for (int i = 0; i < array.Length; i++) + { + int remaining = i; + for (int dimension = array.Rank - 1; dimension >= 0; dimension--) + { + indices[dimension] = remaining % array.GetLength(dimension); + remaining /= array.GetLength(dimension); + } + + array.SetValue(createValue(), indices); + } + } + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + private static Type s_payloadType = typeof(Payload); + + public static void Run() + { + new Test().Run(); + + Type testType = typeof(Test<>).MakeGenericType(s_payloadType); + ((ITest)Activator.CreateInstance(testType)).Run(); + ((ITest)Activator.CreateInstance(testType)).Run(); + } + } + class TestDevirtualization { interface IDevirt diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/MiscTests.cs b/src/tests/nativeaot/SmokeTests/UnitTests/MiscTests.cs index a524d34e26987e..2c830a99bd1cad 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/MiscTests.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/MiscTests.cs @@ -3,12 +3,14 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; class MiscTests { internal static int Run() { TestSurrogateStringLiterals.Run(); + TestLargeStructArrayGC.Run(); return 100; } @@ -35,4 +37,109 @@ private static void CheckSurrogateLiteral(string value, char expected) throw new Exception(((int)value[0]).ToString("X4")); } } + + // Regression test for GC descriptor encoding of arrays whose element is a large + // value type containing two GC references separated by a large (32KB-64KB) + // non-pointer gap. That layout forces the "val_serie" array GCDesc encoding whose + // per-series run/skip counts are stored in 16-bit-limited fields. A skip in + // [32768, 65535] must not be produced by a signed (short) cast that then gets + // sign-extended into the 32-bit field on 64-bit targets: doing so makes the GC + // walk the array with a bogus stride and drop or corrupt element references. + // + // Two encoders are exercised: + // * TestSzArray - GCDesc produced by the AOT compiler (GCDescEncoder). + // * TestMdArray - GCDesc produced by the runtime type loader + // (EETypeCreator.CreateMdArrayGCDesc) for an array type that + // is only materialized at run time via Array.CreateInstance. + class TestLargeStructArrayGC + { + // Two object references: one at offset 0 and one at offset SecondOffset. The + // gap between them is 40000 bytes, inside the problematic [32768, 65535] + // range for the 16-bit skip field. + const int SecondOffset = 40008; + + [StructLayout(LayoutKind.Explicit, Size = SecondOffset + 8)] + struct LargeStruct + { + [FieldOffset(0)] public object First; + [FieldOffset(SecondOffset)] public object Second; + } + + const int ElementCount = 4; + + public static void Run() + { + TestSzArray(); + TestMdArray(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void TestSzArray() + { + var array = new LargeStruct[ElementCount]; + var references = new object[2 * ElementCount]; + for (int i = 0; i < array.Length; i++) + { + array[i].First = references[2 * i] = MakeReference(i, 'a'); + array[i].Second = references[2 * i + 1] = MakeReference(i, 'A'); + } + + Compact(); + + for (int i = 0; i < array.Length; i++) + { + Check("SzArray.First", i, references[2 * i], array[i].First); + Check("SzArray.Second", i, references[2 * i + 1], array[i].Second); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void TestMdArray() + { + // Rank-2 array type materialized at run time by the type loader. + Array array = Array.CreateInstance(typeof(LargeStruct), ElementCount, 1); + var references = new object[2 * ElementCount]; + for (int i = 0; i < ElementCount; i++) + { + object boxed = new LargeStruct + { + First = references[2 * i] = MakeReference(i, 'a'), + Second = references[2 * i + 1] = MakeReference(i, 'A') + }; + array.SetValue(boxed, i, 0); + } + + Compact(); + + for (int i = 0; i < ElementCount; i++) + { + var element = (LargeStruct)array.GetValue(i, 0); + Check("MdArray.First", i, references[2 * i], element.First); + Check("MdArray.Second", i, references[2 * i + 1], element.Second); + } + } + + // Produce a distinct, verifiable heap object per index/tag (not interned). + [MethodImpl(MethodImplOptions.NoInlining)] + static object MakeReference(int i, char tag) => new string(tag, 8 + i); + + // Keep the original objects rooted separately so identity checks detect + // array references that were not updated when the objects moved. + [MethodImpl(MethodImplOptions.NoInlining)] + static void Compact() + { + for (int i = 0; i < 1000; i++) + GC.KeepAlive(new byte[1000]); + + GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); + GC.WaitForPendingFinalizers(); + GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); + } + + static void Check(string which, int index, object expected, object actual) + { + if (!ReferenceEquals(expected, actual)) + throw new Exception($"{which}[{index}]: reference mismatch"); + } + } }