Skip to content
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 @@ -251,8 +251,11 @@ private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePt
}

internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, EETypePtr dstEEType, bool destinationIsByRef = false)
=> CreateChangeTypeArgumentException(srcEEType, Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType)), destinationIsByRef);

internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, Type dstType, bool destinationIsByRef = false)
{
object? destinationTypeName = Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType));
object? destinationTypeName = dstType;
if (destinationIsByRef)
destinationTypeName += "&";
return new ArgumentException(SR.Format(SR.Arg_ObjObjEx, Type.GetTypeFromHandle(new RuntimeTypeHandle(srcEEType)), destinationTypeName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,36 +72,37 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
{
Transform transform = default;

Type argumentType = parameters[i].ParameterType;
var argumentType = (RuntimeType)parameters[i].ParameterType;
if (argumentType.IsByRef)
{
_needsCopyBack = true;
transform |= Transform.ByRef;
argumentType = argumentType.GetElementType()!;
argumentType = (RuntimeType)argumentType.GetElementType()!;
}
Debug.Assert(!argumentType.IsByRef);

EETypePtr eeArgumentType = argumentType.TypeHandle.ToEETypePtr();

if (eeArgumentType.IsValueType)
// This can return a null MethodTable for reference types.
// The compiler makes sure it returns a non-null MT for everything else.
EETypePtr eeArgumentType = argumentType.ToEETypePtrMayBeNull();
if (argumentType.IsValueType)
{
Debug.Assert(argumentType.IsValueType);
Debug.Assert(eeArgumentType.IsValueType);

if (eeArgumentType.IsByRefLike)
_argumentCount = ArgumentCount_NotSupported_ByRefLike;

if (eeArgumentType.IsNullable)
transform |= Transform.Nullable;
}
else if (eeArgumentType.IsPointer)
else if (argumentType.IsPointer)
{
Debug.Assert(argumentType.IsPointer);
Debug.Assert(eeArgumentType.IsPointer);

transform |= Transform.Pointer;
}
else if (eeArgumentType.IsFunctionPointer)
else if (argumentType.IsFunctionPointer)
{
Debug.Assert(argumentType.IsFunctionPointer);
Debug.Assert(eeArgumentType.IsFunctionPointer);

transform |= Transform.FunctionPointer;
}
Expand All @@ -119,19 +120,18 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
{
Transform transform = default;

Type returnType = methodInfo.ReturnType;
var returnType = (RuntimeType)methodInfo.ReturnType;
if (returnType.IsByRef)
{
transform |= Transform.ByRef;
returnType = returnType.GetElementType()!;
returnType = (RuntimeType)returnType.GetElementType()!;
}
Debug.Assert(!returnType.IsByRef);

EETypePtr eeReturnType = returnType.TypeHandle.ToEETypePtr();

if (eeReturnType.IsValueType)
EETypePtr eeReturnType = returnType.ToEETypePtrMayBeNull();
if (returnType.IsValueType)
{
Debug.Assert(returnType.IsValueType);
Debug.Assert(eeReturnType.IsValueType);

if (returnType != typeof(void))
{
Expand All @@ -150,17 +150,17 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk)
_argumentCount = ArgumentCount_NotSupported; // ByRef to void return
}
}
else if (eeReturnType.IsPointer)
else if (returnType.IsPointer)
{
Debug.Assert(returnType.IsPointer);
Debug.Assert(eeReturnType.IsPointer);

transform |= Transform.Pointer;
if ((transform & Transform.ByRef) == 0)
transform |= Transform.AllocateReturnBox;
}
else if (eeReturnType.IsFunctionPointer)
else if (returnType.IsFunctionPointer)
{
Debug.Assert(returnType.IsFunctionPointer);
Debug.Assert(eeReturnType.IsFunctionPointer);

transform |= Transform.FunctionPointer;
if ((transform & Transform.ByRef) == 0)
Expand Down Expand Up @@ -597,6 +597,12 @@ private unsafe ref byte InvokeDirectWithFewArguments(
return defaultValue;
}

private void ThrowForNeverValidNonNullArgument(EETypePtr srcEEType, int index)
{
Debug.Assert(index != 0 || _isStatic);
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, Method.GetParametersAsSpan()[index - (_isStatic ? 0 : 1)].ParameterType, destinationIsByRef: false);
}

private unsafe void CheckArguments(
Span<object?> copyOfParameters,
void* byrefParameters,
Expand Down Expand Up @@ -636,16 +642,25 @@ private unsafe void CheckArguments(
EETypePtr srcEEType = arg.GetEETypePtr();
EETypePtr dstEEType = argumentInfo.Type;

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

arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle);
if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
{
// ByRefs have to be exact match
if ((argumentInfo.Transform & Transform.ByRef) != 0)
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);

arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle);
}
}

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

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

arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle: null);
if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) ||
(dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable
&& castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))))
{
// ByRefs have to be exact match
if ((argumentInfo.Transform & Transform.ByRef) != 0)
throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, argumentInfo.Type, destinationIsByRef: true);

arg = InvokeUtils.CheckArgumentConversions(arg, argumentInfo.Type, InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle: null);
}
}

if ((argumentInfo.Transform & Transform.Reference) == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ internal static void AddSignatureDependency(ref DependencyList dependencies, Nod
if (type.IsPrimitive || type.IsVoid)
return;

// Reflection doesn't need the ability to generate MethodTables out of thin air for reference types.
// Skip generating the dependencies.
if (type.IsGCPointer)
return;

TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);
if (canonType.IsCanonicalSubtype(CanonicalFormKind.Any))
GenericTypesTemplateMap.GetTemplateTypeDependencies(ref dependencies, factory, canonType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static int Run()
{
SanityTest.Run();
TestInstanceMethodOptimization.Run();
TestReflectionInvokeSignatures.Run();
TestAbstractTypeNeverDerivedVirtualsOptimization.Run();
TestAbstractNeverDerivedWithDevirtualizedCall.Run();
TestAbstractDerivedByUnrelatedTypeWithDevirtualizedCall.Run();
Expand Down Expand Up @@ -75,6 +76,32 @@ public static void Run()
}
}

class TestReflectionInvokeSignatures
{
public class Never1 { }

public static void Invoke1(Never1 inst) { }

public struct Allocated1 { }

public static void Invoke2(out Allocated1 inst) { inst = default; }

public static void Run()
{
{
MethodInfo mi = typeof(TestReflectionInvokeSignatures).GetMethod(nameof(Invoke1));
mi.Invoke(null, new object[1]);
ThrowIfPresentWithUsableMethodTable(typeof(TestReflectionInvokeSignatures), nameof(Never1));
}

{
MethodInfo mi = typeof(TestReflectionInvokeSignatures).GetMethod(nameof(Invoke2));
mi.Invoke(null, new object[1]);
ThrowIfNotPresent(typeof(TestReflectionInvokeSignatures), nameof(Allocated1));
}
}
}

class TestAbstractTypeNeverDerivedVirtualsOptimization
{
class UnreferencedType1
Expand Down