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

Allow Marshal.GetFunctionPointerForDelegate() to work with generic types #32963

Open
jonpryor opened this issue Feb 28, 2020 · 6 comments
Open

Comments

@jonpryor
Copy link
Member

There is a difference between Mono & .NET Framework/.NET Core regarding the use of Marshal.GetFunctionPointerForDelegate() and generic delegate types. Consider:

using System;
using System.Runtime.InteropServices;

class App {
    public static void Main ()
    {
        Action<int> a = v => {};
        var p = 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:

COMPlusThrowArgumentException(W("delegate"), W("Argument_NeedNonGenericType"));


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.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Feb 28, 2020
@jkotas
Copy link
Member

jkotas commented 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:

  • 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.

cc @AaronRobinsonMSFT

@AaronRobinsonMSFT AaronRobinsonMSFT added this to the Future milestone Feb 28, 2020
@AaronRobinsonMSFT AaronRobinsonMSFT removed the untriaged New issue has not been triaged by the area owner label Feb 28, 2020
@AaronRobinsonMSFT
Copy link
Member

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?

@jonpryor
Copy link
Member Author

jonpryor commented Mar 6, 2020

Does the recommendation above make sense to you?

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():

typedef struct {
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;

typedef const struct JNINativeInterface_ *JNIEnv

/* partial */ struct JNINativeInterface_ {
    int (*RegisterNatives)(JNIEnv *env, class class, const JNINativeMethod *methods, int nMethods);
}

What we currently do is emit code such as:

partial class /* Java.Lang. */ Object {

	static Delegate cb_toString;
	static Delegate GetToStringHandler ()
	{
		if (cb_toString == null)
			cb_toString = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, IntPtr>) n_ToString);
		return cb_toString;
	}

	static IntPtr n_ToString (IntPtr jnienv, IntPtr native__this)
	{
		Java.Lang.Object __this = global::Java.Lang.Object.GetObject<Java.Lang.Object> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
		return JNIEnv.NewString (__this.ToString ());
	}
}

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 Delegate entirely, e.g.:

public struct JniNativeMethodRegistration {
	public  string      Name;
	public  string      Signature;
	public  IntPtr      Marshaler;

	public JniNativeMethodRegistration (string name, string signature, IntPtr marshaler)
	{
		Name        = name      ?? throw new ArgumentNullException (nameof (name));
		Signature   = signature ?? throw new ArgumentNullException (nameof (signature));
		Marshaler   = marshaler == IntPtr.Zero ? throw new ArgumentNullException (nameof (marshaler)) : marshaler;
	}
}

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 avoid Action<...> and Func<...> by emitting new families of non-generic delegate types for our dispatch infrastructure may be the easier path forward.

@jkotas
Copy link
Member

jkotas commented Mar 7, 2020

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):

[NativeCallable]
static IntPtr n_ToString (IntPtr jnienv, IntPtr native__this)
{
    try
    {
        Java.Lang.Object __this = global::Java.Lang.Object.GetObject<Java.Lang.Object> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
        return JNIEnv.NewString (__this.ToString ());
    }
    catch (Exception e)
    {
        ... whatever you generate with Reflection.Emit today ...        
    }
}

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.

@AaronRobinsonMSFT
Copy link
Member

@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.

jonpryor pushed a commit to dotnet/java-interop that referenced this issue Apr 28, 2020
Fixes: #631

Context: dotnet/runtime#32963
Context: https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md

*Of `Delegate`s and JNI Callbacks…*

~~ Background ~~

In order for Java code to invoke Managed Code such as C#, several
things must happen:

 1. There must be a Java class which declares `native` methods.
 2. The Java class' `native` methods must be [*resolvable*][0]

Java `native` method resolution can be done by [C function name][1]
*or* by using [`JNIEnv::RegisterNatives()`][2]:

	// C++
	struct JNINativeMethod {
	    const char *name;
	    const char *signature;
	    const void *fnPtr;
	};

	/* partial */ struct JNIEnv {
	    jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods);
	};

`JNINativeMethods::fnPtr` is a pointer to a *C callable function*
that accepts [JNI Native Method Arguments][3].

Java.Interop doesn't currently support resolution via C function name,
and instead binds the `JNINativeMethod` struct as
`JniNativeMethodRegistration`, and `JNIEnv::RegisterNatives()` as
`Java.Interop.JniEnvironment.Types.RegisterNatives()`:

	// C#
	public partial struct JniNativeMethodRegistration {
	    public  string    Name;
	    public  string    Signature;
	    public  Delegate  Marshaler;
	}
	public partial class JniEnvironment {
	    public partial class Types {
	        public static void RegisterNatives (JniObjectReference type, JniNativeMethodRegistration [] methods);
	    }
	}

Through the glory that is [Platform Invoke Delegate Marshaling][4]
and/or [`Marshal.GetFunctionPointerForDelegate()`][5], managed code
can provide a `Delegate` instance in
`JniNativeMethodRegistration.Marshaler` and have JNI invoke that
delegate when the corresponding Java `native` method is invoked.

`tools/generator` is responsible for emitting this glue code, e.g.
in order to support registering overrides of
[`java.lang.Object.equals()`][6]:

	// C# emitted by `tools/generator`:
	namespace Java.Lang {
	  partial class Object {
	    static Delegate cb_equals_Ljava_lang_Object_;
	    static Delegate GetEquals_Ljava_lang_Object_Handler ()
	    {
	      if (cb_equals_Ljava_lang_Object_ == null)
	        cb_equals_Ljava_lang_Object_ = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, IntPtr, bool>) n_Equals_Ljava_lang_Object_);
	      return cb_equals_Ljava_lang_Object_;
	    }

	    static bool n_Equals_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_obj)
	    {
	      var __this = global::Java.Lang.Object.GetObject<Java.Lang.Object> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
	      var obj = global::Java.Lang.Object.GetObject<Java.Lang.Object> (native_obj, JniHandleOwnership.DoNotTransfer);
	      bool __ret = __this.Equals (obj);
	      return __ret;
	    }
	  }
	}

`Object.n_Equals_Ljava_lang_Object()` is stored in a
`Func<IntPtr, IntPtr, IntPtr, bool>` -- which conforms to JNI Native
Method Arguments -- and is then provided to
[`JNINativeWrapper.CreateDelegate()`][7], which uses
`System.Reflection.Emit` to "wrap" `n_Equals_Ljava_lang_Object()` for
exception propagation purposes.  Eventually and ultimately, when a C#
class overrides `Java.Lang.Object.Equals()`,
`Object.GetEquals_Ljava_lang_Object_Handler()` will be invoked at
runtime, and `Object.cb_equals_Ljava_lang_Object` will be stored into
`JniNativeMethodRegistration.Marshaler`.


~~ `Action<…>` and `Func<…>` ~~

There is one problem with the above approach: its use of the
`System.Action<…>` and `System.Func<…>` types used at the core of
registering native methods with JNI.  There are two problems with
using these sets of types:

 1. These delegate types only permit up to 16 parameters.  Given that
    *two* parameters are always "eaten" by the `JNIEnv*` pointer and
    a `jobject` to Java's `this` or a `jclass` to the declaring class,
    that means that we can only bind methods taking up to 14 methods.
    Java methods which take more than 14 methods are skipped.

 2. .NET Framework and CoreCLR don't support using generic types with
    the Platform Invoke marshaler and
    [`Marshal.GetFunctionPointerForDelegate()`][8].

(1) has been a longstanding problem, which we've been ignoring.

(2) isn't *yet* a problem, and is something @jonpryor has been keen
to address for awhile.


~~ C# Function Pointers? ~~

There is a proposal to [add Function Pointers to the C# language][9].
This would permit reduced overheads and improved efficiencies in
obtaining a function pointer to pass into Java code.

Unfortunately:

 1. The proposal is still ongoing, with no known release date.
 2. .NET Framework 4.x won't support them.
 3. They can't be used within the current Xamarin.Android architecture.

There doesn't appear to be a way to obtain a `Delegate` from a
`delegate*`, which means `JNINativeWrapper.CreateDelegate()` cannot
be used with Function Pointers.

In order to use Function Pointers, we would likely need to *require*
use of `tools/jnimarshalmethod-gen.exe` (176240d) so that appropriate
JNI Native Method Argument-conforming methods with the
`NativeCallableAttribute` can be generated at app build time,
*avoiding* the current Reflection-heavy registration path which
involves e.g. `Object.GetEquals_Ljava_lang_Object_Handler()`.

Unfortunately, `jnimarshalmethod-gen.exe` isn't "done": it doesn't
work on Windows, and it's use of `AppDomain`s and
`System.Reflection.Emit` look to complicate a future .NET 5 port.


~~ Solution: Generate Delegates ~~

If `Action<…>` and `Func<…>` are to be avoided, and Function Pointers
are out, how do we support more than 14 parameters?

By updating `generator` to emit the required delegate types.

When `Action<…>` or `Func<…>` would previously have been generated,
instead emit *and record the name of* a delegate which follows the
pattern:

  * Type name prefix: `_JniMarshal_PP`
  * Parameter types, using JNI encoding, e.g. `Z` for boolean,
    `I` for int, etc.  *Reference types*, normally encoded as `L…;`
    and Arrays, encoded as `[`, are each encoded as `L`.
    Kotlin unsigned types are encoded as *lower-case* forms of the
    corresponding JNI types, e.g. `i` is an unsigned `I`.
  * Another `_`.
  * The above type encoding for the return type.

For example, `Object.n_Equals_Ljava_lang_Object()` used
`Func<IntPtr, IntPtr, IntPtr, bool>`.  This would become
`_JniMarshal_PPL_Z`.

After the initial binding stage is complete and all required delegate
types are recorded, the `_JniMarshal*` types are emitted into
`__NamespaceMapping__.cs`:

	internal delegate bool _JniMarshal_PPL_Z (IntPtr jnienv, IntPtr klass, IntPtr a);

The cost to declaring all these types is that a binding assembly
contains more types.  `Mono.Android.dll`, for example, grows ~20KB
in size from all the required delegate declarations, pre-linking.


~~ Other ~~

Remove `tools/generator/generator.sln` and replace it with a
`tools/generator/generator.slnf` solution filter file which makes it
easier to work with `generator` in Visual Studio by only loading
needed projects from `Java.Interop.sln`.


[0]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#compiling_loading_and_linking_native_methods
[1]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
[2]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives
[3]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#native_method_arguments
[4]: https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-a-delegate-as-a-callback-method
[5]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getfunctionpointerfordelegate?view=netcore-3.1
[6]: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals%28java.lang.Object%29
[7]: https://github.com/xamarin/xamarin-android/blob/42822e0488185cdf4bca7c0bd05b21ad03dfbd7e/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs#L34-L97
[8]: dotnet/runtime#32963
[9]: https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md
jonpryor pushed a commit to dotnet/java-interop that referenced this issue May 6, 2020
Fixes: #631

Context: dotnet/runtime#32963
Context: https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md

*Of `Delegate`s and JNI Callbacks…*

~~ Background ~~

In order for Java code to invoke Managed Code such as C#, several
things must happen:

 1. There must be a Java class which declares `native` methods.
 2. The Java class' `native` methods must be [*resolvable*][0]

Java `native` method resolution can be done by [C function name][1]
*or* by using [`JNIEnv::RegisterNatives()`][2]:

	// C++
	struct JNINativeMethod {
	    const char *name;
	    const char *signature;
	    const void *fnPtr;
	};

	/* partial */ struct JNIEnv {
	    jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods);
	};

`JNINativeMethods::fnPtr` is a pointer to a *C callable function*
that accepts [JNI Native Method Arguments][3].

Java.Interop doesn't currently support resolution via C function name,
and instead binds the `JNINativeMethod` struct as
`JniNativeMethodRegistration`, and `JNIEnv::RegisterNatives()` as
`Java.Interop.JniEnvironment.Types.RegisterNatives()`:

	// C#
	public partial struct JniNativeMethodRegistration {
	    public  string    Name;
	    public  string    Signature;
	    public  Delegate  Marshaler;
	}
	public partial class JniEnvironment {
	    public partial class Types {
	        public static void RegisterNatives (JniObjectReference type, JniNativeMethodRegistration [] methods);
	    }
	}

Through the glory that is [Platform Invoke Delegate Marshaling][4]
and/or [`Marshal.GetFunctionPointerForDelegate()`][5], managed code
can provide a `Delegate` instance in
`JniNativeMethodRegistration.Marshaler` and have JNI invoke that
delegate when the corresponding Java `native` method is invoked.

`tools/generator` is responsible for emitting this glue code, e.g.
in order to support registering overrides of
[`java.lang.Object.equals()`][6]:

	// C# emitted by `tools/generator`:
	namespace Java.Lang {
	  partial class Object {
	    static Delegate cb_equals_Ljava_lang_Object_;
	    static Delegate GetEquals_Ljava_lang_Object_Handler ()
	    {
	      if (cb_equals_Ljava_lang_Object_ == null)
	        cb_equals_Ljava_lang_Object_ = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, IntPtr, bool>) n_Equals_Ljava_lang_Object_);
	      return cb_equals_Ljava_lang_Object_;
	    }

	    static bool n_Equals_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this, IntPtr native_obj)
	    {
	      var __this = global::Java.Lang.Object.GetObject<Java.Lang.Object> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
	      var obj = global::Java.Lang.Object.GetObject<Java.Lang.Object> (native_obj, JniHandleOwnership.DoNotTransfer);
	      bool __ret = __this.Equals (obj);
	      return __ret;
	    }
	  }
	}

`Object.n_Equals_Ljava_lang_Object()` is stored in a
`Func<IntPtr, IntPtr, IntPtr, bool>` -- which conforms to JNI Native
Method Arguments -- and is then provided to
[`JNINativeWrapper.CreateDelegate()`][7], which uses
`System.Reflection.Emit` to "wrap" `n_Equals_Ljava_lang_Object()` for
exception propagation purposes.  Eventually and ultimately, when a C#
class overrides `Java.Lang.Object.Equals()`,
`Object.GetEquals_Ljava_lang_Object_Handler()` will be invoked at
runtime, and `Object.cb_equals_Ljava_lang_Object` will be stored into
`JniNativeMethodRegistration.Marshaler`.


~~ `Action<…>` and `Func<…>` ~~

There is one problem with the above approach: its use of the
`System.Action<…>` and `System.Func<…>` types used at the core of
registering native methods with JNI.  There are two problems with
using these sets of types:

 1. These delegate types only permit up to 16 parameters.  Given that
    *two* parameters are always "eaten" by the `JNIEnv*` pointer and
    a `jobject` to Java's `this` or a `jclass` to the declaring class,
    that means that we can only bind methods taking up to 14 methods.
    Java methods which take more than 14 methods are skipped.

 2. .NET Framework and CoreCLR don't support using generic types with
    the Platform Invoke marshaler and
    [`Marshal.GetFunctionPointerForDelegate()`][8].

(1) has been a longstanding problem, which we've been ignoring.

(2) isn't *yet* a problem, and is something @jonpryor has been keen
to address for awhile.


~~ C# Function Pointers? ~~

There is a proposal to [add Function Pointers to the C# language][9].
This would permit reduced overheads and improved efficiencies in
obtaining a function pointer to pass into Java code.

Unfortunately:

 1. The proposal is still ongoing, with no known release date.
 2. .NET Framework 4.x won't support them.
 3. They can't be used within the current Xamarin.Android architecture.

There doesn't appear to be a way to obtain a `Delegate` from a
`delegate*`, which means `JNINativeWrapper.CreateDelegate()` cannot
be used with Function Pointers.

In order to use Function Pointers, we would likely need to *require*
use of `tools/jnimarshalmethod-gen.exe` (176240d) so that appropriate
JNI Native Method Argument-conforming methods with the
`NativeCallableAttribute` can be generated at app build time,
*avoiding* the current Reflection-heavy registration path which
involves e.g. `Object.GetEquals_Ljava_lang_Object_Handler()`.

Unfortunately, `jnimarshalmethod-gen.exe` isn't "done": it doesn't
work on Windows, and it's use of `AppDomain`s and
`System.Reflection.Emit` look to complicate a future .NET 5 port.


~~ Solution: Generate Delegates ~~

If `Action<…>` and `Func<…>` are to be avoided, and Function Pointers
are out, how do we support more than 14 parameters?

By updating `generator` to emit the required delegate types.

When `Action<…>` or `Func<…>` would previously have been generated,
instead emit *and record the name of* a delegate which follows the
pattern:

  * Type name prefix: `_JniMarshal_PP`
  * Parameter types, using JNI encoding, e.g. `Z` for boolean,
    `I` for int, etc.  *Reference types*, normally encoded as `L…;`
    and Arrays, encoded as `[`, are each encoded as `L`.
    Kotlin unsigned types are encoded as *lower-case* forms of the
    corresponding JNI types, e.g. `i` is an unsigned `I`.
  * Another `_`.
  * The above type encoding for the return type.

For example, `Object.n_Equals_Ljava_lang_Object()` used
`Func<IntPtr, IntPtr, IntPtr, bool>`.  This would become
`_JniMarshal_PPL_Z`.

After the initial binding stage is complete and all required delegate
types are recorded, the `_JniMarshal*` types are emitted into
`__NamespaceMapping__.cs`:

	internal delegate bool _JniMarshal_PPL_Z (IntPtr jnienv, IntPtr klass, IntPtr a);

The cost to declaring all these types is that a binding assembly
contains more types.  `Mono.Android.dll`, for example, grows ~20KB
in size from all the required delegate declarations, pre-linking.


~~ Other ~~

Remove `tools/generator/generator.sln` and replace it with a
`tools/generator/generator.slnf` solution filter file which makes it
easier to work with `generator` in Visual Studio by only loading
needed projects from `Java.Interop.sln`.


[0]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#compiling_loading_and_linking_native_methods
[1]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
[2]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives
[3]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#native_method_arguments
[4]: https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-a-delegate-as-a-callback-method
[5]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getfunctionpointerfordelegate?view=netcore-3.1
[6]: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals%28java.lang.Object%29
[7]: https://github.com/xamarin/xamarin-android/blob/42822e0488185cdf4bca7c0bd05b21ad03dfbd7e/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs#L34-L97
[8]: dotnet/runtime#32963
[9]: https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue May 13, 2021
rolfbjarne added a commit to xamarin/xamarin-macios that referenced this issue May 13, 2021
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue May 14, 2021
…delegate.

* CoreCLR doesn't support generic Action delegates in reverse (P/Invokes), so
  we need to find a different solution.
* The native CGPDFOperatorTable callback API is not very friendly to us,
  because we can't pass it callback-specific data, which means that the
  managed caller must conform to the FullAOT requirement of the managed
  callback (must be a static function; must have a MonoPInvokeCallback
  attribute).
* We can leverage the new function pointer syntax in C# 9 to make these
  requirements enforced by the C# compiler (unmanaged function pointer +
  UnmanagedCallersOnly attribute). The resulting API is still clunky to use,
  but I don't see any way around that.

Fixes this monotouch-test failure with CoreCLR:

    [FAIL] Tamarin : System.Runtime.InteropServices.MarshalDirectiveException : Cannot marshal 'parameter #3': Non-blittable generic types cannot be marshaled.
               at CoreGraphics.CGPDFOperatorTable.CGPDFOperatorTableSetCallback(IntPtr table, String name, Action`2 callback)
               at CoreGraphics.CGPDFOperatorTable.SetCallback(String name, Action`2 callback)
               at MonoTouchFixtures.CoreGraphics.PDFScannerTest.Tamarin() in xamarin-macios/tests/monotouch-test/CoreGraphics/PDFScannerTest.cs:line 102

Ref: dotnet/runtime#32963
rolfbjarne added a commit to xamarin/xamarin-macios that referenced this issue May 17, 2021
…delegate. (#11560)

* CoreCLR doesn't support generic Action delegates in reverse (P/Invokes), so
  we need to find a different solution.
* The native CGPDFOperatorTable callback API is not very friendly to us,
  because we can't pass it callback-specific data, which means that the
  managed caller must conform to the FullAOT requirement of the managed
  callback (must be a static function; must have a MonoPInvokeCallback
  attribute).
* We can leverage the new function pointer syntax in C# 9 to make these
  requirements enforced by the C# compiler (unmanaged function pointer +
  UnmanagedCallersOnly attribute). The resulting API is still clunky to use,
  but I don't see any way around that.

Fixes this monotouch-test failure with CoreCLR:

    [FAIL] Tamarin : System.Runtime.InteropServices.MarshalDirectiveException : Cannot marshal 'parameter #3': Non-blittable generic types cannot be marshaled.
               at CoreGraphics.CGPDFOperatorTable.CGPDFOperatorTableSetCallback(IntPtr table, String name, Action`2 callback)
               at CoreGraphics.CGPDFOperatorTable.SetCallback(String name, Action`2 callback)
               at MonoTouchFixtures.CoreGraphics.PDFScannerTest.Tamarin() in xamarin-macios/tests/monotouch-test/CoreGraphics/PDFScannerTest.cs:line 102

Ref: dotnet/runtime#32963
@dotnet-policy-service dotnet-policy-service bot added backlog-cleanup-candidate An inactive issue that has been marked for automated closure. no-recent-activity labels Sep 6, 2024
@SupinePandora43
Copy link

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.

This process is part of our issue cleanup automation.

This is a needed feature that will remove need for casting all Action<T> to their non-generic delegate variants,

@dotnet-policy-service dotnet-policy-service bot removed no-recent-activity backlog-cleanup-candidate An inactive issue that has been marked for automated closure. labels Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

5 participants