diff --git a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs index 345c664c61d..a3e817facb9 100644 --- a/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs +++ b/src/Mono.Android/Java.Interop/JavaObjectExtensions.cs @@ -81,41 +81,9 @@ internal static TResult? _JavaCast< if (instance.Handle == IntPtr.Zero) throw new ObjectDisposedException (instance.GetType ().FullName); - Type resultType = typeof (TResult); - if (resultType.IsClass) { - return (TResult) CastClass (instance, resultType); - } - else if (resultType.IsInterface) { - return (TResult?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType); - } - else - throw new NotSupportedException (FormattableString.Invariant ($"Unable to convert type '{instance.GetType ().FullName}' to '{resultType.FullName}'.")); - } - - static IJavaObject CastClass ( - IJavaObject instance, - [DynamicallyAccessedMembers (Constructors)] - Type resultType) - { - var klass = JNIEnv.FindClass (resultType); - try { - if (klass == IntPtr.Zero) - throw new ArgumentException ("Unable to determine JNI class for '" + resultType.FullName + "'.", "TResult"); - if (!JNIEnv.IsInstanceOf (instance.Handle, klass)) - throw new InvalidCastException ( - FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{resultType.FullName}'.")); - } finally { - JNIEnv.DeleteGlobalRef (klass); - } - - if (resultType.IsAbstract) { - // TODO: keep in sync with TypeManager.CreateInstance() algorithm - var invokerType = GetInvokerType (resultType); - if (invokerType == null) - throw new ArgumentException ("Unable to get Invoker for abstract type '" + resultType.FullName + "'.", "TResult"); - resultType = invokerType; - } - return (IJavaObject) TypeManager.CreateProxy (resultType, instance.Handle, JniHandleOwnership.DoNotTransfer); + return (TResult) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, typeof (TResult)) ?? + throw new InvalidCastException ( + FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{typeof (TResult).FullName}'.")); } internal static IJavaObject? JavaCast ( @@ -132,14 +100,9 @@ static IJavaObject CastClass ( if (resultType.IsAssignableFrom (instance.GetType ())) return instance; - if (resultType.IsClass) { - return CastClass (instance, resultType); - } - else if (resultType.IsInterface) { - return (IJavaObject?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType); - } - else - throw new NotSupportedException (FormattableString.Invariant ($"Unable to convert type '{instance.GetType ().FullName}' to '{resultType.FullName}'.")); + return (IJavaObject?) Java.Lang.Object.GetObject (instance.Handle, JniHandleOwnership.DoNotTransfer, resultType) ?? + throw new InvalidCastException ( + FormattableString.Invariant ($"Unable to convert instance of type '{instance.GetType ().FullName}' to type '{resultType.FullName}'.")); } // typeof(Foo) -> FooInvoker diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs index 3f21ea04435..334a9f542a8 100644 --- a/src/Mono.Android/Java.Interop/TypeManager.cs +++ b/src/Mono.Android/Java.Interop/TypeManager.cs @@ -336,7 +336,6 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership 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)}`"); return null; } } finally { diff --git a/tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs b/tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs index e6b133a4eaf..e5e56a839a6 100644 --- a/tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs +++ b/tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs @@ -40,6 +40,32 @@ public void JavaCast_InterfaceCast () } } + [Test] + public void JavaCast_BadInterfaceCast () + { + using var n = new Java.Lang.Integer (42); + var e = Assert.Catch(() => JavaObjectExtensions.JavaCast (n)); + if (e is System.Reflection.TargetInvocationException tie) { + // .NET 8 behavior + Assert.IsTrue (tie.InnerException is InvalidCastException); + } else if (e is InvalidCastException ice) { + // Yay + } else { + Assert.Fail ($"Unexpected exception type: {e.GetType ()}: {e.ToString ()}"); + } + } + + [Test] + public void JavaCast_ObtainOriginalInstance () + { + using var list = new Java.Util.ArrayList (); + using var copy = new Java.Lang.Object (list.Handle, JniHandleOwnership.DoNotTransfer); + using var al = JavaObjectExtensions.JavaCast (copy); + + // Semantic change: in .NET 8, `al` is a new `AbstractListInvoker` instance, not `list` + Assert.AreSame (list, al); + } + [Test] public void JavaCast_InvalidTypeCastThrows () {