Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fixes: dotnet#9038

Changes: dotnet/java-interop@ccafbe6...7a058c0

  * dotnet/java-interop@7a058c0e: [Java.Interop] Add `IJavaPeerable.JavaAs()` extension method (dotnet/java-interop#1234)
  * dotnet/java-interop@6f9defa5: Bump to dotnet/android-tools@3debf8e0 (dotnet/java-interop#1236)
  * dotnet/java-interop@6923fb89: [ci] Add dependabot branches to build triggers (dotnet/java-interop#1233)
  * dotnet/java-interop@573028f3: [ci] Use macOS 13 Ventura build agents (dotnet/java-interop#1235)

dotnet/java-interop@7a058c0e adds a new `IJavaPeerable.JavaAs<T>()`
extension method, to perform type casts which return `null` when
the cast will not succeed instead of throwing an exception.

Update `AndroidValueManager.CreatePeer()` to check for Java-side
type compatibility (by way of `TypeManager.CreateInstance()`).
Previously, this would not throw:

	var instance        = …
	var r               = instance.PeerReference;
	var wrap_instance   = JniRuntime.CurrentRuntime.ValueManager.CreatePeer (
	        reference: ref r,
	        options: JniObjectReferenceOptions.Copy,
	        targetType: typeof (SomeInterfaceThatInstanceDoesNotImplement));
	// `wrap_instance` is not null, `.CreatePeer()` does not throw

	wrap_instance.SomeMethod();
	// will throw, due to a type mismatch.

`AndroidValueManager.CreatePeer()` will now return `null` if the
Java instance referenced by the `reference:` parameter is not
convertible to `targetType`, as per [`JNIEnv::IsAssignableFrom()`][0].

[0]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#IsAssignableFrom
  • Loading branch information
jonpryor authored Jul 8, 2024
1 parent 3a0cce4 commit 35f41dc
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ internal struct JnienvInitializeArgs {
internal static IntPtr java_class_loader;
internal static JniMethodInfo? mid_Class_forName;

static AndroidRuntime? androidRuntime;
internal static AndroidRuntime? androidRuntime;

[UnmanagedCallersOnly]
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
Expand Down
22 changes: 21 additions & 1 deletion src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership

[UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "TypeManager.CreateProxy() does not statically know the value of the 'type' local variable.")]
[UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "TypeManager.CreateProxy() does not statically know the value of the 'type' local variable.")]
internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership transfer, Type? targetType)
internal static IJavaPeerable? CreateInstance (IntPtr handle, JniHandleOwnership transfer, Type? targetType)
{
Type? type = null;
IntPtr class_ptr = JNIEnv.GetObjectClass (handle);
Expand Down Expand Up @@ -318,6 +318,26 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership
type = invokerType;
}

var typeSig = JNIEnvInit.androidRuntime?.TypeManager.GetTypeSignature (type) ?? default;
if (!typeSig.IsValid || typeSig.SimpleReference == null) {
throw new ArgumentException ($"Could not determine Java type corresponding to `{type.AssemblyQualifiedName}`.", nameof (targetType));
}
JniObjectReference typeClass;
try {
typeClass = JniEnvironment.Types.FindClass (typeSig.SimpleReference);
} catch (Exception e) {
throw new ArgumentException ($"Could not find Java class `{typeSig.SimpleReference}`.",
nameof (targetType),
e);
}

var handleClass = JniEnvironment.Types.GetObjectClass (new JniObjectReference (handle));
if (!JniEnvironment.Types.IsAssignableFrom (handleClass, typeClass)) {
Logger.Log (LogLevel.Info, "*jonp*", $"# jonp: can't assign `{GetClassName(handleClass.Handle)}` to `{GetClassName(typeClass.Handle)}`");
JniObjectReference.Dispose (ref handleClass);
JniObjectReference.Dispose (ref typeClass);
return null;
}

IJavaPeerable? result = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public void JavaCast_CheckForManagedSubclasses ()
}
}

[Test]
public void JavaAs ()
{
using var v = new Java.InteropTests.MyJavaInterfaceImpl ();
using var c = v.JavaAs<Java.Lang.ICloneable>();
Assert.IsNotNull (c);
}

static Java.Lang.Object CreateObject ()
{
var ctor = JNIEnv.GetMethodID (Java.Lang.Class.Object, "<init>", "()V");
Expand Down

0 comments on commit 35f41dc

Please sign in to comment.