Skip to content

Commit 35f41dc

Browse files
authored
Fixes: #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
1 parent 3a0cce4 commit 35f41dc

File tree

4 files changed

+31
-3
lines changed

4 files changed

+31
-3
lines changed

Diff for: src/Mono.Android/Android.Runtime/JNIEnvInit.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ internal struct JnienvInitializeArgs {
5050
internal static IntPtr java_class_loader;
5151
internal static JniMethodInfo? mid_Class_forName;
5252

53-
static AndroidRuntime? androidRuntime;
53+
internal static AndroidRuntime? androidRuntime;
5454

5555
[UnmanagedCallersOnly]
5656
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)

Diff for: src/Mono.Android/Java.Interop/TypeManager.cs

+21-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership
267267

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

321+
var typeSig = JNIEnvInit.androidRuntime?.TypeManager.GetTypeSignature (type) ?? default;
322+
if (!typeSig.IsValid || typeSig.SimpleReference == null) {
323+
throw new ArgumentException ($"Could not determine Java type corresponding to `{type.AssemblyQualifiedName}`.", nameof (targetType));
324+
}
325+
JniObjectReference typeClass;
326+
try {
327+
typeClass = JniEnvironment.Types.FindClass (typeSig.SimpleReference);
328+
} catch (Exception e) {
329+
throw new ArgumentException ($"Could not find Java class `{typeSig.SimpleReference}`.",
330+
nameof (targetType),
331+
e);
332+
}
333+
334+
var handleClass = JniEnvironment.Types.GetObjectClass (new JniObjectReference (handle));
335+
if (!JniEnvironment.Types.IsAssignableFrom (handleClass, typeClass)) {
336+
Logger.Log (LogLevel.Info, "*jonp*", $"# jonp: can't assign `{GetClassName(handleClass.Handle)}` to `{GetClassName(typeClass.Handle)}`");
337+
JniObjectReference.Dispose (ref handleClass);
338+
JniObjectReference.Dispose (ref typeClass);
339+
return null;
340+
}
321341

322342
IJavaPeerable? result = null;
323343

Diff for: tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ public void JavaCast_CheckForManagedSubclasses ()
5656
}
5757
}
5858

59+
[Test]
60+
public void JavaAs ()
61+
{
62+
using var v = new Java.InteropTests.MyJavaInterfaceImpl ();
63+
using var c = v.JavaAs<Java.Lang.ICloneable>();
64+
Assert.IsNotNull (c);
65+
}
66+
5967
static Java.Lang.Object CreateObject ()
6068
{
6169
var ctor = JNIEnv.GetMethodID (Java.Lang.Class.Object, "<init>", "()V");

0 commit comments

Comments
 (0)