Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow RuntimeType with missing EEType in some cases #965

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,37 @@ 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
#if FEATURE_COMINTEROP
&& !(elementType is RuntimeCLSIDTypeInfo)
#endif
)
throw ReflectionCoreExecution.ExecutionDomain.CreateMissingArrayTypeException(elementType, isMultiDim: false, 1);

return arrayType;
}

public static RuntimeTypeInfo GetByRefType(this RuntimeTypeInfo targetType)
{
return RuntimeByRefTypeInfo.GetByRefTypeInfo(targetType);
Expand All @@ -64,6 +90,31 @@ public static RuntimeTypeInfo GetConstructedGenericType(this RuntimeTypeInfo gen
return RuntimeConstructedGenericTypeInfo.GetRuntimeConstructedGenericTypeInfo(genericTypeDefinition, genericTypeArguments);
}

public static RuntimeTypeInfo GetConstructedGenericTypeWithTypeHandle(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.ExecutionDomain.CreateMissingConstructedGenericTypeException(genericType.GetGenericTypeDefinition(), genericTypeArguments.CloneTypeArray());
}

return genericType;
}

public static RuntimeTypeInfo GetTypeForRuntimeTypeHandle(this RuntimeTypeHandle typeHandle)
{
Type type = Type.GetTypeFromHandle(typeHandle);
Expand Down Expand Up @@ -284,16 +335,6 @@ private static void ValidateElementType(RuntimeTypeInfo elementType, RuntimeType

if (elementType.IsByRef)
throw new TypeLoadException(SR.Format(SR.ArgumentException_InvalidArrayElementType, 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.
if (TypeUnifier.IsTypeConstructionEagerlyValidated
&& typeHandle.IsNull() && !elementType.ContainsGenericParameters
#if FEATURE_COMINTEROP
&& !(elementType is RuntimeCLSIDTypeInfo)
#endif
)
throw ReflectionCoreExecution.ExecutionDomain.CreateMissingArrayTypeException(elementType, multiDim, rank);
}
}

Expand Down Expand Up @@ -433,21 +474,12 @@ private sealed class ConstructedGenericTypeTable : ConcurrentUnifierWKeyed<Unifi
{
protected sealed override RuntimeConstructedGenericTypeInfo Factory(UnificationKey key)
{
bool atLeastOneOpenType = false;
foreach (RuntimeTypeInfo genericTypeArgument in key.GenericTypeArguments)
{
if (genericTypeArgument.IsByRef || genericTypeArgument.IsGenericTypeDefinition)
throw new ArgumentException(SR.Format(SR.ArgumentException_InvalidTypeArgument, genericTypeArgument));
if (genericTypeArgument.ContainsGenericParameters)
atLeastOneOpenType = true;
}

// 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.
if (TypeUnifier.IsTypeConstructionEagerlyValidated
&& key.TypeHandle.IsNull() && !atLeastOneOpenType)
throw ReflectionCoreExecution.ExecutionDomain.CreateMissingConstructedGenericTypeException(key.GenericTypeDefinition, key.GenericTypeArguments.CloneTypeArray());

return new RuntimeConstructedGenericTypeInfo(key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public sealed override Type MakeArrayType()
// Do not implement this as a call to MakeArrayType(1) - they are not interchangable. 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.GetArrayType();
return this.GetArrayTypeWithTypeHandle();
}

public sealed override Type MakeArrayType(int rank)
Expand All @@ -444,7 +444,7 @@ public sealed override Type MakeArrayType(int rank)

if (rank <= 0)
throw new IndexOutOfRangeException();
return this.GetMultiDimArrayType(rank);
return this.GetMultiDimArrayTypeWithTypeHandle(rank);
}

public sealed override Type MakeByRefType()
Expand Down Expand Up @@ -509,7 +509,7 @@ public sealed override Type MakeGenericType(params Type[] typeArguments)
throw new TypeLoadException(SR.CannotUseByRefLikeTypeInInstantiation);
}

return this.GetConstructedGenericType(runtimeTypeArguments);
return this.GetConstructedGenericTypeWithTypeHandle(runtimeTypeArguments);
}

public sealed override Type MakePointerType()
Expand Down Expand Up @@ -569,11 +569,12 @@ public sealed override RuntimeTypeHandle TypeHandle
if (!typeHandle.IsNull())
return typeHandle;

// If a constructed type doesn't have an type handle, it's either because the reducer tossed it (in which case,
// we would thrown a MissingMetadataException when attempting to construct the type) or because one of
// component types contains open type parameters. Since we eliminated the first case, it must be the second.
// Throwing PlatformNotSupported since the desktop does, in fact, create type handles for open types.
if (HasElementType || IsConstructedGenericType || IsGenericParameter)
// If a type doesn't have a type handle, it's either because we optimized away the EEType
// but the reflection metadata had to be kept around, or because we have an open type somewhere
// (open types never get EETypes). Open types are PlatformNotSupported and there's nothing
// that can be done about that. Missing EEType can be fixed by helping the AOT compiler
// with some hints.
if (!IsGenericTypeDefinition && ContainsGenericParameters)
throw new PlatformNotSupportedException(SR.PlatformNotSupported_NoTypeHandleForOpenTypes);

// If got here, this is a "plain old type" that has metadata but no type handle. We can get here if the only
Expand Down
95 changes: 72 additions & 23 deletions src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ private static int Main()
TestInstanceFields.Run();
TestReflectionInvoke.Run();
#if !CODEGEN_CPP
TypeConstructionTest.Run();
TestThreadStaticFields.Run();
TestByRefReturnInvoke.Run();
TestAssemblyLoad.Run();
Expand Down Expand Up @@ -677,50 +678,50 @@ public static void Run()
{
Console.WriteLine(nameof(TestAttributeExpressions));

// We don't create EETypes for types referenced from typeof which makes
// getting their constructed version to fail at runtime because
// we place restrictions on on the ability of getting System.Type for
// constructed types.
//
// The workaround is to put some dataflow annotations on the Type-typed
// parameter/property/fields.
//
// This is testing that we get an actionable exception message.
//
// The reason why we don't create EETypes for these is size -
// and one wouldn't be able to do much with just the type anyway.
// We would need at least some methods and at that point we have reflectable
// methods and therefore an EEType.
const string attr1expected = "ReflectionTest+TestAttributeExpressions+FirstNeverUsedType*[,]";
TypeAttribute attr1 = typeof(Holder1).GetCustomAttribute<TypeAttribute>();
if (attr1.SomeType.ToString() != attr1expected)
throw new Exception();

// We don't expect to have an EEType because a mention of a type in the custom attribute
// blob is not a sufficient condition to create EETypes.
string exceptionString1 = "";
try
{
typeof(Holder1).GetCustomAttribute<TypeAttribute>();
throw new Exception();
_ = attr1.SomeType.TypeHandle;
}
catch (Exception ex)
{
if (!ex.ToString().Contains("ReflectionTest+TestAttributeExpressions+FirstNeverUsedType*[,]"))
throw;
exceptionString1 = ex.Message;
}
if (!exceptionString1.Contains(attr1expected))
throw new Exception(exceptionString1);

const string attr2expected = "ReflectionTest+TestAttributeExpressions+Gen`1[ReflectionTest+TestAttributeExpressions+SecondNeverUsedType]";
TypeAttribute attr2 = typeof(Holder2).GetCustomAttribute<TypeAttribute>();
if (attr2.SomeType.ToString() != attr2expected)
throw new Exception();

// We don't expect to have an EEType because a mention of a type in the custom attribute
// blob is not a sufficient condition to create EETypes.
string exceptionString2 = "";
try
{
typeof(Holder2).GetCustomAttribute<TypeAttribute>();
_ = attr2.SomeType.TypeHandle;
}
catch (Exception ex)
{
if (!ex.ToString().Contains("ReflectionTest+TestAttributeExpressions+Gen`1[ReflectionTest+TestAttributeExpressions+SecondNeverUsedType]"))
throw;
exceptionString2 = ex.Message;
}
if (!exceptionString2.Contains(attr2expected))
throw new Exception(exceptionString2);

// Make sure we created EEType for the enum array.

EnumArrayAttribute attr3 = typeof(Holder3).GetCustomAttribute<EnumArrayAttribute>();
if (attr3.EnumArray[0] != 0)
throw new Exception();

// Unconstructed types don't have the problem with missing EETypes described above

TypeAttribute attr4 = typeof(Holder4).GetCustomAttribute<TypeAttribute>();
if (attr4.SomeType.ToString() != "ReflectionTest+TestAttributeExpressions+ThirdNeverUsedType")
throw new Exception();
Expand Down Expand Up @@ -1400,6 +1401,54 @@ public static void Run()
}
}

class TypeConstructionTest
{
struct Atom { }

class Gen<T> { }

static Type s_atom = typeof(Atom);

public static void Run()
{
string message1 = "";
try
{
typeof(Gen<>).MakeGenericType(s_atom);
}
catch (Exception ex)
{
message1 = ex.Message;
}
if (!message1.Contains("ReflectionTest.TypeConstructionTest.Gen<ReflectionTest.TypeConstructionTest.Atom>"))
throw new Exception();

string message2 = "";
try
{
s_atom.MakeArrayType();
}
catch (Exception ex)
{
message2 = ex.Message;
}
if (!message2.Contains("ReflectionTest.TypeConstructionTest.Atom[]"))
throw new Exception();

string message3 = "";
try
{
Array.CreateInstance(s_atom, 10);
}
catch (Exception ex)
{
message3 = ex.Message;
}
if (!message3.Contains("ReflectionTest.TypeConstructionTest.Atom[]"))
throw new Exception();
}
}

#region Helpers

private static Type GetTestType(string testName, string typeName)
Expand Down