-
Notifications
You must be signed in to change notification settings - Fork 54
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
Support C# generics in base types #424
Comments
I tried naively removing the arity from the return type, which at least generates valid C# code, but it still doesn't compile due to:
A similar error occurs here: public unsafe global::System.Collections.Generic.ICollection<global::System.String> CriticalExtensionOIDs {
get {
if (id_getCriticalExtensionOIDs == IntPtr.Zero)
id_getCriticalExtensionOIDs = JNIEnv.GetMethodID (class_ref, "getCriticalExtensionOIDs", "()Ljava/util/Set;");
return global::Android.Runtime.JavaSet.FromJniHandle (JNIEnv.CallObjectMethod (((global::Java.Lang.Object) this).Handle, id_getCriticalExtensionOIDs), JniHandleOwnership.TransferLocalRef);
}
} because I'm not sure if that means my change is incorrect, or if that is an additional separate issue that needs to be addressed. |
This should use |
Is this something we would expect Or is this something where we would expect |
Context: #424 One of the issues trying to bind the BouncyCastle-provided [`bcprov-ext-jdk15on-161.jar.`][0] in #424 is that we write generic types with invalid signatures: public List`1<string> MyProperty { get; set; } as this is how a Cecil type reference `FullName` is returned. Since this notation is not useful to us, strip the "arity" information when reading the assembly. This results in the proper type syntax: public List<string> MyProperty { get; set; } [0]: https://www.bouncycastle.org/download/bcprov-ext-jdk15on-161.jar
Yes.
Good question. :-) Have a more concrete example in mind? There are only ("only") 25 instances of $ grep -r JavaSet.ToLocalJniH obj/Debug/android-28/mcw
obj/Debug/android-28/mcw/Java.Util.HashMap.cs: return Android.Runtime.JavaSet.ToLocalJniHandle (__this.EntrySet ());
... None of the existing declarations seem quite that useful for this discussion, as they all deal with non-generic types, so more "demo code" might be helpful. That said, within the context of the However, this might also be interpreted as a limitation in our current binding infrastructure. Consider the // Metadata.xml XPath interface reference: path="/api/package[@name='java.security.cert']/interface[@name='X509Extension']"
[Register ("java/security/cert/X509Extension", "", "Java.Security.Cert.IX509ExtensionInvoker", ApiSince = 1)]
public partial interface IX509Extension : IJavaObject {
System.Collections.Generic.ICollection<string> CriticalExtensionOIDs {
// Metadata.xml XPath method reference: path="/api/package[@name='java.security.cert']/interface[@name='X509Extension']/method[@name='getCriticalExtensionOIDs' and count(parameter)=0]"
[Register ("getCriticalExtensionOIDs", "()Ljava/util/Set;", "GetGetCriticalExtensionOIDsHandler:Java.Security.Cert.IX509ExtensionInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
get;
}
} Reflection-wise, the only way anything can "know" that We may want to consider adding additional attributes to remove the need for this inference, e.g. by using a System.Collections.Generic.ICollection<string> CriticalExtensionOIDs {
[return:JniGenericTypeReference ("java/util/Set", new[]{"java/lang/String"})]
[Register ("getCriticalExtensionOIDs", "()Ljava/util/Set;", "GetGetCriticalExtensionOIDsHandler:Java.Security.Cert.IX509ExtensionInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
get;
}
|
…451) One issue with the BouncyCastle [`bcprov-ext-jdk15on-161.jar`][0] in Issue #424 is that it contains this field: public static final int _3DES_EDE_CBC We studly-case Java field names, however that removes underscores and we end up with `3desEdeCbc`, which is invalid C#. This PR preserves a starting underscore if there was one and the resulting name starts with a number. (`_3desEdeCbc` for this case.) [0]: https://www.bouncycastle.org/download/bcprov-ext-jdk15on-161.jar
Changes: dotnet/java-interop@4e364ba...be58159 Context: dotnet/java-interop#424 `generator` refactoring. Make the `*Invoker` classes `partial` classes. Add `generator --lang-features=interface-constants` option, which emits Java interface constant values into the C# interface declarations. `generator` performance improvements. Allow consumption of `Java.Interop-Tests` by Xamarin.Android.
Is it possible to fix this with metadata.xml? |
Fixes: #967 Context: dotnet/android-libraries#504 Context: #424 Context: #586 Context: #918 Java generics continues to be a "difficulty" in creating bindings. Consider [`ActivityResultContracts.RequestPermission`][0]: // Java public abstract /* partial */ class ActivityResultContract<I, O> { public abstract Intent createIntent(Context context, I input); } public /* partial */ class /* ActivityResultContracts. */ RequestPermission extends ActivityResultContract<String, Boolean> { @OverRide public Intent createIntent(Context context, String input) {…} } The JNI Signature for `ActivityResultContracts.RequestPermission.createIntent()` is `(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;`, i.e. `class-parse` & `generator` believe that the `input` parameter is of type `String`, which we bind by default as `string`. Thus: // C# public abstract partial class ActivityResultContract { public abstract Intent CreateIntent (Context? context, Java.Lang.Object? input) => … } public partial class /* ActivityResultContracts. */ RequestPermission { public override Intent CreateIntent (Context? context, string? input) => … } This fails to compile with a [CS0115][1]: 'RequestPermission.CreateIntent(Context?, string?)': no suitable method found to override as the `input` parameter of `RequestPermission.CreateIntent()` changes from `Java.Lang.Object?` to `string?`. We can attempt to address this via Metadata: <attr path="/api/package[@name='androidx.activity.result.contract']/class[@name='ActivityResultContracts.RequestPermission']/method[@name='createIntent' and count(parameter)=2 and parameter[1][@type='android.content.Context'] and parameter[2][@type='java.lang.String']]" name="managedType" >Java.Lang.Object</attr> This fixes one error, as `generator` now emits: public partial class /* ActivityResultContracts. */ RequestPermission { [Register ("createIntent", "(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;", "")] public override unsafe global::Android.Content.Intent CreateIntent (global::Android.Content.Context context, global::Java.Lang.Object input) { const string __id = "createIntent.(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"; IntPtr native_input = JNIEnv.NewString (input); try { JniArgumentValue* __args = stackalloc JniArgumentValue [2]; __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle); __args [1] = new JniArgumentValue (native_input); var __rm = _members.InstanceMethods.InvokeAbstractObjectMethod (__id, this, __args); return global::Java.Lang.Object.GetObject<global::Android.Content.Intent> (__rm.Handle, JniHandleOwnership.TransferLocalRef); } finally { JNIEnv.DeleteLocalRef (native_input); global::System.GC.KeepAlive (context); global::System.GC.KeepAlive (input); } } } The `override` method declaration is correct. However, this introduces a [CS1503][2] error: IntPtr native_input = JNIEnv.NewString (input); // Error CS1503 Argument 1: cannot convert from 'Java.Lang.Object' to 'string?' The workaround is to remove `createIntent()` ~entirely, and manually bind it, as done in dotnet/android-libraries#512. Fix this issue by always emitting a cast to `(string)` as part of the `JNIEnv.NewString()` invocation, instead emitting: IntPtr native_input = JNIEnv.NewString ((string?) input); This works because `Java.Lang.Object` defines an [explicit conversion to `string?`][3], and if a `Java.Lang.String` instance is provided to the `input` parameter, it's equivalent to calling `.ToString()`. This fix allows the original suggested Metadata solution to work. [0]: https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts.RequestPermission [1]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0115 [2]: https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs1503 [3]: https://github.com/xamarin/xamarin-android/blob/a58d4e9706455227eabb6e5b5103b25da716688b/src/Mono.Android/Java.Lang/Object.cs#L434-L439
Consider the BouncyCastle-provided
bcprov-ext-jdk15on-161.jar
. This Java library contains a type equivalent to:Which looks fine enough, except that it implements the
IX509Extension
interface, which has two properties which are generic types:Unfortunately,
generator
's support for C# generic methods results in the resulting binding...not being valid C# code:The string
global::System.Collections.Generic.ICollection`1<global::System.String>
is not a valid C# type -- theICollection`1<global::System.String>
needs to beICollection<global::System.String>
-- which prevents the binding from compiling. (Along with a host of other issues, all of which should be addressed as well.)generator
needs to be fixed so that this can be properly supported.The text was updated successfully, but these errors were encountered: