Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 6 additions & 43 deletions src/Mono.Android/Java.Interop/JavaObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Exception>(() => JavaObjectExtensions.JavaCast<Java.Lang.IAppendable> (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<Java.Util.AbstractList> (copy);

// Semantic change: in .NET 8, `al` is a new `AbstractListInvoker` instance, not `list`
Assert.AreSame (list, al);
}

[Test]
public void JavaCast_InvalidTypeCastThrows ()
{
Expand Down