Skip to content

Commit beb4ede

Browse files
Try rooting less stuff for reflectable method signatures
1 parent 655b177 commit beb4ede

File tree

4 files changed

+96
-37
lines changed

4 files changed

+96
-37
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,11 @@ private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePt
251251
}
252252

253253
internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, EETypePtr dstEEType, bool destinationIsByRef = false)
254+
=> CreateChangeTypeArgumentException(srcEEType, Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType)), destinationIsByRef);
255+
256+
internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, Type dstType, bool destinationIsByRef = false)
254257
{
255-
object? destinationTypeName = Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType));
258+
object? destinationTypeName = dstType;
256259
if (destinationIsByRef)
257260
destinationTypeName += "&";
258261
return new ArgumentException(SR.Format(SR.Arg_ObjObjEx, Type.GetTypeFromHandle(new RuntimeTypeHandle(srcEEType)), destinationTypeName));

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -72,36 +72,37 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
7272
{
7373
Transform transform = default;
7474

75-
Type argumentType = parameters[i].ParameterType;
75+
var argumentType = (RuntimeType)parameters[i].ParameterType;
7676
if (argumentType.IsByRef)
7777
{
7878
_needsCopyBack = true;
7979
transform |= Transform.ByRef;
80-
argumentType = argumentType.GetElementType()!;
80+
argumentType = (RuntimeType)argumentType.GetElementType()!;
8181
}
8282
Debug.Assert(!argumentType.IsByRef);
8383

84-
EETypePtr eeArgumentType = argumentType.TypeHandle.ToEETypePtr();
85-
86-
if (eeArgumentType.IsValueType)
84+
// This can return a null MethodTable for reference types.
85+
// The compiler makes sure it returns a non-null MT for everything else.
86+
EETypePtr eeArgumentType = argumentType.ToEETypePtrMayBeNull();
87+
if (argumentType.IsValueType)
8788
{
88-
Debug.Assert(argumentType.IsValueType);
89+
Debug.Assert(eeArgumentType.IsValueType);
8990

9091
if (eeArgumentType.IsByRefLike)
9192
_argumentCount = ArgumentCount_NotSupported_ByRefLike;
9293

9394
if (eeArgumentType.IsNullable)
9495
transform |= Transform.Nullable;
9596
}
96-
else if (eeArgumentType.IsPointer)
97+
else if (argumentType.IsPointer)
9798
{
98-
Debug.Assert(argumentType.IsPointer);
99+
Debug.Assert(eeArgumentType.IsPointer);
99100

100101
transform |= Transform.Pointer;
101102
}
102-
else if (eeArgumentType.IsFunctionPointer)
103+
else if (argumentType.IsFunctionPointer)
103104
{
104-
Debug.Assert(argumentType.IsFunctionPointer);
105+
Debug.Assert(eeArgumentType.IsFunctionPointer);
105106

106107
transform |= Transform.FunctionPointer;
107108
}
@@ -119,19 +120,18 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
119120
{
120121
Transform transform = default;
121122

122-
Type returnType = methodInfo.ReturnType;
123+
var returnType = (RuntimeType)methodInfo.ReturnType;
123124
if (returnType.IsByRef)
124125
{
125126
transform |= Transform.ByRef;
126-
returnType = returnType.GetElementType()!;
127+
returnType = (RuntimeType)returnType.GetElementType()!;
127128
}
128129
Debug.Assert(!returnType.IsByRef);
129130

130-
EETypePtr eeReturnType = returnType.TypeHandle.ToEETypePtr();
131-
132-
if (eeReturnType.IsValueType)
131+
EETypePtr eeReturnType = returnType.ToEETypePtrMayBeNull();
132+
if (returnType.IsValueType)
133133
{
134-
Debug.Assert(returnType.IsValueType);
134+
Debug.Assert(eeReturnType.IsValueType);
135135

136136
if (returnType != typeof(void))
137137
{
@@ -150,17 +150,17 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
150150
_argumentCount = ArgumentCount_NotSupported; // ByRef to void return
151151
}
152152
}
153-
else if (eeReturnType.IsPointer)
153+
else if (returnType.IsPointer)
154154
{
155-
Debug.Assert(returnType.IsPointer);
155+
Debug.Assert(eeReturnType.IsPointer);
156156

157157
transform |= Transform.Pointer;
158158
if ((transform & Transform.ByRef) == 0)
159159
transform |= Transform.AllocateReturnBox;
160160
}
161-
else if (eeReturnType.IsFunctionPointer)
161+
else if (returnType.IsFunctionPointer)
162162
{
163-
Debug.Assert(returnType.IsFunctionPointer);
163+
Debug.Assert(eeReturnType.IsFunctionPointer);
164164

165165
transform |= Transform.FunctionPointer;
166166
if ((transform & Transform.ByRef) == 0)
@@ -597,6 +597,12 @@ private unsafe ref byte InvokeDirectWithFewArguments(
597597
return defaultValue;
598598
}
599599

600+
private void ThrowForNeverValidNonNullArgument(EETypePtr srcEEType, int index)
601+
{
602+
Debug.Assert(index != 0 || _isStatic);
603+
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, Method.GetParametersAsSpan()[index - (_isStatic ? 0 : 1)].ParameterType, destinationIsByRef: false);
604+
}
605+
600606
private unsafe void CheckArguments(
601607
Span<object?> copyOfParameters,
602608
void* byrefParameters,
@@ -636,16 +642,25 @@ private unsafe void CheckArguments(
636642
EETypePtr srcEEType = arg.GetEETypePtr();
637643
EETypePtr dstEEType = argumentInfo.Type;
638644

639-
if (!(srcEEType.RawValue == dstEEType.RawValue ||
640-
RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
641-
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
642-
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
645+
if (srcEEType.RawValue != dstEEType.RawValue)
643646
{
644-
// ByRefs have to be exact match
645-
if ((argumentInfo.Transform & Transform.ByRef) != 0)
646-
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);
647+
// Destination type can be null if we don't have a MethodTable for this type. This means one cannot
648+
// possibly pass a valid non-null object instance here.
649+
if (dstEEType.IsNull)
650+
{
651+
ThrowForNeverValidNonNullArgument(srcEEType, i);
652+
}
647653

648-
arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle);
654+
if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
655+
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
656+
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
657+
{
658+
// ByRefs have to be exact match
659+
if ((argumentInfo.Transform & Transform.ByRef) != 0)
660+
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);
661+
662+
arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle);
663+
}
649664
}
650665

651666
if ((argumentInfo.Transform & Transform.Reference) == 0)
@@ -704,16 +719,25 @@ private unsafe void CheckArguments(
704719
EETypePtr srcEEType = arg.GetEETypePtr();
705720
EETypePtr dstEEType = argumentInfo.Type;
706721

707-
if (!(srcEEType.RawValue == dstEEType.RawValue ||
708-
RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
709-
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
710-
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
722+
if (srcEEType.RawValue != dstEEType.RawValue)
711723
{
712-
// ByRefs have to be exact match
713-
if ((argumentInfo.Transform & Transform.ByRef) != 0)
714-
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);
724+
// Destination type can be null if we don't have a MethodTable for this type. This means one cannot
725+
// possibly pass a valid non-null object instance here.
726+
if (dstEEType.IsNull)
727+
{
728+
ThrowForNeverValidNonNullArgument(srcEEType, i);
729+
}
715730

716-
arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle: null);
731+
if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
732+
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
733+
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
734+
{
735+
// ByRefs have to be exact match
736+
if ((argumentInfo.Transform & Transform.ByRef) != 0)
737+
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);
738+
739+
arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle: null);
740+
}
717741
}
718742

719743
if ((argumentInfo.Transform & Transform.Reference) == 0)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ internal static void AddSignatureDependency(ref DependencyList dependencies, Nod
104104
if (type.IsPrimitive || type.IsVoid)
105105
return;
106106

107+
// Reflection doesn't need the ability to generate MethodTables out of thin air for reference types.
108+
// Skip generating the dependencies.
109+
if (type.IsGCPointer)
110+
return;
111+
107112
TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);
108113
if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any))
109114
GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, canonType);

src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static int Run()
1313
{
1414
SanityTest.Run();
1515
TestInstanceMethodOptimization.Run();
16+
TestReflectionInvokeSignatures.Run();
1617
TestAbstractTypeNeverDerivedVirtualsOptimization.Run();
1718
TestAbstractNeverDerivedWithDevirtualizedCall.Run();
1819
TestAbstractDerivedByUnrelatedTypeWithDevirtualizedCall.Run();
@@ -75,6 +76,32 @@ public static void Run()
7576
}
7677
}
7778

79+
class TestReflectionInvokeSignatures
80+
{
81+
public class Never1 { }
82+
83+
public static void Invoke1(Never1 inst) { }
84+
85+
public struct Allocated1 { }
86+
87+
public static void Invoke2(out Allocated1 inst) { inst = default; }
88+
89+
public static void Run()
90+
{
91+
{
92+
MethodInfo mi = typeof(TestReflectionInvokeSignatures).GetMethod(nameof(Invoke1));
93+
mi.Invoke(null, new object[1]);
94+
ThrowIfPresentWithUsableMethodTable(typeof(TestReflectionInvokeSignatures), nameof(Never1));
95+
}
96+
97+
{
98+
MethodInfo mi = typeof(TestReflectionInvokeSignatures).GetMethod(nameof(Invoke2));
99+
mi.Invoke(null, new object[1]);
100+
ThrowIfNotPresent(typeof(TestReflectionInvokeSignatures), nameof(Allocated1));
101+
}
102+
}
103+
}
104+
78105
class TestAbstractTypeNeverDerivedVirtualsOptimization
79106
{
80107
class UnreferencedType1

0 commit comments

Comments
 (0)