diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs index eb9e23790..e08986e61 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs @@ -348,7 +348,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) IJavaPeerable? CreatePeerInstance ( ref JniObjectReference klass, [DynamicallyAccessedMembers (Constructors)] - Type fallbackType, + Type targetType, ref JniObjectReference reference, JniObjectReferenceOptions transfer) { @@ -362,7 +362,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) type = Runtime.TypeManager.GetType (sig); - if (type != null) { + if (type != null && targetType.IsAssignableFrom (type)) { var peer = TryCreatePeerInstance (ref reference, transfer, type); if (peer != null) { @@ -381,7 +381,7 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) } JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - return TryCreatePeerInstance (ref reference, transfer, fallbackType); + return TryCreatePeerInstance (ref reference, transfer, targetType); } IJavaPeerable? TryCreatePeerInstance ( diff --git a/tests/Java.Interop-Tests/Java.Interop-Tests.csproj b/tests/Java.Interop-Tests/Java.Interop-Tests.csproj index cbb684413..4e881e793 100644 --- a/tests/Java.Interop-Tests/Java.Interop-Tests.csproj +++ b/tests/Java.Interop-Tests/Java.Interop-Tests.csproj @@ -36,6 +36,7 @@ + diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index ee3413795..40da2e321 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -37,6 +37,7 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { [GenericHolder.JniTypeName] = typeof (GenericHolder<>), [RenameClassBase.JniTypeName] = typeof (RenameClassBase), [RenameClassDerived.JniTypeName] = typeof (RenameClassDerived), + [AnotherJavaInterfaceImpl.JniTypeName] = typeof (AnotherJavaInterfaceImpl), [CallVirtualFromConstructorBase.JniTypeName] = typeof (CallVirtualFromConstructorBase), [CallVirtualFromConstructorDerived.JniTypeName] = typeof (CallVirtualFromConstructorDerived), [CrossReferenceBridge.JniTypeName] = typeof (CrossReferenceBridge), diff --git a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs index a112f155c..21727ce2e 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs @@ -133,6 +133,30 @@ public void CollectPeers () // TODO } + [Test] + public void CreatePeer_InvalidHandleReturnsNull () + { + var r = new JniObjectReference (); + var o = valueManager.CreatePeer (ref r, JniObjectReferenceOptions.Copy, null); + Assert.IsNull (o); + } + + [Test] + public unsafe void CreatePeer_UsesFallbackType () + { + using var t = new JniType (AnotherJavaInterfaceImpl.JniTypeName); + + var ctor = t.GetConstructor ("()V"); + var lref = t.NewObject (ctor, null); + + using var p = valueManager.CreatePeer (ref lref, JniObjectReferenceOptions.CopyAndDispose, typeof (IJavaInterface)); + + Assert.IsFalse (lref.IsValid); // .CopyAndDispose disposes + + Assert.IsNotNull (p); + Assert.AreSame (typeof (IJavaInterfaceInvoker), p!.GetType ()); + } + [Test] public void CreateValue () { @@ -294,4 +318,38 @@ public class JniRuntimeJniValueManagerContract_NoGCIntegration : JniRuntimeJniVa protected override Type ValueManagerType => ManagedValueManagerType; } #endif // !__ANDROID__ + + // Note: Java side implements JavaInterface, while managed binding DOES NOT. + // This is so that `CreatePeer(…, typeof(IJavaInterface))` tests don't use an existing AnotherJavaInterfaceImpl instance. + // + // This is mostly identical to MyJavaInterfaceImpl; the important difference is that + // it contains an activation constructor, while MyJavaInterfaceImpl does not. + // MyJavaInterfaceImpl can't have one, as that's what provokes the NotSupportedException in the JavaAs() tests. + // + // We want one here so that in "bad" `CreatePeer()` implementations, we'll find this peer and construct it + // before verifying that it satisfies the targetType requirement. + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] + public class AnotherJavaInterfaceImpl : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/AnotherJavaInterfaceImpl"; + + internal static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (AnotherJavaInterfaceImpl)); + + public override JniPeerMembers JniPeerMembers { + get {return _members;} + } + + AnotherJavaInterfaceImpl (ref JniObjectReference reference, JniObjectReferenceOptions options) + : base (ref reference, options) + { + } + + public unsafe AnotherJavaInterfaceImpl () + : base (ref *InvalidJniObjectReference, JniObjectReferenceOptions.None) + { + const string id = "()V"; + var peer = _members.InstanceMethods.StartCreateInstance (id, GetType (), null); + Construct (ref peer, JniObjectReferenceOptions.CopyAndDispose); + _members.InstanceMethods.FinishCreateInstance (id, this, null); + } + } } diff --git a/tests/Java.Interop-Tests/java/net/dot/jni/test/AnotherJavaInterfaceImpl.java b/tests/Java.Interop-Tests/java/net/dot/jni/test/AnotherJavaInterfaceImpl.java new file mode 100644 index 000000000..02005c388 --- /dev/null +++ b/tests/Java.Interop-Tests/java/net/dot/jni/test/AnotherJavaInterfaceImpl.java @@ -0,0 +1,13 @@ +package net.dot.jni.test; + +public class AnotherJavaInterfaceImpl + implements JavaInterface, Cloneable +{ + public String getValue() { + return "Another hello from Java!"; + } + + public Object clone() { + return this; + } +}