You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow Marshal.GetFunctionPointerForDelegate() to work with generic types #32963
There is a difference between Mono & .NET Framework/.NET Core regarding the use of Marshal.GetFunctionPointerForDelegate() and generic delegate types. Consider:
usingSystem;usingSystem.Runtime.InteropServices;classApp{publicstaticvoidMain(){Action<int>a= v =>{};varp=Marshal.GetFunctionPointerForDelegate(a);}}
On .NET Framework, this app fails:
Unhandled Exception: System.ArgumentException: The specified Type must not be a generic type definition.
Parameter name: delegate
at System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegateInternal(Delegate d)
at App.Main()
This works on Mono.
.NET Core currently has the same check as .NET Framework:
Rationale: Xamarin.Android currently uses System.Action<...> and System.Func<...> with Marshal.GetFunctionPointerForDelegate() to register function pointers with JNI. This has always worked on Mono, and Xamarin.Android uses mono, so it's been Fine.
However, it would be nice to use Xamarin.Android's JNI infrastructure on .NET Core. At present, if this were to be done it would fail as soon as we hit Marshal.GetFunctionPointerForDelegate() for method registration.
The Xamarin.Android team could instead alter their binding infrastructure so that Action<...> and Func<...> are not used. The Xamarin.Android team would like to know if this code generator change is required for eventual .NET Core support, or if we can instead forego this change.
Thanks to the Similar issues window, #4547 was suggested. I'm not sure if this is entirely duplicative or not, but Issue #4547 is currently Closed, though it was also added to the .NET 5 milestone, so I'm not entirely sure if these are the same or not.
Reacted by NN, Curtis Wensley, SupinePandora43 and ero
Marshal.GetFunctionPointerForDelegate for generic types is not straightforward to implement in CoreCLR, and it is even more difficult to implement a good AOT compilation scheme for it.
Keep GetFunctionPointerForDelegate on Action for now.
Once the .NET 5 function pointer features comes online, switch Xamarin Android to use it. It will make it compatible with CoreCLR, and also faster and smaller.
but Issue #4547 is currently Closed, though it was also added to the .NET 5 milestone, so I'm not entirely sure if these are the same or not.
@jonpryor The discussion was had and decided that it wasn't the best solution due to the better approach described by @jkotas above. Does the recommendation above make sense to you?
We then lookup Object.GetToStringHandler() at runtime via Reflection, then execute it, and pass the returned Delegate instance to a JniNativeMethodRegistration instance, which relies on the normal P/Invoke marshaler to marshal the Delegate to a function pointer in native code:
We don't explicitly invoke Marshal.GetFunctionPointerForDelegate(). It's implicit via P/Invoke struct marshaling.
I'm not immediately sure how to convert the above code into C#8 function pointers. I'd almost certainly need to drop the use of Delegateentirely, e.g.:
But I still need to get that IntPtr, and "know" that the IntPtr is a C callable function pointer. The Function Pointers doc suggests that delegate* can be converted to a void*:
delegate*<void> ptr1 = &Util.Log;
void* v = &Util.Log;
but that in no means I can pass v to C code and it can be executed! (That may be intended, but it's not explicit.). Especially when the doc also states:
This means invocation of a delegate* will use calli where invocation of a delegate will use callvirt on the Invoke method
Additionally, note the JNINativeWrapper.CreateDelegate() invocation in the above snippet. This uses System.Reflection.Emit to generate a new delegate, which "wraps" n_ToString() in a try/catch block for exception marshaling purposes.
I'm going to go out on a limb and guess that there's no way to use System.Reflection.Emit-generated delegate instances with delegate*.
I am not immediately convinced that Function Pointers are a path forward, and updating our generator to instead avoidAction<...> and Func<...> by emitting new families of non-generic delegate types for our dispatch infrastructure may be the easier path forward.
but that in no means I can pass v to C code and it can be executed!
If the method is marked with NativeCallable attribute, you can absolutely pass vto C code and it can be executed! The C# function pointers spec do not have the details on NativeCallable, but I expect that it is something we are going to fix as we work through the end-to-end scenarios. cc @AaronRobinsonMSFT@333fred
So you would produce this (no static fields, no delegate objects):
And then pass the address of n_ToString to registration.
there's no way to use System.Reflection.Emit-generated delegate instances with delegate*
You should be able to emit method with NativeCallableAttribute, but it seems questionable to me. Statically generated code for this should be a lot better - it would certainly be orders of magnitude smaller and faster than Reflection.Emit-based solution when run on CoreCLR.
@jonpryor See #33005 for the NativeCallableAttribute PR. This is something that should help with your scenario. It is close to complete, but unfortunately isn't as useful as desired until the C# function pointer work is in. See NativeCallableTest.cs for examples of use without C# function pointers.
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
Activity
jkotas commentedon Feb 28, 2020
Marshal.GetFunctionPointerForDelegate for generic types is not straightforward to implement in CoreCLR, and it is even more difficult to implement a good AOT compilation scheme for it.
The recommend way to do this for .NET 5 and beyond is going to use the
NativeCallableAttribute(#32462) and C# function pointers (https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md). This will remove the delegates from the picture completely and make the whole thing faster, smaller and easy for ahead-of-time compilation.My recommendation would be:
cc @AaronRobinsonMSFT
AaronRobinsonMSFT commentedon Feb 28, 2020
@jonpryor The discussion was had and decided that it wasn't the best solution due to the better approach described by @jkotas above. Does the recommendation above make sense to you?
jonpryor commentedon Mar 6, 2020
I'm not sure it does, likely because I'm not fully understanding the "function pointers" proposal.
What we need to be able to do is call
JNIEnv::RegisterNatives():What we currently do is emit code such as:
We then lookup
Object.GetToStringHandler()at runtime via Reflection, then execute it, and pass the returnedDelegateinstance to aJniNativeMethodRegistrationinstance, which relies on the normal P/Invoke marshaler to marshal the Delegate to a function pointer in native code:We don't explicitly invoke
Marshal.GetFunctionPointerForDelegate(). It's implicit via P/Invoke struct marshaling.I'm not immediately sure how to convert the above code into C#8 function pointers. I'd almost certainly need to drop the use of
Delegateentirely, e.g.:But I still need to get that
IntPtr, and "know" that theIntPtris a C callable function pointer. The Function Pointers doc suggests thatdelegate*can be converted to avoid*:but that in no means I can pass
vto C code and it can be executed! (That may be intended, but it's not explicit.). Especially when the doc also states:Additionally, note the
JNINativeWrapper.CreateDelegate()invocation in the above snippet. This usesSystem.Reflection.Emitto generate a new delegate, which "wraps"n_ToString()in a try/catch block for exception marshaling purposes.I'm going to go out on a limb and guess that there's no way to use System.Reflection.Emit-generated delegate instances with
delegate*.I am not immediately convinced that Function Pointers are a path forward, and updating our generator to instead avoid
Action<...>andFunc<...>by emitting new families of non-generic delegate types for our dispatch infrastructure may be the easier path forward.jkotas commentedon Mar 7, 2020
If the method is marked with
NativeCallableattribute, you can absolutely passvto C code and it can be executed! The C# function pointers spec do not have the details onNativeCallable, but I expect that it is something we are going to fix as we work through the end-to-end scenarios. cc @AaronRobinsonMSFT @333fredSo you would produce this (no static fields, no delegate objects):
And then pass the address of
n_ToStringto registration.You should be able to emit method with
NativeCallableAttribute, but it seems questionable to me. Statically generated code for this should be a lot better - it would certainly be orders of magnitude smaller and faster than Reflection.Emit-based solution when run on CoreCLR.AaronRobinsonMSFT commentedon Mar 7, 2020
@jonpryor See #33005 for the
NativeCallableAttributePR. This is something that should help with your scenario. It is close to complete, but unfortunately isn't as useful as desired until the C# function pointer work is in. SeeNativeCallableTest.csfor examples of use without C# function pointers.[generator] Use custom delegates instead of Func/Action. (#632)
[generator] Use custom delegates instead of Func/Action. (#632)
[MediaToolbox] Don't use Action<T> in reverse P/Invokes.
[MediaToolbox] Don't use Action<T> in reverse P/Invokes. (#11541)
[CoreGraphics] Adjust CGPDFOperatorTable to not use a generic Action …
[CoreGraphics] Adjust CGPDFOperatorTable to not use a generic Action …
SupinePandora43 commentedon Sep 9, 2024
This is a needed feature that will remove need for casting all
Action<T>to their non-genericdelegatevariants,[CoreCLR] Dynamically build custom delegate types to support [Export] (…
[generator] Use custom delegates instead of Func/Action. (#632)
jkotas commentedon Sep 2, 2026
Duplicate of #13197