You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
Repository navigation
Commit 3fee05c
Browse filesBrowse the repository at this point in the historyBrowse files
[Java.Interop, generator] Fix GetPeerMembers for interfaces (#486)
Context: dotnet/android#3533
Consider the following Java interface which contains a default
interface method:
// Java
package com.xamarin.android;
public interface DefaultMethodsInterface
{
default int foo () { return 0; }
}
This is bound as:
// C# Binding
public partial interface IDefaultMethodsInterface : IJavaPeerable {
static readonly JniPeerMembers _members = new JniPeerMembers (
"java/DefaultMethodsInterface",
typeof (IDefaultMethodsInterface));
[Register ("foo", "()V", "…")]
virtual unsafe int Foo ()
{
return _members.InstanceMethods.InvokeVirtualInt32Method ("foo.()V", this, null);
}
}
If we "implement" the interface from C# *without* implementing
`IDefaultMethodsInterface.Foo()`:
// C#
class ManagedOverrideDefault : Java.Lang.Object, IDefaultMethodsInterface
{
}
Then invoke the default interface method:
var over = new ManagedOverrideDefault ();
(over as IDefaultMethodsInterface).Foo ();
The attempt to invoke the default implementation of
`IDefaultMethodsInterface.Foo()` causes a crash:
Java.Lang.NoSuchMethodError : no non-static method "Ljava/lang/Object;.foo()I"
The cause of the crash is as follows: `IDefaultMethodsInterface.Foo()`
calls `JniPeerMembers.JniInstanceMethods.InvokeVirtualInt32Method()`
tries to determine if the method is overridden, and if the method is
*not* overridden -- as is the case here -- then we hit the non-virtual
invocation path:
// src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs
var j = Members.GetPeerMembers (self);
var n = j.InstanceMethods.GetMethodInfo (encodedMember);
return JniEnvironment.InstanceMethods.CallNonvirtualIntMethod (self.PeerReference, j.JniPeerType.PeerReference, n, parameters);
`JniInstanceMethods.Members` will be the
`IDefaultMethodsInterface._members` field, and
`Members.GetPeerMembers()` would return `self.JniPeerMembers`.
Because this is a C# class which inherits `Java.Lang.Object`,
`Java.Lang.Object._members` is returned. We then attempt to resolve
`foo()` from `java.lang.Object`, which *does not exist*, which is why
the `NoSuchMethodError` is thrown.
The fix is to "intercept" the `Members.GetPeerMembers()` invocation
so that `j` refers to `IDefaultMethodsInterface._members`, *not*
`Java.Lang.Object._members`. This would allow `n` to refer to
`DefaultMethodsInterface.foo()`, which *does* exist, and *can* be
invoked via `JniEnvironment.InstanceMethods.CallNonvirtualIntMethod()`.
Add the following `JniPeerMembers` constructor overload:
partial class JniPeerMembers {
public JniPeerMembers (string jniPeerTypeName, Type managedPeerType, bool isInterface);
}
Update `JniPeerMembers.GetPeerMembers()` so that if `isInterface` is
true, we return `this` (the *interface*s `JniPeerMembers` instance).
Update `generator` so that `_members` construction within interfaces
sets the `isInterface` parameter to true, resulting in:
partial interface IDefaultMethodsInterface {
static readonly JniPeerMembers _members = new JniPeerMembers (
"java/DefaultMethodsInterface",
typeof (IDefaultMethodsInterface),
isInterface: true);
}
This allows `(over as IDefaultMethodsInterface).Foo()` to work
without an exception being thrown.
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterfaceDefaultMethod.txt
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterfaceDefaultProperty.txt
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterfaceDefaultPropertyGetterOnly.txt
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultMethod.txt
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultProperty.txt
Copy file name to clipboardExpand all lines: tools/generator/Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultPropertyGetterOnly.txt
0 commit comments