From b6f31e14d2c77d65060fb55b33cbc816458cf5e7 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Mon, 18 Mar 2024 04:07:32 +0100 Subject: [PATCH 01/10] Use finalizable handles and vm:deeply-immutable pragma instead of NativeFinalizers for JReference --- .../com/github/dart_lang/jni/JniUtils.java | 5 ++ pkgs/jni/lib/src/jni.dart | 28 ++++++++-- pkgs/jni/lib/src/jreference.dart | 52 +++++++++++-------- .../third_party/jni_bindings_generated.dart | 40 ++++++++++++++ pkgs/jni/src/dartjni.c | 50 +++++++++++++++++- pkgs/jni/src/dartjni.h | 11 ++++ pkgs/jni/test/load_test.dart | 21 ++++++-- 7 files changed, 176 insertions(+), 31 deletions(-) create mode 100644 pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java diff --git a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java new file mode 100644 index 0000000000..f6c15788d2 --- /dev/null +++ b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java @@ -0,0 +1,5 @@ +package com.github.dart_lang.jni; + +public class JniUtils { + public static native Object fromReferenceAddress(long id); +} diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index c9e742c689..52bc8b0f1d 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -62,12 +62,17 @@ abstract final class Jni { _dylibDir = dylibDir; } + static bool _initialized = false; + /// Initializes DartApiDL used for Continuations and interface implementation. static void initDLApi() { - assert(NativeApi.majorVersion == 2); - assert(NativeApi.minorVersion >= 3); - final result = _bindings.InitDartApiDL(NativeApi.initializeApiDLData); - assert(result == 0); + if (!_initialized) { + assert(NativeApi.majorVersion == 2); + assert(NativeApi.minorVersion >= 3); + final result = _bindings.InitDartApiDL(NativeApi.initializeApiDLData); + _initialized = result == 0; + assert(_initialized); + } } /// Spawn an instance of JVM using JNI. This method should be called at the @@ -267,6 +272,21 @@ extension ProtectedJniExtensions on Jni { Pointer result, JObjectPtr object) async { Jni._bindings.resultFor(result, object); } + + static Dart_FinalizableHandle newFinalizableHandle( + Object object, + Pointer reference, + int refType, + ) { + Jni.initDLApi(); + return Jni._bindings.newFinalizableHandle(object, reference, refType); + } + + static void deleteFinalizableHandle( + Dart_FinalizableHandle finalizableHandle, Object object) { + Jni.initDLApi(); + Jni._bindings.deleteFinalizableHandle(finalizableHandle, object); + } } extension AdditionalEnvMethods on GlobalJniEnv { diff --git a/pkgs/jni/lib/src/jreference.dart b/pkgs/jni/lib/src/jreference.dart index cad4193f7c..066df00167 100644 --- a/pkgs/jni/lib/src/jreference.dart +++ b/pkgs/jni/lib/src/jreference.dart @@ -11,11 +11,9 @@ import 'jni.dart'; extension ProtectedJReference on JReference { void setAsReleased() { - if (_released) { - throw DoubleReleaseError(); - } - _released = true; - JGlobalReference._finalizer.detach(this); + ProtectedJniExtensions.deleteFinalizableHandle( + _finalizableHandle, _finalizable); + // FIXME: No [DoubleReleaseError] will be thrown. } void ensureNotNull() { @@ -29,15 +27,25 @@ extension ProtectedJReference on JReference { /// Detaches the finalizer so the underlying pointer will not be deleted. JObjectPtr toPointer() { setAsReleased(); - return _pointer; + return _finalizable._pointer; } } +@pragma('vm:deeply-immutable') +final class _JFinalizable implements Finalizable { + final Pointer _pointer; + + _JFinalizable(this._pointer); +} + +@pragma('vm:deeply-immutable') abstract final class JReference { - final JObjectPtr _pointer; - bool _released = false; + final _JFinalizable _finalizable; + final Dart_FinalizableHandle _finalizableHandle; - JReference(this._pointer); + JReference(this._finalizable, int kind) + : _finalizableHandle = ProtectedJniExtensions.newFinalizableHandle( + _finalizable, _finalizable._pointer, kind); /// The underlying JNI reference. /// @@ -46,12 +54,13 @@ abstract final class JReference { /// Be careful when storing this in a variable since it might have gotten /// released upon use. JObjectPtr get pointer { - if (_released) throw UseAfterReleaseError(); - return _pointer; + // FIXME: No [UseAfterReleaseError] will be thrown. + return _finalizable._pointer; } /// Whether the underlying JNI reference is deleted or not. - bool get isReleased => _released; + // FIXME: releasing does not work. + bool get isReleased => false; /// Whether the underlying JNI reference is `null` or not. bool get isNull; @@ -72,27 +81,26 @@ abstract final class JReference { /// A managed JNI global reference. /// /// Uses a [NativeFinalizer] to delete the JNI global reference when finalized. -final class JGlobalReference extends JReference implements Finalizable { - static final _finalizer = - NativeFinalizer(Jni.env.ptr.ref.DeleteGlobalRef.cast()); - - JGlobalReference(super._reference) { - _finalizer.attach(this, _pointer, detach: this); - } +@pragma('vm:deeply-immutable') +final class JGlobalReference extends JReference { + JGlobalReference(Pointer pointer) + : super(_JFinalizable(pointer), JObjectRefType.JNIGlobalRefType); @override bool get isNull => pointer == nullptr; @override void _deleteReference() { - Jni.env.DeleteGlobalRef(_pointer); + Jni.env.DeleteGlobalRef(_finalizable._pointer); } } -final jNullReference = _JNullReference(); +final JReference jNullReference = _JNullReference(); +@pragma('vm:deeply-immutable') final class _JNullReference extends JReference { - _JNullReference() : super(nullptr); + _JNullReference() + : super(_JFinalizable(nullptr), JObjectRefType.JNIInvalidRefType); @override void _deleteReference() { diff --git a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart index c42711963f..61ce93d2a4 100644 --- a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart @@ -224,6 +224,42 @@ class JniBindings { late final _resultFor = _resultForPtr .asFunction, JObjectPtr)>(); + Dart_FinalizableHandle newFinalizableHandle( + Object object, + JObjectPtr reference, + int refType, + ) { + return _newFinalizableHandle( + object, + reference, + refType, + ); + } + + late final _newFinalizableHandlePtr = _lookup< + ffi.NativeFunction< + Dart_FinalizableHandle Function( + ffi.Handle, JObjectPtr, ffi.Int32)>>('newFinalizableHandle'); + late final _newFinalizableHandle = _newFinalizableHandlePtr + .asFunction(); + + void deleteFinalizableHandle( + Dart_FinalizableHandle finalizableHandle, + Object object, + ) { + return _deleteFinalizableHandle( + finalizableHandle, + object, + ); + } + + late final _deleteFinalizableHandlePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + Dart_FinalizableHandle, ffi.Handle)>>('deleteFinalizableHandle'); + late final _deleteFinalizableHandle = _deleteFinalizableHandlePtr + .asFunction(); + ffi.Pointer GetGlobalEnv() { return _GetGlobalEnv(); } @@ -2003,6 +2039,10 @@ final class _opaque_pthread_cond_t extends ffi.Struct { external ffi.Array __opaque; } +typedef Dart_FinalizableHandle = ffi.Pointer<_Dart_FinalizableHandle>; + +final class _Dart_FinalizableHandle extends ffi.Opaque {} + final class GlobalJniEnvStruct extends ffi.Struct { external ffi.Pointer reserved0; diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c index 382c9bc43c..ad09f38816 100644 --- a/pkgs/jni/src/dartjni.c +++ b/pkgs/jni/src/dartjni.c @@ -8,8 +8,6 @@ #include "dartjni.h" -#include "include/dart_api_dl.h" - void initAllLocks(JniLocks* locks) { init_lock(&locks->classLoadingLock); } @@ -666,6 +664,46 @@ void resultFor(CallbackResult* result, jobject object) { release_lock(&result->lock); } +void doNotFinalize(void* isolate_callback_data, void* peer) {} + +void finalizeLocal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteLocalRef(jniEnv, peer); +} + +void finalizeGlobal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteGlobalRef(jniEnv, peer); +} + +void finalizeWeakGlobal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteWeakGlobalRef(jniEnv, peer); +} + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType) { + switch (refType) { + case JNIInvalidRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, doNotFinalize); + case JNILocalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeLocal); + case JNIGlobalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeGlobal); + case JNIWeakGlobalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, + finalizeWeakGlobal); + } +} + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object) { + return Dart_DeleteFinalizableHandle_DL(finalizableHandle, object); +} + jclass _c_Object = NULL; jclass _c_Long = NULL; @@ -754,3 +792,11 @@ Java_com_github_dart_1lang_jni_PortCleaner_clean(JNIEnv* env, close_signal.type = Dart_CObject_kNull; Dart_PostCObject_DL(port, &close_signal); } + +JNIEXPORT jobject JNICALL +Java_com_github_dart_1lang_jni_JniUtils_fromReferenceAddress(JNIEnv* env, + jclass clazz, + jlong id) { + attach_thread(); + return (jobject)(id); +} diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index e1c79d292c..c3b8b70347 100644 --- a/pkgs/jni/src/dartjni.h +++ b/pkgs/jni/src/dartjni.h @@ -5,6 +5,8 @@ #pragma once // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. +#include "include/dart_api_dl.h" + #include #include #include @@ -388,3 +390,12 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jni/test/load_test.dart b/pkgs/jni/test/load_test.dart index 78f26ecd9b..78e1511776 100644 --- a/pkgs/jni/test/load_test.dart +++ b/pkgs/jni/test/load_test.dart @@ -67,7 +67,7 @@ void run({required TestRunnerCallback testRunner}) { // The actual expect here does not matter. I am just being paranoid // against assigning to `_` because compiler may optimize it. (It has // side effect of calling FFI but still.) - expect(random.reference, isNot(nullptr)); + expect(random.reference.pointer, isNot(nullptr)); }); } }); @@ -76,7 +76,7 @@ void run({required TestRunnerCallback testRunner}) { () { for (int i = 0; i < k256; i++) { final random = newRandom(); - expect(random.reference, isNot(nullptr)); + expect(random.reference.pointer, isNot(nullptr)); random.release(); } }); @@ -86,7 +86,7 @@ void run({required TestRunnerCallback testRunner}) { using((arena) { for (int i = 0; i < 256; i++) { final r = newRandom()..releasedBy(arena); - expect(r.reference, isNot(nullptr)); + expect(r.reference.pointer, isNot(nullptr)); } }); } @@ -103,6 +103,20 @@ void run({required TestRunnerCallback testRunner}) { } }); + void testFinalizer() { + testRunner('Finalizer correctly removes the references', () { + // We are checking if we can run this for large number of times. + // More than the number of available global references. + for (var i = 0; i < k256; ++i) { + final random = newRandom(); + expect(random.reference.pointer, isNot(nullptr)); + if (i % k4 == 0) { + doGC(); + } + } + }); + } + void testRefValidityAfterGC(int delayInSeconds) { testRunner('Validate reference after GC & ${delayInSeconds}s sleep', () { final random = newRandom(); @@ -122,6 +136,7 @@ void run({required TestRunnerCallback testRunner}) { // Dart_ExecuteInternalCommand doesn't exist in Android. if (!Platform.isAndroid) { + testFinalizer(); testRefValidityAfterGC(1); testRefValidityAfterGC(10); } From aa8e05fdbed54b6e9f11e74d204b41153a0c91e5 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 26 Mar 2024 18:00:59 +0100 Subject: [PATCH 02/10] Hook isReleased --- pkgs/jni/CHANGELOG.md | 1 + pkgs/jni/lib/src/jni.dart | 15 +++- pkgs/jni/lib/src/jobject.dart | 3 +- pkgs/jni/lib/src/jreference.dart | 72 +++++++++++++------ .../third_party/jni_bindings_generated.dart | 30 ++++++-- pkgs/jni/src/dartjni.c | 12 +++- pkgs/jni/src/dartjni.h | 12 ++-- .../in_app_java/src/android_utils/dartjni.h | 15 ++++ .../example/kotlin_plugin/src/dartjni.h | 15 ++++ .../example/notification_plugin/src/dartjni.h | 15 ++++ .../pdfbox_plugin/src/third_party/dartjni.h | 15 ++++ .../third_party/c_based/c_bindings/dartjni.h | 15 ++++ .../kotlin_test/c_based/c_bindings/dartjni.h | 15 ++++ .../c_based/c_bindings/dartjni.h | 15 ++++ 14 files changed, 214 insertions(+), 36 deletions(-) diff --git a/pkgs/jni/CHANGELOG.md b/pkgs/jni/CHANGELOG.md index 650f98d78a..3eaee4c9c1 100644 --- a/pkgs/jni/CHANGELOG.md +++ b/pkgs/jni/CHANGELOG.md @@ -45,6 +45,7 @@ `fill` object and not its Java runtime type. - `JObject`s now check the types using `instanceof` in debug mode when using `castTo`. +- Added the ability to share `JObject`s across isolates. ## 0.7.3 diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index 52bc8b0f1d..0bb22eff19 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -243,6 +243,7 @@ extension ProtectedJniExtensions on Jni { /// Returns a new PortContinuation. static JReference newPortContinuation(ReceivePort port) { + Jni.initDLApi(); return JGlobalReference( Jni._bindings .PortContinuation__ctor(port.sendPort.nativePort) @@ -258,6 +259,7 @@ extension ProtectedJniExtensions on Jni { NativeFunction< Pointer Function(Uint64, Pointer, Pointer)>> functionPtr) { + Jni.initDLApi(); return JGlobalReference(Jni._bindings .PortProxy__newInstance( Jni.env.toJStringPtr(binaryName), @@ -273,13 +275,22 @@ extension ProtectedJniExtensions on Jni { Jni._bindings.resultFor(result, object); } - static Dart_FinalizableHandle newFinalizableHandle( + static Dart_FinalizableHandle newJObjectFinalizableHandle( Object object, Pointer reference, int refType, ) { Jni.initDLApi(); - return Jni._bindings.newFinalizableHandle(object, reference, refType); + return Jni._bindings + .newJObjectFinalizableHandle(object, reference, refType); + } + + static Dart_FinalizableHandle newBooleanFinalizableHandle( + Object object, + Pointer reference, + ) { + Jni.initDLApi(); + return Jni._bindings.newBooleanFinalizableHandle(object, reference); } static void deleteFinalizableHandle( diff --git a/pkgs/jni/lib/src/jobject.dart b/pkgs/jni/lib/src/jobject.dart index 78a59b9be2..d351b15631 100644 --- a/pkgs/jni/lib/src/jobject.dart +++ b/pkgs/jni/lib/src/jobject.dart @@ -5,9 +5,10 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import 'package:jni/internal_helpers_for_jnigen.dart'; +import 'accessors.dart'; import 'jni.dart'; +import 'jreference.dart'; import 'lang/jstring.dart'; import 'types.dart'; diff --git a/pkgs/jni/lib/src/jreference.dart b/pkgs/jni/lib/src/jreference.dart index 066df00167..5694fbb329 100644 --- a/pkgs/jni/lib/src/jreference.dart +++ b/pkgs/jni/lib/src/jreference.dart @@ -4,6 +4,7 @@ import 'dart:ffi'; +import 'package:ffi/ffi.dart'; import 'package:jni/src/third_party/generated_bindings.dart'; import 'errors.dart'; @@ -11,9 +12,7 @@ import 'jni.dart'; extension ProtectedJReference on JReference { void setAsReleased() { - ProtectedJniExtensions.deleteFinalizableHandle( - _finalizableHandle, _finalizable); - // FIXME: No [DoubleReleaseError] will be thrown. + _setAsReleased(); } void ensureNotNull() { @@ -26,26 +25,24 @@ extension ProtectedJReference on JReference { /// /// Detaches the finalizer so the underlying pointer will not be deleted. JObjectPtr toPointer() { - setAsReleased(); - return _finalizable._pointer; + _setAsReleased(); + return _finalizable.pointer; } } +/// A thin wrapper around a pointer that makes it [Finalizable]. @pragma('vm:deeply-immutable') final class _JFinalizable implements Finalizable { - final Pointer _pointer; + final Pointer pointer; - _JFinalizable(this._pointer); + _JFinalizable(this.pointer); } @pragma('vm:deeply-immutable') abstract final class JReference { final _JFinalizable _finalizable; - final Dart_FinalizableHandle _finalizableHandle; - JReference(this._finalizable, int kind) - : _finalizableHandle = ProtectedJniExtensions.newFinalizableHandle( - _finalizable, _finalizable._pointer, kind); + JReference(this._finalizable); /// The underlying JNI reference. /// @@ -54,13 +51,12 @@ abstract final class JReference { /// Be careful when storing this in a variable since it might have gotten /// released upon use. JObjectPtr get pointer { - // FIXME: No [UseAfterReleaseError] will be thrown. - return _finalizable._pointer; + if (isReleased) throw UseAfterReleaseError(); + return _finalizable.pointer; } /// Whether the underlying JNI reference is deleted or not. - // FIXME: releasing does not work. - bool get isReleased => false; + bool get isReleased; /// Whether the underlying JNI reference is `null` or not. bool get isNull; @@ -71,11 +67,13 @@ abstract final class JReference { /// /// Further uses of this object will throw [UseAfterReleaseError]. void release() { - setAsReleased(); + _setAsReleased(); _deleteReference(); } void _deleteReference(); + + void _setAsReleased(); } /// A managed JNI global reference. @@ -83,24 +81,54 @@ abstract final class JReference { /// Uses a [NativeFinalizer] to delete the JNI global reference when finalized. @pragma('vm:deeply-immutable') final class JGlobalReference extends JReference { - JGlobalReference(Pointer pointer) - : super(_JFinalizable(pointer), JObjectRefType.JNIGlobalRefType); + /// The finalizable handle that deletes [_JFinalizable.pointer]. + final Dart_FinalizableHandle _jobjectFinalizableHandle; + final Pointer _isReleased; + + JGlobalReference._( + super._finalizable, this._jobjectFinalizableHandle, this._isReleased); + + factory JGlobalReference(Pointer pointer) { + final finalizable = _JFinalizable(pointer); + final isReleased = calloc(); + final jobjectFinalizableHandle = + ProtectedJniExtensions.newJObjectFinalizableHandle( + finalizable, finalizable.pointer, JObjectRefType.JNIGlobalRefType); + ProtectedJniExtensions.newBooleanFinalizableHandle(finalizable, isReleased); + return JGlobalReference._( + finalizable, jobjectFinalizableHandle, isReleased); + } @override bool get isNull => pointer == nullptr; + @override + void _setAsReleased() { + if (isReleased) { + throw DoubleReleaseError(); + } + _isReleased.value = true; + ProtectedJniExtensions.deleteFinalizableHandle( + _jobjectFinalizableHandle, _finalizable); + } + @override void _deleteReference() { - Jni.env.DeleteGlobalRef(_finalizable._pointer); + Jni.env.DeleteGlobalRef(_finalizable.pointer); } + + @override + bool get isReleased => _isReleased.value; } final JReference jNullReference = _JNullReference(); @pragma('vm:deeply-immutable') final class _JNullReference extends JReference { - _JNullReference() - : super(_JFinalizable(nullptr), JObjectRefType.JNIInvalidRefType); + _JNullReference() : super(_JFinalizable(nullptr)); + + @override + bool get isReleased => false; @override void _deleteReference() { @@ -108,7 +136,7 @@ final class _JNullReference extends JReference { } @override - void release() { + void _setAsReleased() { // No need to release `null`. } diff --git a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart index 61ce93d2a4..a575c812f6 100644 --- a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart @@ -224,25 +224,43 @@ class JniBindings { late final _resultFor = _resultForPtr .asFunction, JObjectPtr)>(); - Dart_FinalizableHandle newFinalizableHandle( + Dart_FinalizableHandle newJObjectFinalizableHandle( Object object, JObjectPtr reference, int refType, ) { - return _newFinalizableHandle( + return _newJObjectFinalizableHandle( object, reference, refType, ); } - late final _newFinalizableHandlePtr = _lookup< + late final _newJObjectFinalizableHandlePtr = _lookup< ffi.NativeFunction< - Dart_FinalizableHandle Function( - ffi.Handle, JObjectPtr, ffi.Int32)>>('newFinalizableHandle'); - late final _newFinalizableHandle = _newFinalizableHandlePtr + Dart_FinalizableHandle Function(ffi.Handle, JObjectPtr, + ffi.Int32)>>('newJObjectFinalizableHandle'); + late final _newJObjectFinalizableHandle = _newJObjectFinalizableHandlePtr .asFunction(); + Dart_FinalizableHandle newBooleanFinalizableHandle( + Object object, + ffi.Pointer reference, + ) { + return _newBooleanFinalizableHandle( + object, + reference, + ); + } + + late final _newBooleanFinalizableHandlePtr = _lookup< + ffi.NativeFunction< + Dart_FinalizableHandle Function(ffi.Handle, + ffi.Pointer)>>('newBooleanFinalizableHandle'); + late final _newBooleanFinalizableHandle = + _newBooleanFinalizableHandlePtr.asFunction< + Dart_FinalizableHandle Function(Object, ffi.Pointer)>(); + void deleteFinalizableHandle( Dart_FinalizableHandle finalizableHandle, Object object, diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c index ad09f38816..209043d1ce 100644 --- a/pkgs/jni/src/dartjni.c +++ b/pkgs/jni/src/dartjni.c @@ -681,8 +681,12 @@ void finalizeWeakGlobal(void* isolate_callback_data, void* peer) { (*jniEnv)->DeleteWeakGlobalRef(jniEnv, peer); } +void freeBoolean(void* isolate_callback_data, void* peer) { + free(peer); +} + FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newFinalizableHandle(Dart_Handle object, +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, jobject reference, jobjectRefType refType) { switch (refType) { @@ -698,6 +702,12 @@ Dart_FinalizableHandle newFinalizableHandle(Dart_Handle object, } } +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference) { + return Dart_NewFinalizableHandle_DL(object, reference, 1, freeBoolean); +} + FFI_PLUGIN_EXPORT void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, Dart_Handle object) { diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index c3b8b70347..57bb9812b0 100644 --- a/pkgs/jni/src/dartjni.h +++ b/pkgs/jni/src/dartjni.h @@ -4,9 +4,9 @@ #pragma once -// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include "include/dart_api_dl.h" +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include #include @@ -392,9 +392,13 @@ JniResult PortProxy__newInstance(jobject binaryName, FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); FFI_PLUGIN_EXPORT void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, diff --git a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/example/notification_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/notification_plugin/src/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index e1c79d292c..57bb9812b0 100644 --- a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -4,6 +4,8 @@ #pragma once +#include "include/dart_api_dl.h" + // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -388,3 +390,16 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t functionPtr); FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); From 197e71fcfd91eb7589f78f0a1e681d4b8e64b6ab Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 26 Mar 2024 18:19:57 +0100 Subject: [PATCH 03/10] Remove JniUtils --- .../src/main/java/com/github/dart_lang/jni/JniUtils.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java diff --git a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java deleted file mode 100644 index f6c15788d2..0000000000 --- a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.dart_lang.jni; - -public class JniUtils { - public static native Object fromReferenceAddress(long id); -} From 15ecede1c8c61dc2c2df0ae5b52c84f2405248bb Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 26 Mar 2024 19:03:56 +0100 Subject: [PATCH 04/10] Separate JNI internal functionalities to internal.h --- pkgs/jni/ffigen.yaml | 2 + pkgs/jni/src/CMakeLists.txt | 1 + pkgs/jni/src/dartjni.c | 241 ----------------- pkgs/jni/src/dartjni.h | 30 --- pkgs/jni/src/internal.c | 247 ++++++++++++++++++ pkgs/jni/src/internal.h | 36 +++ .../in_app_java/src/android_utils/dartjni.h | 30 --- .../example/kotlin_plugin/src/dartjni.h | 30 --- .../example/notification_plugin/src/dartjni.h | 30 --- .../pdfbox_plugin/src/third_party/dartjni.h | 30 --- .../third_party/c_based/c_bindings/dartjni.h | 30 --- .../kotlin_test/c_based/c_bindings/dartjni.h | 30 --- .../c_based/c_bindings/dartjni.h | 30 --- 13 files changed, 286 insertions(+), 481 deletions(-) create mode 100644 pkgs/jni/src/internal.c create mode 100644 pkgs/jni/src/internal.h diff --git a/pkgs/jni/ffigen.yaml b/pkgs/jni/ffigen.yaml index 782668e367..0914b40fc4 100644 --- a/pkgs/jni/ffigen.yaml +++ b/pkgs/jni/ffigen.yaml @@ -12,10 +12,12 @@ output: 'lib/src/third_party/jni_bindings_generated.dart' headers: entry-points: - 'src/dartjni.h' # Exports majority of JNI functions + - 'src/internal.h' - 'src/third_party/global_jni_env.h' # Exports GlobalJniEnv type - 'src/jni_constants.h' include-directives: - 'src/dartjni.h' + - 'src/internal.h' - 'src/third_party/global_jni_env.h' - 'third_party/jni.h' # jni.h from Android NDK - 'src/jni_constants.h' diff --git a/pkgs/jni/src/CMakeLists.txt b/pkgs/jni/src/CMakeLists.txt index 2cdb3f724d..685d52b71e 100644 --- a/pkgs/jni/src/CMakeLists.txt +++ b/pkgs/jni/src/CMakeLists.txt @@ -9,6 +9,7 @@ project(jni_library VERSION 0.0.1 LANGUAGES C) add_library(jni SHARED "dartjni.c" + "internal.c" "third_party/global_jni_env.c" "include/dart_api_dl.c" ) diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c index 209043d1ce..5b2ac0c30e 100644 --- a/pkgs/jni/src/dartjni.c +++ b/pkgs/jni/src/dartjni.c @@ -569,244 +569,3 @@ FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv() { attach_thread(); return jniEnv; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data) { - return Dart_InitializeApiDL(data); -} - -// com.github.dart_lang.jni.DartException -jclass _c_DartException = NULL; - -jmethodID _m_DartException__ctor = NULL; -FFI_PLUGIN_EXPORT JniResult DartException__ctor(jstring message) { - 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, "", - "(Ljava/lang/String;)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); - jthrowable exception = check_exception(); - if (exception == NULL) { - _result = to_global_ref(_result); - } - return (JniResult){.value = {.l = _result}, .exception = check_exception()}; -} - -JNIEXPORT void JNICALL -Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, - jclass clazz, - jlong port, - jobject result) { - attach_thread(); - Dart_CObject c_post; - c_post.type = Dart_CObject_kInt64; - c_post.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, result)); - Dart_PostCObject_DL(port, &c_post); -} - -// com.github.dart_lang.jni.PortContinuation -jclass _c_PortContinuation = NULL; - -jmethodID _m_PortContinuation__ctor = NULL; -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j) { - attach_thread(); - load_class_global_ref(&_c_PortContinuation, - "com/github/dart_lang/jni/PortContinuation"); - if (_c_PortContinuation == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_PortContinuation, &_m_PortContinuation__ctor, "", - "(J)V"); - if (_m_PortContinuation__ctor == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_PortContinuation, - _m_PortContinuation__ctor, j); - jthrowable exception = check_exception(); - if (exception == NULL) { - _result = to_global_ref(_result); - } - return (JniResult){.value = {.l = _result}, .exception = check_exception()}; -} - -// com.github.dart_lang.jni.PortProxy -jclass _c_PortProxy = NULL; - -jmethodID _m_PortProxy__newInstance = NULL; -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr) { - attach_thread(); - load_class_global_ref(&_c_PortProxy, "com/github/dart_lang/jni/PortProxy"); - if (_c_PortProxy == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method(_c_PortProxy, &_m_PortProxy__newInstance, "newInstance", - "(Ljava/lang/String;JJJ)Ljava/lang/Object;"); - if (_m_PortProxy__newInstance == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallStaticObjectMethod( - jniEnv, _c_PortProxy, _m_PortProxy__newInstance, binaryName, port, - (jlong)Dart_CurrentIsolate_DL(), functionPtr); - return to_global_ref_result(_result); -} - -FFI_PLUGIN_EXPORT -void resultFor(CallbackResult* result, jobject object) { - acquire_lock(&result->lock); - result->ready = 1; - result->object = object; - signal_cond(&result->cond); - release_lock(&result->lock); -} - -void doNotFinalize(void* isolate_callback_data, void* peer) {} - -void finalizeLocal(void* isolate_callback_data, void* peer) { - attach_thread(); - (*jniEnv)->DeleteLocalRef(jniEnv, peer); -} - -void finalizeGlobal(void* isolate_callback_data, void* peer) { - attach_thread(); - (*jniEnv)->DeleteGlobalRef(jniEnv, peer); -} - -void finalizeWeakGlobal(void* isolate_callback_data, void* peer) { - attach_thread(); - (*jniEnv)->DeleteWeakGlobalRef(jniEnv, peer); -} - -void freeBoolean(void* isolate_callback_data, void* peer) { - free(peer); -} - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType) { - switch (refType) { - case JNIInvalidRefType: - return Dart_NewFinalizableHandle_DL(object, reference, 0, doNotFinalize); - case JNILocalRefType: - return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeLocal); - case JNIGlobalRefType: - return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeGlobal); - case JNIWeakGlobalRefType: - return Dart_NewFinalizableHandle_DL(object, reference, 0, - finalizeWeakGlobal); - } -} - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference) { - return Dart_NewFinalizableHandle_DL(object, reference, 1, freeBoolean); -} - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object) { - return Dart_DeleteFinalizableHandle_DL(finalizableHandle, object); -} - -jclass _c_Object = NULL; -jclass _c_Long = NULL; - -jmethodID _m_Long_init = NULL; - -JNIEXPORT jobjectArray JNICALL -Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jclass clazz, - jlong port, - jlong isolateId, - jlong functionPtr, - jobject proxy, - jstring methodDescriptor, - jobjectArray args) { - CallbackResult* result = (CallbackResult*)malloc(sizeof(CallbackResult)); - if (isolateId != (jlong)Dart_CurrentIsolate_DL()) { - init_lock(&result->lock); - init_cond(&result->cond); - acquire_lock(&result->lock); - result->ready = 0; - result->object = NULL; - - Dart_CObject c_result; - c_result.type = Dart_CObject_kInt64; - c_result.value.as_int64 = (jlong)result; - - Dart_CObject c_method; - c_method.type = Dart_CObject_kInt64; - c_method.value.as_int64 = - (jlong)((*env)->NewGlobalRef(env, methodDescriptor)); - - Dart_CObject c_args; - c_args.type = Dart_CObject_kInt64; - c_args.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, args)); - - Dart_CObject* c_post_arr[] = {&c_result, &c_method, &c_args}; - Dart_CObject c_post; - c_post.type = Dart_CObject_kArray; - c_post.value.as_array.values = c_post_arr; - c_post.value.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]); - - Dart_PostCObject_DL(port, &c_post); - - while (!result->ready) { - wait_for(&result->cond, &result->lock); - } - - release_lock(&result->lock); - destroy_lock(&result->lock); - destroy_cond(&result->cond); - } else { - result->object = ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( - port, (*env)->NewGlobalRef(env, methodDescriptor), - (*env)->NewGlobalRef(env, args)); - } - // Returning an array of length 2. - // [0]: The result pointer, used for cleaning up the global reference, and - // freeing the memory since we passed the ownership to Java. - // [1]: The returned object. - attach_thread(); - load_class_global_ref(&_c_Object, "java/lang/Object"); - load_class_global_ref(&_c_Long, "java/lang/Long"); - load_method(_c_Long, &_m_Long_init, "", "(J)V"); - jobject first = (*env)->NewObject(env, _c_Long, _m_Long_init, (jlong)result); - jobject second = result->object; - jobjectArray arr = (*env)->NewObjectArray(env, 2, _c_Object, NULL); - (*env)->SetObjectArrayElement(env, arr, 0, first); - (*env)->SetObjectArrayElement(env, arr, 1, second); - return arr; -} - -JNIEXPORT void JNICALL -Java_com_github_dart_1lang_jni_PortProxy__1cleanUp(JNIEnv* env, - jclass clazz, - jlong resultPtr) { - CallbackResult* result = (CallbackResult*)resultPtr; - (*env)->DeleteGlobalRef(env, result->object); - free(result); -} - -JNIEXPORT void JNICALL -Java_com_github_dart_1lang_jni_PortCleaner_clean(JNIEnv* env, - jclass clazz, - jlong port) { - Dart_CObject close_signal; - close_signal.type = Dart_CObject_kNull; - Dart_PostCObject_DL(port, &close_signal); -} - -JNIEXPORT jobject JNICALL -Java_com_github_dart_1lang_jni_JniUtils_fromReferenceAddress(JNIEnv* env, - jclass clazz, - jlong id) { - attach_thread(); - return (jobject)(id); -} diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jni/src/dartjni.h +++ b/pkgs/jni/src/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jni/src/internal.c b/pkgs/jni/src/internal.c new file mode 100644 index 0000000000..53fb8985fe --- /dev/null +++ b/pkgs/jni/src/internal.c @@ -0,0 +1,247 @@ + +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "internal.h" + +FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data) { + return Dart_InitializeApiDL(data); +} + +// com.github.dart_lang.jni.DartException +jclass _c_DartException = NULL; + +jmethodID _m_DartException__ctor = NULL; +FFI_PLUGIN_EXPORT JniResult DartException__ctor(jstring message) { + 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, "", + "(Ljava/lang/String;)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); + jthrowable exception = check_exception(); + if (exception == NULL) { + _result = to_global_ref(_result); + } + return (JniResult){.value = {.l = _result}, .exception = check_exception()}; +} + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, + jclass clazz, + jlong port, + jobject result) { + attach_thread(); + Dart_CObject c_post; + c_post.type = Dart_CObject_kInt64; + c_post.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, result)); + Dart_PostCObject_DL(port, &c_post); +} + +// com.github.dart_lang.jni.PortContinuation +jclass _c_PortContinuation = NULL; + +jmethodID _m_PortContinuation__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult PortContinuation__ctor(int64_t j) { + attach_thread(); + load_class_global_ref(&_c_PortContinuation, + "com/github/dart_lang/jni/PortContinuation"); + if (_c_PortContinuation == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_PortContinuation, &_m_PortContinuation__ctor, "", + "(J)V"); + if (_m_PortContinuation__ctor == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_PortContinuation, + _m_PortContinuation__ctor, j); + jthrowable exception = check_exception(); + if (exception == NULL) { + _result = to_global_ref(_result); + } + return (JniResult){.value = {.l = _result}, .exception = check_exception()}; +} + +// com.github.dart_lang.jni.PortProxy +jclass _c_PortProxy = NULL; + +jmethodID _m_PortProxy__newInstance = NULL; +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr) { + attach_thread(); + load_class_global_ref(&_c_PortProxy, "com/github/dart_lang/jni/PortProxy"); + if (_c_PortProxy == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method(_c_PortProxy, &_m_PortProxy__newInstance, "newInstance", + "(Ljava/lang/String;JJJ)Ljava/lang/Object;"); + if (_m_PortProxy__newInstance == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallStaticObjectMethod( + jniEnv, _c_PortProxy, _m_PortProxy__newInstance, binaryName, port, + (jlong)Dart_CurrentIsolate_DL(), functionPtr); + return to_global_ref_result(_result); +} + +FFI_PLUGIN_EXPORT +void resultFor(CallbackResult* result, jobject object) { + acquire_lock(&result->lock); + result->ready = 1; + result->object = object; + signal_cond(&result->cond); + release_lock(&result->lock); +} + +void doNotFinalize(void* isolate_callback_data, void* peer) {} + +void finalizeLocal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteLocalRef(jniEnv, peer); +} + +void finalizeGlobal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteGlobalRef(jniEnv, peer); +} + +void finalizeWeakGlobal(void* isolate_callback_data, void* peer) { + attach_thread(); + (*jniEnv)->DeleteWeakGlobalRef(jniEnv, peer); +} + +void freeBoolean(void* isolate_callback_data, void* peer) { + free(peer); +} + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType) { + switch (refType) { + case JNIInvalidRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, doNotFinalize); + case JNILocalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeLocal); + case JNIGlobalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, finalizeGlobal); + case JNIWeakGlobalRefType: + return Dart_NewFinalizableHandle_DL(object, reference, 0, + finalizeWeakGlobal); + } +} + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference) { + return Dart_NewFinalizableHandle_DL(object, reference, 1, freeBoolean); +} + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object) { + return Dart_DeleteFinalizableHandle_DL(finalizableHandle, object); +} + +jclass _c_Object = NULL; +jclass _c_Long = NULL; + +jmethodID _m_Long_init = NULL; + +JNIEXPORT jobjectArray JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jclass clazz, + jlong port, + jlong isolateId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args) { + CallbackResult* result = (CallbackResult*)malloc(sizeof(CallbackResult)); + if (isolateId != (jlong)Dart_CurrentIsolate_DL()) { + init_lock(&result->lock); + init_cond(&result->cond); + acquire_lock(&result->lock); + result->ready = 0; + result->object = NULL; + + Dart_CObject c_result; + c_result.type = Dart_CObject_kInt64; + c_result.value.as_int64 = (jlong)result; + + Dart_CObject c_method; + c_method.type = Dart_CObject_kInt64; + c_method.value.as_int64 = + (jlong)((*env)->NewGlobalRef(env, methodDescriptor)); + + Dart_CObject c_args; + c_args.type = Dart_CObject_kInt64; + c_args.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, args)); + + Dart_CObject* c_post_arr[] = {&c_result, &c_method, &c_args}; + Dart_CObject c_post; + c_post.type = Dart_CObject_kArray; + c_post.value.as_array.values = c_post_arr; + c_post.value.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]); + + Dart_PostCObject_DL(port, &c_post); + + while (!result->ready) { + wait_for(&result->cond, &result->lock); + } + + release_lock(&result->lock); + destroy_lock(&result->lock); + destroy_cond(&result->cond); + } else { + result->object = ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( + port, (*env)->NewGlobalRef(env, methodDescriptor), + (*env)->NewGlobalRef(env, args)); + } + // Returning an array of length 2. + // [0]: The result pointer, used for cleaning up the global reference, and + // freeing the memory since we passed the ownership to Java. + // [1]: The returned object. + attach_thread(); + load_class_global_ref(&_c_Object, "java/lang/Object"); + load_class_global_ref(&_c_Long, "java/lang/Long"); + load_method(_c_Long, &_m_Long_init, "", "(J)V"); + jobject first = (*env)->NewObject(env, _c_Long, _m_Long_init, (jlong)result); + jobject second = result->object; + jobjectArray arr = (*env)->NewObjectArray(env, 2, _c_Object, NULL); + (*env)->SetObjectArrayElement(env, arr, 0, first); + (*env)->SetObjectArrayElement(env, arr, 1, second); + return arr; +} + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1cleanUp(JNIEnv* env, + jclass clazz, + jlong resultPtr) { + CallbackResult* result = (CallbackResult*)resultPtr; + (*env)->DeleteGlobalRef(env, result->object); + free(result); +} + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortCleaner_clean(JNIEnv* env, + jclass clazz, + jlong port) { + Dart_CObject close_signal; + close_signal.type = Dart_CObject_kNull; + Dart_PostCObject_DL(port, &close_signal); +} + +JNIEXPORT jobject JNICALL +Java_com_github_dart_1lang_jni_JniUtils_fromReferenceAddress(JNIEnv* env, + jclass clazz, + jlong id) { + attach_thread(); + return (jobject)(id); +} diff --git a/pkgs/jni/src/internal.h b/pkgs/jni/src/internal.h new file mode 100644 index 0000000000..9378080142 --- /dev/null +++ b/pkgs/jni/src/internal.h @@ -0,0 +1,36 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#pragma once + +#include "dartjni.h" +#include "include/dart_api_dl.h" + +FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); + +FFI_PLUGIN_EXPORT +JniResult DartException__ctor(jstring message); + +FFI_PLUGIN_EXPORT +JniResult PortContinuation__ctor(int64_t j); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); + +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, + jobject reference, + jobjectRefType refType); + +FFI_PLUGIN_EXPORT +Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, + bool* reference); + +FFI_PLUGIN_EXPORT +void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, + Dart_Handle object); diff --git a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/example/notification_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/notification_plugin/src/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); diff --git a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 57bb9812b0..418565e74d 100644 --- a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -4,8 +4,6 @@ #pragma once -#include "include/dart_api_dl.h" - // Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include #include @@ -375,31 +373,3 @@ static inline JniResult to_global_ref_result(jobject ref) { } return result; } - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newJObjectFinalizableHandle(Dart_Handle object, - jobject reference, - jobjectRefType refType); - -FFI_PLUGIN_EXPORT -Dart_FinalizableHandle newBooleanFinalizableHandle(Dart_Handle object, - bool* reference); - -FFI_PLUGIN_EXPORT -void deleteFinalizableHandle(Dart_FinalizableHandle finalizableHandle, - Dart_Handle object); From 10bd50c3e3a3c12340ebb5c984121cab14aae997 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 28 Mar 2024 16:40:35 +0100 Subject: [PATCH 05/10] Fix Windows, address comments --- pkgs/jni/CHANGELOG.md | 2 ++ pkgs/jni/lib/src/errors.dart | 19 ++++++------------- pkgs/jni/lib/src/jni.dart | 12 ++++++------ pkgs/jni/lib/src/jobject.dart | 4 ++++ pkgs/jni/src/dartjni.h | 8 ++++++++ pkgs/jni/src/internal.c | 3 ++- pkgs/jni/src/internal.h | 4 ++++ pkgs/jni/test/global_env_test.dart | 3 --- .../in_app_java/src/android_utils/dartjni.h | 8 ++++++++ pkgs/jnigen/example/kotlin_plugin/README.md | 2 -- .../kotlin_plugin/example/lib/main.dart | 1 - .../example/kotlin_plugin/src/dartjni.h | 8 ++++++++ .../example/notification_plugin/src/dartjni.h | 8 ++++++++ .../pdfbox_plugin/src/third_party/dartjni.h | 8 ++++++++ .../third_party/c_based/c_bindings/dartjni.h | 8 ++++++++ .../kotlin_test/c_based/c_bindings/dartjni.h | 8 ++++++++ .../c_based/c_bindings/dartjni.h | 8 ++++++++ .../test/test_util/bindings_test_setup.dart | 1 - pkgs/jnigen/tool/generate_runtime_tests.dart | 1 - 19 files changed, 88 insertions(+), 28 deletions(-) diff --git a/pkgs/jni/CHANGELOG.md b/pkgs/jni/CHANGELOG.md index 3eaee4c9c1..26fc554711 100644 --- a/pkgs/jni/CHANGELOG.md +++ b/pkgs/jni/CHANGELOG.md @@ -45,6 +45,8 @@ `fill` object and not its Java runtime type. - `JObject`s now check the types using `instanceof` in debug mode when using `castTo`. +- **Breaking Change**: `Jni.initDLApi()` is renamed to `Jni.ensureInitialized()` + and it is no longer needed to be called by users. - Added the ability to share `JObject`s across isolates. ## 0.7.3 diff --git a/pkgs/jni/lib/src/errors.dart b/pkgs/jni/lib/src/errors.dart index e95cf6db81..ca9c48e611 100644 --- a/pkgs/jni/lib/src/errors.dart +++ b/pkgs/jni/lib/src/errors.dart @@ -7,24 +7,17 @@ import 'package:jni/src/third_party/generated_bindings.dart'; // TODO(#567): Add the fact that [JException] is now a [JObject] to the // CHANGELOG. -final class UseAfterReleaseError extends Error { - @override - String toString() { - return 'Use after release error'; - } +final class UseAfterReleaseError extends StateError { + UseAfterReleaseError() : super('Use after release error'); } // TODO(#567): Use NullPointerError once it's available. -final class JNullError extends Error { - @override - String toString() => 'The reference was null'; +final class JNullError extends StateError { + JNullError() : super('The reference was null'); } -final class DoubleReleaseError extends Error { - @override - String toString() { - return 'Double release error'; - } +final class DoubleReleaseError extends StateError { + DoubleReleaseError() : super('Double release error'); } /// Represents JNI errors that might be returned by methods like diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index 0bb22eff19..8ea626ec29 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -65,7 +65,7 @@ abstract final class Jni { static bool _initialized = false; /// Initializes DartApiDL used for Continuations and interface implementation. - static void initDLApi() { + static void _ensureInitialized() { if (!_initialized) { assert(NativeApi.majorVersion == 2); assert(NativeApi.minorVersion >= 3); @@ -243,7 +243,7 @@ extension ProtectedJniExtensions on Jni { /// Returns a new PortContinuation. static JReference newPortContinuation(ReceivePort port) { - Jni.initDLApi(); + Jni._ensureInitialized(); return JGlobalReference( Jni._bindings .PortContinuation__ctor(port.sendPort.nativePort) @@ -259,7 +259,7 @@ extension ProtectedJniExtensions on Jni { NativeFunction< Pointer Function(Uint64, Pointer, Pointer)>> functionPtr) { - Jni.initDLApi(); + Jni._ensureInitialized(); return JGlobalReference(Jni._bindings .PortProxy__newInstance( Jni.env.toJStringPtr(binaryName), @@ -280,7 +280,7 @@ extension ProtectedJniExtensions on Jni { Pointer reference, int refType, ) { - Jni.initDLApi(); + Jni._ensureInitialized(); return Jni._bindings .newJObjectFinalizableHandle(object, reference, refType); } @@ -289,13 +289,13 @@ extension ProtectedJniExtensions on Jni { Object object, Pointer reference, ) { - Jni.initDLApi(); + Jni._ensureInitialized(); return Jni._bindings.newBooleanFinalizableHandle(object, reference); } static void deleteFinalizableHandle( Dart_FinalizableHandle finalizableHandle, Object object) { - Jni.initDLApi(); + Jni._ensureInitialized(); Jni._bindings.deleteFinalizableHandle(finalizableHandle, object); } } diff --git a/pkgs/jni/lib/src/jobject.dart b/pkgs/jni/lib/src/jobject.dart index d351b15631..df3e468c37 100644 --- a/pkgs/jni/lib/src/jobject.dart +++ b/pkgs/jni/lib/src/jobject.dart @@ -67,6 +67,10 @@ class JObject { bool get isNull => reference.isNull; + /// Releases the underlying [reference]. + /// + /// Releasing in one isolate while using or releasing in another isolate might + /// crash in the JNI layer. void release() { reference.release(); } diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jni/src/dartjni.h +++ b/pkgs/jni/src/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jni/src/internal.c b/pkgs/jni/src/internal.c index 53fb8985fe..fad71c46ca 100644 --- a/pkgs/jni/src/internal.c +++ b/pkgs/jni/src/internal.c @@ -117,7 +117,8 @@ void finalizeWeakGlobal(void* isolate_callback_data, void* peer) { } void freeBoolean(void* isolate_callback_data, void* peer) { - free(peer); + // To match the platform implementation of Dart's calloc. + free_mem(peer); } FFI_PLUGIN_EXPORT diff --git a/pkgs/jni/src/internal.h b/pkgs/jni/src/internal.h index 9378080142..8aea7426ef 100644 --- a/pkgs/jni/src/internal.h +++ b/pkgs/jni/src/internal.h @@ -2,6 +2,10 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// TODO(#660): Temporarily separated from dartjni.h to prevent unnecessary +// copies for based bindings. Will be merged with dartjni.h once the C-based +// bindings are removed. + #pragma once #include "dartjni.h" diff --git a/pkgs/jni/test/global_env_test.dart b/pkgs/jni/test/global_env_test.dart index 3fd5a776a4..dd398a6ba8 100644 --- a/pkgs/jni/test/global_env_test.dart +++ b/pkgs/jni/test/global_env_test.dart @@ -46,9 +46,6 @@ void run({required TestRunnerCallback testRunner}) { // // For examples of a higher level API, see `jni_object_tests.dart`. final env = Jni.env; - testRunner('initDLApi', () { - Jni.initDLApi(); - }); testRunner('get JNI Version', () { expect(Jni.env.GetVersion(), isNot(equals(0))); diff --git a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/example/kotlin_plugin/README.md b/pkgs/jnigen/example/kotlin_plugin/README.md index 2a915c81c8..fc2af80f5b 100644 --- a/pkgs/jnigen/example/kotlin_plugin/README.md +++ b/pkgs/jnigen/example/kotlin_plugin/README.md @@ -11,8 +11,6 @@ The `example/` app must be built at least once in _release_ mode (eg `flutter bu Note that `jnigen.yaml` of this example contains the option `suspend_fun_to_async: true`. This will generate `async` method bindings from Kotlin's `suspend fun`s. -For Kotlin coroutines to work, `Jni.initDLApi()` must be run first. - ## Creating a new Kotlin plugin Running `flutter create --template=plugin_ffi --platform=android kotlin_plugin` creates the skeleton of an Android plugin. diff --git a/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart b/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart index 50d14348bc..f10804c71b 100644 --- a/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart +++ b/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart @@ -3,7 +3,6 @@ import 'package:kotlin_plugin/kotlin_plugin.dart'; import 'package:jni/jni.dart'; void main() { - Jni.initDLApi(); runApp(const MyApp()); } diff --git a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/example/notification_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/notification_plugin/src/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 418565e74d..f834d1577b 100644 --- a/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/pkgs/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -79,6 +79,10 @@ static inline void destroy_cond(ConditionVariable* cond) { // Not available. } +static inline void free_mem(void* mem) { + CoTaskMemFree(mem); +} + #elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include @@ -118,6 +122,10 @@ static inline void destroy_cond(ConditionVariable* cond) { pthread_cond_destroy(cond); } +static inline void free_mem(void* mem) { + free(mem); +} + #else #error "No locking/condition variable support; Possibly unsupported platform" diff --git a/pkgs/jnigen/test/test_util/bindings_test_setup.dart b/pkgs/jnigen/test/test_util/bindings_test_setup.dart index c493ad1443..0c9d723d2f 100644 --- a/pkgs/jnigen/test/test_util/bindings_test_setup.dart +++ b/pkgs/jnigen/test/test_util/bindings_test_setup.dart @@ -69,7 +69,6 @@ Future bindingsTestSetup() async { kotlinTestJar, ]); } - Jni.initDLApi(); } void bindingsTestTeardown() { diff --git a/pkgs/jnigen/tool/generate_runtime_tests.dart b/pkgs/jnigen/tool/generate_runtime_tests.dart index 75a1ea2fb1..a95e414c16 100644 --- a/pkgs/jnigen/tool/generate_runtime_tests.dart +++ b/pkgs/jnigen/tool/generate_runtime_tests.dart @@ -117,7 +117,6 @@ void test(String description, TestCaseCallback testCase) { } void main() { - Jni.initDLApi(); $runStrings } '''; From 6c5025695eb7ce928b78124bd20207c665c3d873 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 2 Apr 2024 16:44:49 +0200 Subject: [PATCH 06/10] Address comments --- pkgs/jni/CHANGELOG.md | 11 +++++++-- pkgs/jni/lib/src/jreference.dart | 3 +++ pkgs/jni/test/isolate_test.dart | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 pkgs/jni/test/isolate_test.dart diff --git a/pkgs/jni/CHANGELOG.md b/pkgs/jni/CHANGELOG.md index 26fc554711..effdd657b2 100644 --- a/pkgs/jni/CHANGELOG.md +++ b/pkgs/jni/CHANGELOG.md @@ -45,9 +45,16 @@ `fill` object and not its Java runtime type. - `JObject`s now check the types using `instanceof` in debug mode when using `castTo`. -- **Breaking Change**: `Jni.initDLApi()` is renamed to `Jni.ensureInitialized()` - and it is no longer needed to be called by users. +- **Breaking Change**: `Jni.initDLApi()` is removed. - Added the ability to share `JObject`s across isolates. + ```dart + // This now works. + final foo = 'foo'.toJString(); + Isolate.run(() { + // `foo` is usable from another isolate. + print(foo); + }); + ``` ## 0.7.3 diff --git a/pkgs/jni/lib/src/jreference.dart b/pkgs/jni/lib/src/jreference.dart index 5694fbb329..6f7ddfe669 100644 --- a/pkgs/jni/lib/src/jreference.dart +++ b/pkgs/jni/lib/src/jreference.dart @@ -63,6 +63,9 @@ abstract final class JReference { /// Deletes the underlying JNI reference and marks this as released. /// + /// Releasing in one isolate while using or releasing in another isolate might + /// crash in the JNI layer. + /// /// Throws [DoubleReleaseError] if this is already released. /// /// Further uses of this object will throw [UseAfterReleaseError]. diff --git a/pkgs/jni/test/isolate_test.dart b/pkgs/jni/test/isolate_test.dart new file mode 100644 index 0000000000..bd1f70345f --- /dev/null +++ b/pkgs/jni/test/isolate_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'dart:isolate'; + +import 'package:jni/jni.dart'; +import 'package:test/test.dart'; + +import 'test_util/test_util.dart'; + +void main() { + // Don't forget to initialize JNI. + if (!Platform.isAndroid) { + checkDylibIsUpToDate(); + Jni.spawnIfNotExists(dylibDir: "build/jni_libs", jvmOptions: ["-Xmx128m"]); + } + run(testRunner: test); +} + +void run({required TestRunnerCallback testRunner}) { + testRunner("Sharing JObject across isolates", () async { + final foo = 'foo'.toJString(); + final result = await Isolate.run(() => foo.toDartString()); + expect(result, 'foo'); + }); + + testRunner("Creating an object on both two different isolates", () async { + // This also means that [Jni._ensureInitialized()] has been called in both + // isolates. + final foo = 'foo'.toJString(); + final bar = await Isolate.run(() { + return 'bar'.toJString(); + }); + expect(foo.toDartString(), 'foo'); + expect(bar.toDartString(), 'bar'); + }); +} From 9c7885290cc1d3de2842fd9a50180a4c59b8e640 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 2 Apr 2024 17:01:52 +0200 Subject: [PATCH 07/10] Fix CI --- pkgs/jni/test/isolate_test.dart | 11 +++++------ pkgs/jni/test/test_util/test_util.dart | 6 ++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/pkgs/jni/test/isolate_test.dart b/pkgs/jni/test/isolate_test.dart index bd1f70345f..0920b7c8c1 100644 --- a/pkgs/jni/test/isolate_test.dart +++ b/pkgs/jni/test/isolate_test.dart @@ -20,7 +20,8 @@ void main() { } void run({required TestRunnerCallback testRunner}) { - testRunner("Sharing JObject across isolates", () async { + testRunner("Sharing JObject across isolates", + skip: 'Not yet available on Dart stable', () async { final foo = 'foo'.toJString(); final result = await Isolate.run(() => foo.toDartString()); expect(result, 'foo'); @@ -29,11 +30,9 @@ void run({required TestRunnerCallback testRunner}) { testRunner("Creating an object on both two different isolates", () async { // This also means that [Jni._ensureInitialized()] has been called in both // isolates. - final foo = 'foo'.toJString(); - final bar = await Isolate.run(() { - return 'bar'.toJString(); + 'foo'.toJString(); + await Isolate.run(() { + 'bar'.toJString(); }); - expect(foo.toDartString(), 'foo'); - expect(bar.toDartString(), 'bar'); }); } diff --git a/pkgs/jni/test/test_util/test_util.dart b/pkgs/jni/test/test_util/test_util.dart index 07e711aa49..fcdafad2c1 100644 --- a/pkgs/jni/test/test_util/test_util.dart +++ b/pkgs/jni/test/test_util/test_util.dart @@ -7,10 +7,8 @@ import 'dart:io'; import 'package:jni/src/build_util/build_util.dart'; typedef TestCaseCallback = void Function(); -typedef TestRunnerCallback = void Function( - String description, - TestCaseCallback test, -); +typedef TestRunnerCallback = void + Function(String description, TestCaseCallback test, {Object? skip}); final currentDir = Directory.current.uri; final dllSuffix = From e73191778678b2c5ab2db34aea51f06ed919d531 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 2 Apr 2024 17:07:02 +0200 Subject: [PATCH 08/10] fix on device tests --- pkgs/jni/example/integration_test/on_device_jni_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/jni/example/integration_test/on_device_jni_test.dart b/pkgs/jni/example/integration_test/on_device_jni_test.dart index cdc7296691..0e0629bfa0 100644 --- a/pkgs/jni/example/integration_test/on_device_jni_test.dart +++ b/pkgs/jni/example/integration_test/on_device_jni_test.dart @@ -16,8 +16,10 @@ import '../../test/boxed_test.dart' as boxed_test; import '../../test/type_test.dart' as type_test; import '../../test/load_test.dart' as load_test; -void integrationTestRunner(String description, void Function() testCallback) { - testWidgets(description, (widgetTester) async => testCallback()); +void integrationTestRunner(String description, void Function() testCallback, + {Object? skip}) { + testWidgets(description, (widgetTester) async => testCallback(), + skip: skip != null && skip != false); } void main() { From aa9696b1935f3a0f0073c07a526beb915f689087 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 2 Apr 2024 18:23:09 +0200 Subject: [PATCH 09/10] more test fixes --- .../integration_test/on_device_jni_test.dart | 4 +++- pkgs/jni/lib/src/jni.dart | 4 +++- pkgs/jni/test/global_env_test.dart | 15 ++++++++------- pkgs/jni/test/isolate_test.dart | 9 +++++++-- pkgs/jni/test/jobject_test.dart | 15 +++++---------- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/pkgs/jni/example/integration_test/on_device_jni_test.dart b/pkgs/jni/example/integration_test/on_device_jni_test.dart index 0e0629bfa0..c8e5f5954a 100644 --- a/pkgs/jni/example/integration_test/on_device_jni_test.dart +++ b/pkgs/jni/example/integration_test/on_device_jni_test.dart @@ -15,10 +15,11 @@ import '../../test/jarray_test.dart' as jarray_test; import '../../test/boxed_test.dart' as boxed_test; import '../../test/type_test.dart' as type_test; import '../../test/load_test.dart' as load_test; +import '../../test/isolate_test.dart' as isolate_test; void integrationTestRunner(String description, void Function() testCallback, {Object? skip}) { - testWidgets(description, (widgetTester) async => testCallback(), + testWidgets(description, (_) async => testCallback(), skip: skip != null && skip != false); } @@ -35,6 +36,7 @@ void main() { boxed_test.run, type_test.run, load_test.run, + isolate_test.run, ]; for (var testSuite in testSuites) { testSuite(testRunner: integrationTestRunner); diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index 8ea626ec29..4322fbd579 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -59,7 +59,9 @@ abstract final class Jni { /// On flutter it's done by library. On dart standalone we don't /// know the library path.) static void setDylibDir({required String dylibDir}) { - _dylibDir = dylibDir; + if (!Platform.isAndroid) { + _dylibDir = dylibDir; + } } static bool _initialized = false; diff --git a/pkgs/jni/test/global_env_test.dart b/pkgs/jni/test/global_env_test.dart index dd398a6ba8..315225002d 100644 --- a/pkgs/jni/test/global_env_test.dart +++ b/pkgs/jni/test/global_env_test.dart @@ -166,31 +166,32 @@ void run({required TestRunnerCallback testRunner}) { })); testRunner( 'Env create reference methods should retain their default behavior', () { + final systemClass = using((arena) { + return env.FindClass("java/lang/System".toNativeChars(arena)); + }); final systemOut = using((arena) { - final systemClass = - env.FindClass("java/lang/System".toNativeChars(arena)); final outField = env.GetStaticFieldID( systemClass, "out".toNativeChars(arena), "Ljava/io/PrintStream;".toNativeChars(arena)); - env.DeleteGlobalRef(systemClass); return env.GetStaticObjectField(systemClass, outField); }); var refType = env.GetObjectRefType(systemOut); - expect(refType, equals(JObjectRefType.JNIGlobalRefType)); + expect(refType, JObjectRefType.JNIGlobalRefType); final localRef = env.NewLocalRef(systemOut); refType = env.GetObjectRefType(localRef); - expect(refType, equals(JObjectRefType.JNILocalRefType)); + expect(refType, JObjectRefType.JNILocalRefType); final weakRef = env.NewWeakGlobalRef(systemOut); refType = env.GetObjectRefType(weakRef); - expect(refType, equals(JObjectRefType.JNIWeakGlobalRefType)); + expect(refType, JObjectRefType.JNIWeakGlobalRefType); final globalRef = env.NewGlobalRef(localRef); refType = env.GetObjectRefType(globalRef); - expect(refType, equals(JObjectRefType.JNIGlobalRefType)); + expect(refType, JObjectRefType.JNIGlobalRefType); env.DeleteGlobalRef(globalRef); env.DeleteWeakGlobalRef(weakRef); env.DeleteLocalRef(localRef); env.DeleteGlobalRef(systemOut); + env.DeleteGlobalRef(systemClass); }); testRunner('long methods return long int without loss of precision', () { using((arena) { diff --git a/pkgs/jni/test/isolate_test.dart b/pkgs/jni/test/isolate_test.dart index 0920b7c8c1..47f901c6af 100644 --- a/pkgs/jni/test/isolate_test.dart +++ b/pkgs/jni/test/isolate_test.dart @@ -23,16 +23,21 @@ void run({required TestRunnerCallback testRunner}) { testRunner("Sharing JObject across isolates", skip: 'Not yet available on Dart stable', () async { final foo = 'foo'.toJString(); - final result = await Isolate.run(() => foo.toDartString()); + final result = await Isolate.run(() { + Jni.setDylibDir(dylibDir: 'build/jni_libs'); + return foo.toDartString(); + }); expect(result, 'foo'); }); - testRunner("Creating an object on both two different isolates", () async { + testRunner("Creating an object on two different isolates", () async { // This also means that [Jni._ensureInitialized()] has been called in both // isolates. 'foo'.toJString(); await Isolate.run(() { + Jni.setDylibDir(dylibDir: 'build/jni_libs'); 'bar'.toJString(); + Isolate.current.kill(); }); }); } diff --git a/pkgs/jni/test/jobject_test.dart b/pkgs/jni/test/jobject_test.dart index 6487f21685..720b7f3f39 100644 --- a/pkgs/jni/test/jobject_test.dart +++ b/pkgs/jni/test/jobject_test.dart @@ -80,7 +80,7 @@ void run({required TestRunnerCallback testRunner}) { final integerClass = JClass.forName("java/lang/Integer"); expect( () => integerClass.staticMethodId("parseInt", "(Ljava/lang/String;)I")( - integerClass, JString.type, [nullptr]), + integerClass, jint.type, [nullptr]), throwsException); integerClass.release(); }); @@ -231,8 +231,7 @@ void run({required TestRunnerCallback testRunner}) { }); testRunner("Isolate", () async { - final receivePort = ReceivePort(); - await Isolate.spawn((sendPort) { + final random = await Isolate.run(() { // On standalone target, make sure to call [setDylibDir] before accessing // any JNI function in a new isolate. // @@ -246,13 +245,9 @@ void run({required TestRunnerCallback testRunner}) { .call(random, jint.type, [256]); random.release(); randomClass.release(); - // A workaround for `--pause-isolates-on-exit`. Otherwise getting test - // with coverage pauses indefinitely here. - // https://github.com/dart-lang/coverage/issues/472 - sendPort.send(result); - Isolate.current.kill(); - }, receivePort.sendPort); - final random = await receivePort.first as int; + return result; + }); + expect(random, greaterThanOrEqualTo(0)); expect(random, lessThan(256)); }); From 847988a6aca3dcd124cf5e26a9681d7204b0a9ce Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 2 Apr 2024 19:54:09 +0200 Subject: [PATCH 10/10] even more test fixes --- pkgs/jni/test/isolate_test.dart | 13 ++++++++----- pkgs/jni/test/jobject_test.dart | 12 +++++++++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkgs/jni/test/isolate_test.dart b/pkgs/jni/test/isolate_test.dart index 47f901c6af..9b54f02ee1 100644 --- a/pkgs/jni/test/isolate_test.dart +++ b/pkgs/jni/test/isolate_test.dart @@ -23,10 +23,13 @@ void run({required TestRunnerCallback testRunner}) { testRunner("Sharing JObject across isolates", skip: 'Not yet available on Dart stable', () async { final foo = 'foo'.toJString(); - final result = await Isolate.run(() { + final port = ReceivePort(); + await Isolate.spawn((sendPort) { Jni.setDylibDir(dylibDir: 'build/jni_libs'); - return foo.toDartString(); - }); + sendPort.send(foo.toDartString()); + Isolate.current.kill(); + }, port.sendPort); + final result = await port.first; expect(result, 'foo'); }); @@ -34,10 +37,10 @@ void run({required TestRunnerCallback testRunner}) { // This also means that [Jni._ensureInitialized()] has been called in both // isolates. 'foo'.toJString(); - await Isolate.run(() { + await Isolate.spawn((_) { Jni.setDylibDir(dylibDir: 'build/jni_libs'); 'bar'.toJString(); Isolate.current.kill(); - }); + }, null); }); } diff --git a/pkgs/jni/test/jobject_test.dart b/pkgs/jni/test/jobject_test.dart index 720b7f3f39..1f1c419644 100644 --- a/pkgs/jni/test/jobject_test.dart +++ b/pkgs/jni/test/jobject_test.dart @@ -231,7 +231,8 @@ void run({required TestRunnerCallback testRunner}) { }); testRunner("Isolate", () async { - final random = await Isolate.run(() { + final receivePort = ReceivePort(); + await Isolate.spawn((sendPort) { // On standalone target, make sure to call [setDylibDir] before accessing // any JNI function in a new isolate. // @@ -245,8 +246,13 @@ void run({required TestRunnerCallback testRunner}) { .call(random, jint.type, [256]); random.release(); randomClass.release(); - return result; - }); + // A workaround for `--pause-isolates-on-exit`. Otherwise getting test + // with coverage pauses indefinitely here. + // https://github.com/dart-lang/coverage/issues/472 + sendPort.send(result); + Isolate.current.kill(); + }, receivePort.sendPort); + final random = await receivePort.first as int; expect(random, greaterThanOrEqualTo(0)); expect(random, lessThan(256));