diff --git a/pkgs/jni/CHANGELOG.md b/pkgs/jni/CHANGELOG.md index 650f98d78..effdd657b 100644 --- a/pkgs/jni/CHANGELOG.md +++ b/pkgs/jni/CHANGELOG.md @@ -45,6 +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 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/example/integration_test/on_device_jni_test.dart b/pkgs/jni/example/integration_test/on_device_jni_test.dart index cdc729669..c8e5f5954 100644 --- a/pkgs/jni/example/integration_test/on_device_jni_test.dart +++ b/pkgs/jni/example/integration_test/on_device_jni_test.dart @@ -15,9 +15,12 @@ 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) { - testWidgets(description, (widgetTester) async => testCallback()); +void integrationTestRunner(String description, void Function() testCallback, + {Object? skip}) { + testWidgets(description, (_) async => testCallback(), + skip: skip != null && skip != false); } void main() { @@ -33,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/ffigen.yaml b/pkgs/jni/ffigen.yaml index 782668e36..0914b40fc 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/lib/src/errors.dart b/pkgs/jni/lib/src/errors.dart index e95cf6db8..ca9c48e61 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 c9e742c68..4322fbd57 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart @@ -59,15 +59,22 @@ 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; + /// 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); + static void _ensureInitialized() { + 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 @@ -238,6 +245,7 @@ extension ProtectedJniExtensions on Jni { /// Returns a new PortContinuation. static JReference newPortContinuation(ReceivePort port) { + Jni._ensureInitialized(); return JGlobalReference( Jni._bindings .PortContinuation__ctor(port.sendPort.nativePort) @@ -253,6 +261,7 @@ extension ProtectedJniExtensions on Jni { NativeFunction< Pointer Function(Uint64, Pointer, Pointer)>> functionPtr) { + Jni._ensureInitialized(); return JGlobalReference(Jni._bindings .PortProxy__newInstance( Jni.env.toJStringPtr(binaryName), @@ -267,6 +276,30 @@ extension ProtectedJniExtensions on Jni { Pointer result, JObjectPtr object) async { Jni._bindings.resultFor(result, object); } + + static Dart_FinalizableHandle newJObjectFinalizableHandle( + Object object, + Pointer reference, + int refType, + ) { + Jni._ensureInitialized(); + return Jni._bindings + .newJObjectFinalizableHandle(object, reference, refType); + } + + static Dart_FinalizableHandle newBooleanFinalizableHandle( + Object object, + Pointer reference, + ) { + Jni._ensureInitialized(); + return Jni._bindings.newBooleanFinalizableHandle(object, reference); + } + + static void deleteFinalizableHandle( + Dart_FinalizableHandle finalizableHandle, Object object) { + Jni._ensureInitialized(); + Jni._bindings.deleteFinalizableHandle(finalizableHandle, object); + } } extension AdditionalEnvMethods on GlobalJniEnv { diff --git a/pkgs/jni/lib/src/jobject.dart b/pkgs/jni/lib/src/jobject.dart index 78a59b9be..df3e468c3 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'; @@ -66,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/lib/src/jreference.dart b/pkgs/jni/lib/src/jreference.dart index cad4193f7..6f7ddfe66 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,11 +12,7 @@ import 'jni.dart'; extension ProtectedJReference on JReference { void setAsReleased() { - if (_released) { - throw DoubleReleaseError(); - } - _released = true; - JGlobalReference._finalizer.detach(this); + _setAsReleased(); } void ensureNotNull() { @@ -28,16 +25,24 @@ extension ProtectedJReference on JReference { /// /// Detaches the finalizer so the underlying pointer will not be deleted. JObjectPtr toPointer() { - setAsReleased(); - return _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; + + _JFinalizable(this.pointer); +} + +@pragma('vm:deeply-immutable') abstract final class JReference { - final JObjectPtr _pointer; - bool _released = false; + final _JFinalizable _finalizable; - JReference(this._pointer); + JReference(this._finalizable); /// The underlying JNI reference. /// @@ -46,53 +51,87 @@ 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; + if (isReleased) throw UseAfterReleaseError(); + return _finalizable.pointer; } /// Whether the underlying JNI reference is deleted or not. - bool get isReleased => _released; + bool get isReleased; /// Whether the underlying JNI reference is `null` or not. bool get isNull; /// 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]. void release() { - setAsReleased(); + _setAsReleased(); _deleteReference(); } void _deleteReference(); + + void _setAsReleased(); } /// 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 { + /// 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(_pointer); + Jni.env.DeleteGlobalRef(_finalizable.pointer); } + + @override + bool get isReleased => _isReleased.value; } -final jNullReference = _JNullReference(); +final JReference jNullReference = _JNullReference(); +@pragma('vm:deeply-immutable') final class _JNullReference extends JReference { - _JNullReference() : super(nullptr); + _JNullReference() : super(_JFinalizable(nullptr)); + + @override + bool get isReleased => false; @override void _deleteReference() { @@ -100,7 +139,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 c42711963..a575c812f 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,60 @@ class JniBindings { late final _resultFor = _resultForPtr .asFunction, JObjectPtr)>(); + Dart_FinalizableHandle newJObjectFinalizableHandle( + Object object, + JObjectPtr reference, + int refType, + ) { + return _newJObjectFinalizableHandle( + object, + reference, + refType, + ); + } + + late final _newJObjectFinalizableHandlePtr = _lookup< + ffi.NativeFunction< + 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, + ) { + 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 +2057,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/CMakeLists.txt b/pkgs/jni/src/CMakeLists.txt index 2cdb3f724..685d52b71 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 382c9bc43..5b2ac0c30 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); } @@ -571,186 +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); -} - -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); -} diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index e1c79d292..f834d1577 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" @@ -373,18 +381,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); diff --git a/pkgs/jni/src/internal.c b/pkgs/jni/src/internal.c new file mode 100644 index 000000000..fad71c46c --- /dev/null +++ b/pkgs/jni/src/internal.c @@ -0,0 +1,248 @@ + +// 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) { + // To match the platform implementation of Dart's calloc. + free_mem(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 000000000..8aea7426e --- /dev/null +++ b/pkgs/jni/src/internal.h @@ -0,0 +1,40 @@ +// 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. + +// 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" +#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/jni/test/global_env_test.dart b/pkgs/jni/test/global_env_test.dart index 3fd5a776a..315225002 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))); @@ -169,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 new file mode 100644 index 000000000..9b54f02ee --- /dev/null +++ b/pkgs/jni/test/isolate_test.dart @@ -0,0 +1,46 @@ +// 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", + skip: 'Not yet available on Dart stable', () async { + final foo = 'foo'.toJString(); + final port = ReceivePort(); + await Isolate.spawn((sendPort) { + Jni.setDylibDir(dylibDir: 'build/jni_libs'); + sendPort.send(foo.toDartString()); + Isolate.current.kill(); + }, port.sendPort); + final result = await port.first; + expect(result, 'foo'); + }); + + 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.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 6487f2168..1f1c41964 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(); }); @@ -253,6 +253,7 @@ void run({required TestRunnerCallback testRunner}) { Isolate.current.kill(); }, receivePort.sendPort); final random = await receivePort.first as int; + expect(random, greaterThanOrEqualTo(0)); expect(random, lessThan(256)); }); diff --git a/pkgs/jni/test/load_test.dart b/pkgs/jni/test/load_test.dart index 78f26ecd9..78e151177 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); } diff --git a/pkgs/jni/test/test_util/test_util.dart b/pkgs/jni/test/test_util/test_util.dart index 07e711aa4..fcdafad2c 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 = 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); diff --git a/pkgs/jnigen/example/kotlin_plugin/README.md b/pkgs/jnigen/example/kotlin_plugin/README.md index 2a915c81c..fc2af80f5 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 50d14348b..f10804c71 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h index e1c79d292..f834d1577 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" @@ -373,18 +381,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); 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); 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 e1c79d292..f834d1577 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" @@ -373,18 +381,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); diff --git a/pkgs/jnigen/test/test_util/bindings_test_setup.dart b/pkgs/jnigen/test/test_util/bindings_test_setup.dart index c493ad144..0c9d723d2 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 75a1ea2fb..a95e414c1 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 } ''';