Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

[J.I, generator] Fix GetPeerMembers for interfaces. - #486

Merged
jonpryor merged 1 commit into
masterfrom
interface-peers
Aug 28, 2019
Merged

[J.I, generator] Fix GetPeerMembers for interfaces.#486
jonpryor merged 1 commit into
masterfrom
interface-peers

Conversation

@jpobst

Copy link
Copy Markdown
Contributor

The following DIM case currently fails:

Java:
package com.xamarin.android;
public interface DefaultMethodsInterface
{
default int foo () { return 0; }
}
C#
class ManagedOverrideDefault : Java.Lang.Object, IDefaultMethodsInterface
{
}
var over = new ManagedOverrideDefault ();
(over as IDefaultMethodsInterface).Foo ();

with:

Java.Lang.NoSuchMethodError : no non-static method "Ljava/lang/Object;.foo()I"

This adds a parameter to the JniPeerMembers constructor denoting this type is an Interface rather than a Class, so that GetPeerMembers can correctly resolve the method.

@jpobst
jpobstforce-pushed the interface-peers branch 5 times, most recently from e988ee5 to b2b4cf5CompareAugust 27, 2019 19:52
Comment threadsrc/Java.Interop/Java.Interop/JniPeerMembers.cs
Comment threadsrc/Java.Interop/Java.Interop/JniPeerMembers.cs Outdated
@jpobst

Copy link
Copy Markdown
ContributorAuthor

Updated to address review feedback.

@jonpryor
jonpryor merged commit 3fee05c into masterAug 28, 2019
@jonpryor
jonpryor deleted the interface-peers branch August 28, 2019 17:53
jonpryor pushed a commit that referenced this pull request Aug 29, 2019
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.
steveisok pushed a commit that referenced this pull request Aug 30, 2019
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.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 13, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@jpobst@jonpryor