Skip to content

pkgs/jni: Support Throwing Java Exceptions in Interface Impl. #1211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
6 changes: 3 additions & 3 deletions pkgs/jni/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.8.0"
version: "0.9.3-wip"
js:
dependency: transitive
description:
Expand Down Expand Up @@ -490,10 +490,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "14.1.0"
version: "14.2.1"
watcher:
dependency: transitive
description:
Expand Down
43 changes: 26 additions & 17 deletions pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
import java.lang.reflect.*;

public class PortProxy implements InvocationHandler {
private static final PortCleaner cleaner = new PortCleaner();

static {
System.loadLibrary("dartjni");
}

private static final PortCleaner cleaner = new PortCleaner();
private final long port;
private final long isolateId;
private final long functionPtr;
Expand Down Expand Up @@ -73,22 +74,6 @@ public static Object newInstance(String binaryName, long port, long isolateId, l
return obj;
}

private static final class DartException extends Exception {
private DartException(String message) {
super(message);
}
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws DartException {
Object[] result = _invoke(port, isolateId, functionPtr, proxy, getDescriptor(method), args);
_cleanUp((Long) result[0]);
if (result[1] instanceof DartException) {
throw (DartException) result[1];
}
return result[1];
}

/// Returns an array with two objects:
/// [0]: The address of the result pointer used for the clean-up.
/// [1]: The result of the invocation.
Expand All @@ -101,4 +86,28 @@ private static native Object[] _invoke(
Object[] args);

private static native void _cleanUp(long resultPtr);

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object[] result = _invoke(port, isolateId, functionPtr, proxy, getDescriptor(method), args);
_cleanUp((Long) result[0]);
if (result[1] instanceof DartException) {
Throwable cause = ((DartException) result[1]).cause;
if (cause != null) {
throw cause;
} else {
throw (DartException) result[1];
}
}
return result[1];
}

private static final class DartException extends Exception {
Throwable cause;

private DartException(String message, Throwable cause) {
super(message);
this.cause = cause;
}
}
}
18 changes: 13 additions & 5 deletions pkgs/jni/lib/src/jni.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import 'dart:io';
import 'dart:isolate';

import 'package:ffi/ffi.dart';
import 'package:jni/jni.dart';
import 'package:path/path.dart';

import 'errors.dart';
import 'jreference.dart';
import 'third_party/generated_bindings.dart';
import 'accessors.dart';
import 'third_party/generated_bindings.dart';

String _getLibraryFileName(String base) {
if (Platform.isLinux || Platform.isAndroid) {
Expand Down Expand Up @@ -221,6 +220,8 @@ typedef _SetJniGettersDartType = void Function(Pointer<Void>, Pointer<Void>);

/// Extensions for use by `jnigen` generated code.
extension ProtectedJniExtensions on Jni {
static final _jThrowableClass = JClass.forName('java/lang/Throwable');

static Pointer<T> Function<T extends NativeType>(String) initGeneratedLibrary(
String name) {
var path = _getLibraryFileName(name);
Expand All @@ -237,9 +238,16 @@ extension ProtectedJniExtensions on Jni {
}

/// Returns a new DartException.
static Pointer<Void> newDartException(String message) {
static Pointer<Void> newDartException(Object exception) {
JObjectPtr? cause;
if (exception is JObject &&
Jni.env.IsInstanceOf(
exception.reference.pointer, _jThrowableClass.reference.pointer)) {
cause = exception.reference.pointer;
}
return Jni._bindings
.DartException__ctor(Jni.env.toJStringPtr(message))
.DartException__ctor(
Jni.env.toJStringPtr(exception.toString()), cause ?? nullptr)
.objectPointer;
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/jni/lib/src/jprimitives.dart
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ final class jdoubleType extends JType<jdouble>
}

abstract final class jvoid extends JPrimitive {
static const type = jdoubleType();
static const type = jvoidType();
}

final class jvoidType extends JType<jvoid> with JCallable<jvoid, void> {
Expand Down
92 changes: 71 additions & 21 deletions pkgs/jni/lib/src/third_party/jni_bindings_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,19 @@ class JniBindings {

JniResult DartException__ctor(
JStringPtr message,
JThrowablePtr cause,
) {
return _DartException__ctor(
message,
cause,
);
}

late final _DartException__ctorPtr =
_lookup<ffi.NativeFunction<JniResult Function(JStringPtr)>>(
'DartException__ctor');
late final _DartException__ctor =
_DartException__ctorPtr.asFunction<JniResult Function(JStringPtr)>();
late final _DartException__ctorPtr = _lookup<
ffi.NativeFunction<JniResult Function(JStringPtr, JThrowablePtr)>>(
'DartException__ctor');
late final _DartException__ctor = _DartException__ctorPtr.asFunction<
JniResult Function(JStringPtr, JThrowablePtr)>();

JniResult PortContinuation__ctor(
int j,
Expand Down Expand Up @@ -2081,30 +2083,78 @@ final class CallbackResult extends ffi.Struct {
external JObjectPtr object;
}

typedef MutexLock = pthread_mutex_t;
typedef pthread_mutex_t = __darwin_pthread_mutex_t;
typedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t;
typedef MutexLock = CRITICAL_SECTION;
typedef CRITICAL_SECTION = RTL_CRITICAL_SECTION;
typedef RTL_CRITICAL_SECTION = _RTL_CRITICAL_SECTION;

final class _opaque_pthread_mutex_t extends ffi.Struct {
@ffi.Long()
external int __sig;
final class _RTL_CRITICAL_SECTION extends ffi.Struct {
external PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

@ffi.Array.multi([56])
external ffi.Array<ffi.Char> __opaque;
@LONG()
external int LockCount;

@LONG()
external int RecursionCount;

external HANDLE OwningThread;

external HANDLE LockSemaphore;

@ULONG_PTR()
external int SpinCount;
}

typedef PRTL_CRITICAL_SECTION_DEBUG = ffi.Pointer<_RTL_CRITICAL_SECTION_DEBUG>;

final class _RTL_CRITICAL_SECTION_DEBUG extends ffi.Struct {
@WORD()
external int Type;

@WORD()
external int CreatorBackTraceIndex;

external ffi.Pointer<_RTL_CRITICAL_SECTION> CriticalSection;

external LIST_ENTRY ProcessLocksList;

@DWORD()
external int EntryCount;

@DWORD()
external int ContentionCount;

@DWORD()
external int Flags;

@WORD()
external int CreatorBackTraceIndexHigh;

@WORD()
external int Identifier;
}

typedef ConditionVariable = pthread_cond_t;
typedef pthread_cond_t = __darwin_pthread_cond_t;
typedef __darwin_pthread_cond_t = _opaque_pthread_cond_t;
typedef WORD = ffi.UnsignedShort;
typedef LIST_ENTRY = _LIST_ENTRY;

final class _LIST_ENTRY extends ffi.Struct {
external ffi.Pointer<_LIST_ENTRY> Flink;

external ffi.Pointer<_LIST_ENTRY> Blink;
}

final class _opaque_pthread_cond_t extends ffi.Struct {
@ffi.Long()
external int __sig;
typedef DWORD = ffi.UnsignedLong;
typedef LONG = ffi.Long;
typedef HANDLE = ffi.Pointer<ffi.Void>;
typedef ULONG_PTR = ffi.UnsignedLongLong;
typedef ConditionVariable = CONDITION_VARIABLE;
typedef CONDITION_VARIABLE = RTL_CONDITION_VARIABLE;
typedef RTL_CONDITION_VARIABLE = _RTL_CONDITION_VARIABLE;

@ffi.Array.multi([40])
external ffi.Array<ffi.Char> __opaque;
final class _RTL_CONDITION_VARIABLE extends ffi.Struct {
external PVOID Ptr;
}

typedef PVOID = ffi.Pointer<ffi.Void>;
typedef Dart_FinalizableHandle = ffi.Pointer<_Dart_FinalizableHandle>;

final class _Dart_FinalizableHandle extends ffi.Opaque {}
Expand Down
9 changes: 5 additions & 4 deletions pkgs/jni/src/dartjni.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,18 +683,19 @@ FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data) {
jclass _c_DartException = NULL;

jmethodID _m_DartException__ctor = NULL;
FFI_PLUGIN_EXPORT JniResult DartException__ctor(jstring message) {
FFI_PLUGIN_EXPORT JniResult DartException__ctor(jstring message,
jthrowable cause) {
attach_thread();
load_class_global_ref(&_c_DartException,
"com/github/dart_lang/jni/PortProxy$DartException");
if (_c_DartException == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
load_method(_c_DartException, &_m_DartException__ctor, "<init>",
"(Ljava/lang/String;)V");
"(Ljava/lang/String;Ljava/lang/Throwable;)V");
if (_m_DartException__ctor == NULL)
return (JniResult){.value = {.j = 0}, .exception = check_exception()};
jobject _result = (*jniEnv)->NewObject(jniEnv, _c_DartException,
_m_DartException__ctor, message);
jobject _result = (*jniEnv)->NewObject(
jniEnv, _c_DartException, _m_DartException__ctor, message, cause);
jthrowable exception = check_exception();
if (exception == NULL) {
_result = to_global_ref(_result);
Expand Down
2 changes: 1 addition & 1 deletion pkgs/jni/src/dartjni.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ static inline JniResult to_global_ref_result(jobject ref) {
FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);

FFI_PLUGIN_EXPORT
JniResult DartException__ctor(jstring message);
JniResult DartException__ctor(jstring message, jthrowable cause);

FFI_PLUGIN_EXPORT
JniResult PortContinuation__ctor(int64_t j);
Expand Down
3 changes: 2 additions & 1 deletion pkgs/jnigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

- Fixed a bug where wrong argument types were generated for varargs.
- Fixed the macOS arm64 varargs issue caused by the previous release.

- Support throwing Java exceptions from Dart code.
([#1209](https://github.com/dart-lang/native/issues/1209))

## 0.9.2

Expand Down
2 changes: 1 addition & 1 deletion pkgs/jnigen/lib/src/bindings/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ class $name$typeParamsDef extends $superName {
}
s.write('''
} catch (e) {
return $_protectedExtension.newDartException(e.toString());
return $_protectedExtension.newDartException(e);
}
return jni.nullptr;
}
Expand Down
Loading
Loading