Skip to content

Commit

Permalink
Fix automatic conversions of function/method args for java interfaces… (
Browse files Browse the repository at this point in the history
jython#296)

Handle automatic conversions of function/method args for Java interfaces when default methods are present. Prior to this change (and from Java 8 on) the presence of default methods in interfaces would prevent Jython from recognising them as a match in Java method invocation. A test is added.
  • Loading branch information
pturmel authored Jan 16, 2024
1 parent 67415e0 commit 117aa69
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 3 deletions.
1 change: 1 addition & 0 deletions ACKNOWLEDGMENTS
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ Jython in ways large and small, in no particular order:
Zhihong (Ted) Yu
Matthew Schweiss (GitHub: TheMatt2)
Diogo Teles Sant'Anna (GitHub: diogoteles08)
Phil Turmel (GitHub: pturmel)


Local Variables:
Expand Down
24 changes: 23 additions & 1 deletion Lib/test/test_java_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import org.python.core.Options

from javatests import Issue1833
from javatests.ProxyTests import NullToString, Person
from javatests.ProxyTests import NullToString, Person, ConsumerCaller

from clamp import SerializableProxies
from unittest.case import skip
Expand Down Expand Up @@ -914,6 +914,13 @@ def __call__(self):
self.data.append(42)


class ConsumerMethod(object):
def __init__(self):
self.data = list()
def doit(self, item):
self.data.append(item)


class SingleMethodInterfaceTest(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -958,6 +965,20 @@ def test_some_noncallable_object(self):
exc.exception, TypeError,
"submit(): 1st arg can't be coerced to java.util.concurrent.Callable, java.lang.Runnable")

def test_consumer_function(self):
x = list()
def f(item):
x.append(item)
future = self.executor.submit(ConsumerCaller(f))
future.get()
self.assertEqual(x, [42])

def test_consumer_method(self):
obj = ConsumerMethod()
future = self.executor.submit(ConsumerCaller(obj.doit))
future.get()
self.assertEqual(obj.data, [42])


def test_main():
test_support.run_unittest(
Expand All @@ -983,5 +1004,6 @@ def test_main():
SingleMethodInterfaceTest,
)


if __name__ == "__main__":
test_main()
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ New Features


Jython 2.7.4a1 Bugs fixed
- [ GH-277 ] Fix automatic conversions of function/method args for java
interfaces with default methods.
- [ GH-269 ] Upgrade Google Guava to 32.0.1 (CVE-2023-2976)
- [ GH-264 ] Create a security policy
- [ GH-254 ] Regression in socket.socket.sendall for sending Unicode
Expand Down
5 changes: 4 additions & 1 deletion src/org/python/core/PyFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@ public Object __tojava__(Class<?> c) {
String name = null;
for (Method method : c.getMethods()) {
if (method.getDeclaringClass() != Object.class) {
if (method.isDefault()) {
continue;
}
if (name == null || name.equals(method.getName())) {
name = method.getName();
} else {
Expand All @@ -541,7 +544,7 @@ private Object proxy( Class<?> c ) {
@Override
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
// Handle invocation when invoked through Proxy (as coerced to single method interface)
if (method.getDeclaringClass() == Object.class) {
if (method.getDeclaringClass() == Object.class || method.isDefault()) {
return method.invoke( this, args );
} else if (args == null || args.length == 0) {
return __call__().__tojava__(method.getReturnType());
Expand Down
5 changes: 4 additions & 1 deletion src/org/python/core/PyMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ public Object __tojava__(Class<?> c) {
String name = null;
for (Method method : c.getMethods()) {
if (method.getDeclaringClass() != Object.class) {
if (method.isDefault()) {
continue;
}
if (name == null || name.equals(method.getName())) {
name = method.getName();
} else {
Expand All @@ -404,7 +407,7 @@ private Object proxy( Class<?> c ) {

public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
// Handle invocation when invoked through Proxy (as coerced to single method interface)
if (method.getDeclaringClass() == Object.class) {
if (method.getDeclaringClass() == Object.class || method.isDefault()) {
return method.invoke( this, args );
} else if (args == null || args.length == 0) {
return __call__().__tojava__(method.getReturnType());
Expand Down
14 changes: 14 additions & 0 deletions tests/java/javatests/ProxyTests.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package javatests;

import java.util.function.Consumer;

public class ProxyTests {

public static class Person {
Expand Down Expand Up @@ -39,4 +41,16 @@ public static class NullToString {
public String toString() { return null; }
}

public static class ConsumerCaller implements Runnable {
protected final Consumer<Object> target;

public ConsumerCaller(Consumer<Object> target) {
this.target = target;
}

@Override
public void run() {
target.accept(42);
}
}
}

0 comments on commit 117aa69

Please sign in to comment.