Skip to content

Commit c6f64ab

Browse files
authored
Support invoke of function pointer type arg (#90270)
1 parent a666f59 commit c6f64ab

File tree

9 files changed

+81
-27
lines changed

9 files changed

+81
-27
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,11 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio
838838
Debug.Assert(type.IsPointer);
839839
obj = Pointer.Box((void*)Unsafe.As<byte, IntPtr>(ref byref), type);
840840
}
841+
else if ((_returnTransform & Transform.FunctionPointer) != 0)
842+
{
843+
Debug.Assert(Type.GetTypeFromMethodTable(_returnType.ToPointer()).IsFunctionPointer);
844+
obj = RuntimeImports.RhBox(EETypePtr.EETypePtrOf<IntPtr>(), ref byref);
845+
}
841846
else if ((_returnTransform & Transform.Reference) != 0)
842847
{
843848
Debug.Assert((_returnTransform & Transform.ByRef) != 0);

src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ namespace System.Reflection
1111
internal static class InvokeUtils
1212
{
1313
// This method is similar to the NativeAot method ConvertOrWidenPrimitivesEnumsAndPointersIfPossible().
14-
public static object ConvertOrWiden(Type srcType, object srcObject, Type dstType, CorElementType dstElementType)
14+
public static object ConvertOrWiden(RuntimeType srcType, object srcObject, RuntimeType dstType, CorElementType dstElementType)
1515
{
1616
object dstObject;
1717

18-
if (dstType.IsPointer)
18+
if (dstType.IsPointer || dstType.IsFunctionPointer)
1919
{
2020
if (TryConvertPointer(srcObject, out object? dstPtr))
2121
{

src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static unsafe InvokeFunc_Obj4Args CreateInvokeDelegate_Obj4Args(MethodBas
6767
break;
6868
}
6969

70-
if (parameterType.IsPointer)
70+
if (parameterType.IsPointer || parameterType.IsFunctionPointer)
7171
{
7272
Unbox(il, typeof(IntPtr));
7373
}
@@ -124,7 +124,7 @@ public static unsafe InvokeFunc_ObjSpanArgs CreateInvokeDelegate_ObjSpanArgs(Met
124124
il.Emit(OpCodes.Call, Methods.Span_get_Item());
125125
il.Emit(OpCodes.Ldind_Ref);
126126

127-
if (parameterType.IsPointer)
127+
if (parameterType.IsPointer || parameterType.IsFunctionPointer)
128128
{
129129
Unbox(il, typeof(IntPtr));
130130
}
@@ -186,7 +186,7 @@ public static unsafe InvokeFunc_RefArgs CreateInvokeDelegate_RefArgs(MethodBase
186186
RuntimeType parameterType = (RuntimeType)parameters[i].ParameterType;
187187
if (!parameterType.IsByRef)
188188
{
189-
il.Emit(OpCodes.Ldobj, parameterType.IsPointer ? typeof(IntPtr) : parameterType);
189+
il.Emit(OpCodes.Ldobj, parameterType.IsPointer || parameterType.IsFunctionPointer ? typeof(IntPtr) : parameterType);
190190
}
191191
}
192192

@@ -269,10 +269,14 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method,
269269
il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle());
270270
il.Emit(OpCodes.Call, Methods.Pointer_Box());
271271
}
272+
else if (returnType.IsFunctionPointer)
273+
{
274+
il.Emit(OpCodes.Box, typeof(IntPtr));
275+
}
272276
else if (returnType.IsByRef)
273277
{
274278
// Check for null ref return.
275-
Type elementType = returnType.GetElementType()!;
279+
RuntimeType elementType = (RuntimeType)returnType.GetElementType()!;
276280
Label retValueOk = il.DefineLabel();
277281
il.Emit(OpCodes.Dup);
278282
il.Emit(OpCodes.Brtrue_S, retValueOk);
@@ -293,6 +297,10 @@ private static void EmitCallAndReturnHandling(ILGenerator il, MethodBase method,
293297
il.Emit(OpCodes.Call, Methods.Type_GetTypeFromHandle());
294298
il.Emit(OpCodes.Call, Methods.Pointer_Box());
295299
}
300+
else if (elementType.IsFunctionPointer)
301+
{
302+
il.Emit(OpCodes.Box, typeof(IntPtr));
303+
}
296304
else
297305
{
298306
il.Emit(OpCodes.Ldobj, elementType);

src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -944,9 +944,9 @@ private CheckValueStatus TryChangeType(ref object? value, ref bool copyBack)
944944

945945
if (value == null)
946946
{
947-
if (IsPointer)
947+
if (IsPointer || IsFunctionPointer)
948948
{
949-
// Pass an IntPtr instead of null for pointers.
949+
// Pass an IntPtr instead of null.
950950
value = default(IntPtr);
951951
return CheckValueStatus.Success;
952952
}
@@ -971,7 +971,7 @@ private CheckValueStatus TryChangeType(ref object? value, ref bool copyBack)
971971
// - Enum treated as underlying type
972972
// - Pointer (*) types to IntPtr (if dest is IntPtr)
973973
// - System.Reflection.Pointer to appropriate pointer (*) type (if dest is pointer type)
974-
if (IsPointer || IsEnum || IsPrimitive)
974+
if (IsPointer || IsEnum || IsPrimitive || IsFunctionPointer)
975975
return TryChangeTypeSpecial(ref value);
976976

977977
return CheckValueStatus.ArgumentException;

src/libraries/System.Reflection/tests/MethodInfoTests.cs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -895,32 +895,24 @@ private static void SecondCall(MethodInfo mi)
895895
}
896896

897897
[Fact]
898-
private static unsafe void TestFunctionPointers()
898+
private static unsafe void TestFunctionPointerDirect()
899899
{
900-
void* fn = FunctionPointerMethods.GetFunctionPointer();
901-
902900
// Sanity checks for direct invocation.
901+
void* fn = FunctionPointerMethods.GetFunctionPointer();
903902
Assert.True(FunctionPointerMethods.GetFunctionPointer()(42));
904903
Assert.True(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 42));
905904
Assert.True(FunctionPointerMethods.CallFcnPtr_Void(fn, 42));
906905
Assert.False(FunctionPointerMethods.GetFunctionPointer()(41));
907906
Assert.False(FunctionPointerMethods.CallFcnPtr_IntPtr((IntPtr)fn, 41));
908907
Assert.False(FunctionPointerMethods.CallFcnPtr_Void(fn, 41));
908+
}
909909

910-
MethodInfo m;
910+
[Fact]
911+
private static unsafe void TestFunctionPointerAsIntPtrArgType()
912+
{
913+
void* fn = FunctionPointerMethods.GetFunctionPointer();
911914

912-
m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
913-
if (PlatformDetection.IsNativeAot)
914-
{
915-
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
916-
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
917-
}
918-
else
919-
{
920-
// System.ArgumentException : Object of type 'System.IntPtr' cannot be converted to type 'System.Boolean(System.Int32)'
921-
Assert.Throws<ArgumentException>(() => m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
922-
Assert.Throws<ArgumentException>(() => m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
923-
}
915+
MethodInfo m;
924916

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

926+
[Fact]
927+
private static unsafe void TestFunctionPointerAsUIntPtrArgType()
928+
{
929+
void* fn = FunctionPointerMethods.GetFunctionPointer();
930+
931+
MethodInfo m;
932+
933+
m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_UIntPtr));
934+
Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
935+
Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));
936+
937+
m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_Void));
938+
Assert.True((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 42 }));
939+
Assert.False((bool)m.Invoke(null, new object[] { (UIntPtr)fn, 41 }));
940+
}
941+
942+
[Fact]
943+
private static unsafe void TestFunctionPointerAsArgType()
944+
{
945+
void* fn = FunctionPointerMethods.GetFunctionPointer();
946+
MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_FP));
947+
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
948+
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
949+
}
950+
951+
[Fact]
952+
private static unsafe void TestFunctionPointerAsReturnType()
953+
{
954+
MethodInfo m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.GetFunctionPointer));
955+
object ret = m.Invoke(null, null);
956+
Assert.IsType<IntPtr>(ret);
957+
Assert.True((IntPtr)ret != 0);
958+
}
959+
934960
//Methods for Reflection Metadata
935961
private void DummyMethod1(string str, int iValue, long lValue)
936962
{
@@ -1347,6 +1373,11 @@ public static unsafe bool CallFcnPtr_IntPtr(IntPtr fn, int value)
13471373
return ((delegate*<int, bool>)fn)(value);
13481374
}
13491375

1376+
public static unsafe bool CallFcnPtr_UIntPtr(UIntPtr fn, int value)
1377+
{
1378+
return ((delegate*<int, bool>)fn)(value);
1379+
}
1380+
13501381
public static unsafe bool CallFcnPtr_Void(void* fn, int value)
13511382
{
13521383
return ((delegate*<int, bool>)fn)(value);

src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,14 @@ private CheckValueStatus TryChangeTypeSpecial(
18051805
}
18061806
}
18071807
}
1808+
else if (IsFunctionPointer)
1809+
{
1810+
if (value is IntPtr or UIntPtr)
1811+
return CheckValueStatus.Success;
1812+
1813+
value = (IntPtr)value;
1814+
return CheckValueStatus.Success;
1815+
}
18081816

18091817
return CheckValueStatus.ArgumentException;
18101818
}

src/mono/mono/metadata/marshal-lightweight.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method,
436436
/* nothing to do */
437437
break;
438438
case MONO_TYPE_PTR:
439+
case MONO_TYPE_FNPTR:
439440
/* The result is an IntPtr */
440441
mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class);
441442
break;

src/mono/mono/metadata/object.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5097,10 +5097,10 @@ invoke_byrefs_extract_argument (gpointer *params_byref, int i, MonoType *t)
50975097
else
50985098
t = m_class_get_byval_arg (t->data.generic_class->container_class);
50995099
goto again;
5100-
case MONO_TYPE_PTR: {
5100+
case MONO_TYPE_PTR:
5101+
case MONO_TYPE_FNPTR:
51015102
result = *(gpointer*)params_byref [i];
51025103
break;
5103-
}
51045104
default:
51055105
g_error ("type 0x%x not handled in ves_icall_InternalInvoke", t->type);
51065106
}

src/mono/mono/mini/mini-runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3268,6 +3268,7 @@ create_runtime_invoke_info (MonoMethod *method, gpointer compiled_method, gboole
32683268
info->ret_box_class = mono_class_from_mono_type_internal (ret_type);
32693269
break;
32703270
case MONO_TYPE_PTR:
3271+
case MONO_TYPE_FNPTR:
32713272
info->ret_box_class = mono_defaults.int_class;
32723273
break;
32733274
case MONO_TYPE_STRING:

0 commit comments

Comments
 (0)