diff --git a/eng/testing/tests.singlefile.targets b/eng/testing/tests.singlefile.targets index 9c5585602da775..233db988324e90 100644 --- a/eng/testing/tests.singlefile.targets +++ b/eng/testing/tests.singlefile.targets @@ -50,10 +50,6 @@ - - - - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs index b990dac4712ebb..1045f76bf927b9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -53,7 +53,8 @@ public abstract class ExecutionEnvironment //============================================================================================== // Invoke and field access support. //============================================================================================== - public abstract MethodBaseInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles); + public abstract void ValidateGenericMethodConstraints(MethodInfo method); + public abstract MethodBaseInvoker TryGetMethodInvokerNoConstraintCheck(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles); public abstract FieldAccessor TryGetFieldAccessor(MetadataReader reader, RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle fieldTypeHandle, FieldHandle fieldHandle); //============================================================================================== @@ -108,7 +109,7 @@ internal MethodBaseInvoker GetMethodInvoker(RuntimeTypeInfo declaringType, QMeth { genericMethodTypeArgumentHandles[i] = genericMethodTypeArguments[i].TypeHandle; } - MethodBaseInvoker methodInvoker = TryGetMethodInvoker(typeDefinitionHandle, methodHandle, genericMethodTypeArgumentHandles); + MethodBaseInvoker methodInvoker = TryGetMethodInvokerNoConstraintCheck(typeDefinitionHandle, methodHandle, genericMethodTypeArgumentHandles); if (methodInvoker == null) exception = ReflectionCoreExecution.ExecutionEnvironment.CreateNonInvokabilityException(exceptionPertainant); return methodInvoker; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 6fb23a261f3337..d592f7506f7aec 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -26,6 +26,7 @@ using System.Runtime.InteropServices; using System.Threading; +using Internal.Reflection.Core.Execution; using Internal.Runtime.CompilerHelpers; using Internal.Runtime.CompilerServices; @@ -70,6 +71,20 @@ public static object RawNewObject(RuntimeTypeHandle typeHandle) return RuntimeImports.RhNewObject(typeHandle.ToMethodTable()); } + internal static void EnsureMethodTableSafeToAllocate(MethodTable* mt) + { + // We might be dealing with a "necessary" MethodTable (in the ILCompiler terms). + // This MethodTable is okay for casting, but must not be allocated on the GC heap. + Debug.Assert(MethodTable.Of()->NumVtableSlots > 0); + if (mt->NumVtableSlots == 0) + { + // This is a type without a vtable or GCDesc. We must not allow creating an instance of it + throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(Type.GetTypeFromMethodTable(mt)); + } + // Paranoid check: not-meant-for-GC-heap types should be reliably identifiable by empty vtable. + Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(mt) != 0); + } + // // Perform the equivalent of a "newarr" The resulting array is zero-initialized. // @@ -77,7 +92,12 @@ public static Array NewArray(RuntimeTypeHandle typeHandleForArrayType, int count { // Don't make the easy mistake of passing in the element MethodTable rather than the "array of element" MethodTable. Debug.Assert(typeHandleForArrayType.ToMethodTable()->IsSzArray); - return RuntimeImports.RhNewArray(typeHandleForArrayType.ToMethodTable(), count); + + MethodTable* mt = typeHandleForArrayType.ToMethodTable(); + + EnsureMethodTableSafeToAllocate(mt); + + return RuntimeImports.RhNewArray(mt, count); } // @@ -109,7 +129,7 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray // We just checked above that all lower bounds are zero. In that case, we should actually allocate // a new SzArray instead. Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(typeHandleForArrayType.ToMethodTable()->RelatedParameterType))!; - return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToMethodTable(), lengths[0]); + return NewArray(elementType.MakeArrayType().TypeHandle, lengths[0]); } // Create a local copy of the lengths that cannot be modified by the caller diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index 09439509355e88..fbbd850e5bb90f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs @@ -78,7 +78,7 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in if (rank == 1) { - return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToMethodTable(), pLengths[0]); + return RuntimeAugments.NewArray(elementType.MakeArrayType().TypeHandle, pLengths[0]); } else { @@ -112,15 +112,15 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i } } - MethodTable* eeType = arrayType.TypeHandle.ToMethodTable(); if (rank == 1) { // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated // as an SzArray. SzArray is castable to MdArray rank 1. - if (!eeType->IsSzArray) - eeType = arrayType.GetElementType().MakeArrayType().TypeHandle.ToMethodTable(); + RuntimeTypeHandle arrayTypeHandle = arrayType.IsSZArray + ? arrayType.TypeHandle + : arrayType.GetElementType().MakeArrayType().TypeHandle; - return RuntimeImports.RhNewArray(eeType, pLengths[0]); + return RuntimeAugments.NewArray(arrayTypeHandle, pLengths[0]); } else { @@ -129,6 +129,7 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i for (int i = 0; i < rank; i++) pImmutableLengths[i] = pLengths[i]; + MethodTable* eeType = arrayType.TypeHandle.ToMethodTable(); return NewMultiDimArray(eeType, pImmutableLengths, rank); } } @@ -662,6 +663,7 @@ internal static unsafe Array NewMultiDimArray(MethodTable* eeType, int* pLengths if (maxArrayDimensionLengthOverflow) throw new OutOfMemoryException(); // "Array dimensions exceeded supported range." + Debug.Assert(eeType->NumVtableSlots != 0, "Compiler enforces we never have unconstructed MTs for multi-dim arrays since those can be template-constructed anytime"); Array ret = RuntimeImports.RhNewArray(eeType, (int)totalLength); ref int bounds = ref ret.GetRawMultiDimArrayBounds(); 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 cc1855f53408dd..7cd2e9588f5125 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 @@ -37,42 +37,16 @@ namespace System.Reflection.Runtime.General { internal static partial class TypeUnifier { - [FeatureSwitchDefinition("System.Reflection.IsTypeConstructionEagerlyValidated")] - // This can be replaced at native compile time using a feature switch. - internal static bool IsTypeConstructionEagerlyValidated => true; - public static RuntimeTypeInfo GetArrayType(this RuntimeTypeInfo elementType) { return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1); } - public static RuntimeTypeInfo GetArrayTypeWithTypeHandle(this RuntimeTypeInfo elementType) - { - return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: false, rank: 1).WithVerifiedTypeHandle(elementType); - } - public static RuntimeTypeInfo GetMultiDimArrayType(this RuntimeTypeInfo elementType, int rank) { return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank); } - public static RuntimeTypeInfo GetMultiDimArrayTypeWithTypeHandle(this RuntimeTypeInfo elementType, int rank) - { - return RuntimeArrayTypeInfo.GetArrayTypeInfo(elementType, multiDim: true, rank: rank).WithVerifiedTypeHandle(elementType); - } - - private static RuntimeArrayTypeInfo WithVerifiedTypeHandle(this RuntimeArrayTypeInfo arrayType, RuntimeTypeInfo elementType) - { - // We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result - // type would be an open type. - RuntimeTypeHandle typeHandle = arrayType.InternalTypeHandleIfAvailable; - if (IsTypeConstructionEagerlyValidated - && typeHandle.IsNull() && !elementType.ContainsGenericParameters) - throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(arrayType.ToType()); - - return arrayType; - } - public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType) { return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType); @@ -88,29 +62,9 @@ public static RuntimeTypeInfo GetConstructedGenericTypeNoConstraintCheck(this Ru return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfoNoConstraintCheck(genericTypeDefinition, genericTypeArguments); } - public static RuntimeTypeInfo GetConstructedGenericTypeWithTypeHandle(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) + public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo genericTypeDefinition, RuntimeTypeInfo[] genericTypeArguments) { - return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments).WithVerifiedTypeHandle(genericTypeArguments); - } - - private static RuntimeConstructedGenericTypeInfo WithVerifiedTypeHandle(this RuntimeConstructedGenericTypeInfo genericType, RuntimeTypeInfo[] genericTypeArguments) - { - // We only permit creating parameterized types if the pay-for-play policy specifically allows them *or* if the result - // type would be an open type. - RuntimeTypeHandle typeHandle = genericType.InternalTypeHandleIfAvailable; - if (IsTypeConstructionEagerlyValidated && typeHandle.IsNull()) - { - bool atLeastOneOpenType = false; - foreach (RuntimeTypeInfo genericTypeArgument in genericTypeArguments) - { - if (genericTypeArgument.ContainsGenericParameters) - atLeastOneOpenType = true; - } - if (!atLeastOneOpenType) - throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(genericType.ToType()); - } - - return genericType; + return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments); } public static RuntimeTypeInfo GetRuntimeTypeInfoForRuntimeTypeHandle(this RuntimeTypeHandle typeHandle) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index a0a726a4607edf..b8ae37e6981619 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -144,7 +144,9 @@ public sealed override MethodInfo MakeGenericMethod(params Type[] typeArguments) if (typeArguments.Length != GenericTypeParameters.Length) throw new ArgumentException(SR.Format(SR.Argument_NotEnoughGenArguments, typeArguments.Length, GenericTypeParameters.Length)); RuntimeMethodInfo methodInfo = (RuntimeMethodInfo)RuntimeConstructedGenericMethodInfo.GetRuntimeConstructedGenericMethodInfo(this, genericTypeArguments); - MethodBaseInvoker _ = methodInfo.MethodInvoker; // For compatibility with other Make* apis, trigger any missing metadata exceptions now rather than later. + + ReflectionCoreExecution.ExecutionEnvironment.ValidateGenericMethodConstraints(methodInfo); + return methodInfo; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs index 3a1c3718a00989..da86ebb4499cf1 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -404,14 +404,14 @@ public Type MakeArrayType() // Do not implement this as a call to MakeArrayType(1) - they are not interchangeable. MakeArrayType() returns a // vector type ("SZArray") while MakeArrayType(1) returns a multidim array of rank 1. These are distinct types // in the ECMA model and in CLR Reflection. - return this.GetArrayTypeWithTypeHandle().ToType(); + return this.GetArrayType().ToType(); } public Type MakeArrayType(int rank) { if (rank <= 0) throw new IndexOutOfRangeException(); - return this.GetMultiDimArrayTypeWithTypeHandle(rank).ToType(); + return this.GetMultiDimArrayType(rank).ToType(); } public Type MakePointerType() @@ -475,7 +475,7 @@ public Type MakeGenericType(Type[] typeArguments) throw new TypeLoadException(SR.CannotUseByRefLikeTypeInInstantiation); } - return this.GetConstructedGenericTypeWithTypeHandle(runtimeTypeArguments!).ToType(); + return this.GetConstructedGenericType(runtimeTypeArguments!).ToType(); } public Type DeclaringType diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index b30bfd88ef0822..be875ba1731c52 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -322,14 +322,7 @@ public static unsafe object GetUninitializedObject( throw new NotSupportedException(SR.NotSupported_ByRefLike); } - Debug.Assert(MethodTable.Of()->NumVtableSlots > 0); - if (mt->NumVtableSlots == 0) - { - // This is a type without a vtable or GCDesc. We must not allow creating an instance of it - throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(type); - } - // Paranoid check: not-meant-for-GC-heap types should be reliably identifiable by empty vtable. - Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(mt) != 0); + RuntimeAugments.EnsureMethodTableSafeToAllocate(mt); if (mt->IsNullable) { @@ -364,13 +357,7 @@ public static unsafe object GetUninitializedObject( if (mt->ElementType == EETypeElementType.Void || mt->IsGenericTypeDefinition || mt->IsByRef || mt->IsPointer || mt->IsFunctionPointer) throw new ArgumentException(SR.Arg_TypeNotSupported); - if (mt->NumVtableSlots == 0) - { - // This is a type without a vtable or GCDesc. We must not allow creating an instance of it - throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(Type.GetTypeFromHandle(type)); - } - // Paranoid check: not-meant-for-GC-heap types should be reliably identifiable by empty vtable. - Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(mt) != 0); + RuntimeAugments.EnsureMethodTableSafeToAllocate(mt); if (!mt->IsValueType) { diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs index ace9bda16ba26c..10c1ac5bb45da7 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs @@ -218,14 +218,14 @@ public sealed override unsafe bool TryGetConstructedGenericTypeForComponentsNoCo return TypeLoaderEnvironment.Instance.TryGetConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle); } - public sealed override MethodBaseInvoker TryGetMethodInvoker(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + public sealed override void ValidateGenericMethodConstraints(MethodInfo method) { - MethodBase methodInfo = ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); + ConstraintValidator.EnsureSatisfiesClassConstraints(method); + } - // Validate constraints first. This is potentially useless work if the method already exists, but it prevents bad - // inputs to reach the type loader (we don't have support to e.g. represent pointer types within the type loader) - if (genericMethodTypeArgumentHandles != null && genericMethodTypeArgumentHandles.Length > 0) - ConstraintValidator.EnsureSatisfiesClassConstraints((MethodInfo)methodInfo); + public sealed override MethodBaseInvoker TryGetMethodInvokerNoConstraintCheck(RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles) + { + MethodBase methodInfo = ExecutionDomain.GetMethod(declaringTypeHandle, methodHandle, genericMethodTypeArgumentHandles); MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer(methodHandle); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs index 19e7b8866f5bd1..6ae20dfaff9083 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ArrayMapNode.cs @@ -49,15 +49,14 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section hashTableSection = writer.NewSection(); hashTableSection.Place(typeMapHashTable); - foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) { if (!type.IsArray) continue; var arrayType = (ArrayType)type; - // Look at the constructed type symbol. If a constructed type wasn't emitted, then the array map entry isn't valid for use - IEETypeNode arrayTypeSymbol = factory.ConstructedTypeSymbol(arrayType); + IEETypeNode arrayTypeSymbol = factory.NecessaryTypeSymbol(arrayType); Vertex vertex = writer.GetUnsignedConstant(_externalReferences.GetIndex(arrayTypeSymbol)); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs index 3c2835ed9210ae..69df3a1a4eeff1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesHashtableNode.cs @@ -45,14 +45,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Section nativeSection = nativeWriter.NewSection(); nativeSection.Place(hashtable); - // We go over constructed EETypes only. The places that need to consult this hashtable at runtime - // all need constructed EETypes. Placing unconstructed EETypes into this hashtable could make us - // accidentally satisfy e.g. MakeGenericType for something that was only used in a cast. Those - // should throw MissingRuntimeArtifact instead. - // - // We already make sure "necessary" EETypes that could potentially be loaded at runtime through - // the dynamic type loader get upgraded to constructed EETypes at AOT compile time. - foreach (var type in factory.MetadataManager.GetTypesWithConstructedEETypes()) + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) { // If this is an instantiated non-canonical generic type, add it to the generic instantiations hashtable if (!type.HasInstantiation || type.IsGenericDefinition || type.IsCanonicalSubtype(CanonicalFormKind.Any)) diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index ab104f7a4deedd..328e2bb3b5d141 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -565,9 +565,6 @@ - - - diff --git a/src/tests/nativeaot/Directory.Build.props b/src/tests/nativeaot/Directory.Build.props index 7c8005b8de4738..17342717f61c8e 100644 --- a/src/tests/nativeaot/Directory.Build.props +++ b/src/tests/nativeaot/Directory.Build.props @@ -3,10 +3,6 @@ - - false - true diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index d9f0f0e3e9fcbb..a0c6dc36b9e254 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -806,6 +806,18 @@ struct AlsoNeverAllocated public override int GetHashCode() => 500; } + class NeverAllocatedButUsedInGenericMethod + { + } + + class Atom; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Type GetNeverAllocatedButUsedInGenericMethod() => typeof(NeverAllocatedButUsedInGenericMethod<>); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static object GenericMethod() => null; + public static void Run() { Console.WriteLine(nameof(TestGetUninitializedObject)); @@ -820,6 +832,31 @@ public static void Run() var obj2 = RuntimeHelpers.GetUninitializedObject(typeof(AlsoNeverAllocated)); if (obj2.GetHashCode() != 500) throw new Exception(); + + // Do what's needed so that we force an unconstructed MT for NeverAllocatedButUsedInGenericMethod into the program + // 1. Statically call the method + // 2. Make the method visible target of reflection + // This will force the compiler to place the method generic dictionary into a hashtable addressable using the instantiation. + GenericMethod>(); + typeof(TestGetUninitializedObject).GetMethod(nameof(GenericMethod)); + + Type t1 = GetNeverAllocatedButUsedInGenericMethod().MakeGenericType(typeof(Atom)); + _ = t1.TypeHandle; // Type handle is only suitable for casting but we can get it + + bool thrown = true; + try + { + // Needs to throw, the MT is only a necessary MT, not constructed MT + RuntimeHelpers.GetUninitializedObject(t1); + thrown = false; + } + catch (NotSupportedException e) + { + if (!e.Message.Contains("ReflectionTest+TestGetUninitializedObject+NeverAllocatedButUsedInGenericMethod`1[ReflectionTest+TestGetUninitializedObject+Atom]")) + throw new Exception(); + } + if (!thrown) + throw new Exception(); } } @@ -1933,17 +1970,24 @@ static void Check(string type, string method, string param, bool hasParam, strin class TypeConstructionTest { struct Atom { } + struct ArrayElementUsedInGenericDictionary { } class Gen { } static Type s_atom = typeof(Atom); + [MethodImpl(MethodImplOptions.NoInlining)] + static Type GetArrayElementUsedInGenericDictionary() => typeof(ArrayElementUsedInGenericDictionary); + + [MethodImpl(MethodImplOptions.NoInlining)] + public static object GenericMethod() => null; + public static void Run() { string message1 = ""; try { - typeof(Gen<>).MakeGenericType(s_atom); + _ = typeof(Gen<>).MakeGenericType(s_atom).TypeHandle; } catch (Exception ex) { @@ -1955,7 +1999,7 @@ public static void Run() string message2 = ""; try { - s_atom.MakeArrayType(); + _ = s_atom.MakeArrayType().TypeHandle; } catch (Exception ex) { @@ -1975,6 +2019,24 @@ public static void Run() } if (!message3.Contains("ReflectionTest+TypeConstructionTest+Atom[]")) throw new Exception(); + + // Do what's needed so that we force an unconstructed MT for ArrayElementUsedInGenericDictionary[] into the program + // 1. Statically call the method + // 2. Make the method visible target of reflection + // This will force the compiler to place the method generic dictionary into a hashtable addressable using the instantiation. + GenericMethod(); + typeof(TestGetUninitializedObject).GetMethod(nameof(GenericMethod)); + string message4 = ""; + try + { + Array.CreateInstance(GetArrayElementUsedInGenericDictionary(), 10); + } + catch (Exception ex) + { + message4 = ex.Message; + } + if (!message4.Contains("ReflectionTest+TypeConstructionTest+ArrayElementUsedInGenericDictionary[]")) + throw new Exception(); } } @@ -2183,7 +2245,7 @@ public static void Run() bool exists = false; try { - typeof(GenericClass<>).MakeGenericType(GetAtom3()); + _ = typeof(GenericClass<>).MakeGenericType(GetAtom3()).TypeHandle; exists = true; } catch