diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs index 04a0b66662cda..04caa2251bbdd 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs @@ -838,6 +838,11 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio Debug.Assert(type.IsPointer); obj = Pointer.Box((void*)Unsafe.As(ref byref), type); } + else if ((_returnTransform & Transform.FunctionPointer) != 0) + { + Debug.Assert(Type.GetTypeFromMethodTable(_returnType.ToPointer()).IsFunctionPointer); + obj = RuntimeImports.RhBox(EETypePtr.EETypePtrOf(), ref byref); + } else if ((_returnTransform & Transform.Reference) != 0) { Debug.Assert((_returnTransform & Transform.ByRef) != 0); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs index 1471d1d977ef1..a701be7564566 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokeUtils.cs @@ -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)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs index a85055391ae9d..c890452df3ca2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -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)); } @@ -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)); } @@ -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); } } @@ -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); @@ -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); diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs index c44e69d03a03d..9bb9541c571bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs @@ -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; } @@ -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; diff --git a/src/libraries/System.Reflection/tests/MethodInfoTests.cs b/src/libraries/System.Reflection/tests/MethodInfoTests.cs index ed8499dc251a1..769fc94266a08 100644 --- a/src/libraries/System.Reflection/tests/MethodInfoTests.cs +++ b/src/libraries/System.Reflection/tests/MethodInfoTests.cs @@ -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(() => m.Invoke(null, new object[] { (IntPtr)fn, 42 })); - Assert.Throws(() => 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 })); @@ -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(ret); + Assert.True((IntPtr)ret != 0); + } + //Methods for Reflection Metadata private void DummyMethod1(string str, int iValue, long lValue) { @@ -1347,6 +1373,11 @@ public static unsafe bool CallFcnPtr_IntPtr(IntPtr fn, int value) return ((delegate*)fn)(value); } + public static unsafe bool CallFcnPtr_UIntPtr(UIntPtr fn, int value) + { + return ((delegate*)fn)(value); + } + public static unsafe bool CallFcnPtr_Void(void* fn, int value) { return ((delegate*)fn)(value); diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 2308d3b778049..e7feb145b1884 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -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; } diff --git a/src/mono/mono/metadata/marshal-lightweight.c b/src/mono/mono/metadata/marshal-lightweight.c index 9a0b55f3e4d60..3c5b0de4c67bc 100644 --- a/src/mono/mono/metadata/marshal-lightweight.c +++ b/src/mono/mono/metadata/marshal-lightweight.c @@ -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; diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 98d3e436068be..0b79c0d713b67 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -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); } diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 4132709f8f2bb..d075623de8cf7 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -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: