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

Support invoke of function pointer type arg #90270

Merged
merged 5 commits into from
Aug 14, 2023
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 @@ -838,6 +838,11 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio
Debug.Assert(type.IsPointer);
obj = Pointer.Box((void*)Unsafe.As<byte, IntPtr>(ref byref), type);
}
else if ((_returnTransform & Transform.FunctionPointer) != 0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this change, a checked build would throw in the else statement below where it tried to box _returnType.

{
Debug.Assert(Type.GetTypeFromMethodTable(_returnType.ToPointer()).IsFunctionPointer);
obj = RuntimeImports.RhBox(EETypePtr.EETypePtrOf<IntPtr>(), ref byref);
}
else if ((_returnTransform & Transform.Reference) != 0)
{
Debug.Assert((_returnTransform & Transform.ByRef) != 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace System.Reflection
internal static class InvokeUtils
{
// This method is similar to the NativeAot method ConvertOrWidenPrimitivesEnumsAndPointersIfPossible().
public static object ConvertOrWiden(Type srcType, object srcObject, Type dstType, CorElementType dstElementType)
public static object ConvertOrWiden(RuntimeType srcType, object srcObject, RuntimeType dstType, CorElementType dstElementType)
{
object dstObject;

if (dstType.IsPointer)
if (dstType.IsPointer || dstType.IsFunctionPointer)
{
if (TryConvertPointer(srcObject, out object? dstPtr))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public static unsafe InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBas
break;
}

if (parameterType.IsPointer)
if (parameterType.IsPointer || parameterType.IsFunctionPointer)
{
Unbox(il, typeof(IntPtr));
}
Expand Down Expand Up @@ -124,7 +124,7 @@ public static unsafe InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(Met
il.Emit(OpCodes.Call, Methods.Span_get_Item());
il.Emit(OpCodes.Ldind_Ref);

if (parameterType.IsPointer)
if (parameterType.IsPointer || parameterType.IsFunctionPointer)
{
Unbox(il, typeof(IntPtr));
}
Expand Down Expand Up @@ -186,7 +186,7 @@ public static unsafe InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase
RuntimeType parameterType = (RuntimeType)parameters[i].ParameterType;
if (!parameterType.IsByRef)
{
il.Emit(OpCodes.Ldobj, parameterType.IsPointer ? typeof(IntPtr) : parameterType);
il.Emit(OpCodes.Ldobj, parameterType.IsPointer || parameterType.IsFunctionPointer ? typeof(IntPtr) : parameterType);
}
}

Expand Down Expand Up @@ -269,10 +269,14 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method,
il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle());
il.Emit(OpCodes.Call, Methods.Pointer_Box());
}
else if (returnType.IsFunctionPointer)
{
il.Emit(OpCodes.Box, typeof(IntPtr));
}
else if (returnType.IsByRef)
{
// Check for null ref return.
Type elementType = returnType.GetElementType()!;
RuntimeType elementType = (RuntimeType)returnType.GetElementType()!;
Label retValueOk = il.DefineLabel();
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue_S, retValueOk);
Expand All @@ -293,6 +297,10 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method,
il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle());
il.Emit(OpCodes.Call, Methods.Pointer_Box());
}
else if (elementType.IsFunctionPointer)
{
il.Emit(OpCodes.Box, typeof(IntPtr));
}
else
{
il.Emit(OpCodes.Ldobj, elementType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -944,9 +944,9 @@ private CheckValueStatus TryChangeType(ref object? value, ref bool copyBack)

if (value == null)
{
if (IsPointer)
if (IsPointer || IsFunctionPointer)
{
// Pass an IntPtr instead of null for pointers.
// Pass an IntPtr instead of null.
value = default(IntPtr);
return CheckValueStatus.Success;
}
Expand All @@ -971,7 +971,7 @@ private CheckValueStatus TryChangeType(ref object? value, ref bool copyBack)
// - Enum treated as underlying type
// - Pointer (*) types to IntPtr (if dest is IntPtr)
// - System.Reflection.Pointer to appropriate pointer (*) type (if dest is pointer type)
if (IsPointer || IsEnum || IsPrimitive)
if (IsPointer || IsEnum || IsPrimitive || IsFunctionPointer)
return TryChangeTypeSpecial(ref value);

return CheckValueStatus.ArgumentException;
Expand Down
63 changes: 47 additions & 16 deletions src/libraries/System.Reflection/tests/MethodInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -895,32 +895,24 @@ private static void SecondCall(MethodInfo mi)
}

[Fact]
private static unsafe void TestFunctionPointers()
private static unsafe void TestFunctionPointerDirect()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();

// Sanity checks for direct invocation.
void* fn = FunctionPointerMethods.GetFunctionPointer();
Assert.True(FunctionPointerMethods.GetFunctionPointer()(42));
Assert.True(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 42));
Assert.True(FunctionPointerMethods.CallFcnPtr_Void(fn, 42));
Assert.False(FunctionPointerMethods.GetFunctionPointer()(41));
Assert.False(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 41));
Assert.False(FunctionPointerMethods.CallFcnPtr_Void(fn, 41));
}

MethodInfo m;
[Fact]
private static unsafe void TestFunctionPointerAsIntPtrArgType()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();

m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
if (PlatformDetection.IsNativeAot)
{
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
}
else
{
// System.ArgumentException : Object of type 'System.IntPtr' cannot be converted to type 'System.Boolean(System.Int32)'
Assert.Throws<ArgumentException>(() => m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
Assert.Throws<ArgumentException>(() => m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
}
MethodInfo m;

m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_IntPtr));
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
Expand All @@ -931,6 +923,40 @@ private static unsafe void TestFunctionPointers()
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
}

[Fact]
private static unsafe void TestFunctionPointerAsUIntPtrArgType()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();

MethodInfo m;

m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_UIntPtr));
Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));

m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));
}

[Fact]
private static unsafe void TestFunctionPointerAsArgType()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();
MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
}

[Fact]
private static unsafe void TestFunctionPointerAsReturnType()
{
MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.GetFunctionPointer));
object ret = m.Invoke(null, null);
Assert.IsType<IntPtr>(ret);
Assert.True((IntPtr)ret != 0);
}

//Methods for Reflection Metadata
private void DummyMethod1(string str, int iValue, long lValue)
{
Expand Down Expand Up @@ -1347,6 +1373,11 @@ public static unsafe bool CallFcnPtr_IntPtr(IntPtr fn, int value)
return ((delegate*<int, bool>)fn)(value);
}

public static unsafe bool CallFcnPtr_UIntPtr(UIntPtr fn, int value)
{
return ((delegate*<int, bool>)fn)(value);
}

public static unsafe bool CallFcnPtr_Void(void* fn, int value)
{
return ((delegate*<int, bool>)fn)(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1805,6 +1805,14 @@ private CheckValueStatus TryChangeTypeSpecial(
}
}
}
else if (IsFunctionPointer)
{
if (value is IntPtr or UIntPtr)
return CheckValueStatus.Success;

value = (IntPtr)value;
return CheckValueStatus.Success;
}

return CheckValueStatus.ArgumentException;
}
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/marshal-lightweight.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method,
/* nothing to do */
break;
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
/* The result is an IntPtr */
mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class);
break;
Expand Down
4 changes: 2 additions & 2 deletions src/mono/mono/metadata/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -5097,10 +5097,10 @@ invoke_byrefs_extract_argument (gpointer *params_byref, int i, MonoType *t)
else
t = m_class_get_byval_arg (t->data.generic_class->container_class);
goto again;
case MONO_TYPE_PTR: {
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
result = *(gpointer*)params_byref [i];
break;
}
default:
g_error ("type 0x%x not handled in ves_icall_InternalInvoke", t->type);
}
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3268,6 +3268,7 @@ create_runtime_invoke_info (MonoMethod *method, gpointer compiled_method, gboole
info->ret_box_class = mono_class_from_mono_type_internal (ret_type);
break;
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
info->ret_box_class = mono_defaults.int_class;
break;
case MONO_TYPE_STRING:
Expand Down