Skip to content

Commit

Permalink
Run object default methods for interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi committed Dec 11, 2024
1 parent 2cecc88 commit 9f9a1b5
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 18 deletions.
2 changes: 2 additions & 0 deletions pkgs/jni/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
them in a for-loop or use methods such as `map` on them.

- Added nullable type classes for all Java objects.
- Fixed a problem where interfaces implemented in Dart would crash when calling
the default object methods: `equals`, `hashCode`, and `toString`.

## 0.12.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@

public class PortProxyBuilder implements InvocationHandler {
private static final PortCleaner cleaner = new PortCleaner();
private static final Method equals;
private static final Method hashCode;
private static final Method toString;

static {
Class<Object> object = Object.class;
try {
equals = object.getDeclaredMethod("equals", object);
hashCode = object.getDeclaredMethod("hashCode");
toString = object.getDeclaredMethod("toString");
} catch (NoSuchMethodException e) {
// Never happens.
throw new Error();
}
System.loadLibrary("dartjni");
}

Expand Down Expand Up @@ -73,7 +85,7 @@ private static void appendType(StringBuilder descriptor, Class<?> type) {
}

public void addImplementation(
String binaryName, long port, long functionPointer, List<String> asyncMethods) {
String binaryName, long port, long functionPointer, List<String> asyncMethods) {
implementations.put(binaryName, new DartImplementation(port, functionPointer));
this.asyncMethods.addAll(asyncMethods);
}
Expand All @@ -91,8 +103,8 @@ public Object build() throws ClassNotFoundException {
classes.add(Class.forName(binaryName));
}
Object obj =
Proxy.newProxyInstance(
classes.get(0).getClassLoader(), classes.toArray(new Class<?>[0]), this);
Proxy.newProxyInstance(
classes.get(0).getClassLoader(), classes.toArray(new Class<?>[0]), this);
for (DartImplementation implementation : implementations.values()) {
cleaner.register(obj, implementation.port);
}
Expand All @@ -103,30 +115,39 @@ public Object build() throws ClassNotFoundException {
/// [0]: The address of the result pointer used for the clean-up.
/// [1]: The result of the invocation.
private static native Object[] _invoke(
long port,
long isolateId,
long functionPtr,
Object proxy,
String methodDescriptor,
Object[] args,
boolean isBlocking);
long port,
long isolateId,
long functionPtr,
Object proxy,
String methodDescriptor,
Object[] args,
boolean isBlocking);

private static native void _cleanUp(long resultPtr);

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.equals(equals)) {
return proxy == args[0];
}
if (method.equals(hashCode)) {
return System.identityHashCode(proxy);
}
if (method.equals(toString)) {
return proxy.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(proxy));
}
DartImplementation implementation = implementations.get(method.getDeclaringClass().getName());
String descriptor = getDescriptor(method);
boolean isBlocking = !asyncMethods.contains(descriptor);
Object[] result =
_invoke(
implementation.port,
isolateId,
implementation.pointer,
proxy,
descriptor,
args,
isBlocking);
_invoke(
implementation.port,
isolateId,
implementation.pointer,
proxy,
descriptor,
args,
isBlocking);
if (!isBlocking) {
return null;
}
Expand Down
10 changes: 10 additions & 0 deletions pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,16 @@ void registerTests(String groupName, TestRunnerCallback test) {
}
});
}
test('Object methods work', () {
final runnable = MyRunnable.implement($MyRunnable(
run: () {},
));
expect(runnable == runnable, true);
expect(runnable != runnable, false);
expect(runnable.hashCode, runnable.hashCode);
expect(runnable.toString(), runnable.toString());
runnable.release();
});
}
group('Dart exceptions are handled', () {
for (final exception in [UnimplementedError(), 'Hello!']) {
Expand Down

0 comments on commit 9f9a1b5

Please sign in to comment.