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

CS1503 changing string to JLO via managedType #967

Closed
jpobst opened this issue Apr 5, 2022 · 1 comment · Fixed by #970 or xamarin/xamarin-android#6939
Closed

CS1503 changing string to JLO via managedType #967

jpobst opened this issue Apr 5, 2022 · 1 comment · Fixed by #970 or xamarin/xamarin-android#6939
Labels
bug Component does not function as intended generator Issues binding a Java library (generator, class-parse, etc.)

Comments

@jpobst
Copy link
Contributor

jpobst commented Apr 5, 2022

Context: xamarin/AndroidX#512

If you need to change a string to JLO for a method parameter, the generated code produces a CS1503 error because it generates this code:

// Metadata.xml XPath method reference: 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']]"
[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);
	}
}

This produces:

Error CS1503 Argument 1: cannot convert from 'Java.Lang.Object' to 'string?'

For this line:

  IntPtr native_input = JNIEnv.NewString (input);

Although there is a cast from JLO to string, the cast is explicit, meaning we need to use this:

  IntPtr native_input = JNIEnv.NewString ((string?)input);
@jpobst jpobst added bug Component does not function as intended generator Issues binding a Java library (generator, class-parse, etc.) labels Apr 5, 2022
@jpobst
Copy link
Contributor Author

jpobst commented Apr 11, 2022

There was some concern that the explicit cast may not work as expected at runtime because Java.Lang.String does not implement IConvertable. For the following tests:

var str = new Java.Lang.String ("my_string");
var obj = (Java.Lang.Object) str;

Console.WriteLine (str);
Console.WriteLine (obj);
Console.WriteLine ((string)obj);
Console.WriteLine (Convert.ToString (obj));

All outputs are my_string, which appears to be what we want.

jonpryor pushed a commit that referenced this issue Apr 19, 2022
Fixes: #967

Context: xamarin/AndroidX#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 xamarin/AndroidX#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
jonpryor added a commit to jonpryor/xamarin-android that referenced this issue Apr 19, 2022
Fixes: xamarin/java.interop#967

Changes: xamarin/java.interop@05bfece...05eddd9

  * xamarin/java.interop@05eddd9a: [generator] Add string cast to prevent CS1503 (xamarin#970)
  * xamarin/java.interop@37cff251: [Java.Base, generator] Bind all of package java.io (xamarin#968)
  * xamarin/java.interop@a65d6fb4: [Java.Base, generator] Bind all of package java.lang (xamarin#966)
  * xamarin/java.interop@ed9c2abf: [Java.Interop-MonoAndroid] Set Version after Directory.Build.props (xamarin#965)
jonpryor added a commit to jonpryor/xamarin-android that referenced this issue Apr 19, 2022
Fixes: xamarin/java.interop#967

Changes: xamarin/java.interop@05bfece...05eddd9

  * xamarin/java.interop@05eddd9a: [generator] Add string cast to prevent CS1503 (xamarin#970)
  * xamarin/java.interop@37cff251: [Java.Base, generator] Bind all of package java.io (xamarin#968)
  * xamarin/java.interop@a65d6fb4: [Java.Base, generator] Bind all of package java.lang (xamarin#966)
  * xamarin/java.interop@ed9c2abf: [Java.Interop-MonoAndroid] Set Version after Directory.Build.props (xamarin#965)
jonpryor added a commit to jonpryor/xamarin-android that referenced this issue Apr 21, 2022
Fixes: xamarin/java.interop#967

Changes: xamarin/java.interop@05bfece...2a882d2

  * xamarin/java.interop@2a882d2d: [generator] Fix xamarin-android/src/Mono.Android build (xamarin#972)
  * xamarin/java.interop@968e0f5f: [Directory.Build.props] Set dummy $(PackageVersion) to appease NuGet (xamarin#971)
  * xamarin/java.interop@05eddd9a: [generator] Add string cast to prevent CS1503 (xamarin#970)
  * xamarin/java.interop@37cff251: [Java.Base, generator] Bind all of package java.io (xamarin#968)
  * xamarin/java.interop@a65d6fb4: [Java.Base, generator] Bind all of package java.lang (xamarin#966)
  * xamarin/java.interop@ed9c2abf: [Java.Interop-MonoAndroid] Set Version after Directory.Build.props (xamarin#965)
jonpryor added a commit to xamarin/xamarin-android that referenced this issue Apr 21, 2022
Fixes: xamarin/java.interop#967

Changes: xamarin/java.interop@05bfece...2a882d2

  * xamarin/java.interop@2a882d2d: [generator] Fix xamarin-android/src/Mono.Android build (#972)
  * xamarin/java.interop@968e0f5f: [Directory.Build.props] Set dummy $(PackageVersion) to appease NuGet (#971)
  * xamarin/java.interop@05eddd9a: [generator] Add string cast to prevent CS1503 (#970)
  * xamarin/java.interop@37cff251: [Java.Base, generator] Bind all of package java.io (#968)
  * xamarin/java.interop@a65d6fb4: [Java.Base, generator] Bind all of package java.lang (#966)
  * xamarin/java.interop@ed9c2abf: [Java.Interop-MonoAndroid] Set Version after Directory.Build.props (#965)
jonathanpeppers pushed a commit to xamarin/xamarin-android that referenced this issue Apr 25, 2022
Fixes: xamarin/java.interop#967

Changes: xamarin/java.interop@05bfece...2a882d2

  * xamarin/java.interop@2a882d2d: [generator] Fix xamarin-android/src/Mono.Android build (#972)
  * xamarin/java.interop@968e0f5f: [Directory.Build.props] Set dummy $(PackageVersion) to appease NuGet (#971)
  * xamarin/java.interop@05eddd9a: [generator] Add string cast to prevent CS1503 (#970)
  * xamarin/java.interop@37cff251: [Java.Base, generator] Bind all of package java.io (#968)
  * xamarin/java.interop@a65d6fb4: [Java.Base, generator] Bind all of package java.lang (#966)
  * xamarin/java.interop@ed9c2abf: [Java.Interop-MonoAndroid] Set Version after Directory.Build.props (#965)
@github-actions github-actions bot locked and limited conversation to collaborators Apr 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Component does not function as intended generator Issues binding a Java library (generator, class-parse, etc.)
Projects
None yet
1 participant