Skip to content

Commit

Permalink
Support invoke of function pointer type arg
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Aug 10, 2023
1 parent f5889ec commit 6d616d3
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3668,6 +3668,9 @@ private CheckValueStatus TryChangeTypeSpecial(ref object value)
return CheckValueStatus.Success;
}

// Remove this once Mono support for function pointers (#71095) is addressed.
internal bool IsIntPtrBasedFunctionPointer => IsFunctionPointer;

#endregion

#region Function Pointer
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.IsIntPtrBasedFunctionPointer)
{
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.IsIntPtrBasedFunctionPointer)
{
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.IsIntPtrBasedFunctionPointer)
{
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.IsIntPtrBasedFunctionPointer ? 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.IsIntPtrBasedFunctionPointer)
{
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.IsIntPtrBasedFunctionPointer)
{
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 || IsIntPtrBasedFunctionPointer)
{
// 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 || IsIntPtrBasedFunctionPointer)
return TryChangeTypeSpecial(ref value);

return CheckValueStatus.ArgumentException;
Expand Down
34 changes: 20 additions & 14 deletions src/libraries/System.Reflection/tests/MethodInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -894,8 +894,27 @@ private static void SecondCall(MethodInfo mi)
Assert.Contains("TestAssembly", asm.ToString());
}

// Mono will throw System.ArgumentException : Object of type 'System.IntPtr' cannot be converted to type 'System.Boolean(System.Int32)'.
[ActiveIssue("https://github.com/dotnet/runtime/issues/71095", TestRuntimes.Mono)]
private static unsafe void TestFunctionPointersWithFunctionPointerArgs()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();
MethodInfo m;

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 }));

// Verify return type; currently returned as a boxed IntPtr.
m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.GetFunctionPointer));
object ret = m.Invoke(null, null);
Assert.IsType<IntPtr>(ret);
Assert.True((IntPtr)ret != 0);
}

// Mono will throw System.ArgumentException : Object of type 'System.IntPtr' cannot be converted to type 'System.Boolean(System.Int32)'.
[Fact]
private static unsafe void TestFunctionPointers()
private static unsafe void TestFunctionPointersWithIntPtrArgs()
{
void* fn = FunctionPointerMethods.GetFunctionPointer();

Expand All @@ -909,19 +928,6 @@ private static unsafe void TestFunctionPointers()

MethodInfo m;

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 }));
}

m = GetMethod(typeof(FunctionPointerMethods), nameof(FunctionPointerMethods.CallFcnPtr_IntPtr));
Assert.True((bool)m.Invoke(null, new object[] { (IntPtr)fn, 42 }));
Assert.False((bool)m.Invoke(null, new object[] { (IntPtr)fn, 41 }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,11 @@ private CheckValueStatus TryChangeTypeSpecial(
return CheckValueStatus.ArgumentException;
}

// Remove this once Mono support for function pointers (#71095) is addressed.
#pragma warning disable CA1822
internal bool IsIntPtrBasedFunctionPointer => false;
#pragma warning restore CA1822

// Binder uses some incompatible conversion rules. For example
// int value cannot be used with decimal parameter but in other
// ways it's more flexible than normal convertor, for example
Expand Down

0 comments on commit 6d616d3

Please sign in to comment.