From 1af8e2c54df744933140ed92eb842a46d80e4c4a Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Wed, 14 Jun 2023 16:40:45 +0200 Subject: [PATCH 01/25] setup --- .../com/github/dart_lang/jni/PortProxy.java | 49 +++++ .../com/github/dart_lang/jni/PortProxy.java | 49 +++++ jni/src/dartjni.c | 42 ++++- jni/src/dartjni.h | 9 + .../in_app_java/lib/android_utils.dart | 22 +++ .../in_app_java/src/android_utils/dartjni.h | 9 + jnigen/example/kotlin_plugin/src/dartjni.h | 9 + .../example/notification_plugin/src/dartjni.h | 9 + .../pdfbox_plugin/src/third_party/dartjni.h | 9 + jnigen/lib/src/bindings/dart_generator.dart | 32 ++++ jnigen/lib/src/config/config_types.dart | 1 + jnigen/lib/src/elements/elements.dart | 2 + jnigen/lib/src/elements/elements.g.dart | 7 + .../third_party/c_based/c_bindings/dartjni.h | 9 + .../kotlin_test/c_based/c_bindings/dartjni.h | 9 + jnigen/test/package_resolver_test.dart | 8 +- .../c_based/c_bindings/dartjni.h | 9 + .../c_based/c_bindings/simple_package.c | 97 ++++++++++ .../c_based/dart_bindings/simple_package.dart | 177 ++++++++++++++++++ .../dart_bindings/simple_package.dart | 177 ++++++++++++++++++ jnigen/test/simple_package_test/generate.dart | 3 + .../jnigen/interfaces/MyInterface.java | 13 ++ .../interfaces/MyInterfaceConsumer.java | 12 ++ 23 files changed, 757 insertions(+), 6 deletions(-) create mode 100644 jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java create mode 100644 jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java create mode 100644 jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java create mode 100644 jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java new file mode 100644 index 00000000..1f7e8b18 --- /dev/null +++ b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -0,0 +1,49 @@ +// Copyright (c) 2023, 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. + +package com.github.dart_lang.jni; + +import java.lang.reflect.*; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class PortProxy implements InvocationHandler { + private final long port; + private final Map> map; + + private PortProxy(long port) { + this.port = port; + this.map = new ConcurrentHashMap<>(); + } + + private BlockingQueue queueOf(String key) { + if (!map.containsKey(key)) { + map.put(key, new ArrayBlockingQueue<>(1)); + } + return map.get(key); + } + + public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { + Class clazz = Class.forName(binaryName); + return Proxy.newProxyInstance( + clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String uuid = UUID.randomUUID().toString(); + _invoke(port, uuid, proxy, method, args); + return queueOf(uuid).poll(10, TimeUnit.SECONDS); + } + + public void resultFor(String uuid, Object object) { + queueOf(uuid).offer(object); + } + + private native void _invoke(long port, String uuid, Object proxy, Method method, Object[] args); +} diff --git a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java new file mode 100644 index 00000000..1f7e8b18 --- /dev/null +++ b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -0,0 +1,49 @@ +// Copyright (c) 2023, 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. + +package com.github.dart_lang.jni; + +import java.lang.reflect.*; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class PortProxy implements InvocationHandler { + private final long port; + private final Map> map; + + private PortProxy(long port) { + this.port = port; + this.map = new ConcurrentHashMap<>(); + } + + private BlockingQueue queueOf(String key) { + if (!map.containsKey(key)) { + map.put(key, new ArrayBlockingQueue<>(1)); + } + return map.get(key); + } + + public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { + Class clazz = Class.forName(binaryName); + return Proxy.newProxyInstance( + clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String uuid = UUID.randomUUID().toString(); + _invoke(port, uuid, proxy, method, args); + return queueOf(uuid).poll(10, TimeUnit.SECONDS); + } + + public void resultFor(String uuid, Object object) { + queueOf(uuid).offer(object); + } + + private native void _invoke(long port, String uuid, Object proxy, Method method, Object[] args); +} diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index 19a32ebe..006e18ee 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -574,10 +574,10 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, jlong port, jobject result) { - Dart_CObject dartPtr; - dartPtr.type = Dart_CObject_kInt64; - dartPtr.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, result)); - Dart_PostCObject_DL(port, &dartPtr); + 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 @@ -602,3 +602,37 @@ JniResult PortContinuation__ctor(int64_t j) { } return (JniResult){.value = {.l = _result}, .exception = check_exception()}; } + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args) { + Dart_CObject c_uuid; + c_uuid.type = Dart_CObject_kInt64; + c_uuid.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, uuid)); + + Dart_CObject c_proxy; + c_proxy.type = Dart_CObject_kInt64; + c_proxy.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, proxy)); + + Dart_CObject c_method; + c_method.type = Dart_CObject_kInt64; + c_method.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, method)); + + Dart_CObject c_args; + c_args.type = Dart_CObject_kArray; + c_args.as_array.values = args; + c_args.as_array.length = sizeof(args) / sizeof(jobject); + + Dart_CObject* c_post_arr[] = {&c_uuid, &c_proxy, &c_method, &c_args}; + Dart_CObject c_post; + c_post.type = Dart_CObject_kArray; + c_post.as_array.values = c_post_arr; + c_post.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]); + + Dart_PostCObject_DL(port, &c_post); +} \ No newline at end of file diff --git a/jni/src/dartjni.h b/jni/src/dartjni.h index c0713af5..75107bf9 100644 --- a/jni/src/dartjni.h +++ b/jni/src/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 028fa429..716cba84 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1547,6 +1547,14 @@ class EmojiCompat_GlyphChecker extends jni.JObject { return _hasGlyph(reference, charSequence.reference, start, end, sdkAdded) .boolean; } + + // Here will be an interface implementation method + static EmojiCompat_GlyphChecker implement( + bool Function(jni.JObject charSequence, int start, int end, int sdkAdded) + hasGlyph, + ) { + throw UnimplementedError(); + } } class $EmojiCompat_GlyphCheckerType @@ -1609,6 +1617,13 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { ) { return _load(reference, loaderCallback.reference).check(); } + + // Here will be an interface implementation method + static EmojiCompat_MetadataRepoLoader implement( + void Function(EmojiCompat_MetadataRepoLoaderCallback loaderCallback) load, + ) { + throw UnimplementedError(); + } } class $EmojiCompat_MetadataRepoLoaderType @@ -1837,6 +1852,13 @@ class EmojiCompat_SpanFactory extends jni.JObject { return const jni.JObjectType() .fromRef(_createSpan(reference, rasterizer.reference).object); } + + // Here will be an interface implementation method + static EmojiCompat_SpanFactory implement( + jni.JObject Function(jni.JObject rasterizer) createSpan, + ) { + throw UnimplementedError(); + } } class $EmojiCompat_SpanFactoryType diff --git a/jnigen/example/in_app_java/src/android_utils/dartjni.h b/jnigen/example/in_app_java/src/android_utils/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/example/kotlin_plugin/src/dartjni.h b/jnigen/example/kotlin_plugin/src/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/jnigen/example/kotlin_plugin/src/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/example/notification_plugin/src/dartjni.h b/jnigen/example/notification_plugin/src/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/example/notification_plugin/src/dartjni.h +++ b/jnigen/example/notification_plugin/src/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 2854e6be..6085f327 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -399,6 +399,21 @@ class $name$typeParamsDef extends $superName { method.accept(methodGenerator); } + // Interface implementation + if (node.declKind == DeclKind.interfaceKind) { + s.write(''' + // Here will be an interface implementation method + static $name$typeParamsCall implement$typeParamsDef( +'''); + final methodImplementCall = _MethodImplementCall(resolver, s); + for (final method in node.methods) { + method.accept(methodImplementCall); + } + s.write(''') { + throw UnimplementedError(); + }'''); + } + // End of Class definition s.writeln('}'); @@ -1367,3 +1382,20 @@ class _TypeVarLocator extends TypeVisitor>> { return {}; } } + +class _MethodImplementCall extends Visitor { + final Resolver resolver; + final StringSink s; + + _MethodImplementCall(this.resolver, this.s); + + @override + void visit(Method node) { + final returnType = node.returnType.accept(_TypeGenerator(resolver)); + final name = node.finalName; + final args = node.params.accept(_ParamDef(resolver)).join(', '); + s.write(''' + $returnType Function($args) $name, +'''); + } +} diff --git a/jnigen/lib/src/config/config_types.dart b/jnigen/lib/src/config/config_types.dart index c1d166bf..a6dbe900 100644 --- a/jnigen/lib/src/config/config_types.dart +++ b/jnigen/lib/src/config/config_types.dart @@ -415,6 +415,7 @@ class Config { ); } final classDecl = ClassDecl( + declKind: DeclKind.classKind, binaryName: binaryName, ) ..path = '$importPath/$filePath' diff --git a/jnigen/lib/src/elements/elements.dart b/jnigen/lib/src/elements/elements.dart index ceaee017..d49f38bb 100644 --- a/jnigen/lib/src/elements/elements.dart +++ b/jnigen/lib/src/elements/elements.dart @@ -57,6 +57,7 @@ class ClassDecl extends ClassMember implements Element { ClassDecl({ this.annotations = const [], this.javadoc, + required this.declKind, this.modifiers = const {}, required this.binaryName, this.parentName, @@ -77,6 +78,7 @@ class ClassDecl extends ClassMember implements Element { final List annotations; final KotlinClass? kotlinClass; final JavaDocComment? javadoc; + final DeclKind declKind; final String binaryName; final String? parentName; List typeParams; diff --git a/jnigen/lib/src/elements/elements.g.dart b/jnigen/lib/src/elements/elements.g.dart index 4531342e..a6a76630 100644 --- a/jnigen/lib/src/elements/elements.g.dart +++ b/jnigen/lib/src/elements/elements.g.dart @@ -14,6 +14,7 @@ ClassDecl _$ClassDeclFromJson(Map json) => ClassDecl( javadoc: json['javadoc'] == null ? null : JavaDocComment.fromJson(json['javadoc'] as Map), + declKind: $enumDecode(_$DeclKindEnumMap, json['declKind']), modifiers: (json['modifiers'] as List?) ?.map((e) => e as String) .toSet() ?? @@ -48,6 +49,12 @@ ClassDecl _$ClassDeclFromJson(Map json) => ClassDecl( : KotlinClass.fromJson(json['kotlinClass'] as Map), ); +const _$DeclKindEnumMap = { + DeclKind.classKind: 'CLASS', + DeclKind.interfaceKind: 'INTERFACE', + DeclKind.enumKind: 'ENUM', +}; + TypeUsage _$TypeUsageFromJson(Map json) => TypeUsage( shorthand: json['shorthand'] as String, kind: $enumDecode(_$KindEnumMap, json['kind']), diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/test/package_resolver_test.dart b/jnigen/test/package_resolver_test.dart index 8053b5aa..a29e0436 100644 --- a/jnigen/test/package_resolver_test.dart +++ b/jnigen/test/package_resolver_test.dart @@ -20,9 +20,11 @@ void main() async { final resolver = Resolver( importedClasses: { 'org.apache.pdfbox.pdmodel.PDDocument': ClassDecl( + declKind: DeclKind.classKind, binaryName: 'org.apache.pdfbox.pdmodel.PDDocument', )..path = 'package:pdfbox/pdfbox.dart', 'android.os.Process': ClassDecl( + declKind: DeclKind.classKind, binaryName: 'android.os.Process', )..path = 'package:android/os.dart', }, @@ -70,8 +72,10 @@ void main() async { test( 'resolve $binaryName', () => expect( - resolver - .resolvePrefix(ClassDecl(binaryName: binaryName)..path = ''), + resolver.resolvePrefix(ClassDecl( + declKind: DeclKind.classKind, + binaryName: binaryName, + )..path = ''), equals(testCase.expectedName))); } } diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index c0713af5..75107bf9 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -376,3 +376,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject result); FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, + jobject thiz, + jlong port, + jstring uuid, + jobject proxy, + jobject method, + jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c index 6c0f3e24..e9dfb1d0 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c +++ b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c @@ -2310,6 +2310,103 @@ JniResult StringValuedMap__ctor() { return to_global_ref_result(_result); } +// com.github.dart_lang.jnigen.interfaces.MyInterface +jclass _c_MyInterface = NULL; + +jmethodID _m_MyInterface__voidCallback = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterface__voidCallback(jobject self_, jobject s) { + load_env(); + load_class_global_ref(&_c_MyInterface, + "com/github/dart_lang/jnigen/interfaces/MyInterface"); + if (_c_MyInterface == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_MyInterface, &_m_MyInterface__voidCallback, "voidCallback", + "(Ljava/lang/String;)V"); + if (_m_MyInterface__voidCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_MyInterface__voidCallback, s); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_MyInterface__stringCallback = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterface__stringCallback(jobject self_, jobject s) { + load_env(); + load_class_global_ref(&_c_MyInterface, + "com/github/dart_lang/jnigen/interfaces/MyInterface"); + if (_c_MyInterface == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_MyInterface, &_m_MyInterface__stringCallback, "stringCallback", + "(Ljava/lang/String;)Ljava/lang/String;"); + if (_m_MyInterface__stringCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_MyInterface__stringCallback, s); + return to_global_ref_result(_result); +} + +jmethodID _m_MyInterface__varCallback = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterface__varCallback(jobject self_, jobject t) { + load_env(); + load_class_global_ref(&_c_MyInterface, + "com/github/dart_lang/jnigen/interfaces/MyInterface"); + if (_c_MyInterface == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_MyInterface, &_m_MyInterface__varCallback, "varCallback", + "(Ljava/lang/Object;)Ljava/lang/Object;"); + if (_m_MyInterface__varCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, + _m_MyInterface__varCallback, t); + return to_global_ref_result(_result); +} + +// com.github.dart_lang.jnigen.interfaces.MyInterfaceConsumer +jclass _c_MyInterfaceConsumer = NULL; + +jmethodID _m_MyInterfaceConsumer__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterfaceConsumer__ctor() { + load_env(); + load_class_global_ref( + &_c_MyInterfaceConsumer, + "com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer"); + if (_c_MyInterfaceConsumer == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_MyInterfaceConsumer, &_m_MyInterfaceConsumer__ctor, "", + "()V"); + if (_m_MyInterfaceConsumer__ctor == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_MyInterfaceConsumer, + _m_MyInterfaceConsumer__ctor); + return to_global_ref_result(_result); +} + +jmethodID _m_MyInterfaceConsumer__consumeMyInterface = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterfaceConsumer__consumeMyInterface(jobject myInterface, + jobject s) { + load_env(); + load_class_global_ref( + &_c_MyInterfaceConsumer, + "com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer"); + if (_c_MyInterfaceConsumer == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method(_c_MyInterfaceConsumer, + &_m_MyInterfaceConsumer__consumeMyInterface, + "consumeMyInterface", + "(Lcom/github/dart_lang/jnigen/interfaces/" + "MyInterface;Ljava/lang/String;)V"); + if (_m_MyInterfaceConsumer__consumeMyInterface == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallStaticVoidMethod(jniEnv, _c_MyInterfaceConsumer, + _m_MyInterfaceConsumer__consumeMyInterface, + myInterface, s); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + // com.github.dart_lang.jnigen.annotations.JsonSerializable$Case jclass _c_JsonSerializable_Case = NULL; diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index d0ad47c7..97bd4df5 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -2827,6 +2827,183 @@ class $StringValuedMapType<$K extends jni.JObject> } } +/// from: com.github.dart_lang.jnigen.interfaces.MyInterface +class MyInterface<$T extends jni.JObject> extends jni.JObject { + @override + late final jni.JObjType> $type = type(T); + + final jni.JObjType<$T> T; + + MyInterface.fromRef( + this.T, + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static $MyInterfaceType<$T> type<$T extends jni.JObject>( + jni.JObjType<$T> T, + ) { + return $MyInterfaceType( + T, + ); + } + + static final _voidCallback = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("MyInterface__voidCallback") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract void voidCallback(java.lang.String s) + void voidCallback( + jni.JString s, + ) { + return _voidCallback(reference, s.reference).check(); + } + + static final _stringCallback = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("MyInterface__stringCallback") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract java.lang.String stringCallback(java.lang.String s) + /// The returned object must be deleted after use, by calling the `delete` method. + jni.JString stringCallback( + jni.JString s, + ) { + return const jni.JStringType() + .fromRef(_stringCallback(reference, s.reference).object); + } + + static final _varCallback = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("MyInterface__varCallback") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract T varCallback(T t) + /// The returned object must be deleted after use, by calling the `delete` method. + $T varCallback( + $T t, + ) { + return T.fromRef(_varCallback(reference, t.reference).object); + } + + // Here will be an interface implementation method + static MyInterface<$T> implement<$T extends jni.JObject>( + void Function(jni.JString s) voidCallback, + jni.JString Function(jni.JString s) stringCallback, + $T Function($T t) varCallback, + ) { + throw UnimplementedError(); + } +} + +class $MyInterfaceType<$T extends jni.JObject> + extends jni.JObjType> { + final jni.JObjType<$T> T; + + const $MyInterfaceType( + this.T, + ); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/interfaces/MyInterface;"; + + @override + MyInterface<$T> fromRef(jni.JObjectPtr ref) => MyInterface.fromRef(T, ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => Object.hash($MyInterfaceType, T); + + @override + bool operator ==(Object other) { + return other.runtimeType == ($MyInterfaceType<$T>) && + other is $MyInterfaceType<$T> && + T == other.T; + } +} + +/// from: com.github.dart_lang.jnigen.interfaces.MyInterfaceConsumer +class MyInterfaceConsumer extends jni.JObject { + @override + late final jni.JObjType $type = type; + + MyInterfaceConsumer.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $MyInterfaceConsumerType(); + static final _ctor = jniLookup>( + "MyInterfaceConsumer__ctor") + .asFunction(); + + /// from: public void () + /// The returned object must be deleted after use, by calling the `delete` method. + factory MyInterfaceConsumer() { + return MyInterfaceConsumer.fromRef(_ctor().object); + } + + static final _consumeMyInterface = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "MyInterfaceConsumer__consumeMyInterface") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: static public void consumeMyInterface(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeMyInterface( + MyInterface myInterface, + jni.JString s, + ) { + return _consumeMyInterface(myInterface.reference, s.reference).check(); + } +} + +class $MyInterfaceConsumerType extends jni.JObjType { + const $MyInterfaceConsumerType(); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer;"; + + @override + MyInterfaceConsumer fromRef(jni.JObjectPtr ref) => + MyInterfaceConsumer.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($MyInterfaceConsumerType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($MyInterfaceConsumerType) && + other is $MyInterfaceConsumerType; + } +} + /// from: com.github.dart_lang.jnigen.annotations.JsonSerializable$Case class JsonSerializable_Case extends jni.JObject { @override diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 3394d3d9..194072c5 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2674,6 +2674,183 @@ class $StringValuedMapType<$K extends jni.JObject> } } +/// from: com.github.dart_lang.jnigen.interfaces.MyInterface +class MyInterface<$T extends jni.JObject> extends jni.JObject { + @override + late final jni.JObjType> $type = type(T); + + final jni.JObjType<$T> T; + + MyInterface.fromRef( + this.T, + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + static final _class = + jni.Jni.findJClass(r"com/github/dart_lang/jnigen/interfaces/MyInterface"); + + /// The type which includes information such as the signature of this class. + static $MyInterfaceType<$T> type<$T extends jni.JObject>( + jni.JObjType<$T> T, + ) { + return $MyInterfaceType( + T, + ); + } + + static final _id_voidCallback = jni.Jni.accessors.getMethodIDOf( + _class.reference, r"voidCallback", r"(Ljava/lang/String;)V"); + + /// from: public abstract void voidCallback(java.lang.String s) + void voidCallback( + jni.JString s, + ) { + return jni.Jni.accessors.callMethodWithArgs(reference, _id_voidCallback, + jni.JniCallType.voidType, [s.reference]).check(); + } + + static final _id_stringCallback = jni.Jni.accessors.getMethodIDOf( + _class.reference, + r"stringCallback", + r"(Ljava/lang/String;)Ljava/lang/String;"); + + /// from: public abstract java.lang.String stringCallback(java.lang.String s) + /// The returned object must be deleted after use, by calling the `delete` method. + jni.JString stringCallback( + jni.JString s, + ) { + return const jni.JStringType().fromRef(jni.Jni.accessors.callMethodWithArgs( + reference, + _id_stringCallback, + jni.JniCallType.objectType, + [s.reference]).object); + } + + static final _id_varCallback = jni.Jni.accessors.getMethodIDOf( + _class.reference, + r"varCallback", + r"(Ljava/lang/Object;)Ljava/lang/Object;"); + + /// from: public abstract T varCallback(T t) + /// The returned object must be deleted after use, by calling the `delete` method. + $T varCallback( + $T t, + ) { + return T.fromRef(jni.Jni.accessors.callMethodWithArgs(reference, + _id_varCallback, jni.JniCallType.objectType, [t.reference]).object); + } + + // Here will be an interface implementation method + static MyInterface<$T> implement<$T extends jni.JObject>( + void Function(jni.JString s) voidCallback, + jni.JString Function(jni.JString s) stringCallback, + $T Function($T t) varCallback, + ) { + throw UnimplementedError(); + } +} + +class $MyInterfaceType<$T extends jni.JObject> + extends jni.JObjType> { + final jni.JObjType<$T> T; + + const $MyInterfaceType( + this.T, + ); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/interfaces/MyInterface;"; + + @override + MyInterface<$T> fromRef(jni.JObjectPtr ref) => MyInterface.fromRef(T, ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => Object.hash($MyInterfaceType, T); + + @override + bool operator ==(Object other) { + return other.runtimeType == ($MyInterfaceType<$T>) && + other is $MyInterfaceType<$T> && + T == other.T; + } +} + +/// from: com.github.dart_lang.jnigen.interfaces.MyInterfaceConsumer +class MyInterfaceConsumer extends jni.JObject { + @override + late final jni.JObjType $type = type; + + MyInterfaceConsumer.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + static final _class = jni.Jni.findJClass( + r"com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer"); + + /// The type which includes information such as the signature of this class. + static const type = $MyInterfaceConsumerType(); + static final _id_ctor = + jni.Jni.accessors.getMethodIDOf(_class.reference, r"", r"()V"); + + /// from: public void () + /// The returned object must be deleted after use, by calling the `delete` method. + factory MyInterfaceConsumer() { + return MyInterfaceConsumer.fromRef(jni.Jni.accessors + .newObjectWithArgs(_class.reference, _id_ctor, []).object); + } + + static final _id_consumeMyInterface = jni.Jni.accessors.getStaticMethodIDOf( + _class.reference, + r"consumeMyInterface", + r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;)V"); + + /// from: static public void consumeMyInterface(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeMyInterface( + MyInterface myInterface, + jni.JString s, + ) { + return jni.Jni.accessors.callStaticMethodWithArgs( + _class.reference, + _id_consumeMyInterface, + jni.JniCallType.voidType, + [myInterface.reference, s.reference]).check(); + } +} + +class $MyInterfaceConsumerType extends jni.JObjType { + const $MyInterfaceConsumerType(); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer;"; + + @override + MyInterfaceConsumer fromRef(jni.JObjectPtr ref) => + MyInterfaceConsumer.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($MyInterfaceConsumerType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($MyInterfaceConsumerType) && + other is $MyInterfaceConsumerType; + } +} + /// from: com.github.dart_lang.jnigen.annotations.JsonSerializable$Case class JsonSerializable_Case extends jni.JObject { @override diff --git a/jnigen/test/simple_package_test/generate.dart b/jnigen/test/simple_package_test/generate.dart index 18c38e0d..c156830c 100644 --- a/jnigen/test/simple_package_test/generate.dart +++ b/jnigen/test/simple_package_test/generate.dart @@ -31,6 +31,8 @@ var javaFiles = [ join(javaPrefix, 'generics', 'StringStack.java'), join(javaPrefix, 'generics', 'StringValuedMap.java'), join(javaPrefix, 'generics', 'StringKeyedMap.java'), + join(javaPrefix, 'interfaces', 'MyInterface.java'), + join(javaPrefix, 'interfaces', 'MyInterfaceConsumer.java'), join(javaPrefix, 'annotations', 'JsonSerializable.java'), join(javaPrefix, 'annotations', 'MyDataClass.java'), ]; @@ -57,6 +59,7 @@ Config getConfig([BindingsType bindingsType = BindingsType.cBased]) { 'com.github.dart_lang.jnigen.simple_package', 'com.github.dart_lang.jnigen.pkg2', 'com.github.dart_lang.jnigen.generics', + 'com.github.dart_lang.jnigen.interfaces', 'com.github.dart_lang.jnigen.annotations', ], logLevel: Level.INFO, diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java new file mode 100644 index 00000000..d73b040f --- /dev/null +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java @@ -0,0 +1,13 @@ +// Copyright (c) 2023, 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. + +package com.github.dart_lang.jnigen.interfaces; + +public interface MyInterface { + void voidCallback(String s); + + String stringCallback(String s); + + T varCallback(T t); +} diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java new file mode 100644 index 00000000..8deb7aed --- /dev/null +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java @@ -0,0 +1,12 @@ +// Copyright (c) 2023, 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. + +package com.github.dart_lang.jnigen.interfaces; + +public class MyInterfaceConsumer { + public static void consumeMyInterface(MyInterface myInterface, String s) { + String result = myInterface.stringCallback(s); + myInterface.voidCallback(result); + } +} From 3ff33fba9715feefc101b97a1bf92737c2d55737 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 27 Jun 2023 19:05:23 +0200 Subject: [PATCH 02/25] add C methods + bindings for PortProxy --- jni/lib/src/jni.dart | 28 ++++++-- .../third_party/jni_bindings_generated.dart | 65 +++++++++++++++++++ jni/src/dartjni.c | 35 ++++++++++ jni/src/dartjni.h | 6 ++ .../in_app_java/src/android_utils/dartjni.h | 6 ++ 5 files changed, 135 insertions(+), 5 deletions(-) diff --git a/jni/lib/src/jni.dart b/jni/lib/src/jni.dart index 0698b84d..a2d55b04 100644 --- a/jni/lib/src/jni.dart +++ b/jni/lib/src/jni.dart @@ -193,11 +193,6 @@ abstract class Jni { static final accessors = JniAccessors(_bindings.GetAccessors()); - /// Returns a new PortContinuation. - static JObjectPtr newPortContinuation(ReceivePort port) { - return _bindings.PortContinuation__ctor(port.sendPort.nativePort).object; - } - /// Returns current application context on Android. static JObjectPtr getCachedApplicationContext() { return _bindings.GetApplicationContext(); @@ -301,6 +296,29 @@ extension ProtectedJniExtensions on Jni { final lookup = dl.lookup; return lookup; } + + /// Returns a new PortContinuation. + static JObjectPtr newPortContinuation(ReceivePort port) { + return Jni._bindings + .PortContinuation__ctor(port.sendPort.nativePort) + .object; + } + + /// Returns a new PortProxy for a class with the given [binaryName]. + static JObjectPtr newPortProxy(String binaryName, ReceivePort port) { + return Jni._bindings + .PortProxy__newInstance( + Jni.env.toJStringPtr(binaryName), + port.sendPort.nativePort, + ) + .object; + } + + /// Return the result of a callback specified by the [uuid]. + static void returnResultFor( + JObjectPtr proxy, JStringPtr uuid, JObjectPtr result) { + Jni._bindings.PortProxy__resultFor(proxy, uuid, result).check(); + } } extension AdditionalEnvMethods on GlobalJniEnv { diff --git a/jni/lib/src/third_party/jni_bindings_generated.dart b/jni/lib/src/third_party/jni_bindings_generated.dart index 7cb2bdb8..85d038be 100644 --- a/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/jni/lib/src/third_party/jni_bindings_generated.dart @@ -192,6 +192,71 @@ class JniBindings { late final _PortContinuation__ctor = _PortContinuation__ctorPtr.asFunction(); + JniResult PortProxy__newInstance( + JObjectPtr binaryName, + int port, + ) { + return _PortProxy__newInstance( + binaryName, + port, + ); + } + + late final _PortProxy__newInstancePtr = + _lookup>( + 'PortProxy__newInstance'); + late final _PortProxy__newInstance = _PortProxy__newInstancePtr.asFunction< + JniResult Function(JObjectPtr, int)>(); + + JniResult PortProxy__resultFor( + JObjectPtr self_, + JObjectPtr uuid, + JObjectPtr object, + ) { + return _PortProxy__resultFor( + self_, + uuid, + object, + ); + } + + late final _PortProxy__resultForPtr = _lookup< + ffi.NativeFunction< + JniResult Function( + JObjectPtr, JObjectPtr, JObjectPtr)>>('PortProxy__resultFor'); + late final _PortProxy__resultFor = _PortProxy__resultForPtr.asFunction< + JniResult Function(JObjectPtr, JObjectPtr, JObjectPtr)>(); + + void Java_com_github_dart_1lang_jni_PortProxy__1invoke( + ffi.Pointer env, + JObjectPtr thiz, + int port, + JStringPtr uuid, + JObjectPtr proxy, + JObjectPtr method, + JObjectArrayPtr args, + ) { + return _Java_com_github_dart_1lang_jni_PortProxy__1invoke( + env, + thiz, + port, + uuid, + proxy, + method, + args, + ); + } + + late final _Java_com_github_dart_1lang_jni_PortProxy__1invokePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, JObjectPtr, JLongMarker, + JStringPtr, JObjectPtr, JObjectPtr, JObjectArrayPtr)>>( + 'Java_com_github_dart_1lang_jni_PortProxy__1invoke'); + late final _Java_com_github_dart_1lang_jni_PortProxy__1invoke = + _Java_com_github_dart_1lang_jni_PortProxy__1invokePtr.asFunction< + void Function(ffi.Pointer, JObjectPtr, int, JStringPtr, + JObjectPtr, JObjectPtr, JObjectArrayPtr)>(); + ffi.Pointer GetGlobalEnv() { return _GetGlobalEnv(); } diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index 006e18ee..817b3c15 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -603,6 +603,41 @@ JniResult PortContinuation__ctor(int64_t j) { 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) { + load_env(); + 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;J)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); + return to_global_ref_result(_result); +} + +jmethodID _m_PortProxy__resultFor = NULL; +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object) { + load_env(); + 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_method(_c_PortProxy, &_m_PortProxy__resultFor, "resultFor", + "(Ljava/lang/String;Ljava/lang/Object;)V"); + if (_m_PortProxy__resultFor == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_PortProxy__resultFor, uuid, + object); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jni/src/dartjni.h b/jni/src/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jni/src/dartjni.h +++ b/jni/src/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/example/in_app_java/src/android_utils/dartjni.h b/jnigen/example/in_app_java/src/android_utils/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, From d0e6b1170e0bdd434fafff369fa3b3da399c6e67 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Wed, 28 Jun 2023 11:20:14 +0200 Subject: [PATCH 03/25] move newPortCont. to ProtectedJniExt. --- jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart | 3 ++- jnigen/example/kotlin_plugin/src/dartjni.h | 6 ++++++ jnigen/example/notification_plugin/src/dartjni.h | 6 ++++++ jnigen/example/pdfbox_plugin/src/third_party/dartjni.h | 6 ++++++ jnigen/lib/src/bindings/dart_generator.dart | 5 +++-- .../third_party/c_based/c_bindings/dartjni.h | 6 ++++++ jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h | 6 ++++++ jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart | 6 ++++-- jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart | 6 ++++-- .../test/simple_package_test/c_based/c_bindings/dartjni.h | 6 ++++++ 10 files changed, 49 insertions(+), 7 deletions(-) diff --git a/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart b/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart index 78a7ded8..b0c6ab97 100644 --- a/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart +++ b/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart @@ -57,7 +57,8 @@ class Example extends jni.JObject { /// The returned object must be deleted after use, by calling the `delete` method. Future thinkBeforeAnswering() async { final $p = ReceivePort(); - final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p)); + final $c = + jni.JObject.fromRef(ProtectedJniExtensions.newPortContinuation($p)); _thinkBeforeAnswering(reference, $c.reference).object; final $o = jni.JObjectPtr.fromAddress(await $p.first); final $k = const jni.JStringType().getClass().reference; diff --git a/jnigen/example/kotlin_plugin/src/dartjni.h b/jnigen/example/kotlin_plugin/src/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/jnigen/example/kotlin_plugin/src/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/example/notification_plugin/src/dartjni.h b/jnigen/example/notification_plugin/src/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/example/notification_plugin/src/dartjni.h +++ b/jnigen/example/notification_plugin/src/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 6085f327..a793a9f0 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -33,6 +33,7 @@ const _voidPointer = '$_ffi.Pointer<$_ffi.Void>'; const _typeParamPrefix = '\$'; // Misc. +const _protectedExtension = 'ProtectedJniExtensions'; const _classRef = '_class.reference'; const _env = '$_jni.Jni.env'; const _accessors = '$_jni.Jni.accessors'; @@ -128,7 +129,7 @@ class DartGenerator extends Visitor> { // Auto-generated initialization code. final $_ffi.Pointer Function(String sym) $_lookup = - ProtectedJniExtensions.initGeneratedLibrary("${config.outputConfig.cConfig!.libraryName}"); + $_protectedExtension.initGeneratedLibrary("${config.outputConfig.cConfig!.libraryName}"); '''; @@ -1125,7 +1126,7 @@ class _MethodGenerator extends Visitor { s.write('''async { $typeInference final \$p = ReceivePort(); - final \$c = $_jObject.fromRef($_jni.Jni.newPortContinuation(\$p)); + final \$c = $_jObject.fromRef($_protectedExtension.newPortContinuation(\$p)); $callExpr; final \$o = $_jPointer.fromAddress(await \$p.first); final \$k = $returnTypeClass.getClass().reference; diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, diff --git a/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart b/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart index b8e33c26..4c465c55 100644 --- a/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart +++ b/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart @@ -61,7 +61,8 @@ class SuspendFun extends jni.JObject { /// The returned object must be deleted after use, by calling the `delete` method. Future sayHello() async { final $p = ReceivePort(); - final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p)); + final $c = + jni.JObject.fromRef(ProtectedJniExtensions.newPortContinuation($p)); _sayHello(reference, $c.reference).object; final $o = jni.JObjectPtr.fromAddress(await $p.first); final $k = const jni.JStringType().getClass().reference; @@ -87,7 +88,8 @@ class SuspendFun extends jni.JObject { jni.JString string, ) async { final $p = ReceivePort(); - final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p)); + final $c = + jni.JObject.fromRef(ProtectedJniExtensions.newPortContinuation($p)); _sayHello1(reference, string.reference, $c.reference).object; final $o = jni.JObjectPtr.fromAddress(await $p.first); final $k = const jni.JStringType().getClass().reference; diff --git a/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart b/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart index a0510088..c9f757c6 100644 --- a/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart +++ b/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart @@ -54,7 +54,8 @@ class SuspendFun extends jni.JObject { /// The returned object must be deleted after use, by calling the `delete` method. Future sayHello() async { final $p = ReceivePort(); - final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p)); + final $c = + jni.JObject.fromRef(ProtectedJniExtensions.newPortContinuation($p)); jni.Jni.accessors.callMethodWithArgs(reference, _id_sayHello, jni.JniCallType.objectType, [$c.reference]).object; final $o = jni.JObjectPtr.fromAddress(await $p.first); @@ -76,7 +77,8 @@ class SuspendFun extends jni.JObject { jni.JString string, ) async { final $p = ReceivePort(); - final $c = jni.JObject.fromRef(jni.Jni.newPortContinuation($p)); + final $c = + jni.JObject.fromRef(ProtectedJniExtensions.newPortContinuation($p)); jni.Jni.accessors.callMethodWithArgs(reference, _id_sayHello1, jni.JniCallType.objectType, [string.reference, $c.reference]).object; final $o = jni.JObjectPtr.fromAddress(await $p.first); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 75107bf9..3a4a0429 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -377,6 +377,12 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, int64_t port); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, From 73f2baf574f801a77d78e3cdaa6382eb0dc32c6d Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Wed, 28 Jun 2023 20:00:33 +0200 Subject: [PATCH 04/25] attach finalizable to close the port for interfaces - generate descriptor in doclet backend as well --- .../com/github/dart_lang/jni/PortProxy.java | 44 +++++++++++++- .../com/github/dart_lang/jni/PortProxy.java | 45 +++++++++++++- jni/lib/src/lang/reflect/jmethod.dart | 1 + jni/lib/src/method_invocation.dart | 22 +++++++ jni/src/dartjni.c | 26 ++++---- jni/src/dartjni.h | 2 +- .../in_app_java/lib/android_utils.dart | 60 ++++++++++++++++++- .../in_app_java/src/android_utils/dartjni.h | 2 +- jnigen/example/kotlin_plugin/src/dartjni.h | 2 +- .../example/notification_plugin/src/dartjni.h | 2 +- .../pdfbox_plugin/src/third_party/dartjni.h | 2 +- jnigen/java/pom.xml | 5 ++ .../jnigen/apisummarizer/doclet/AstEnv.java | 45 +++++++++++++- .../apisummarizer/doclet/ElementBuilders.java | 10 ++++ .../doclet/SummarizerDoclet.java | 1 + jnigen/lib/src/bindings/dart_generator.dart | 31 +++++++++- jnigen/lib/src/bindings/kotlin_processor.dart | 2 +- jnigen/lib/src/elements/elements.dart | 4 +- jnigen/lib/src/elements/elements.g.dart | 2 +- .../third_party/c_based/c_bindings/dartjni.h | 2 +- .../kotlin_test/c_based/c_bindings/dartjni.h | 2 +- .../c_based/c_bindings/dartjni.h | 2 +- .../c_based/dart_bindings/simple_package.dart | 26 +++++++- .../dart_bindings/simple_package.dart | 26 +++++++- 24 files changed, 323 insertions(+), 43 deletions(-) create mode 100644 jni/lib/src/lang/reflect/jmethod.dart create mode 100644 jni/lib/src/method_invocation.dart diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java index 1f7e8b18..5f608b21 100644 --- a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -28,6 +28,45 @@ private BlockingQueue queueOf(String key) { return map.get(key); } + private static String getDescriptor(Method method) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append(method.getName()).append("("); + Class[] parameterTypes = method.getParameterTypes(); + for (Class paramType : parameterTypes) { + appendType(descriptor, paramType); + } + descriptor.append(")"); + appendType(descriptor, method.getReturnType()); + return descriptor.toString(); + } + + private static void appendType(StringBuilder descriptor, Class type) { + if (type == void.class) { + descriptor.append("V"); + } else if (type == boolean.class) { + descriptor.append("Z"); + } else if (type == byte.class) { + descriptor.append("B"); + } else if (type == char.class) { + descriptor.append("C"); + } else if (type == short.class) { + descriptor.append("S"); + } else if (type == int.class) { + descriptor.append("I"); + } else if (type == long.class) { + descriptor.append("J"); + } else if (type == float.class) { + descriptor.append("F"); + } else if (type == double.class) { + descriptor.append("D"); + } else if (type.isArray()) { + descriptor.append('['); + appendType(descriptor, type.getComponentType()); + } else { + descriptor.append("L").append(type.getName().replace('.', '/')).append(";"); + } + } + public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { Class clazz = Class.forName(binaryName); return Proxy.newProxyInstance( @@ -37,7 +76,7 @@ public static Object newInstance(String binaryName, long port) throws ClassNotFo @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String uuid = UUID.randomUUID().toString(); - _invoke(port, uuid, proxy, method, args); + _invoke(port, uuid, proxy, getDescriptor(method), args); return queueOf(uuid).poll(10, TimeUnit.SECONDS); } @@ -45,5 +84,6 @@ public void resultFor(String uuid, Object object) { queueOf(uuid).offer(object); } - private native void _invoke(long port, String uuid, Object proxy, Method method, Object[] args); + private native void _invoke( + long port, String uuid, Object proxy, String methodDescriptor, Object[] args); } diff --git a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java index 1f7e8b18..28804a52 100644 --- a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -28,16 +28,55 @@ private BlockingQueue queueOf(String key) { return map.get(key); } + private static String getDescriptor(Method method) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append(method.getName()).append("("); + Class[] parameterTypes = method.getParameterTypes(); + for (Class paramType : parameterTypes) { + appendType(descriptor, paramType); + } + descriptor.append(")"); + appendType(descriptor, method.getReturnType()); + return descriptor.toString(); + } + + private static void appendType(StringBuilder descriptor, Class type) { + if (type == void.class) { + descriptor.append("V"); + } else if (type == boolean.class) { + descriptor.append("Z"); + } else if (type == byte.class) { + descriptor.append("B"); + } else if (type == char.class) { + descriptor.append("C"); + } else if (type == short.class) { + descriptor.append("S"); + } else if (type == int.class) { + descriptor.append("I"); + } else if (type == long.class) { + descriptor.append("J"); + } else if (type == float.class) { + descriptor.append("F"); + } else if (type == double.class) { + descriptor.append("D"); + } else if (type.isArray()) { + descriptor.append('['); + appendType(descriptor, type.getComponentType()); + } else { + descriptor.append("L").append(type.getName().replace('.', '/')).append(";"); + } + } + public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { Class clazz = Class.forName(binaryName); return Proxy.newProxyInstance( - clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); + clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String uuid = UUID.randomUUID().toString(); - _invoke(port, uuid, proxy, method, args); + _invoke(port, uuid, proxy, getDescriptor(method), args); return queueOf(uuid).poll(10, TimeUnit.SECONDS); } @@ -45,5 +84,5 @@ public void resultFor(String uuid, Object object) { queueOf(uuid).offer(object); } - private native void _invoke(long port, String uuid, Object proxy, Method method, Object[] args); + private native void _invoke(long port, String uuid, Object proxy, String methodDescriptor, Object[] args); } diff --git a/jni/lib/src/lang/reflect/jmethod.dart b/jni/lib/src/lang/reflect/jmethod.dart new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/jni/lib/src/lang/reflect/jmethod.dart @@ -0,0 +1 @@ + diff --git a/jni/lib/src/method_invocation.dart b/jni/lib/src/method_invocation.dart new file mode 100644 index 00000000..06679433 --- /dev/null +++ b/jni/lib/src/method_invocation.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2023, 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 'lang/jstring.dart'; +import 'jarray.dart'; +import 'jobject.dart'; + +class MethodInvocation { + final JString uuid; + final JString methodDescriptor; + final JArray args; + + MethodInvocation._(this.uuid, this.methodDescriptor, this.args); + + factory MethodInvocation.fromMessage(List message) { + final uuid = JString.fromRef(message[0]); + final methodDescriptor = JString.fromRef(message[1]); + final args = JArray.fromRef(const JObjectType(), message[2]); + return MethodInvocation._(uuid, methodDescriptor, args); + } +} diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index 817b3c15..42188b04 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -574,6 +574,7 @@ Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, 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)); @@ -586,6 +587,7 @@ 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) @@ -609,7 +611,7 @@ jclass _c_PortProxy = NULL; jmethodID _m_PortProxy__newInstance = NULL; FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port) { - load_env(); + 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()}; @@ -625,7 +627,7 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t port) { jmethodID _m_PortProxy__resultFor = NULL; FFI_PLUGIN_EXPORT JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object) { - load_env(); + 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()}; @@ -644,30 +646,26 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args) { + attach_thread(); Dart_CObject c_uuid; c_uuid.type = Dart_CObject_kInt64; c_uuid.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, uuid)); - Dart_CObject c_proxy; - c_proxy.type = Dart_CObject_kInt64; - c_proxy.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, proxy)); - Dart_CObject c_method; c_method.type = Dart_CObject_kInt64; - c_method.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, method)); + c_method.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, methodDescriptor)); Dart_CObject c_args; - c_args.type = Dart_CObject_kArray; - c_args.as_array.values = args; - c_args.as_array.length = sizeof(args) / sizeof(jobject); + c_args.type = Dart_CObject_kInt64; + c_args.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, args)); - Dart_CObject* c_post_arr[] = {&c_uuid, &c_proxy, &c_method, &c_args}; + Dart_CObject* c_post_arr[] = {&c_uuid, &c_method, &c_args}; Dart_CObject c_post; c_post.type = Dart_CObject_kArray; - c_post.as_array.values = c_post_arr; - c_post.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]); + 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); } \ No newline at end of file diff --git a/jni/src/dartjni.h b/jni/src/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jni/src/dartjni.h +++ b/jni/src/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 716cba84..7ad34bf9 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1549,10 +1549,28 @@ class EmojiCompat_GlyphChecker extends jni.JObject { } // Here will be an interface implementation method - static EmojiCompat_GlyphChecker implement( + ReceivePort? _$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory EmojiCompat_GlyphChecker.implement( bool Function(jni.JObject charSequence, int start, int end, int sdkAdded) hasGlyph, ) { + final $p = ReceivePort(); + final $x = EmojiCompat_GlyphChecker.fromRef( + ProtectedJniExtensions.newPortProxy( + r"androidx.emoji2.text.EmojiCompat$GlyphChecker", $p), + ).._$p = $p; + _finalizer.attach($x, $p, detach: $x); throw UnimplementedError(); } } @@ -1619,9 +1637,27 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { } // Here will be an interface implementation method - static EmojiCompat_MetadataRepoLoader implement( + ReceivePort? _$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory EmojiCompat_MetadataRepoLoader.implement( void Function(EmojiCompat_MetadataRepoLoaderCallback loaderCallback) load, ) { + final $p = ReceivePort(); + final $x = EmojiCompat_MetadataRepoLoader.fromRef( + ProtectedJniExtensions.newPortProxy( + r"androidx.emoji2.text.EmojiCompat$MetadataRepoLoader", $p), + ).._$p = $p; + _finalizer.attach($x, $p, detach: $x); throw UnimplementedError(); } } @@ -1854,9 +1890,27 @@ class EmojiCompat_SpanFactory extends jni.JObject { } // Here will be an interface implementation method - static EmojiCompat_SpanFactory implement( + ReceivePort? _$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory EmojiCompat_SpanFactory.implement( jni.JObject Function(jni.JObject rasterizer) createSpan, ) { + final $p = ReceivePort(); + final $x = EmojiCompat_SpanFactory.fromRef( + ProtectedJniExtensions.newPortProxy( + r"androidx.emoji2.text.EmojiCompat$SpanFactory", $p), + ).._$p = $p; + _finalizer.attach($x, $p, detach: $x); throw UnimplementedError(); } } diff --git a/jnigen/example/in_app_java/src/android_utils/dartjni.h b/jnigen/example/in_app_java/src/android_utils/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/example/kotlin_plugin/src/dartjni.h b/jnigen/example/kotlin_plugin/src/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/jnigen/example/kotlin_plugin/src/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/example/notification_plugin/src/dartjni.h b/jnigen/example/notification_plugin/src/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/example/notification_plugin/src/dartjni.h +++ b/jnigen/example/notification_plugin/src/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/java/pom.xml b/jnigen/java/pom.xml index 226bb03f..30d17bf7 100644 --- a/jnigen/java/pom.xml +++ b/jnigen/java/pom.xml @@ -38,6 +38,11 @@ kotlinx-metadata-jvm 0.6.2 + + io.soabase.asm-mirror-descriptor + asm-mirror-descriptor + 1 + junit junit diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java index 71e31c4c..45f6b440 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java @@ -5,12 +5,20 @@ package com.github.dart_lang.jnigen.apisummarizer.doclet; import com.sun.source.util.DocTrees; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.SourceVersion; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import jdk.javadoc.doclet.DocletEnvironment; +import java.util.Locale; +import java.util.Map; + /** Class to hold utility classes initialized from DocletEnvironment. */ -public class AstEnv { +public class AstEnv implements ProcessingEnvironment { public final Types types; public final Elements elements; public final DocTrees trees; @@ -24,4 +32,39 @@ public AstEnv(Types types, Elements elements, DocTrees trees) { public static AstEnv fromEnvironment(DocletEnvironment env) { return new AstEnv(env.getTypeUtils(), env.getElementUtils(), env.getDocTrees()); } + + @Override + public Map getOptions() { + return null; + } + + @Override + public Messager getMessager() { + return null; + } + + @Override + public Filer getFiler() { + return null; + } + + @Override + public Elements getElementUtils() { + return elements; + } + + @Override + public Types getTypeUtils() { + return types; + } + + @Override + public SourceVersion getSourceVersion() { + return null; + } + + @Override + public Locale getLocale() { + return null; + } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java index d0f2301c..f6f423ed 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java @@ -7,6 +7,9 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.*; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; import com.sun.source.doctree.DocCommentTree; +import io.soabase.asm.mirror.MirrorMethodReader; +import io.soabase.asm.mirror.SignatureMirrorType; + import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; @@ -195,6 +198,13 @@ public TypeUsage typeUsage(TypeMirror type) { public Method method(ExecutableElement e) { var m = new Method(); m.name = e.getSimpleName().toString(); + var methodReader = new MirrorMethodReader(env); + methodReader.readMethod( + (access, name, desc, signature, exceptions) -> { + m.descriptor = desc; + return null; + }, + e); m.modifiers = e.getModifiers().stream().map(Modifier::toString).collect(Collectors.toSet()); m.typeParams = e.getTypeParameters().stream().map(this::typeParam).collect(Collectors.toList()); m.returnType = typeUsage(e.getReturnType()); diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java index abbc7cc6..e8fffc88 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java @@ -13,6 +13,7 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.ElementScanner9; + import jdk.javadoc.doclet.Doclet; import jdk.javadoc.doclet.DocletEnvironment; import jdk.javadoc.doclet.Reporter; diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index a793a9f0..26158d77 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -404,13 +404,40 @@ class $name$typeParamsDef extends $superName { if (node.declKind == DeclKind.interfaceKind) { s.write(''' // Here will be an interface implementation method - static $name$typeParamsCall implement$typeParamsDef( + ReceivePort? _\$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _\$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory $name.implement( '''); + final typeClassesDef = _encloseIfNotEmpty( + '{\n', + typeParams + .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,') + .join(_newLine(depth: 1)), + '}\n', + ); + final typeClassesCall = + typeParams.map((typeParam) => '$typeParam,').join(_newLine(depth: 2)); final methodImplementCall = _MethodImplementCall(resolver, s); for (final method in node.methods) { method.accept(methodImplementCall); } - s.write(''') { + s.write('''$typeClassesDef) { + final \$p = ReceivePort(); + final \$x = $name.fromRef( + $typeClassesCall + $_protectedExtension.newPortProxy(r"${node.binaryName}", \$p), + ).._\$p = \$p; + _finalizer.attach(\$x, \$p, detach: \$x); throw UnimplementedError(); }'''); } diff --git a/jnigen/lib/src/bindings/kotlin_processor.dart b/jnigen/lib/src/bindings/kotlin_processor.dart index 4a48aa8c..900470d8 100644 --- a/jnigen/lib/src/bindings/kotlin_processor.dart +++ b/jnigen/lib/src/bindings/kotlin_processor.dart @@ -31,7 +31,7 @@ class _KotlinClassProcessor extends Visitor { functions[signature] = function; } for (final method in node.methods) { - final signature = method.name + method.descriptor!; + final signature = method.name + method.descriptor; if (functions.containsKey(signature)) { method.accept(_KotlinMethodProcessor(functions[signature]!)); } diff --git a/jnigen/lib/src/elements/elements.dart b/jnigen/lib/src/elements/elements.dart index d49f38bb..bc33c18e 100644 --- a/jnigen/lib/src/elements/elements.dart +++ b/jnigen/lib/src/elements/elements.dart @@ -448,7 +448,7 @@ class Method extends ClassMember implements Element { this.javadoc, this.modifiers = const {}, required this.name, - this.descriptor, + required this.descriptor, this.typeParams = const [], this.params = const [], required this.returnType, @@ -468,7 +468,7 @@ class Method extends ClassMember implements Element { /// Can be used to match with [KotlinFunction]'s descriptor. /// /// Can create a unique signature in combination with [name]. - final String? descriptor; + final String descriptor; /// The [ClassDecl] where this method is defined. /// diff --git a/jnigen/lib/src/elements/elements.g.dart b/jnigen/lib/src/elements/elements.g.dart index a6a76630..3aef2076 100644 --- a/jnigen/lib/src/elements/elements.g.dart +++ b/jnigen/lib/src/elements/elements.g.dart @@ -107,7 +107,7 @@ Method _$MethodFromJson(Map json) => Method( .toSet() ?? const {}, name: json['name'] as String, - descriptor: json['descriptor'] as String?, + descriptor: json['descriptor'] as String, typeParams: (json['typeParams'] as List?) ?.map((e) => TypeParam.fromJson(e as Map)) .toList() ?? diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 3a4a0429..9b7d8b83 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -389,5 +389,5 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jlong port, jstring uuid, jobject proxy, - jobject method, + jobject methodDescriptor, jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index 97bd4df5..500e6586 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -2897,11 +2897,31 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { } // Here will be an interface implementation method - static MyInterface<$T> implement<$T extends jni.JObject>( + ReceivePort? _$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory MyInterface.implement( void Function(jni.JString s) voidCallback, jni.JString Function(jni.JString s) stringCallback, - $T Function($T t) varCallback, - ) { + $T Function($T t) varCallback, { + required jni.JObjType<$T> T, + }) { + final $p = ReceivePort(); + final $x = MyInterface.fromRef( + T, + ProtectedJniExtensions.newPortProxy( + r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), + ).._$p = $p; + _finalizer.attach($x, $p, detach: $x); throw UnimplementedError(); } } diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 194072c5..eada063c 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2741,11 +2741,31 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { } // Here will be an interface implementation method - static MyInterface<$T> implement<$T extends jni.JObject>( + ReceivePort? _$p; + + static final Finalizer _finalizer = + Finalizer((port) => port.close()); + + @override + void delete() { + _$p?.close(); + _finalizer.detach(this); + super.delete(); + } + + factory MyInterface.implement( void Function(jni.JString s) voidCallback, jni.JString Function(jni.JString s) stringCallback, - $T Function($T t) varCallback, - ) { + $T Function($T t) varCallback, { + required jni.JObjType<$T> T, + }) { + final $p = ReceivePort(); + final $x = MyInterface.fromRef( + T, + ProtectedJniExtensions.newPortProxy( + r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), + ).._$p = $p; + _finalizer.attach($x, $p, detach: $x); throw UnimplementedError(); } } From b13b77e42fd94d2c801e4b63861caa0f6ec914d8 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Mon, 3 Jul 2023 14:58:05 +0200 Subject: [PATCH 05/25] add missing JCharacter boxed type, .implement method completed for interfaces --- jni/lib/internal_helpers_for_jnigen.dart | 3 +- jni/lib/jni_symbols.yaml | 4 + jni/lib/src/lang/jcharacter.dart | 64 +++++++++ jni/lib/src/lang/jnumber.dart | 12 +- jni/lib/src/lang/lang.dart | 1 + jni/test/boxed_test.dart | 15 ++ .../in_app_java/lib/android_utils.dart | 93 +++++++++++-- jnigen/lib/src/bindings/dart_generator.dart | 129 +++++++++++++++--- jnigen/lib/src/elements/elements.dart | 29 ++-- .../c_based/c_bindings/simple_package.c | 21 +++ .../c_based/dart_bindings/simple_package.dart | 95 ++++++++++++- .../dart_bindings/simple_package.dart | 90 +++++++++++- .../jnigen/interfaces/MyInterface.java | 2 + 13 files changed, 492 insertions(+), 66 deletions(-) create mode 100644 jni/lib/src/lang/jcharacter.dart diff --git a/jni/lib/internal_helpers_for_jnigen.dart b/jni/lib/internal_helpers_for_jnigen.dart index 7e1fc8e7..257b0c25 100644 --- a/jni/lib/internal_helpers_for_jnigen.dart +++ b/jni/lib/internal_helpers_for_jnigen.dart @@ -6,6 +6,7 @@ /// not to be used directly. library internal_helpers_for_jnigen; +export 'src/accessors.dart'; export 'src/jni.dart' show ProtectedJniExtensions; export 'src/jreference.dart'; -export 'src/accessors.dart'; +export 'src/method_invocation.dart'; diff --git a/jni/lib/jni_symbols.yaml b/jni/lib/jni_symbols.yaml index 8b1f9ec8..5159dc9c 100644 --- a/jni/lib/jni_symbols.yaml +++ b/jni/lib/jni_symbols.yaml @@ -41,6 +41,10 @@ files: name: JBoolean type_class: JBooleanType super_count: 1 + 'java.lang.Character': + name: JCharacter + type_class: JCharacterType + super_count: 1 'java.util.Set': name: JSet type_class: JSetType diff --git a/jni/lib/src/lang/jcharacter.dart b/jni/lib/src/lang/jcharacter.dart new file mode 100644 index 00000000..6ab76cf7 --- /dev/null +++ b/jni/lib/src/lang/jcharacter.dart @@ -0,0 +1,64 @@ +import '../accessors.dart'; +import '../jni.dart'; +import '../jobject.dart'; +import '../jvalues.dart'; +import '../third_party/generated_bindings.dart'; +import '../types.dart'; + +class JCharacterType extends JObjType { + const JCharacterType(); + + @override + String get signature => r"Ljava/lang/Character;"; + + @override + JCharacter fromRef(JObjectPtr ref) => JCharacter.fromRef(ref); + + @override + JObjType get superType => const JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => (JCharacterType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == (JCharacterType) && other is JCharacterType; + } +} + +class JCharacter extends JObject { + @override + // ignore: overridden_fields + late final JObjType $type = type; + + JCharacter.fromRef( + JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = JCharacterType(); + + static final _class = Jni.findJClass(r"java/lang/Character"); + + static final _ctorId = + Jni.accessors.getMethodIDOf(_class.reference, r"", r"(C)V"); + + JCharacter(int c) + : super.fromRef(Jni.accessors.newObjectWithArgs( + _class.reference, _ctorId, [JValueChar(c)]).object); + + static final _charValueId = + Jni.accessors.getMethodIDOf(_class.reference, r"charValue", r"()C"); + + int charValue({bool deleteOriginal = false}) { + final ret = Jni.accessors.callMethodWithArgs( + reference, _charValueId, JniCallType.charType, []).char; + if (deleteOriginal) { + delete(); + } + return ret; + } +} diff --git a/jni/lib/src/lang/jnumber.dart b/jni/lib/src/lang/jnumber.dart index 8461f108..cab30332 100644 --- a/jni/lib/src/lang/jnumber.dart +++ b/jni/lib/src/lang/jnumber.dart @@ -7,13 +7,14 @@ import '../jni.dart'; import '../jobject.dart'; import '../third_party/generated_bindings.dart'; import '../types.dart'; -import 'jinteger.dart'; -import 'jshort.dart'; -import 'jlong.dart'; +import 'jboolean.dart'; +import 'jbyte.dart'; +import 'jcharacter.dart'; import 'jdouble.dart'; import 'jfloat.dart'; -import 'jbyte.dart'; -import 'jboolean.dart'; +import 'jinteger.dart'; +import 'jlong.dart'; +import 'jshort.dart'; class JNumberType extends JObjType { const JNumberType(); @@ -137,6 +138,7 @@ extension IntToJava on int { JShort toJShort() => JShort(this); JInteger toJInteger() => JInteger(this); JLong toJLong() => JLong(this); + JCharacter toJCharacter() => JCharacter(this); } extension DoubleToJava on double { diff --git a/jni/lib/src/lang/lang.dart b/jni/lib/src/lang/lang.dart index e4c1af72..786488c0 100644 --- a/jni/lib/src/lang/lang.dart +++ b/jni/lib/src/lang/lang.dart @@ -4,6 +4,7 @@ export 'jboolean.dart'; export 'jbyte.dart'; +export 'jcharacter.dart'; export 'jdouble.dart'; export 'jfloat.dart'; export 'jinteger.dart'; diff --git a/jni/test/boxed_test.dart b/jni/test/boxed_test.dart index 43489e88..88d8686d 100644 --- a/jni/test/boxed_test.dart +++ b/jni/test/boxed_test.dart @@ -26,6 +26,13 @@ void run({required TestRunnerCallback testRunner}) { expect((-val).toJByte().byteValue(deleteOriginal: true), -val); }); }); + testRunner('JCharacter', () { + const val = 1 << 5; + using((arena) { + expect(JCharacter(val).charValue(deleteOriginal: true), val); + expect(JCharacter(0).charValue(deleteOriginal: true), 0); + }); + }); testRunner('JShort', () { const val = 1 << 10; using((arena) { @@ -79,6 +86,14 @@ void run({required TestRunnerCallback testRunner}) { expect(a.$type.hashCode, b.$type.hashCode); }); }); + testRunner('JCharacter.\$type hashCode and ==', () { + using((arena) { + final a = JCharacter(1)..deletedIn(arena); + final b = JCharacter(2)..deletedIn(arena); + expect(a.$type, b.$type); + expect(a.$type.hashCode, b.$type.hashCode); + }); + }); testRunner('JShort.\$type hashCode and ==', () { using((arena) { final a = JShort(1)..deletedIn(arena); diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 7ad34bf9..dd1a6231 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1548,7 +1548,6 @@ class EmojiCompat_GlyphChecker extends jni.JObject { .boolean; } - // Here will be an interface implementation method ReceivePort? _$p; static final Finalizer _finalizer = @@ -1561,17 +1560,44 @@ class EmojiCompat_GlyphChecker extends jni.JObject { super.delete(); } - factory EmojiCompat_GlyphChecker.implement( - bool Function(jni.JObject charSequence, int start, int end, int sdkAdded) + factory EmojiCompat_GlyphChecker.implement({ + required bool Function( + jni.JObject charSequence, int start, int end, int sdkAdded) hasGlyph, - ) { + }) { final $p = ReceivePort(); final $x = EmojiCompat_GlyphChecker.fromRef( ProtectedJniExtensions.newPortProxy( r"androidx.emoji2.text.EmojiCompat$GlyphChecker", $p), ).._$p = $p; _finalizer.attach($x, $p, detach: $x); - throw UnimplementedError(); + $p.listen(($m) { + final $i = MethodInvocation.fromMessage($m); + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $u = $i.uuid; + final $a = $i.args; + if ($d == r"hasGlyph(Ljava/lang/CharSequence;III)Z") { + final $r = hasGlyph( + $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), + $a[1] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.toJBoolean().reference, + ); + return; + } + }); + return $x; } } @@ -1636,7 +1662,6 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { return _load(reference, loaderCallback.reference).check(); } - // Here will be an interface implementation method ReceivePort? _$p; static final Finalizer _finalizer = @@ -1649,16 +1674,37 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { super.delete(); } - factory EmojiCompat_MetadataRepoLoader.implement( - void Function(EmojiCompat_MetadataRepoLoaderCallback loaderCallback) load, - ) { + factory EmojiCompat_MetadataRepoLoader.implement({ + required void Function( + EmojiCompat_MetadataRepoLoaderCallback loaderCallback) + load, + }) { final $p = ReceivePort(); final $x = EmojiCompat_MetadataRepoLoader.fromRef( ProtectedJniExtensions.newPortProxy( r"androidx.emoji2.text.EmojiCompat$MetadataRepoLoader", $p), ).._$p = $p; _finalizer.attach($x, $p, detach: $x); - throw UnimplementedError(); + $p.listen(($m) { + final $i = MethodInvocation.fromMessage($m); + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $u = $i.uuid; + final $a = $i.args; + if ($d == + r"load(Landroidx/emoji2/text/EmojiCompat/MetadataRepoLoaderCallback;)V") { + load( + $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), + deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + jni.nullptr, + ); + return; + } + }); + return $x; } } @@ -1889,7 +1935,6 @@ class EmojiCompat_SpanFactory extends jni.JObject { .fromRef(_createSpan(reference, rasterizer.reference).object); } - // Here will be an interface implementation method ReceivePort? _$p; static final Finalizer _finalizer = @@ -1902,16 +1947,34 @@ class EmojiCompat_SpanFactory extends jni.JObject { super.delete(); } - factory EmojiCompat_SpanFactory.implement( - jni.JObject Function(jni.JObject rasterizer) createSpan, - ) { + factory EmojiCompat_SpanFactory.implement({ + required jni.JObject Function(jni.JObject rasterizer) createSpan, + }) { final $p = ReceivePort(); final $x = EmojiCompat_SpanFactory.fromRef( ProtectedJniExtensions.newPortProxy( r"androidx.emoji2.text.EmojiCompat$SpanFactory", $p), ).._$p = $p; _finalizer.attach($x, $p, detach: $x); - throw UnimplementedError(); + $p.listen(($m) { + final $i = MethodInvocation.fromMessage($m); + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $u = $i.uuid; + final $a = $i.args; + if ($d == + r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;") { + final $r = createSpan( + $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.reference, + ); + return; + } + }); + return $x; } } diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 26158d77..8b3f36a7 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -403,7 +403,6 @@ class $name$typeParamsDef extends $superName { // Interface implementation if (node.declKind == DeclKind.interfaceKind) { s.write(''' - // Here will be an interface implementation method ReceivePort? _\$p; static final Finalizer _finalizer = @@ -416,30 +415,39 @@ class $name$typeParamsDef extends $superName { super.delete(); } - factory $name.implement( + factory $name.implement({ '''); - final typeClassesDef = _encloseIfNotEmpty( - '{\n', - typeParams - .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,') - .join(_newLine(depth: 1)), - '}\n', - ); + final typeClassesDef = typeParams + .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,') + .join(_newLine(depth: 1)); final typeClassesCall = typeParams.map((typeParam) => '$typeParam,').join(_newLine(depth: 2)); final methodImplementCall = _MethodImplementCall(resolver, s); for (final method in node.methods) { method.accept(methodImplementCall); } - s.write('''$typeClassesDef) { + s.write('''$typeClassesDef + }) { final \$p = ReceivePort(); final \$x = $name.fromRef( $typeClassesCall $_protectedExtension.newPortProxy(r"${node.binaryName}", \$p), ).._\$p = \$p; _finalizer.attach(\$x, \$p, detach: \$x); - throw UnimplementedError(); - }'''); + \$p.listen((\$m) { + final \$i = MethodInvocation.fromMessage(\$m); + final \$d = \$i.methodDescriptor.toDartString(deleteOriginal: true); + final \$u = \$i.uuid; + final \$a = \$i.args; +'''); + final proxyMethodIf = _ProxyMethodIf(resolver, s); + for (final method in node.methods) { + method.accept(proxyMethodIf); + } + s.write('''}); + return \$x; + } + '''); } // End of Class definition @@ -534,7 +542,7 @@ class _TypeGenerator extends TypeVisitor { String visitArrayType(ArrayType node) { final innerType = node.type; if (innerType.kind == Kind.primitive) { - return '$_jArray<$_jni.${(innerType.type as PrimitiveType).jniType}>'; + return '$_jArray<$_jni.j${(innerType.type as PrimitiveType).name}>'; } return '$_jArray<${innerType.accept(this)}>'; } @@ -608,9 +616,14 @@ class _TypeClass { /// Generates the type class. class _TypeClassGenerator extends TypeVisitor<_TypeClass> { final bool isConst; + final bool boxPrimitives; final Resolver resolver; - _TypeClassGenerator(this.resolver, {this.isConst = true}); + _TypeClassGenerator( + this.resolver, { + this.isConst = true, + this.boxPrimitives = false, + }); @override _TypeClass visitArrayType(ArrayType node) { @@ -671,7 +684,8 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { @override _TypeClass visitPrimitiveType(PrimitiveType node) { final ifConst = isConst ? 'const ' : ''; - return _TypeClass('$ifConst$_jni.${node.jniType}Type()', true); + final name = boxPrimitives ? 'J${node.boxedName}' : 'j${node.name}'; + return _TypeClass('$ifConst$_jni.${name}Type()', true); } @override @@ -1411,6 +1425,7 @@ class _TypeVarLocator extends TypeVisitor>> { } } +/// The argument for .implement method of interfaces. class _MethodImplementCall extends Visitor { final Resolver resolver; final StringSink s; @@ -1423,7 +1438,89 @@ class _MethodImplementCall extends Visitor { final name = node.finalName; final args = node.params.accept(_ParamDef(resolver)).join(', '); s.write(''' - $returnType Function($args) $name, + required $returnType Function($args) $name, +'''); + } +} + +/// The if statement to check which method has been called from the proxy class. +class _ProxyMethodIf extends Visitor { + final Resolver resolver; + final StringSink s; + + _ProxyMethodIf(this.resolver, this.s); + + @override + void visit(Method node) { + final signature = node.javaSig; + final name = node.finalName; + s.write(''' + if (\$d == r"$signature") { + ${node.returnType.name == 'void' ? '' : 'final \$r = '}$name( +'''); + for (var i = 0; i < node.params.length; ++i) { + node.params[i].accept(_ProxyParamCast(resolver, s, paramIndex: i)); + } + const returnBox = _ProxyReturnBox(); + s.write(''' + ); + ProtectedJniExtensions.returnResultFor( + \$x.reference, + \$u.reference, + ${node.returnType.accept(returnBox)}, + ); + return; + } '''); } } + +/// Generates casting to the correct parameter type from the list of JObject +/// arguments received from the call to the proxy class. +class _ProxyParamCast extends Visitor { + final Resolver resolver; + final StringSink s; + final int paramIndex; + + _ProxyParamCast( + this.resolver, + this.s, { + required this.paramIndex, + }); + + @override + void visit(Param node) { + final typeClass = node.type + .accept(_TypeClassGenerator(resolver, boxPrimitives: true)) + .name; + s.write('\$a[$paramIndex].castTo($typeClass, deleteOriginal: true)'); + if (node.type.kind == Kind.primitive) { + // Convert to Dart type. + final name = (node.type.type as PrimitiveType).name; + s.write('.${name}Value(deleteOriginal: true)'); + } + s.writeln(','); + } +} + +/// Boxes the returned primitive value into the correct Boxed type. +/// Only returns the reference for non primitive types. +/// Returns null for void. +/// +/// For example `$r.toJInteger().reference` when the return type is `integer`. +class _ProxyReturnBox extends TypeVisitor { + const _ProxyReturnBox(); + + @override + String visitNonPrimitiveType(ReferredType node) { + return '\$r.reference'; + } + + @override + String visitPrimitiveType(PrimitiveType node) { + if (node.name == 'void') { + return '$_jni.nullptr'; + } + return '\$r.toJ${node.name.capitalize()}().reference'; + } +} diff --git a/jnigen/lib/src/elements/elements.dart b/jnigen/lib/src/elements/elements.dart index bc33c18e..2bf7e56e 100644 --- a/jnigen/lib/src/elements/elements.dart +++ b/jnigen/lib/src/elements/elements.dart @@ -257,7 +257,7 @@ class PrimitiveType extends ReferredType { name: 'byte', signature: 'B', dartType: 'int', - jniType: 'jbyte', + boxedName: 'Byte', cType: 'int8_t', ffiType: 'Int8', ), @@ -265,7 +265,7 @@ class PrimitiveType extends ReferredType { name: 'short', signature: 'S', dartType: 'int', - jniType: 'jshort', + boxedName: 'Short', cType: 'int16_t', ffiType: 'Int16', ), @@ -273,7 +273,7 @@ class PrimitiveType extends ReferredType { name: 'char', signature: 'C', dartType: 'int', - jniType: 'jchar', + boxedName: 'Character', cType: 'uint16_t', ffiType: 'Uint16', ), @@ -281,7 +281,7 @@ class PrimitiveType extends ReferredType { name: 'int', signature: 'I', dartType: 'int', - jniType: 'jint', + boxedName: 'Integer', cType: 'int32_t', ffiType: 'Int32', ), @@ -289,7 +289,7 @@ class PrimitiveType extends ReferredType { name: 'long', signature: 'J', dartType: 'int', - jniType: 'jlong', + boxedName: 'Long', cType: 'int64_t', ffiType: 'Int64', ), @@ -297,7 +297,7 @@ class PrimitiveType extends ReferredType { name: 'float', signature: 'F', dartType: 'double', - jniType: 'jfloat', + boxedName: 'Float', cType: 'float', ffiType: 'Float', ), @@ -305,7 +305,7 @@ class PrimitiveType extends ReferredType { name: 'double', signature: 'D', dartType: 'double', - jniType: 'jdouble', + boxedName: 'Double', cType: 'double', ffiType: 'Double', ), @@ -313,7 +313,7 @@ class PrimitiveType extends ReferredType { name: 'boolean', signature: 'Z', dartType: 'bool', - jniType: 'jboolean', + boxedName: 'Boolean', cType: 'uint8_t', ffiType: 'Uint8', ), @@ -321,7 +321,7 @@ class PrimitiveType extends ReferredType { name: 'void', signature: 'V', dartType: 'void', - jniType: 'jvoid', // Never used + boxedName: 'Void', // Not used. cType: 'void', ffiType: 'Void', ), @@ -331,7 +331,7 @@ class PrimitiveType extends ReferredType { required this.name, required this.signature, required this.dartType, - required this.jniType, + required this.boxedName, required this.cType, required this.ffiType, }); @@ -341,7 +341,7 @@ class PrimitiveType extends ReferredType { final String signature; final String dartType; - final String jniType; + final String boxedName; final String cType; final String ffiType; @@ -491,12 +491,7 @@ class Method extends ClassMember implements Element { TypeUsage? asyncReturnType; @JsonKey(includeFromJson: false) - late String javaSig = _javaSig(); - - String _javaSig() { - final paramNames = params.map((p) => p.type.name).join(', '); - return '${returnType.name} $name($paramNames)'; - } + late final String javaSig = '$name$descriptor'; bool get isCtor => name == ''; diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c index e9dfb1d0..ba2389c7 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c +++ b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c @@ -2363,6 +2363,27 @@ JniResult MyInterface__varCallback(jobject self_, jobject t) { return to_global_ref_result(_result); } +jmethodID _m_MyInterface__manyPrimitives = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterface__manyPrimitives(jobject self_, + int32_t a, + uint8_t b, + uint16_t c, + double d) { + load_env(); + load_class_global_ref(&_c_MyInterface, + "com/github/dart_lang/jnigen/interfaces/MyInterface"); + if (_c_MyInterface == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_MyInterface, &_m_MyInterface__manyPrimitives, "manyPrimitives", + "(IZCD)J"); + if (_m_MyInterface__manyPrimitives == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = (*jniEnv)->CallLongMethod( + jniEnv, self_, _m_MyInterface__manyPrimitives, a, b, c, d); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + // com.github.dart_lang.jnigen.interfaces.MyInterfaceConsumer jclass _c_MyInterfaceConsumer = NULL; diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index 500e6586..76baf4e8 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -2896,7 +2896,28 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { return T.fromRef(_varCallback(reference, t.reference).object); } - // Here will be an interface implementation method + static final _manyPrimitives = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Int32, + ffi.Uint8, + ffi.Uint16, + ffi.Double)>>("MyInterface__manyPrimitives") + .asFunction< + jni.JniResult Function( + ffi.Pointer, int, int, int, double)>(); + + /// from: public abstract long manyPrimitives(int a, boolean b, char c, double d) + int manyPrimitives( + int a, + bool b, + int c, + double d, + ) { + return _manyPrimitives(reference, a, b ? 1 : 0, c, d).long; + } + ReceivePort? _$p; static final Finalizer _finalizer = @@ -2909,10 +2930,11 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { super.delete(); } - factory MyInterface.implement( - void Function(jni.JString s) voidCallback, - jni.JString Function(jni.JString s) stringCallback, - $T Function($T t) varCallback, { + factory MyInterface.implement({ + required void Function(jni.JString s) voidCallback, + required jni.JString Function(jni.JString s) stringCallback, + required $T Function($T t) varCallback, + required int Function(int a, bool b, int c, double d) manyPrimitives, required jni.JObjType<$T> T, }) { final $p = ReceivePort(); @@ -2922,7 +2944,68 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), ).._$p = $p; _finalizer.attach($x, $p, detach: $x); - throw UnimplementedError(); + $p.listen(($m) { + final $i = MethodInvocation.fromMessage($m); + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $u = $i.uuid; + final $a = $i.args; + if ($d == r"voidCallback(Ljava/lang/String;)V") { + voidCallback( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + jni.nullptr, + ); + return; + } + if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { + final $r = stringCallback( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.reference, + ); + return; + } + if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { + final $r = varCallback( + $a[0].castTo(T, deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.reference, + ); + return; + } + if ($d == r"manyPrimitives(IZCD)J") { + final $r = manyPrimitives( + $a[0] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[1] + .castTo(const jni.JBooleanType(), deleteOriginal: true) + .booleanValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JCharacterType(), deleteOriginal: true) + .charValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JDoubleType(), deleteOriginal: true) + .doubleValue(deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.toJLong().reference, + ); + return; + } + }); + return $x; } } diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index eada063c..19f969d9 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2740,7 +2740,23 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { _id_varCallback, jni.JniCallType.objectType, [t.reference]).object); } - // Here will be an interface implementation method + static final _id_manyPrimitives = jni.Jni.accessors + .getMethodIDOf(_class.reference, r"manyPrimitives", r"(IZCD)J"); + + /// from: public abstract long manyPrimitives(int a, boolean b, char c, double d) + int manyPrimitives( + int a, + bool b, + int c, + double d, + ) { + return jni.Jni.accessors.callMethodWithArgs( + reference, + _id_manyPrimitives, + jni.JniCallType.longType, + [jni.JValueInt(a), b ? 1 : 0, jni.JValueChar(c), d]).long; + } + ReceivePort? _$p; static final Finalizer _finalizer = @@ -2753,10 +2769,11 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { super.delete(); } - factory MyInterface.implement( - void Function(jni.JString s) voidCallback, - jni.JString Function(jni.JString s) stringCallback, - $T Function($T t) varCallback, { + factory MyInterface.implement({ + required void Function(jni.JString s) voidCallback, + required jni.JString Function(jni.JString s) stringCallback, + required $T Function($T t) varCallback, + required int Function(int a, bool b, int c, double d) manyPrimitives, required jni.JObjType<$T> T, }) { final $p = ReceivePort(); @@ -2766,7 +2783,68 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), ).._$p = $p; _finalizer.attach($x, $p, detach: $x); - throw UnimplementedError(); + $p.listen(($m) { + final $i = MethodInvocation.fromMessage($m); + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $u = $i.uuid; + final $a = $i.args; + if ($d == r"voidCallback(Ljava/lang/String;)V") { + voidCallback( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + jni.nullptr, + ); + return; + } + if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { + final $r = stringCallback( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.reference, + ); + return; + } + if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { + final $r = varCallback( + $a[0].castTo(T, deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.reference, + ); + return; + } + if ($d == r"manyPrimitives(IZCD)J") { + final $r = manyPrimitives( + $a[0] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[1] + .castTo(const jni.JBooleanType(), deleteOriginal: true) + .booleanValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JCharacterType(), deleteOriginal: true) + .charValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JDoubleType(), deleteOriginal: true) + .doubleValue(deleteOriginal: true), + ); + ProtectedJniExtensions.returnResultFor( + $x.reference, + $u.reference, + $r.toJLong().reference, + ); + return; + } + }); + return $x; } } diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java index d73b040f..97b243e5 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterface.java @@ -10,4 +10,6 @@ public interface MyInterface { String stringCallback(String s); T varCallback(T t); + + long manyPrimitives(int a, boolean b, char c, double d); } From becc6e5c74303eacdf89790389865892c2e0e35b Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 6 Jul 2023 21:18:59 +0200 Subject: [PATCH 06/25] fix descriptor issues --- .../in_app_java/lib/android_utils.dart | 34 ++---- .../src/android_utils/android_utils.c | 15 ++- jnigen/java/pom.xml | 5 - .../apisummarizer/disasm/AsmClassVisitor.java | 25 +++- .../disasm/AsmMethodSignatureVisitor.java | 18 ++- .../apisummarizer/disasm/AsmSummarizer.java | 6 +- .../disasm/AsmTypeUsageSignatureVisitor.java | 5 +- .../apisummarizer/disasm/TypeUtils.java | 11 +- .../jnigen/apisummarizer/doclet/AstEnv.java | 37 +----- .../apisummarizer/doclet/ElementBuilders.java | 13 -- .../apisummarizer/elements/ClassDecl.java | 2 - jnigen/lib/src/bindings/c_generator.dart | 70 +---------- jnigen/lib/src/bindings/dart_generator.dart | 6 +- jnigen/lib/src/bindings/descriptor.dart | 115 ++++++++++++++++++ jnigen/lib/src/bindings/kotlin_processor.dart | 2 +- jnigen/lib/src/bindings/unnester.dart | 11 +- jnigen/lib/src/config/config_types.dart | 75 ++++++++---- jnigen/lib/src/elements/elements.dart | 21 +++- jnigen/lib/src/elements/elements.g.dart | 3 +- jnigen/lib/src/generate_bindings.dart | 4 +- jnigen/lib/src/summary/summary.dart | 10 +- jnigen/test/descriptor_test.dart | 42 +++++++ jnigen/test/jackson_core_test/generate.dart | 2 +- .../c_based/c_bindings/jackson_core.c | 2 +- .../fasterxml/jackson/core/JsonParser.dart | 4 +- jnigen/test/kotlin_test/generate.dart | 2 +- 26 files changed, 314 insertions(+), 226 deletions(-) create mode 100644 jnigen/lib/src/bindings/descriptor.dart create mode 100644 jnigen/test/descriptor_test.dart diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index dd1a6231..f7b7a461 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1691,7 +1691,7 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { final $u = $i.uuid; final $a = $i.args; if ($d == - r"load(Landroidx/emoji2/text/EmojiCompat/MetadataRepoLoaderCallback;)V") { + r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V") { load( $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), deleteOriginal: true), @@ -2739,18 +2739,14 @@ class Build_VERSION extends jni.JObject { static jni.JString get SECURITY_PATCH => const jni.JStringType().fromRef(_get_SECURITY_PATCH().object); - static final _ctor = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer)>>("Build_VERSION__ctor") - .asFunction)>(); + static final _ctor = jniLookup>( + "Build_VERSION__ctor") + .asFunction(); - /// from: public void (android.os.Build $parent) + /// from: public void () /// The returned object must be deleted after use, by calling the `delete` method. - factory Build_VERSION( - Build $parent, - ) { - return Build_VERSION.fromRef(_ctor($parent.reference).object); + factory Build_VERSION() { + return Build_VERSION.fromRef(_ctor().object); } } @@ -2893,18 +2889,14 @@ class Build_VERSION_CODES extends jni.JObject { /// from: static public final int TIRAMISU static const TIRAMISU = 33; - static final _ctor = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer)>>("Build_VERSION_CODES__ctor") - .asFunction)>(); + static final _ctor = jniLookup>( + "Build_VERSION_CODES__ctor") + .asFunction(); - /// from: public void (android.os.Build $parent) + /// from: public void () /// The returned object must be deleted after use, by calling the `delete` method. - factory Build_VERSION_CODES( - Build $parent, - ) { - return Build_VERSION_CODES.fromRef(_ctor($parent.reference).object); + factory Build_VERSION_CODES() { + return Build_VERSION_CODES.fromRef(_ctor().object); } } diff --git a/jnigen/example/in_app_java/src/android_utils/android_utils.c b/jnigen/example/in_app_java/src/android_utils/android_utils.c index c41ff6e9..a6e491c9 100644 --- a/jnigen/example/in_app_java/src/android_utils/android_utils.c +++ b/jnigen/example/in_app_java/src/android_utils/android_utils.c @@ -1440,17 +1440,16 @@ jclass _c_Build_VERSION = NULL; jmethodID _m_Build_VERSION__ctor = NULL; FFI_PLUGIN_EXPORT -JniResult Build_VERSION__ctor(jobject _parent) { +JniResult Build_VERSION__ctor() { load_env(); load_class_global_ref(&_c_Build_VERSION, "android/os/Build$VERSION"); if (_c_Build_VERSION == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Build_VERSION, &_m_Build_VERSION__ctor, "", - "(Landroid/os/Build;)V"); + load_method(_c_Build_VERSION, &_m_Build_VERSION__ctor, "", "()V"); if (_m_Build_VERSION__ctor == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_Build_VERSION, - _m_Build_VERSION__ctor, _parent); + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_Build_VERSION, _m_Build_VERSION__ctor); return to_global_ref_result(_result); } @@ -1615,18 +1614,18 @@ jclass _c_Build_VERSION_CODES = NULL; jmethodID _m_Build_VERSION_CODES__ctor = NULL; FFI_PLUGIN_EXPORT -JniResult Build_VERSION_CODES__ctor(jobject _parent) { +JniResult Build_VERSION_CODES__ctor() { load_env(); load_class_global_ref(&_c_Build_VERSION_CODES, "android/os/Build$VERSION_CODES"); if (_c_Build_VERSION_CODES == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; load_method(_c_Build_VERSION_CODES, &_m_Build_VERSION_CODES__ctor, "", - "(Landroid/os/Build;)V"); + "()V"); if (_m_Build_VERSION_CODES__ctor == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; jobject _result = (*jniEnv)->NewObject(jniEnv, _c_Build_VERSION_CODES, - _m_Build_VERSION_CODES__ctor, _parent); + _m_Build_VERSION_CODES__ctor); return to_global_ref_result(_result); } diff --git a/jnigen/java/pom.xml b/jnigen/java/pom.xml index 30d17bf7..226bb03f 100644 --- a/jnigen/java/pom.xml +++ b/jnigen/java/pom.xml @@ -38,11 +38,6 @@ kotlinx-metadata-jvm 0.6.2 - - io.soabase.asm-mirror-descriptor - asm-mirror-descriptor - 1 - junit junit diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java index 6a536d32..2ad407f5 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java @@ -8,6 +8,8 @@ import com.github.dart_lang.jnigen.apisummarizer.util.SkipException; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; import java.util.*; +import java.util.stream.Collectors; + import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; @@ -27,6 +29,9 @@ public List getVisited() { List visited = new ArrayList<>(); Stack visiting = new Stack<>(); + /// Actual access for the inner classes as originally defined. + Map actualAccess = new HashMap<>(); + public AsmClassVisitor() { super(AsmConstants.API); } @@ -42,9 +47,8 @@ public void visit( var current = new ClassDecl(); visiting.push(current); var type = Type.getObjectType(name); - current.binaryName = type.getClassName(); - current.modifiers = TypeUtils.access(access); - current.parentName = TypeUtils.parentName(type); + current.binaryName = name.replace('/', '.'); + current.modifiers = TypeUtils.access(actualAccess.getOrDefault(current.binaryName, access)); current.declKind = TypeUtils.declKind(access); current.superclass = TypeUtils.typeUsage(Type.getObjectType(superName), null); current.interfaces = @@ -56,6 +60,21 @@ public void visit( super.visit(version, access, name, signature, superName, interfaces); } + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + var binaryName = name.replace('/', '.'); + actualAccess.put(binaryName, access); + var alreadyVisitedInnerClass = + visited.stream() + .filter(decl -> decl.binaryName.equals(binaryName)) + .collect(Collectors.toList()); + // If the order of visit is outer first inner second. + // We still want to correct the modifiers. + if (!alreadyVisitedInnerClass.isEmpty()) { + alreadyVisitedInnerClass.get(0).modifiers = TypeUtils.access(access); + } + } + @Override public FieldVisitor visitField( int access, String name, String descriptor, String signature, Object value) { diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java index 9067f9eb..dcf6cf41 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java @@ -9,9 +9,12 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.TypeUsage; import org.objectweb.asm.signature.SignatureVisitor; +import java.util.ArrayList; +import java.util.List; + public class AsmMethodSignatureVisitor extends SignatureVisitor { private final Method method; - private int paramIndex = -1; + private final List paramTypes = new ArrayList<>(); public AsmMethodSignatureVisitor(Method method) { super(AsmConstants.API); @@ -43,13 +46,22 @@ public SignatureVisitor visitInterfaceBound() { @Override public SignatureVisitor visitReturnType() { + // Visiting params finished. + // + // In case of non-static inner class constructor, there is an extra parent parameter + // at the beginning which is not accounted for by the signature. So we fill the types + // for the last [paramTypes.size()] params. + int startIndex = method.params.size() - paramTypes.size(); + for (int i = 0; i < paramTypes.size(); ++i) { + method.params.get(startIndex + i).type = paramTypes.get(i); + } return new AsmTypeUsageSignatureVisitor(method.returnType); } @Override public SignatureVisitor visitParameterType() { - paramIndex++; - return new AsmTypeUsageSignatureVisitor(method.params.get(paramIndex).type); + paramTypes.add(new TypeUsage()); + return new AsmTypeUsageSignatureVisitor(paramTypes.get(paramTypes.size() - 1)); } @Override diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java index 391b8316..7b15cdd0 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java @@ -15,15 +15,13 @@ public class AsmSummarizer { public static List run(List inputProviders) { - List parsed = new ArrayList<>(); + var visitor = new AsmClassVisitor(); for (var provider : inputProviders) { var inputStream = provider.getInputStream(); var classReader = wrapCheckedException(ClassReader::new, inputStream); - var visitor = new AsmClassVisitor(); classReader.accept(visitor, 0); - parsed.addAll(visitor.getVisited()); provider.close(); } - return parsed; + return visitor.getVisited(); } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java index 3c52c104..001d7fe2 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java @@ -93,7 +93,8 @@ public SignatureVisitor visitTypeArgument(char wildcard) { @Override public void visitInnerClassType(String name) { - super.visitInnerClassType(name); - // TODO(#139) support nested generic classes + typeUsage.shorthand += "." + name; + ((TypeUsage.DeclaredType)typeUsage.type).binaryName += "$" + name; + ((TypeUsage.DeclaredType)typeUsage.type).simpleName = name; } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java index 521c02d5..8f185652 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/TypeUtils.java @@ -19,14 +19,6 @@ class TypeUtils { - public static String parentName(Type type) { - var className = type.getClassName(); - if (!className.contains("$")) { - return null; - } - return className.split("\\$")[0]; - } - public static String simpleName(Type type) { var internalName = type.getInternalName(); if (type.getInternalName().length() == 1) { @@ -46,7 +38,8 @@ public static TypeUsage typeUsage(Type type, @SuppressWarnings("unused") String case OBJECT: usage.kind = TypeUsage.Kind.DECLARED; usage.type = - new TypeUsage.DeclaredType(type.getClassName(), TypeUtils.simpleName(type), null); + new TypeUsage.DeclaredType( + type.getInternalName().replace('/', '.'), TypeUtils.simpleName(type), null); break; case ARRAY: usage.kind = TypeUsage.Kind.ARRAY; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java index 45f6b440..856e98c7 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java @@ -18,7 +18,7 @@ import java.util.Map; /** Class to hold utility classes initialized from DocletEnvironment. */ -public class AstEnv implements ProcessingEnvironment { +public class AstEnv { public final Types types; public final Elements elements; public final DocTrees trees; @@ -32,39 +32,4 @@ public AstEnv(Types types, Elements elements, DocTrees trees) { public static AstEnv fromEnvironment(DocletEnvironment env) { return new AstEnv(env.getTypeUtils(), env.getElementUtils(), env.getDocTrees()); } - - @Override - public Map getOptions() { - return null; - } - - @Override - public Messager getMessager() { - return null; - } - - @Override - public Filer getFiler() { - return null; - } - - @Override - public Elements getElementUtils() { - return elements; - } - - @Override - public Types getTypeUtils() { - return types; - } - - @Override - public SourceVersion getSourceVersion() { - return null; - } - - @Override - public Locale getLocale() { - return null; - } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java index f6f423ed..da5641eb 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java @@ -7,8 +7,6 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.*; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; import com.sun.source.doctree.DocCommentTree; -import io.soabase.asm.mirror.MirrorMethodReader; -import io.soabase.asm.mirror.SignatureMirrorType; import java.util.HashMap; import java.util.List; @@ -43,10 +41,6 @@ private void fillInFromTypeElement(TypeElement e, ClassDecl c) { throw new RuntimeException( "Unexpected element kind " + e.getKind() + " on " + c.binaryName); } - var parent = e.getEnclosingElement(); - if (parent instanceof TypeElement) { - c.parentName = env.elements.getBinaryName((TypeElement) parent).toString(); - } c.javadoc = docComment(env.trees.getDocCommentTree(e)); c.typeParams = StreamUtil.map(e.getTypeParameters(), this::typeParam); var superclass = e.getSuperclass(); @@ -198,13 +192,6 @@ public TypeUsage typeUsage(TypeMirror type) { public Method method(ExecutableElement e) { var m = new Method(); m.name = e.getSimpleName().toString(); - var methodReader = new MirrorMethodReader(env); - methodReader.readMethod( - (access, name, desc, signature, exceptions) -> { - m.descriptor = desc; - return null; - }, - e); m.modifiers = e.getModifiers().stream().map(Modifier::toString).collect(Collectors.toSet()); m.typeParams = e.getTypeParameters().stream().map(this::typeParam).collect(Collectors.toList()); m.returnType = typeUsage(e.getReturnType()); diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java index 231f0f5b..c2cbeceb 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java @@ -26,8 +26,6 @@ public class ClassDecl { * uses $ instead of dot (.) before nested classes. */ public String binaryName; - - public String parentName; public List typeParams = new ArrayList<>(); public List methods = new ArrayList<>(); public List fields = new ArrayList<>(); diff --git a/jnigen/lib/src/bindings/c_generator.dart b/jnigen/lib/src/bindings/c_generator.dart index dedef766..79b07c8b 100644 --- a/jnigen/lib/src/bindings/c_generator.dart +++ b/jnigen/lib/src/bindings/c_generator.dart @@ -11,72 +11,6 @@ import '../util/find_package.dart'; import '../util/string_util.dart'; import 'visitor.dart'; -/// JVM representation of type signatures. -/// -/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures -class Descriptor extends TypeVisitor { - const Descriptor(); - - @override - String visitArrayType(ArrayType node) { - final inner = node.type.accept(this); - return '[$inner'; - } - - @override - String visitDeclaredType(DeclaredType node) { - final internalName = node.binaryName.replaceAll('.', '/'); - return 'L$internalName;'; - } - - @override - String visitPrimitiveType(PrimitiveType node) { - return node.signature; - } - - @override - String visitTypeVar(TypeVar node) { - // It should be possible to compute the erasure of a type - // in parser itself. - // TODO(#23): Use erasure of the type variable here. - // This is just a (wrong) placeholder - return super.visitTypeVar(node); - } - - @override - String visitWildcard(Wildcard node) { - final extendsBound = node.extendsBound?.accept(this); - return extendsBound ?? 'Ljava/lang/Object;'; - } - - @override - String visitNonPrimitiveType(ReferredType node) { - return "Ljava/lang/Object;"; - } -} - -/// Generates JNI Method signatures. -/// -/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures -/// Also see: [Descriptor] -class MethodSignature extends Visitor { - const MethodSignature(); - - @override - String visit(Method node) { - final s = StringBuffer(); - s.write('('); - s.write(node.params - .map((param) => param.type) - .accept(const Descriptor()) - .join()); - s.write(')'); - final returnType = node.returnType.accept(const Descriptor()); - s.write(returnType); - return s.toString(); - } -} - class CFieldName extends Visitor { const CFieldName(); @@ -255,7 +189,7 @@ class _CMethodGenerator extends Visitor { if (!node.isCtor && !node.isStatic) 'jobject self_', ...node.params.accept(const _CParamGenerator(addReturnType: true)), ].join(','); - final jniSignature = node.accept(const MethodSignature()); + final jniSignature = node.descriptor; final ifStaticMethodID = node.isStatic ? 'static_' : ''; var javaReturnType = node.returnType.type; @@ -357,7 +291,7 @@ $cReturnType ${cMethodPrefix}_$fieldNameInC($formalArgs) { $_loadEnvCall ${node.classDecl.accept(_CLoadClassGenerator())} load_${ifStaticField}field($classVar, &$fieldVar, "$fieldName", - "${node.type.accept(const Descriptor())}"); + "${node.type.descriptor}"); $accessorStatements } diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 8b3f36a7..9e29cd81 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -880,7 +880,7 @@ class _FieldGenerator extends Visitor { void writeDartOnlyAccessor(Field node) { final name = node.finalName; final ifStatic = node.isStatic ? 'Static' : ''; - final descriptor = node.type.accept(const Descriptor()); + final descriptor = node.type.descriptor; s.write(''' static final _id_$name = $_accessors.get${ifStatic}FieldIDOf( @@ -1007,10 +1007,10 @@ class _MethodGenerator extends Visitor { void writeDartOnlyAccessor(Method node) { final name = node.finalName; final ifStatic = node.isStatic ? 'Static' : ''; - final signature = node.accept(const MethodSignature()); + final descriptor = node.descriptor; s.write(''' static final _id_$name = $_accessors.get${ifStatic}MethodIDOf( - $_classRef, r"${node.name}", r"$signature"); + $_classRef, r"${node.name}", r"$descriptor"); '''); } diff --git a/jnigen/lib/src/bindings/descriptor.dart b/jnigen/lib/src/bindings/descriptor.dart new file mode 100644 index 00000000..01f1a76e --- /dev/null +++ b/jnigen/lib/src/bindings/descriptor.dart @@ -0,0 +1,115 @@ +import '../elements/elements.dart'; +import 'visitor.dart'; + +/// Adds the method descriptor to all methods in all of the classes. +/// +/// ASM already fills the descriptor field, but +class Descriptor extends Visitor { + const Descriptor(); + + @override + void visit(Classes node) { + for (final classDecl in node.decls.values) { + classDecl.accept(const _ClassDescriptor()); + } + } +} + +class _ClassDescriptor extends Visitor { + const _ClassDescriptor(); + + @override + void visit(ClassDecl node) { + final methodDescriptor = MethodDescriptor(node.allTypeParams); + for (final method in node.methods) { + method.descriptor ??= method.accept(methodDescriptor); + } + final typeDescriptor = TypeDescriptor(node.allTypeParams); + for (final field in node.fields) { + field.type.descriptor = field.type.accept(typeDescriptor); + } + for (final interface in node.interfaces) { + interface.descriptor = interface.accept(typeDescriptor); + } + } +} + +/// Generates JNI Method descriptor. +/// +/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures +/// Also see: [TypeDescriptor] +class MethodDescriptor extends Visitor { + final List typeParams; + + MethodDescriptor(this.typeParams); + + @override + String visit(Method node) { + final s = StringBuffer(); + final typeParamsIncludingThis = [...typeParams, ...node.typeParams]; + final typeDescriptor = TypeDescriptor(typeParamsIncludingThis); + s.write('('); + for (final param in node.params) { + final desc = param.type.accept(typeDescriptor); + param.type.descriptor = desc; + s.write(desc); + } + s.write(')'); + final returnTypeDesc = + node.returnType.accept(TypeDescriptor(typeParamsIncludingThis)); + node.returnType.descriptor = returnTypeDesc; + s.write(returnTypeDesc); + return s.toString(); + } +} + +/// JVM representation of type signatures. +/// +/// https://docs.oracle.com/en/java/javase/18/docs/specs/jni/types.html#type-signatures +class TypeDescriptor extends TypeVisitor { + final List typeParams; + + TypeDescriptor(this.typeParams); + + @override + String visitArrayType(ArrayType node) { + final inner = node.type.accept(this); + return '[$inner'; + } + + @override + String visitDeclaredType(DeclaredType node) { + final internalName = node.binaryName.replaceAll('.', '/'); + return 'L$internalName;'; + } + + @override + String visitPrimitiveType(PrimitiveType node) { + return node.signature; + } + + @override + String visitTypeVar(TypeVar node) { + final typeParam = typeParams.lastWhere( + (typeParam) => typeParam.name == node.name, + orElse: () { + print('${node.name} was not found!'); + throw 'error'; + }, + ); + return typeParam.bounds.isEmpty + ? super.visitTypeVar(node) + : typeParam.bounds.first.accept(this); + } + + @override + String visitWildcard(Wildcard node) { + final extendsBound = node.extendsBound?.accept(this); + return extendsBound ?? super.visitWildcard(node); + } + + @override + String visitNonPrimitiveType(ReferredType node) { + return 'Ljava/lang/Object;'; + } +} diff --git a/jnigen/lib/src/bindings/kotlin_processor.dart b/jnigen/lib/src/bindings/kotlin_processor.dart index 900470d8..4a48aa8c 100644 --- a/jnigen/lib/src/bindings/kotlin_processor.dart +++ b/jnigen/lib/src/bindings/kotlin_processor.dart @@ -31,7 +31,7 @@ class _KotlinClassProcessor extends Visitor { functions[signature] = function; } for (final method in node.methods) { - final signature = method.name + method.descriptor; + final signature = method.name + method.descriptor!; if (functions.containsKey(signature)) { method.accept(_KotlinMethodProcessor(functions[signature]!)); } diff --git a/jnigen/lib/src/bindings/unnester.dart b/jnigen/lib/src/bindings/unnester.dart index 00522051..d9323df9 100644 --- a/jnigen/lib/src/bindings/unnester.dart +++ b/jnigen/lib/src/bindings/unnester.dart @@ -10,6 +10,8 @@ import 'visitor.dart'; /// Nested classes are not supported in Dart. So this "unnests" them into /// separate classes. class Unnester extends Visitor { + const Unnester(); + @override void visit(node) { final classProcessor = _ClassUnnester(); @@ -53,9 +55,14 @@ class _MethodUnnester extends Visitor { void visit(Method node) { assert(!node.classDecl.isStatic); assert(node.classDecl.isNested); - if (node.isCtor || node.isStatic) { + // TODO(#319): Unnest the methods in APISummarizer itself. + // For now the nullity of [node.descriptor] identifies if the doclet + // backend was used and the method would potentially need "unnesting". + if ((node.isCtor || node.isStatic) && node.descriptor == null) { // Non-static nested classes take an instance of their outer class as the - // first parameter. This is not accounted for by the summarizer, so we + // first parameter. + // + // This is not accounted for by the **doclet** summarizer, so we // manually add it as the first parameter. final parentTypeParamCount = node.classDecl.allTypeParams.length - node.classDecl.typeParams.length; diff --git a/jnigen/lib/src/config/config_types.dart b/jnigen/lib/src/config/config_types.dart index a6dbe900..c032dc26 100644 --- a/jnigen/lib/src/config/config_types.dart +++ b/jnigen/lib/src/config/config_types.dart @@ -117,13 +117,39 @@ class AndroidSdkConfig { String? androidExample; } +extension on String { + /// Converts the enum name from camelCase to snake_case. + String toSnakeCase() { + return splitMapJoin( + RegExp('[A-Z]'), + onMatch: (p) => '_${p[0]!.toLowerCase()}', + ); + } +} + +extension on Iterable { + Map valuesMap() { + return Map.fromEntries(map((e) => MapEntry(e.name.toSnakeCase(), e))); + } +} + +T _getEnumValueFromString( + Map values, String? name, T defaultVal) { + if (name == null) return defaultVal; + final value = values[name]; + if (value == null) { + throw ConfigException('Got: $name, allowed: ${values.keys}'); + } + return value; +} + /// Additional options to pass to the summary generator component. class SummarizerOptions { SummarizerOptions( {this.extraArgs = const [], this.workingDirectory, this.backend}); List extraArgs; Uri? workingDirectory; - String? backend; + SummarizerBackend? backend; } /// Backend for reading summary of Java libraries @@ -135,14 +161,15 @@ enum SummarizerBackend { doclet, } -T _getEnumValueFromString( - Map values, String? name, T defaultVal) { - if (name == null) return defaultVal; - final value = values[name]; - if (value == null) { - throw ConfigException('Got: $name, allowed: ${values.keys}'); - } - return value; +SummarizerBackend? getSummarizerBackend( + String? name, + SummarizerBackend? defaultVal, +) { + return _getEnumValueFromString( + SummarizerBackend.values.valuesMap(), + name, + defaultVal, + ); } void _ensureIsDirectory(String name, Uri path) { @@ -155,31 +182,27 @@ void _ensureIsDirectory(String name, Uri path) { enum OutputStructure { packageStructure, singleFile } OutputStructure getOutputStructure(String? name, OutputStructure defaultVal) { - const values = { - 'package_structure': OutputStructure.packageStructure, - 'single_file': OutputStructure.singleFile, - }; - return _getEnumValueFromString(values, name, defaultVal); + return _getEnumValueFromString( + OutputStructure.values.valuesMap(), + name, + defaultVal, + ); } enum BindingsType { cBased, dartOnly } extension GetConfigString on BindingsType { - static const _configStrings = { - BindingsType.cBased: 'c_based', - BindingsType.dartOnly: 'dart_only', - }; String getConfigString() { - return _configStrings[this]!; + return name.toSnakeCase(); } } BindingsType getBindingsType(String? name, BindingsType defaultVal) { - const values = { - 'c_based': BindingsType.cBased, - 'dart_only': BindingsType.dartOnly, - }; - return _getEnumValueFromString(values, name, defaultVal); + return _getEnumValueFromString( + BindingsType.values.valuesMap(), + name, + defaultVal, + ); } class CCodeOutputConfig { @@ -351,7 +374,7 @@ class Config { final MavenDownloads? mavenDownloads; /// Additional options for the summarizer component. - final SummarizerOptions? summarizerOptions; + SummarizerOptions? summarizerOptions; /// List of dependencies. final List? imports; @@ -536,7 +559,7 @@ class Config { classes: must(prov.getStringList, [], _Props.classes), summarizerOptions: SummarizerOptions( extraArgs: prov.getStringList(_Props.summarizerArgs) ?? const [], - backend: prov.getString(_Props.backend), + backend: getSummarizerBackend(prov.getString(_Props.backend), null), workingDirectory: prov.getPath(_Props.summarizerWorkingDir), ), exclude: BindingExclusions( diff --git a/jnigen/lib/src/elements/elements.dart b/jnigen/lib/src/elements/elements.dart index 2bf7e56e..020f05db 100644 --- a/jnigen/lib/src/elements/elements.dart +++ b/jnigen/lib/src/elements/elements.dart @@ -60,7 +60,6 @@ class ClassDecl extends ClassMember implements Element { required this.declKind, this.modifiers = const {}, required this.binaryName, - this.parentName, this.typeParams = const [], this.methods = const [], this.fields = const [], @@ -80,7 +79,6 @@ class ClassDecl extends ClassMember implements Element { final JavaDocComment? javadoc; final DeclKind declKind; final String binaryName; - final String? parentName; List typeParams; List methods; List fields; @@ -173,7 +171,13 @@ class ClassDecl extends ClassMember implements Element { bool get isObject => superCount == 0; - bool get isNested => parentName != null; + @JsonKey(includeFromJson: false) + late final String? parentName = binaryName.contains(r'$') + ? binaryName.splitMapJoin(RegExp(r'\$[^$]+$'), onMatch: (_) => '') + : null; + + @JsonKey(includeFromJson: false) + late final isNested = parentName != null; } @JsonEnum() @@ -208,10 +212,14 @@ class TypeUsage { @JsonKey(name: "type") final Map typeJson; - /// Populated by in [TypeUsage.fromJson]. + /// Populated by [TypeUsage.fromJson]. @JsonKey(includeFromJson: false) late final ReferredType type; + /// Populated by [Descriptor]. + @JsonKey(includeFromJson: false) + late String descriptor; + String get name => type.name; // Since json_serializable doesn't directly support union types, @@ -448,7 +456,7 @@ class Method extends ClassMember implements Element { this.javadoc, this.modifiers = const {}, required this.name, - required this.descriptor, + this.descriptor, this.typeParams = const [], this.params = const [], required this.returnType, @@ -468,7 +476,8 @@ class Method extends ClassMember implements Element { /// Can be used to match with [KotlinFunction]'s descriptor. /// /// Can create a unique signature in combination with [name]. - final String descriptor; + /// Populated either by the ASM backend or [Descriptor]. + String? descriptor; /// The [ClassDecl] where this method is defined. /// diff --git a/jnigen/lib/src/elements/elements.g.dart b/jnigen/lib/src/elements/elements.g.dart index 3aef2076..dcd35dd3 100644 --- a/jnigen/lib/src/elements/elements.g.dart +++ b/jnigen/lib/src/elements/elements.g.dart @@ -20,7 +20,6 @@ ClassDecl _$ClassDeclFromJson(Map json) => ClassDecl( .toSet() ?? const {}, binaryName: json['binaryName'] as String, - parentName: json['parentName'] as String?, typeParams: (json['typeParams'] as List?) ?.map((e) => TypeParam.fromJson(e as Map)) .toList() ?? @@ -107,7 +106,7 @@ Method _$MethodFromJson(Map json) => Method( .toSet() ?? const {}, name: json['name'] as String, - descriptor: json['descriptor'] as String, + descriptor: json['descriptor'] as String?, typeParams: (json['typeParams'] as List?) ?.map((e) => TypeParam.fromJson(e as Map)) .toList() ?? diff --git a/jnigen/lib/src/generate_bindings.dart b/jnigen/lib/src/generate_bindings.dart index 174828eb..883ca857 100644 --- a/jnigen/lib/src/generate_bindings.dart +++ b/jnigen/lib/src/generate_bindings.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'bindings/c_generator.dart'; import 'bindings/dart_generator.dart'; +import 'bindings/descriptor.dart'; import 'bindings/excluder.dart'; import 'bindings/kotlin_processor.dart'; import 'bindings/linker.dart'; @@ -39,7 +40,8 @@ Future generateJniBindings(Config config) async { classes.accept(Excluder(config)); classes.accept(KotlinProcessor()); await classes.accept(Linker(config)); - classes.accept(Unnester()); + classes.accept(const Unnester()); + classes.accept(const Descriptor()); classes.accept(Renamer(config)); final cBased = config.outputConfig.bindingsType == BindingsType.cBased; diff --git a/jnigen/lib/src/summary/summary.dart b/jnigen/lib/src/summary/summary.dart index 09c13f5c..17062758 100644 --- a/jnigen/lib/src/summary/summary.dart +++ b/jnigen/lib/src/summary/summary.dart @@ -44,11 +44,7 @@ class SummarizerCommand { this.workingDirectory, this.backend, }) : sourcePaths = sourcePath ?? [], - classPaths = classPath ?? [] { - if (backend != null && !{'asm', 'doclet'}.contains(backend)) { - throw ArgumentError('Supported backends: asm, doclet'); - } - } + classPaths = classPath ?? []; static const sourcePathsOption = '-s'; static const classPathsOption = '-c'; @@ -60,7 +56,7 @@ class SummarizerCommand { List classes; Uri? workingDirectory; - String? backend; + SummarizerBackend? backend; void addSourcePaths(List paths) { sourcePaths.addAll(paths); @@ -91,7 +87,7 @@ class SummarizerCommand { _addPathParam(args, sourcePathsOption, sourcePaths); _addPathParam(args, classPathsOption, classPaths); if (backend != null) { - args.addAll(['--backend', backend!]); + args.addAll(['--backend', backend!.name]); } args.addAll(extraArgs); args.addAll(classes); diff --git a/jnigen/test/descriptor_test.dart b/jnigen/test/descriptor_test.dart new file mode 100644 index 00000000..42c7c35f --- /dev/null +++ b/jnigen/test/descriptor_test.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2023, 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 'package:jnigen/src/bindings/descriptor.dart'; +import 'package:jnigen/src/bindings/linker.dart'; +import 'package:jnigen/src/bindings/unnester.dart'; +import 'package:jnigen/src/config/config_types.dart'; +import 'package:jnigen/src/summary/summary.dart'; +import 'package:test/test.dart'; + +import 'simple_package_test/generate.dart' as simple_package_test; +import 'kotlin_test/generate.dart' as kotlin_test; +import 'jackson_core_test/generate.dart' as jackson_core_test; +import 'test_util/test_util.dart'; + +void main() { + checkLocallyBuiltDependencies(); + test('Method descriptor generation', () async { + final configGetters = [ + simple_package_test.getConfig, + kotlin_test.getConfig, + jackson_core_test.getConfig + ]; + for (final getConfig in configGetters) { + final config = getConfig(); + config.summarizerOptions = + SummarizerOptions(backend: SummarizerBackend.asm); + final classes = await getSummary(config); + await classes.accept(Linker(config)); + classes.accept(const Unnester()); + for (final decl in classes.decls.values) { + // Checking if the descriptor from ASM matches the one generated by + // [MethodDescriptor]. + final methodDescriptor = MethodDescriptor(decl.allTypeParams); + for (final method in decl.methods) { + expect(method.descriptor, method.accept(methodDescriptor)); + } + } + } + }); +} diff --git a/jnigen/test/jackson_core_test/generate.dart b/jnigen/test/jackson_core_test/generate.dart index 7077bfa7..9b9b37f9 100644 --- a/jnigen/test/jackson_core_test/generate.dart +++ b/jnigen/test/jackson_core_test/generate.dart @@ -43,7 +43,7 @@ Config getConfig({ jarDir: join(thirdPartyDir, 'jar'), ), summarizerOptions: SummarizerOptions( - backend: useAsm ? 'asm' : null, + backend: useAsm ? SummarizerBackend.asm : null, ), preamble: jacksonPreamble, outputConfig: OutputConfig( diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/jackson_core.c b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/jackson_core.c index 74bf137c..0f6b727d 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/jackson_core.c +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/jackson_core.c @@ -3383,7 +3383,7 @@ JniResult JsonParser__readValueAsTree(jobject self_) { if (_c_JsonParser == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; load_method(_c_JsonParser, &_m_JsonParser__readValueAsTree, "readValueAsTree", - "()Ljava/lang/Object;"); + "()Lcom/fasterxml/jackson/core/TreeNode;"); if (_m_JsonParser__readValueAsTree == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, diff --git a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart index 601b4042..d2147575 100644 --- a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart +++ b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart @@ -2463,7 +2463,9 @@ class JsonParser extends jni.JObject { } static final _id_readValueAsTree = jni.Jni.accessors.getMethodIDOf( - _class.reference, r"readValueAsTree", r"()Ljava/lang/Object;"); + _class.reference, + r"readValueAsTree", + r"()Lcom/fasterxml/jackson/core/TreeNode;"); /// from: public T readValueAsTree() /// The returned object must be deleted after use, by calling the `delete` method. diff --git a/jnigen/test/kotlin_test/generate.dart b/jnigen/test/kotlin_test/generate.dart index 11b89ea5..61686679 100644 --- a/jnigen/test/kotlin_test/generate.dart +++ b/jnigen/test/kotlin_test/generate.dart @@ -63,7 +63,7 @@ Config getConfig([BindingsType bindingsType = BindingsType.cBased]) { structure: OutputStructure.singleFile, ), ), - summarizerOptions: SummarizerOptions(backend: 'asm'), + summarizerOptions: SummarizerOptions(backend: SummarizerBackend.asm), preamble: preamble, ); return config; From 8d3ed93f505ae8dbdb40432e4ee9267e0e90148e Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 11 Jul 2023 10:15:51 +0200 Subject: [PATCH 07/25] fix comment --- jnigen/lib/src/bindings/descriptor.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jnigen/lib/src/bindings/descriptor.dart b/jnigen/lib/src/bindings/descriptor.dart index 01f1a76e..f7d774ae 100644 --- a/jnigen/lib/src/bindings/descriptor.dart +++ b/jnigen/lib/src/bindings/descriptor.dart @@ -1,9 +1,9 @@ import '../elements/elements.dart'; import 'visitor.dart'; -/// Adds the method descriptor to all methods in all of the classes. +/// Adds the type and method descriptor to all methods in all of the classes. /// -/// ASM already fills the descriptor field, but +/// ASM already fills the descriptor field for methods but doclet does not. class Descriptor extends Visitor { const Descriptor(); From a5b2095b13953747b5d8cf6454eb2842eee4f3bc Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 14 Jul 2023 22:23:49 +0200 Subject: [PATCH 08/25] synchronize in the C layer instead + auto build jni.jar + test --- .../com/github/dart_lang/jni/PortProxy.java | 14 +-- jni/bin/setup.dart | 45 +++++--- jni/ffigen.yaml | 12 ++- jni/java/pom.xml | 1 + .../com/github/dart_lang/jni/PortProxy.java | 33 +++--- jni/lib/src/jni.dart | 9 +- jni/lib/src/method_invocation.dart | 28 +++-- .../third_party/jni_bindings_generated.dart | 101 +++++++++--------- jni/pubspec.yaml | 2 +- jni/src/dartjni.c | 88 ++++++++------- jni/src/dartjni.h | 78 +++++++++++--- .../in_app_java/lib/android_utils.dart | 21 ++-- .../in_app_java/src/android_utils/dartjni.h | 78 +++++++++++--- jnigen/example/kotlin_plugin/src/dartjni.h | 78 +++++++++++--- .../example/notification_plugin/src/dartjni.h | 78 +++++++++++--- .../pdfbox_plugin/src/third_party/dartjni.h | 78 +++++++++++--- jnigen/lib/src/bindings/dart_generator.dart | 7 +- jnigen/pubspec.yaml | 2 +- .../third_party/c_based/c_bindings/dartjni.h | 78 +++++++++++--- .../kotlin_test/c_based/c_bindings/dartjni.h | 78 +++++++++++--- jnigen/test/kotlin_test/jni.jar | Bin 2755 -> 0 bytes .../c_based/c_bindings/dartjni.h | 78 +++++++++++--- .../c_based/c_bindings/simple_package.c | 18 ++-- .../c_based/dart_bindings/simple_package.dart | 42 ++++---- .../dart_bindings/simple_package.dart | 42 ++++---- .../interfaces/MyInterfaceConsumer.java | 11 +- .../runtime_test_registrant.dart | 26 +++++ .../test/test_util/bindings_test_setup.dart | 2 +- 28 files changed, 805 insertions(+), 323 deletions(-) delete mode 100644 jnigen/test/kotlin_test/jni.jar diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java index 5f608b21..53871125 100644 --- a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -13,15 +13,18 @@ import java.util.concurrent.TimeUnit; public class PortProxy implements InvocationHandler { + static { + System.loadLibrary("dartjni"); + } + private final long port; - private final Map> map; + private static final Map> map = new ConcurrentHashMap<>(); private PortProxy(long port) { this.port = port; - this.map = new ConcurrentHashMap<>(); } - private BlockingQueue queueOf(String key) { + private static BlockingQueue queueOf(String key) { if (!map.containsKey(key)) { map.put(key, new ArrayBlockingQueue<>(1)); } @@ -69,8 +72,7 @@ private static void appendType(StringBuilder descriptor, Class type) { public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { Class clazz = Class.forName(binaryName); - return Proxy.newProxyInstance( - clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); + return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, new PortProxy(port)); } @Override @@ -80,7 +82,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return queueOf(uuid).poll(10, TimeUnit.SECONDS); } - public void resultFor(String uuid, Object object) { + public static void resultFor(String uuid, Object object) { queueOf(uuid).offer(object); } diff --git a/jni/bin/setup.dart b/jni/bin/setup.dart index 12ee6f97..7727cc14 100644 --- a/jni/bin/setup.dart +++ b/jni/bin/setup.dart @@ -93,11 +93,11 @@ void verboseLog(String msg) { } } -/// Find path to C sources in pub cache for package specified by [packageName]. +/// Find path to C or Java sources in pub cache for package specified by +/// [packageName]. /// -/// It's assumed C FFI sources are in "src/" relative to package root. /// If package cannot be found, null is returned. -Future findSources(String packageName) async { +Future findSources(String packageName, String subDirectory) async { final packageConfig = await findPackageConfig(Directory.current); if (packageConfig == null) { throw UnsupportedError("Please run from project root."); @@ -106,7 +106,7 @@ Future findSources(String packageName) async { if (package == null) { throw UnsupportedError("Cannot find package: $packageName"); } - return package.root.resolve("src").toFilePath(); + return package.root.resolve(subDirectory).toFilePath(); } /// Return '/src' directories of all dependencies which has a CMakeLists.txt @@ -170,7 +170,8 @@ void main(List arguments) async { final sources = options.sources; for (var packageName in options.packages) { - sources.add(await findSources(packageName)); + // It's assumed C FFI sources are in "src/" relative to package root. + sources.add(await findSources(packageName, 'src')); } if (sources.isEmpty) { final dependencySources = await findDependencySources(); @@ -185,6 +186,28 @@ void main(List arguments) async { exitCode = 1; return; } + + final currentDirUri = Uri.directory("."); + final buildPath = options.buildPath ?? + currentDirUri.resolve(_defaultRelativeBuildPath).toFilePath(); + final buildDir = Directory(buildPath); + await buildDir.create(recursive: true); + + final javaSrc = await findSources('jni', 'java'); + final targetJar = File.fromUri(buildDir.uri.resolve('jni.jar')); + if (!needsBuild(targetJar, Directory.fromUri(Uri.directory(javaSrc)))) { + verboseLog('Last modified of ${targetJar.path}: ' + '${targetJar.lastModifiedSync()}.'); + stderr.writeln('Target newer than source, skipping build.'); + } else { + verboseLog('Running mvn package for jni java sources to $buildPath.'); + await runCommand( + 'mvn', + ['package', '-Dtarget=${buildDir.absolute.path}'], + await findSources('jni', 'java'), + ); + } + for (var srcPath in sources) { final srcDir = Directory(srcPath); if (!srcDir.existsSync()) { @@ -194,20 +217,14 @@ void main(List arguments) async { } verboseLog("srcPath: $srcPath"); - - final currentDirUri = Uri.directory("."); - final buildPath = options.buildPath ?? - currentDirUri.resolve(_defaultRelativeBuildPath).toFilePath(); - final buildDir = Directory(buildPath); - await buildDir.create(recursive: true); verboseLog("buildPath: $buildPath"); final targetFileUri = buildDir.uri.resolve(getTargetName(srcDir)); final targetFile = File.fromUri(targetFileUri); if (!needsBuild(targetFile, srcDir)) { - verboseLog("last modified of ${targetFile.path}: " - "${targetFile.lastModifiedSync()}"); - stderr.writeln("target newer than source, skipping build"); + verboseLog("Last modified of ${targetFile.path}: " + "${targetFile.lastModifiedSync()}."); + stderr.writeln('Target newer than source, skipping build.'); continue; } diff --git a/jni/ffigen.yaml b/jni/ffigen.yaml index 1704f430..6e741507 100644 --- a/jni/ffigen.yaml +++ b/jni/ffigen.yaml @@ -37,6 +37,11 @@ functions: - 'release_lock' - 'init_lock' - 'destroy_lock' + - 'init_cond' + - 'destroy_cond' + - 'signal_cond' + - 'wait_for' + - 'thread_id' - 'load_class' - 'load_class_global_ref' - 'attach_thread' @@ -44,11 +49,15 @@ functions: - 'load_static_method' - 'load_field' - 'load_static_field' + - 'load_class_platform' + - 'load_class_local_ref' - 'to_global_ref' + - 'to_global_ref_result' - 'check_exception' - 'load_env' - # This is a native function in Java. No need to call it from Dart. + # Native functions in Java. No need to call them from Dart. - 'Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith' + - 'Java_com_github_dart_1lang_jni_PortProxy__1invoke' structs: exclude: - 'JniContext' @@ -57,6 +66,7 @@ structs: - '_JNIEnv' - 'JNIInvokeInterface' - '__va_list_tag' + - 'CallbackResult' rename: ## opaque struct definitions, base types of jfieldID and jmethodID '_jfieldID': 'jfieldID_' diff --git a/jni/java/pom.xml b/jni/java/pom.xml index 62f121e3..2bd6f88d 100644 --- a/jni/java/pom.xml +++ b/jni/java/pom.xml @@ -16,6 +16,7 @@ jni + ${target} diff --git a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java index 28804a52..390f0fa0 100644 --- a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -13,19 +13,16 @@ import java.util.concurrent.TimeUnit; public class PortProxy implements InvocationHandler { + static { + System.loadLibrary("dartjni"); + } + private final long port; - private final Map> map; + private final long threadId; - private PortProxy(long port) { + private PortProxy(long port, long threadId) { this.port = port; - this.map = new ConcurrentHashMap<>(); - } - - private BlockingQueue queueOf(String key) { - if (!map.containsKey(key)) { - map.put(key, new ArrayBlockingQueue<>(1)); - } - return map.get(key); + this.threadId = threadId; } private static String getDescriptor(Method method) { @@ -67,22 +64,16 @@ private static void appendType(StringBuilder descriptor, Class type) { } } - public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { + public static Object newInstance(String binaryName, long port, long threadId) throws ClassNotFoundException { Class clazz = Class.forName(binaryName); - return Proxy.newProxyInstance( - clazz.getClassLoader(), clazz.getInterfaces(), new PortProxy(port)); + return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new PortProxy(port, threadId)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String uuid = UUID.randomUUID().toString(); - _invoke(port, uuid, proxy, getDescriptor(method), args); - return queueOf(uuid).poll(10, TimeUnit.SECONDS); - } - - public void resultFor(String uuid, Object object) { - queueOf(uuid).offer(object); + return _invoke(port, threadId, proxy, getDescriptor(method), args); } - private native void _invoke(long port, String uuid, Object proxy, String methodDescriptor, Object[] args); + private native Object _invoke( + long port, long threadId, Object proxy, String methodDescriptor, Object[] args); } diff --git a/jni/lib/src/jni.dart b/jni/lib/src/jni.dart index a2d55b04..7cb0fb84 100644 --- a/jni/lib/src/jni.dart +++ b/jni/lib/src/jni.dart @@ -36,6 +36,7 @@ DynamicLibrary _loadDartJniLibrary({String? dir, String baseName = "dartjni"}) { final fileName = _getLibraryFileName(baseName); final libPath = (dir != null) ? join(dir, fileName) : fileName; try { + print(libPath); final dylib = DynamicLibrary.open(libPath); return dylib; } on Error { @@ -314,10 +315,10 @@ extension ProtectedJniExtensions on Jni { .object; } - /// Return the result of a callback specified by the [uuid]. - static void returnResultFor( - JObjectPtr proxy, JStringPtr uuid, JObjectPtr result) { - Jni._bindings.PortProxy__resultFor(proxy, uuid, result).check(); + /// Return the result of a callback.. + static void returnResult( + Pointer result, JObjectPtr object) async { + Jni._bindings.resultFor(result, object); } } diff --git a/jni/lib/src/method_invocation.dart b/jni/lib/src/method_invocation.dart index 06679433..35bfd92d 100644 --- a/jni/lib/src/method_invocation.dart +++ b/jni/lib/src/method_invocation.dart @@ -2,21 +2,37 @@ // 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:ffi'; + +import 'third_party/generated_bindings.dart'; + import 'lang/jstring.dart'; import 'jarray.dart'; import 'jobject.dart'; class MethodInvocation { - final JString uuid; + final Pointer result; final JString methodDescriptor; final JArray args; - MethodInvocation._(this.uuid, this.methodDescriptor, this.args); + MethodInvocation._(this.result, this.methodDescriptor, this.args); + + factory MethodInvocation._fromAddresses( + int resultAddress, + int descriptorAddress, + int argsAddress, + ) { + return MethodInvocation._( + Pointer.fromAddress(resultAddress), + JString.fromRef(Pointer.fromAddress(descriptorAddress)), + JArray.fromRef( + const JObjectType(), + Pointer.fromAddress(argsAddress), + ), + ); + } factory MethodInvocation.fromMessage(List message) { - final uuid = JString.fromRef(message[0]); - final methodDescriptor = JString.fromRef(message[1]); - final args = JArray.fromRef(const JObjectType(), message[2]); - return MethodInvocation._(uuid, methodDescriptor, args); + return MethodInvocation._fromAddresses(message[0], message[1], message[2]); } } diff --git a/jni/lib/src/third_party/jni_bindings_generated.dart b/jni/lib/src/third_party/jni_bindings_generated.dart index 85d038be..6ed523fe 100644 --- a/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/jni/lib/src/third_party/jni_bindings_generated.dart @@ -178,6 +178,23 @@ class JniBindings { late final _InitDartApiDL = _InitDartApiDLPtr.asFunction)>(); + void resultFor( + ffi.Pointer result, + JObjectPtr object, + ) { + return _resultFor( + result, + object, + ); + } + + late final _resultForPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, JObjectPtr)>>('resultFor'); + late final _resultFor = _resultForPtr + .asFunction, JObjectPtr)>(); + JniResult PortContinuation__ctor( int j, ) { @@ -208,55 +225,6 @@ class JniBindings { late final _PortProxy__newInstance = _PortProxy__newInstancePtr.asFunction< JniResult Function(JObjectPtr, int)>(); - JniResult PortProxy__resultFor( - JObjectPtr self_, - JObjectPtr uuid, - JObjectPtr object, - ) { - return _PortProxy__resultFor( - self_, - uuid, - object, - ); - } - - late final _PortProxy__resultForPtr = _lookup< - ffi.NativeFunction< - JniResult Function( - JObjectPtr, JObjectPtr, JObjectPtr)>>('PortProxy__resultFor'); - late final _PortProxy__resultFor = _PortProxy__resultForPtr.asFunction< - JniResult Function(JObjectPtr, JObjectPtr, JObjectPtr)>(); - - void Java_com_github_dart_1lang_jni_PortProxy__1invoke( - ffi.Pointer env, - JObjectPtr thiz, - int port, - JStringPtr uuid, - JObjectPtr proxy, - JObjectPtr method, - JObjectArrayPtr args, - ) { - return _Java_com_github_dart_1lang_jni_PortProxy__1invoke( - env, - thiz, - port, - uuid, - proxy, - method, - args, - ); - } - - late final _Java_com_github_dart_1lang_jni_PortProxy__1invokePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer, JObjectPtr, JLongMarker, - JStringPtr, JObjectPtr, JObjectPtr, JObjectArrayPtr)>>( - 'Java_com_github_dart_1lang_jni_PortProxy__1invoke'); - late final _Java_com_github_dart_1lang_jni_PortProxy__1invoke = - _Java_com_github_dart_1lang_jni_PortProxy__1invokePtr.asFunction< - void Function(ffi.Pointer, JObjectPtr, int, JStringPtr, - JObjectPtr, JObjectPtr, JObjectArrayPtr)>(); - ffi.Pointer GetGlobalEnv() { return _GetGlobalEnv(); } @@ -2001,6 +1969,41 @@ class JavaVMOption extends ffi.Struct { external ffi.Pointer extraInfo; } +class CallbackResult extends ffi.Struct { + external MutexLock lock; + + external ConditionVariable cond; + + @ffi.Int() + external int ready; + + external JObjectPtr object; +} + +typedef MutexLock = pthread_mutex_t; +typedef pthread_mutex_t = __darwin_pthread_mutex_t; +typedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t; + +class _opaque_pthread_mutex_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([56]) + external ffi.Array __opaque; +} + +typedef ConditionVariable = pthread_cond_t; +typedef pthread_cond_t = __darwin_pthread_cond_t; +typedef __darwin_pthread_cond_t = _opaque_pthread_cond_t; + +class _opaque_pthread_cond_t extends ffi.Struct { + @ffi.Long() + external int __sig; + + @ffi.Array.multi([40]) + external ffi.Array __opaque; +} + class GlobalJniEnvStruct extends ffi.Struct { external ffi.Pointer reserved0; diff --git a/jni/pubspec.yaml b/jni/pubspec.yaml index 0c1068a3..6fa45cc8 100644 --- a/jni/pubspec.yaml +++ b/jni/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.6.0-dev.1 repository: https://github.com/dart-lang/jnigen/tree/main/jni environment: - sdk: '>=2.17.1 <4.0.0' + sdk: '>=2.19.0 <4.0.0' flutter: '>=2.11.0' dependencies: diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index 42188b04..eaa13533 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -616,56 +616,70 @@ JniResult PortProxy__newInstance(jobject binaryName, int64_t port) { if (_c_PortProxy == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; load_static_method(_c_PortProxy, &_m_PortProxy__newInstance, "newInstance", - "(Ljava/lang/String;J)Ljava/lang/Object;"); + "(Ljava/lang/String;JJ)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); + jniEnv, _c_PortProxy, _m_PortProxy__newInstance, binaryName, port, + thread_id()); return to_global_ref_result(_result); } -jmethodID _m_PortProxy__resultFor = NULL; FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object) { - 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_method(_c_PortProxy, &_m_PortProxy__resultFor, "resultFor", - "(Ljava/lang/String;Ljava/lang/Object;)V"); - if (_m_PortProxy__resultFor == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_PortProxy__resultFor, uuid, - object); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +void resultFor(CallbackResult* result, jobject object) { + acquire_lock(&result->lock); + result->ready = 1; + result->object = object; + signal_cond(&result->cond); + release_lock(&result->lock); } -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, jlong port, - jstring uuid, + jlong threadId, jobject proxy, - jobject methodDescriptor, + jstring methodDescriptor, jobjectArray args) { - attach_thread(); - Dart_CObject c_uuid; - c_uuid.type = Dart_CObject_kInt64; - c_uuid.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, uuid)); - - 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_uuid, &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]); + if (threadId != thread_id()) { + attach_thread(); + + CallbackResult result; + result.ready = 0; + result.object = NULL; + init_lock(&result.lock); + init_cond(&result.cond); + + acquire_lock(&result.lock); + + 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); + release_lock(&result.lock); + while (!result.ready) { + wait_for(&result.lock, &result.cond); + } + destroy_lock(&result.lock); + destroy_cond(&result.cond); + return result.object; + } - Dart_PostCObject_DL(port, &c_post); } \ No newline at end of file diff --git a/jni/src/dartjni.h b/jni/src/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jni/src/dartjni.h +++ b/jni/src/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index f7b7a461..0a68dbc1 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1574,7 +1574,7 @@ class EmojiCompat_GlyphChecker extends jni.JObject { $p.listen(($m) { final $i = MethodInvocation.fromMessage($m); final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $u = $i.uuid; + final $c = $i.result; final $a = $i.args; if ($d == r"hasGlyph(Ljava/lang/CharSequence;III)Z") { final $r = hasGlyph( @@ -1589,9 +1589,8 @@ class EmojiCompat_GlyphChecker extends jni.JObject { .castTo(const jni.JIntegerType(), deleteOriginal: true) .intValue(deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.toJBoolean().reference, ); return; @@ -1688,7 +1687,7 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { $p.listen(($m) { final $i = MethodInvocation.fromMessage($m); final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $u = $i.uuid; + final $c = $i.result; final $a = $i.args; if ($d == r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V") { @@ -1696,9 +1695,8 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, jni.nullptr, ); return; @@ -1959,16 +1957,15 @@ class EmojiCompat_SpanFactory extends jni.JObject { $p.listen(($m) { final $i = MethodInvocation.fromMessage($m); final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $u = $i.uuid; + final $c = $i.result; final $a = $i.args; if ($d == r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;") { final $r = createSpan( $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.reference, ); return; diff --git a/jnigen/example/in_app_java/src/android_utils/dartjni.h b/jnigen/example/in_app_java/src/android_utils/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/kotlin_plugin/src/dartjni.h b/jnigen/example/kotlin_plugin/src/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/jnigen/example/kotlin_plugin/src/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/notification_plugin/src/dartjni.h b/jnigen/example/notification_plugin/src/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/example/notification_plugin/src/dartjni.h +++ b/jnigen/example/notification_plugin/src/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 9e29cd81..3438409f 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -437,7 +437,7 @@ class $name$typeParamsDef extends $superName { \$p.listen((\$m) { final \$i = MethodInvocation.fromMessage(\$m); final \$d = \$i.methodDescriptor.toDartString(deleteOriginal: true); - final \$u = \$i.uuid; + final \$c = \$i.result; final \$a = \$i.args; '''); final proxyMethodIf = _ProxyMethodIf(resolver, s); @@ -1464,9 +1464,8 @@ class _ProxyMethodIf extends Visitor { const returnBox = _ProxyReturnBox(); s.write(''' ); - ProtectedJniExtensions.returnResultFor( - \$x.reference, - \$u.reference, + ProtectedJniExtensions.returnResult( + \$c, ${node.returnType.accept(returnBox)}, ); return; diff --git a/jnigen/pubspec.yaml b/jnigen/pubspec.yaml index ee704308..0e745b2a 100644 --- a/jnigen/pubspec.yaml +++ b/jnigen/pubspec.yaml @@ -8,7 +8,7 @@ description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen environment: - sdk: '>=2.17.0 <4.0.0' + sdk: '>=2.19.0 <4.0.0' dependencies: json_annotation: ^4.8.0 diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/kotlin_test/jni.jar b/jnigen/test/kotlin_test/jni.jar deleted file mode 100644 index 37a1bfc8ee9b578163cffca1479743f667050a80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2755 zcmWIWW@h1H0D+Cg_F-TKl;8x?zOEsTx}JV+`TRsgZ*3~+9=KSU$gDb`lo)+nNojal9t?R_W{$xqm6fx}s zDiu5DbO#B02L+eR)mkee!!0z~I7qZc_;k=`k)E$QU(NvKy^M>!4s4qGbLr8QnKK0z zio~v;lCwbfSruE=vOO_-Vi;h~V_;y(0R%Exo{6)s^EsINY5OxU^*DWx};_fP?<9o@87o69dB*po{hKYRfMwan8>x$;>NFEXmBz z(@V}tEH3U1ew`=eDDtoE_MvH)ytuDm#S}@*L8O zO!KYOo?Vgo=OOctfMdqotw9EvCyL(PuX#RarhR?=U$zfI`XMX=Ap%{>JsgLtuK!5N zusxiYef;dYRIWCU&f76lB%5x=D7R@Oab2+p6G%TX(=R@|_~OS|QKt)zcE;b_y7Sw? zLf4}kS-0Cd7aULH;5~8lz$z`Fh~N!!$9YvGjpoZNHnWG<4BUNq4TywyDC`rK)OQD_>0UNMHTys$BO@H<4(b zuAIgbt9SHTbGd!pRC0TH~eb3S==-2-0XD^`y_&HdQUKYShU-B0@pj=*|VQp zn@`O#y{-^g7$tUimY?SPy>*w1Ub}5}pSR@Q47c@(J7R7Lr+buG@Ge^rU;W4RRgGMx z$qonWJ$61T@M?Nes{CI0!bay&fI&V zb}Va8uJobe&cA^x|798O-SZ;qRH0|K>G5LU*Sgaed&bHhXlyxJyDN^-;LU~UQ}i3} z1h$(Uk20v!aDmu%bt>qbhKIUzeV0tsfG8*hR*hsrJVhnK5|qytoICQ ztG%>T=J+O+mlqZ7EuWftN^;Ll{`y+P{?YGkHFpYgGn#hWKbKrA`k|sJOnv=%rTPEa zmVRKCHa@pX{-)Z#ng2i3HZ`Z#L^|wq+O+sWP1>g=vG&a~)bi&&zBcoGa@i@Zvxbhh z6kjsh95s%&`_GQZR_KKTKQM*D3y0jqveZ0ya>gu4q%jre7p3c^RwU*Yz&W(48awqQTaTz$-zB?KmswQ8 zTPK_j{Pm+??%d_#Timwt;iKT zsPkb${c;l#<*8Z=YPx1GQ1tpBcddKR(!lK6=z#frj%T%EnGViRSYq(_NK@hSM$y{_ zP1X)6Ceq969^TBUsO2pMT z?pk;{gp`z<%0GAK0OK|ri1mn!n0FaIs zKqg2ByuQS(5maMB07#=CFkd2RM6N$^8v&|85k|P+G6GVc!i>RQqrwbY(pZhx7_9Xx za)@Fp3}J5k|6dju!0@Pq6^$^LVJ{pJn!PYJqZXC84N`#l5163-|8K@`5VYuonTEaa zgc-P`@gNbVL5osEKw;0V2t${$VFVPiU%^fU8H&ie0p6@YJq!#yKp4Qxz!1w0;sF54 Cwu%=3 diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 9b7d8b83..44eed945 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + DeleteCriticalSection(cond); +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,49 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +static inline uint64_t thread_id() { +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return pthread_mach_thread_np(pthread_self()); +#else + return pthread_self(); +#endif +} + +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,6 +420,8 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); + JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, jobject thiz, @@ -380,14 +433,11 @@ JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT JniResult PortProxy__newInstance(jobject binaryName, int64_t port); -FFI_PLUGIN_EXPORT -JniResult PortProxy__resultFor(jobject self_, jobject uuid, jobject object); - -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jstring uuid, - jobject proxy, - jobject methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c index ba2389c7..ed6da52a 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c +++ b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c @@ -2405,10 +2405,10 @@ JniResult MyInterfaceConsumer__ctor() { return to_global_ref_result(_result); } -jmethodID _m_MyInterfaceConsumer__consumeMyInterface = NULL; +jmethodID _m_MyInterfaceConsumer__consumeOnAnotherThread = NULL; FFI_PLUGIN_EXPORT -JniResult MyInterfaceConsumer__consumeMyInterface(jobject myInterface, - jobject s) { +JniResult MyInterfaceConsumer__consumeOnAnotherThread(jobject myInterface, + jobject s) { load_env(); load_class_global_ref( &_c_MyInterfaceConsumer, @@ -2416,15 +2416,15 @@ JniResult MyInterfaceConsumer__consumeMyInterface(jobject myInterface, if (_c_MyInterfaceConsumer == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; load_static_method(_c_MyInterfaceConsumer, - &_m_MyInterfaceConsumer__consumeMyInterface, - "consumeMyInterface", + &_m_MyInterfaceConsumer__consumeOnAnotherThread, + "consumeOnAnotherThread", "(Lcom/github/dart_lang/jnigen/interfaces/" "MyInterface;Ljava/lang/String;)V"); - if (_m_MyInterfaceConsumer__consumeMyInterface == NULL) + if (_m_MyInterfaceConsumer__consumeOnAnotherThread == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallStaticVoidMethod(jniEnv, _c_MyInterfaceConsumer, - _m_MyInterfaceConsumer__consumeMyInterface, - myInterface, s); + (*jniEnv)->CallStaticVoidMethod( + jniEnv, _c_MyInterfaceConsumer, + _m_MyInterfaceConsumer__consumeOnAnotherThread, myInterface, s); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index 76baf4e8..eaeabfe1 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -2947,15 +2947,14 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { $p.listen(($m) { final $i = MethodInvocation.fromMessage($m); final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $u = $i.uuid; + final $c = $i.result; final $a = $i.args; if ($d == r"voidCallback(Ljava/lang/String;)V") { voidCallback( $a[0].castTo(const jni.JStringType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, jni.nullptr, ); return; @@ -2964,9 +2963,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $r = stringCallback( $a[0].castTo(const jni.JStringType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.reference, ); return; @@ -2975,9 +2973,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $r = varCallback( $a[0].castTo(T, deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.reference, ); return; @@ -2997,9 +2994,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { .castTo(const jni.JDoubleType(), deleteOriginal: true) .doubleValue(deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.toJLong().reference, ); return; @@ -3062,21 +3058,25 @@ class MyInterfaceConsumer extends jni.JObject { return MyInterfaceConsumer.fromRef(_ctor().object); } - static final _consumeMyInterface = jniLookup< + static final _consumeOnAnotherThread = jniLookup< ffi.NativeFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>>( - "MyInterfaceConsumer__consumeMyInterface") + "MyInterfaceConsumer__consumeOnAnotherThread") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: static public void consumeMyInterface(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) - static void consumeMyInterface( - MyInterface myInterface, - jni.JString s, - ) { - return _consumeMyInterface(myInterface.reference, s.reference).check(); + /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeOnAnotherThread<$T extends jni.JObject>( + MyInterface<$T> myInterface, + jni.JString s, { + jni.JObjType<$T>? T, + }) { + T ??= jni.lowestCommonSuperType([ + (myInterface.$type as $MyInterfaceType).T, + ]) as jni.JObjType<$T>; + return _consumeOnAnotherThread(myInterface.reference, s.reference).check(); } } diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 19f969d9..094f7a59 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2786,15 +2786,14 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { $p.listen(($m) { final $i = MethodInvocation.fromMessage($m); final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $u = $i.uuid; + final $c = $i.result; final $a = $i.args; if ($d == r"voidCallback(Ljava/lang/String;)V") { voidCallback( $a[0].castTo(const jni.JStringType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, jni.nullptr, ); return; @@ -2803,9 +2802,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $r = stringCallback( $a[0].castTo(const jni.JStringType(), deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.reference, ); return; @@ -2814,9 +2812,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $r = varCallback( $a[0].castTo(T, deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.reference, ); return; @@ -2836,9 +2833,8 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { .castTo(const jni.JDoubleType(), deleteOriginal: true) .doubleValue(deleteOriginal: true), ); - ProtectedJniExtensions.returnResultFor( - $x.reference, - $u.reference, + ProtectedJniExtensions.returnResult( + $c, $r.toJLong().reference, ); return; @@ -2904,19 +2900,23 @@ class MyInterfaceConsumer extends jni.JObject { .newObjectWithArgs(_class.reference, _id_ctor, []).object); } - static final _id_consumeMyInterface = jni.Jni.accessors.getStaticMethodIDOf( + static final _id_consumeOnAnotherThread = jni.Jni.accessors.getStaticMethodIDOf( _class.reference, - r"consumeMyInterface", + r"consumeOnAnotherThread", r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;)V"); - /// from: static public void consumeMyInterface(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) - static void consumeMyInterface( - MyInterface myInterface, - jni.JString s, - ) { + /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeOnAnotherThread<$T extends jni.JObject>( + MyInterface<$T> myInterface, + jni.JString s, { + jni.JObjType<$T>? T, + }) { + T ??= jni.lowestCommonSuperType([ + (myInterface.$type as $MyInterfaceType).T, + ]) as jni.JObjType<$T>; return jni.Jni.accessors.callStaticMethodWithArgs( _class.reference, - _id_consumeMyInterface, + _id_consumeOnAnotherThread, jni.JniCallType.voidType, [myInterface.reference, s.reference]).check(); } diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java index 8deb7aed..0d88b478 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java @@ -5,8 +5,13 @@ package com.github.dart_lang.jnigen.interfaces; public class MyInterfaceConsumer { - public static void consumeMyInterface(MyInterface myInterface, String s) { - String result = myInterface.stringCallback(s); - myInterface.voidCallback(result); + public static void consumeOnAnotherThread(MyInterface myInterface, String s) { + var thread = + new Thread( + () -> { + String result = myInterface.stringCallback(s); + myInterface.voidCallback(result); + }); + thread.start(); } } diff --git a/jnigen/test/simple_package_test/runtime_test_registrant.dart b/jnigen/test/simple_package_test/runtime_test_registrant.dart index f3075ac7..8c362f05 100644 --- a/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -528,6 +528,32 @@ void registerTests(String groupName, TestRunnerCallback test) { }); }); }); + + group('interface implementation', () { + test('MyInterface.implement', () { + final myInterface = MyInterface.implement( + voidCallback: (s) { + print(s.toDartString()); + }, + stringCallback: (s) { + print(s.toDartString()); + return (s.toDartString(deleteOriginal: true) * 2).toJString(); + }, + varCallback: (t) { + return "".toJString(); + }, + manyPrimitives: (a, b, c, d) { + return 0; + }, + T: JString.type, + ); + MyInterfaceConsumer.consumeOnAnotherThread( + myInterface, + "hello".toJString(), + ); + }); + }); + group('$groupName (load tests)', () { const k4 = 4 * 1024; // This is a round number, unlike say 4000 const k256 = 256 * 1024; diff --git a/jnigen/test/test_util/bindings_test_setup.dart b/jnigen/test/test_util/bindings_test_setup.dart index b4d292cb..c493ad14 100644 --- a/jnigen/test/test_util/bindings_test_setup.dart +++ b/jnigen/test/test_util/bindings_test_setup.dart @@ -19,7 +19,7 @@ import 'test_util.dart'; final simplePackageTest = join('test', 'simple_package_test'); final jacksonCoreTest = join('test', 'jackson_core_test'); final kotlinTest = join('test', 'kotlin_test'); -final jniJar = join(kotlinTest, 'jni.jar'); +final jniJar = join('build', 'jni_libs', 'jni.jar'); final simplePackageTestJava = join(simplePackageTest, 'java'); final kotlinTestKotlin = join(kotlinTest, 'kotlin'); From 13d0ada23473287c515005abf1b69fd37e1dea31 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 01:03:04 +0200 Subject: [PATCH 09/25] call the function pointer when on the same thread --- .../com/github/dart_lang/jni/PortProxy.java | 43 ++- .../com/github/dart_lang/jni/PortProxy.java | 25 +- jni/lib/src/jni.dart | 9 +- jni/lib/src/method_invocation.dart | 12 +- .../third_party/jni_bindings_generated.dart | 11 +- jni/src/dartjni.c | 20 +- jni/src/dartjni.h | 17 +- .../in_app_java/lib/android_utils.dart | 271 +++++++++++++----- .../in_app_java/src/android_utils/dartjni.h | 17 +- jnigen/example/kotlin_plugin/src/dartjni.h | 17 +- .../example/notification_plugin/src/dartjni.h | 17 +- .../pdfbox_plugin/src/third_party/dartjni.h | 17 +- jnigen/lib/src/bindings/dart_generator.dart | 163 ++++++++--- .../third_party/c_based/c_bindings/dartjni.h | 17 +- .../kotlin_test/c_based/c_bindings/dartjni.h | 17 +- .../c_based/c_bindings/dartjni.h | 17 +- .../c_based/c_bindings/simple_package.c | 23 ++ .../c_based/dart_bindings/simple_package.dart | 178 ++++++++---- .../dart_bindings/simple_package.dart | 178 ++++++++---- .../interfaces/MyInterfaceConsumer.java | 10 +- .../runtime_test_registrant.dart | 36 ++- 21 files changed, 776 insertions(+), 339 deletions(-) diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java index 53871125..a75a914f 100644 --- a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -5,12 +5,6 @@ package com.github.dart_lang.jni; import java.lang.reflect.*; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; public class PortProxy implements InvocationHandler { static { @@ -18,17 +12,13 @@ public class PortProxy implements InvocationHandler { } private final long port; - private static final Map> map = new ConcurrentHashMap<>(); + private final long threadId; + private final long functionPtr; - private PortProxy(long port) { + private PortProxy(long port, long threadId, long functionPtr) { this.port = port; - } - - private static BlockingQueue queueOf(String key) { - if (!map.containsKey(key)) { - map.put(key, new ArrayBlockingQueue<>(1)); - } - return map.get(key); + this.threadId = threadId; + this.functionPtr = functionPtr; } private static String getDescriptor(Method method) { @@ -70,22 +60,23 @@ private static void appendType(StringBuilder descriptor, Class type) { } } - public static Object newInstance(String binaryName, long port) throws ClassNotFoundException { + public static Object newInstance(String binaryName, long port, long threadId, long functionPtr) + throws ClassNotFoundException { Class clazz = Class.forName(binaryName); - return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, new PortProxy(port)); + return Proxy.newProxyInstance( + clazz.getClassLoader(), new Class[] {clazz}, new PortProxy(port, threadId, functionPtr)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String uuid = UUID.randomUUID().toString(); - _invoke(port, uuid, proxy, getDescriptor(method), args); - return queueOf(uuid).poll(10, TimeUnit.SECONDS); - } - - public static void resultFor(String uuid, Object object) { - queueOf(uuid).offer(object); + return _invoke(port, threadId, functionPtr, proxy, getDescriptor(method), args); } - private native void _invoke( - long port, String uuid, Object proxy, String methodDescriptor, Object[] args); + private native Object _invoke( + long port, + long threadId, + long functionPtr, + Object proxy, + String methodDescriptor, + Object[] args); } diff --git a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java index 390f0fa0..a75a914f 100644 --- a/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ b/jni/java/src/main/java/com/github/dart_lang/jni/PortProxy.java @@ -5,12 +5,6 @@ package com.github.dart_lang.jni; import java.lang.reflect.*; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; public class PortProxy implements InvocationHandler { static { @@ -19,10 +13,12 @@ public class PortProxy implements InvocationHandler { private final long port; private final long threadId; + private final long functionPtr; - private PortProxy(long port, long threadId) { + private PortProxy(long port, long threadId, long functionPtr) { this.port = port; this.threadId = threadId; + this.functionPtr = functionPtr; } private static String getDescriptor(Method method) { @@ -64,16 +60,23 @@ private static void appendType(StringBuilder descriptor, Class type) { } } - public static Object newInstance(String binaryName, long port, long threadId) throws ClassNotFoundException { + public static Object newInstance(String binaryName, long port, long threadId, long functionPtr) + throws ClassNotFoundException { Class clazz = Class.forName(binaryName); - return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new PortProxy(port, threadId)); + return Proxy.newProxyInstance( + clazz.getClassLoader(), new Class[] {clazz}, new PortProxy(port, threadId, functionPtr)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return _invoke(port, threadId, proxy, getDescriptor(method), args); + return _invoke(port, threadId, functionPtr, proxy, getDescriptor(method), args); } private native Object _invoke( - long port, long threadId, Object proxy, String methodDescriptor, Object[] args); + long port, + long threadId, + long functionPtr, + Object proxy, + String methodDescriptor, + Object[] args); } diff --git a/jni/lib/src/jni.dart b/jni/lib/src/jni.dart index 7cb0fb84..1ff861e1 100644 --- a/jni/lib/src/jni.dart +++ b/jni/lib/src/jni.dart @@ -306,11 +306,18 @@ extension ProtectedJniExtensions on Jni { } /// Returns a new PortProxy for a class with the given [binaryName]. - static JObjectPtr newPortProxy(String binaryName, ReceivePort port) { + static JObjectPtr newPortProxy( + String binaryName, + ReceivePort port, + Pointer< + NativeFunction< + Pointer Function(Uint64, Pointer, Pointer)>> + functionPtr) { return Jni._bindings .PortProxy__newInstance( Jni.env.toJStringPtr(binaryName), port.sendPort.nativePort, + functionPtr.address, ) .object; } diff --git a/jni/lib/src/method_invocation.dart b/jni/lib/src/method_invocation.dart index 35bfd92d..69cdc4a0 100644 --- a/jni/lib/src/method_invocation.dart +++ b/jni/lib/src/method_invocation.dart @@ -10,19 +10,19 @@ import 'lang/jstring.dart'; import 'jarray.dart'; import 'jobject.dart'; -class MethodInvocation { +class $MethodInvocation { final Pointer result; final JString methodDescriptor; final JArray args; - MethodInvocation._(this.result, this.methodDescriptor, this.args); + $MethodInvocation._(this.result, this.methodDescriptor, this.args); - factory MethodInvocation._fromAddresses( + factory $MethodInvocation.fromAddresses( int resultAddress, int descriptorAddress, int argsAddress, ) { - return MethodInvocation._( + return $MethodInvocation._( Pointer.fromAddress(resultAddress), JString.fromRef(Pointer.fromAddress(descriptorAddress)), JArray.fromRef( @@ -32,7 +32,7 @@ class MethodInvocation { ); } - factory MethodInvocation.fromMessage(List message) { - return MethodInvocation._fromAddresses(message[0], message[1], message[2]); + factory $MethodInvocation.fromMessage(List message) { + return $MethodInvocation.fromAddresses(message[0], message[1], message[2]); } } diff --git a/jni/lib/src/third_party/jni_bindings_generated.dart b/jni/lib/src/third_party/jni_bindings_generated.dart index 6ed523fe..ef83bb57 100644 --- a/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/jni/lib/src/third_party/jni_bindings_generated.dart @@ -212,18 +212,21 @@ class JniBindings { JniResult PortProxy__newInstance( JObjectPtr binaryName, int port, + int functionPtr, ) { return _PortProxy__newInstance( binaryName, port, + functionPtr, ); } - late final _PortProxy__newInstancePtr = - _lookup>( - 'PortProxy__newInstance'); + late final _PortProxy__newInstancePtr = _lookup< + ffi.NativeFunction< + JniResult Function( + JObjectPtr, ffi.Int64, ffi.Int64)>>('PortProxy__newInstance'); late final _PortProxy__newInstance = _PortProxy__newInstancePtr.asFunction< - JniResult Function(JObjectPtr, int)>(); + JniResult Function(JObjectPtr, int, int)>(); ffi.Pointer GetGlobalEnv() { return _GetGlobalEnv(); diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index eaa13533..b00cfb20 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -610,18 +610,20 @@ jclass _c_PortProxy = NULL; jmethodID _m_PortProxy__newInstance = NULL; FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port) { +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;JJ)Ljava/lang/Object;"); + "(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, - thread_id()); + thread_id(), functionPtr); return to_global_ref_result(_result); } @@ -639,11 +641,13 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobject thiz, jlong port, jlong threadId, + jlong functionPtr, jobject proxy, jstring methodDescriptor, jobjectArray args) { + attach_thread(); if (threadId != thread_id()) { - attach_thread(); + printf("on a different thread!!!\n"); CallbackResult result; result.ready = 0; @@ -652,7 +656,7 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, init_cond(&result.cond); acquire_lock(&result.lock); - + Dart_CObject c_result; c_result.type = Dart_CObject_kInt64; c_result.value.as_int64 = (jlong)(&result); @@ -680,6 +684,10 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, destroy_lock(&result.lock); destroy_cond(&result.cond); return result.object; + } else { + printf("on the same thread\n"); + return ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( + port, (*env)->NewGlobalRef(env, methodDescriptor), + (*env)->NewGlobalRef(env, args)); } - } \ No newline at end of file diff --git a/jni/src/dartjni.h b/jni/src/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jni/src/dartjni.h +++ b/jni/src/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 0a68dbc1..264e9cf6 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1548,18 +1548,74 @@ class EmojiCompat_GlyphChecker extends jni.JObject { .boolean; } + /// Maps a specific port to the implemented methods. + static final Map> _$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _$types = {}; + ReceivePort? _$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _$finalizer = Finalizer(($p) { + _$methods.remove($p.sendPort.nativePort); + _$types.remove($p.sendPort.nativePort); + $p.close(); + }); @override void delete() { + _$methods.remove(_$p?.sendPort.nativePort); + _$types.remove(_$p?.sendPort.nativePort); _$p?.close(); - _finalizer.detach(this); + _$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $a = $i.args; + if ($d == r"hasGlyph(Ljava/lang/CharSequence;III)Z") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), + $a[1] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + ); + return $r.toJBoolean().reference; + } + return jni.nullptr; + } + factory EmojiCompat_GlyphChecker.implement({ required bool Function( jni.JObject charSequence, int start, int end, int sdkAdded) @@ -1568,33 +1624,20 @@ class EmojiCompat_GlyphChecker extends jni.JObject { final $p = ReceivePort(); final $x = EmojiCompat_GlyphChecker.fromRef( ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$GlyphChecker", $p), + r"androidx.emoji2.text.EmojiCompat$GlyphChecker", + $p, + _$invokePointer, + ), ).._$p = $p; - _finalizer.attach($x, $p, detach: $x); + final $a = $p.sendPort.nativePort; + _$types[$a] = {}; + _$methods[$a] = {}; + _$methods[$a]![r"hasGlyph(Ljava/lang/CharSequence;III)Z"] = hasGlyph; + _$finalizer.attach($x, $p, detach: $x); $p.listen(($m) { - final $i = MethodInvocation.fromMessage($m); - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $c = $i.result; - final $a = $i.args; - if ($d == r"hasGlyph(Ljava/lang/CharSequence;III)Z") { - final $r = hasGlyph( - $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), - $a[1] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[2] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[3] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.toJBoolean().reference, - ); - return; - } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); }); return $x; } @@ -1661,18 +1704,67 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { return _load(reference, loaderCallback.reference).check(); } + /// Maps a specific port to the implemented methods. + static final Map> _$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _$types = {}; + ReceivePort? _$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _$finalizer = Finalizer(($p) { + _$methods.remove($p.sendPort.nativePort); + _$types.remove($p.sendPort.nativePort); + $p.close(); + }); @override void delete() { + _$methods.remove(_$p?.sendPort.nativePort); + _$types.remove(_$p?.sendPort.nativePort); _$p?.close(); - _finalizer.detach(this); + _$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $a = $i.args; + if ($d == + r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V") { + _$methods[$p]![$d]!( + $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), + deleteOriginal: true), + ); + return jni.nullptr; + } + return jni.nullptr; + } + factory EmojiCompat_MetadataRepoLoader.implement({ required void Function( EmojiCompat_MetadataRepoLoaderCallback loaderCallback) @@ -1681,26 +1773,22 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { final $p = ReceivePort(); final $x = EmojiCompat_MetadataRepoLoader.fromRef( ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$MetadataRepoLoader", $p), + r"androidx.emoji2.text.EmojiCompat$MetadataRepoLoader", + $p, + _$invokePointer, + ), ).._$p = $p; - _finalizer.attach($x, $p, detach: $x); + final $a = $p.sendPort.nativePort; + _$types[$a] = {}; + _$methods[$a] = {}; + _$methods[$a]![ + r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V"] = + load; + _$finalizer.attach($x, $p, detach: $x); $p.listen(($m) { - final $i = MethodInvocation.fromMessage($m); - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $c = $i.result; - final $a = $i.args; - if ($d == - r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V") { - load( - $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), - deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - jni.nullptr, - ); - return; - } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); }); return $x; } @@ -1933,43 +2021,88 @@ class EmojiCompat_SpanFactory extends jni.JObject { .fromRef(_createSpan(reference, rasterizer.reference).object); } + /// Maps a specific port to the implemented methods. + static final Map> _$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _$types = {}; + ReceivePort? _$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _$finalizer = Finalizer(($p) { + _$methods.remove($p.sendPort.nativePort); + _$types.remove($p.sendPort.nativePort); + $p.close(); + }); @override void delete() { + _$methods.remove(_$p?.sendPort.nativePort); + _$types.remove(_$p?.sendPort.nativePort); _$p?.close(); - _finalizer.detach(this); + _$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $a = $i.args; + if ($d == + r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), + ); + return $r.reference; + } + return jni.nullptr; + } + factory EmojiCompat_SpanFactory.implement({ required jni.JObject Function(jni.JObject rasterizer) createSpan, }) { final $p = ReceivePort(); final $x = EmojiCompat_SpanFactory.fromRef( ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$SpanFactory", $p), + r"androidx.emoji2.text.EmojiCompat$SpanFactory", + $p, + _$invokePointer, + ), ).._$p = $p; - _finalizer.attach($x, $p, detach: $x); + final $a = $p.sendPort.nativePort; + _$types[$a] = {}; + _$methods[$a] = {}; + _$methods[$a]![ + r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;"] = + createSpan; + _$finalizer.attach($x, $p, detach: $x); $p.listen(($m) { - final $i = MethodInvocation.fromMessage($m); - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $c = $i.result; - final $a = $i.args; - if ($d == - r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;") { - final $r = createSpan( - $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.reference, - ); - return; - } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); }); return $x; } diff --git a/jnigen/example/in_app_java/src/android_utils/dartjni.h b/jnigen/example/in_app_java/src/android_utils/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/jnigen/example/in_app_java/src/android_utils/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/kotlin_plugin/src/dartjni.h b/jnigen/example/kotlin_plugin/src/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/example/kotlin_plugin/src/dartjni.h +++ b/jnigen/example/kotlin_plugin/src/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/notification_plugin/src/dartjni.h b/jnigen/example/notification_plugin/src/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/example/notification_plugin/src/dartjni.h +++ b/jnigen/example/notification_plugin/src/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 3438409f..a33c9935 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -403,18 +403,65 @@ class $name$typeParamsDef extends $superName { // Interface implementation if (node.declKind == DeclKind.interfaceKind) { s.write(''' + /// Maps a specific port to the implemented methods. + static final Map> _\$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _\$types = {}; + ReceivePort? _\$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _\$finalizer = Finalizer((\$p) { + _\$methods.remove(\$p.sendPort.nativePort); + _\$types.remove(\$p.sendPort.nativePort); + \$p.close(); + }); @override void delete() { + _\$methods.remove(_\$p?.sendPort.nativePort); + _\$types.remove(_\$p?.sendPort.nativePort); _\$p?.close(); - _finalizer.detach(this); + _\$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _\$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _\$invokeMethod( + port, + \$MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _\$invokePointer = ffi.Pointer.fromFunction(_\$invoke); + + static ffi.Pointer _\$invokeMethod( + int \$p, + \$MethodInvocation \$i, + ) { + final \$d = \$i.methodDescriptor.toDartString(deleteOriginal: true); + final \$a = \$i.args; + '''); + final proxyMethodIf = _InterfaceMethodIf(resolver, s); + for (final method in node.methods) { + method.accept(proxyMethodIf); + } + s.write(''' + return jni.nullptr; + } + factory $name.implement({ '''); final typeClassesDef = typeParams @@ -422,7 +469,7 @@ class $name$typeParamsDef extends $superName { .join(_newLine(depth: 1)); final typeClassesCall = typeParams.map((typeParam) => '$typeParam,').join(_newLine(depth: 2)); - final methodImplementCall = _MethodImplementCall(resolver, s); + final methodImplementCall = _InterfaceImplementArg(resolver, s); for (final method in node.methods) { method.accept(methodImplementCall); } @@ -431,20 +478,31 @@ class $name$typeParamsDef extends $superName { final \$p = ReceivePort(); final \$x = $name.fromRef( $typeClassesCall - $_protectedExtension.newPortProxy(r"${node.binaryName}", \$p), + $_protectedExtension.newPortProxy( + r"${node.binaryName}", + \$p, + _\$invokePointer, + ), ).._\$p = \$p; - _finalizer.attach(\$x, \$p, detach: \$x); - \$p.listen((\$m) { - final \$i = MethodInvocation.fromMessage(\$m); - final \$d = \$i.methodDescriptor.toDartString(deleteOriginal: true); - final \$c = \$i.result; - final \$a = \$i.args; + final \$a = \$p.sendPort.nativePort; + _\$types[\$a] = {}; + _\$methods[\$a] = {}; '''); - final proxyMethodIf = _ProxyMethodIf(resolver, s); + final typeFiller = _InterfaceTypesFiller(s); + for (final typeParam in node.allTypeParams) { + typeParam.accept(typeFiller); + } + final methodFiller = _InterfaceMethodsFiller(s); for (final method in node.methods) { - method.accept(proxyMethodIf); + method.accept(methodFiller); } - s.write('''}); + s.write(''' + _\$finalizer.attach(\$x, \$p, detach: \$x); + \$p.listen((\$m) { + final \$i = \$MethodInvocation.fromMessage(\$m); + final \$r = _\$invokeMethod(\$p.sendPort.nativePort, \$i); + ProtectedJniExtensions.returnResult(\$i.result, \$r); + }); return \$x; } '''); @@ -616,13 +674,22 @@ class _TypeClass { /// Generates the type class. class _TypeClassGenerator extends TypeVisitor<_TypeClass> { final bool isConst; + + /// Whether or not to return the equivalent boxed type class for primitives. + /// Only for interface implemetation. final bool boxPrimitives; + + /// Whether or not to find the correct type variable from the static map. + /// Only for interface implemetation. + final bool typeVarFromMap; + final Resolver resolver; _TypeClassGenerator( this.resolver, { this.isConst = true, this.boxPrimitives = false, + this.typeVarFromMap = false, }); @override @@ -690,6 +757,9 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { @override _TypeClass visitTypeVar(TypeVar node) { + if (typeVarFromMap) { + return _TypeClass('_\$types[\$p]!["${node.name}"]!', false); + } return _TypeClass(node.name, false); } @@ -1426,11 +1496,11 @@ class _TypeVarLocator extends TypeVisitor>> { } /// The argument for .implement method of interfaces. -class _MethodImplementCall extends Visitor { +class _InterfaceImplementArg extends Visitor { final Resolver resolver; final StringSink s; - _MethodImplementCall(this.resolver, this.s); + _InterfaceImplementArg(this.resolver, this.s); @override void visit(Method node) { @@ -1444,31 +1514,26 @@ class _MethodImplementCall extends Visitor { } /// The if statement to check which method has been called from the proxy class. -class _ProxyMethodIf extends Visitor { +class _InterfaceMethodIf extends Visitor { final Resolver resolver; final StringSink s; - _ProxyMethodIf(this.resolver, this.s); + _InterfaceMethodIf(this.resolver, this.s); @override void visit(Method node) { final signature = node.javaSig; - final name = node.finalName; s.write(''' if (\$d == r"$signature") { - ${node.returnType.name == 'void' ? '' : 'final \$r = '}$name( + ${node.returnType.name == 'void' ? '' : 'final \$r = '}_\$methods[\$p]![\$d]!( '''); for (var i = 0; i < node.params.length; ++i) { - node.params[i].accept(_ProxyParamCast(resolver, s, paramIndex: i)); + node.params[i].accept(_InterfaceParamCast(resolver, s, paramIndex: i)); } - const returnBox = _ProxyReturnBox(); + const returnBox = _InterfaceReturnBox(); s.write(''' ); - ProtectedJniExtensions.returnResult( - \$c, - ${node.returnType.accept(returnBox)}, - ); - return; + return ${node.returnType.accept(returnBox)}; } '''); } @@ -1476,12 +1541,12 @@ class _ProxyMethodIf extends Visitor { /// Generates casting to the correct parameter type from the list of JObject /// arguments received from the call to the proxy class. -class _ProxyParamCast extends Visitor { +class _InterfaceParamCast extends Visitor { final Resolver resolver; final StringSink s; final int paramIndex; - _ProxyParamCast( + _InterfaceParamCast( this.resolver, this.s, { required this.paramIndex, @@ -1490,7 +1555,11 @@ class _ProxyParamCast extends Visitor { @override void visit(Param node) { final typeClass = node.type - .accept(_TypeClassGenerator(resolver, boxPrimitives: true)) + .accept(_TypeClassGenerator( + resolver, + boxPrimitives: true, + typeVarFromMap: true, + )) .name; s.write('\$a[$paramIndex].castTo($typeClass, deleteOriginal: true)'); if (node.type.kind == Kind.primitive) { @@ -1507,8 +1576,8 @@ class _ProxyParamCast extends Visitor { /// Returns null for void. /// /// For example `$r.toJInteger().reference` when the return type is `integer`. -class _ProxyReturnBox extends TypeVisitor { - const _ProxyReturnBox(); +class _InterfaceReturnBox extends TypeVisitor { + const _InterfaceReturnBox(); @override String visitNonPrimitiveType(ReferredType node) { @@ -1523,3 +1592,33 @@ class _ProxyReturnBox extends TypeVisitor { return '\$r.toJ${node.name.capitalize()}().reference'; } } + +/// Fills the static _$types map with the correct type classes for the given +/// port. +class _InterfaceTypesFiller extends Visitor { + final StringSink s; + + _InterfaceTypesFiller(this.s); + + @override + void visit(TypeParam node) { + s.write(''' + _\$types[\$a]!["${node.name}"] = ${node.name}; +'''); + } +} + +/// Fills the static _$method map with the correct callbacks for the given +/// port. +class _InterfaceMethodsFiller extends Visitor { + final StringSink s; + + _InterfaceMethodsFiller(this.s); + + @override + void visit(Method node) { + s.write(''' + _\$methods[\$a]![r"${node.javaSig}"] = ${node.name}; +'''); + } +} diff --git a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h +++ b/jnigen/test/jackson_core_test/third_party/c_based/c_bindings/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/kotlin_test/c_based/c_bindings/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h index 44eed945..cbb1f7f5 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h +++ b/jnigen/test/simple_package_test/c_based/c_bindings/dartjni.h @@ -431,13 +431,16 @@ FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, int64_t port); +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); JNIEXPORT jobject JNICALL Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, - jobject thiz, - jlong port, - jlong threadId, - jobject proxy, - jstring methodDescriptor, - jobjectArray args); + jobject thiz, + jlong port, + jlong threadId, + jlong functionPtr, + jobject proxy, + jstring methodDescriptor, + jobjectArray args); diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c index ed6da52a..30308c5f 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c +++ b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c @@ -2428,6 +2428,29 @@ JniResult MyInterfaceConsumer__consumeOnAnotherThread(jobject myInterface, return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } +jmethodID _m_MyInterfaceConsumer__consumeOnSameThread = NULL; +FFI_PLUGIN_EXPORT +JniResult MyInterfaceConsumer__consumeOnSameThread(jobject myInterface, + jobject s) { + load_env(); + load_class_global_ref( + &_c_MyInterfaceConsumer, + "com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer"); + if (_c_MyInterfaceConsumer == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method(_c_MyInterfaceConsumer, + &_m_MyInterfaceConsumer__consumeOnSameThread, + "consumeOnSameThread", + "(Lcom/github/dart_lang/jnigen/interfaces/" + "MyInterface;Ljava/lang/String;)V"); + if (_m_MyInterfaceConsumer__consumeOnSameThread == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallStaticVoidMethod(jniEnv, _c_MyInterfaceConsumer, + _m_MyInterfaceConsumer__consumeOnSameThread, + myInterface, s); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + // com.github.dart_lang.jnigen.annotations.JsonSerializable$Case jclass _c_JsonSerializable_Case = NULL; diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index eaeabfe1..cabc8e01 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -2918,18 +2918,94 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { return _manyPrimitives(reference, a, b ? 1 : 0, c, d).long; } + /// Maps a specific port to the implemented methods. + static final Map> _$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _$types = {}; + ReceivePort? _$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _$finalizer = Finalizer(($p) { + _$methods.remove($p.sendPort.nativePort); + _$types.remove($p.sendPort.nativePort); + $p.close(); + }); @override void delete() { + _$methods.remove(_$p?.sendPort.nativePort); + _$types.remove(_$p?.sendPort.nativePort); _$p?.close(); - _finalizer.detach(this); + _$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $a = $i.args; + if ($d == r"voidCallback(Ljava/lang/String;)V") { + _$methods[$p]![$d]!( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + return jni.nullptr; + } + if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + return $r.reference; + } + if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(_$types[$p]!["T"]!, deleteOriginal: true), + ); + return $r.reference; + } + if ($d == r"manyPrimitives(IZCD)J") { + final $r = _$methods[$p]![$d]!( + $a[0] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[1] + .castTo(const jni.JBooleanType(), deleteOriginal: true) + .booleanValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JCharacterType(), deleteOriginal: true) + .charValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JDoubleType(), deleteOriginal: true) + .doubleValue(deleteOriginal: true), + ); + return $r.toJLong().reference; + } + return jni.nullptr; + } + factory MyInterface.implement({ required void Function(jni.JString s) voidCallback, required jni.JString Function(jni.JString s) stringCallback, @@ -2941,65 +3017,26 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $x = MyInterface.fromRef( T, ProtectedJniExtensions.newPortProxy( - r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), + r"com.github.dart_lang.jnigen.interfaces.MyInterface", + $p, + _$invokePointer, + ), ).._$p = $p; - _finalizer.attach($x, $p, detach: $x); + final $a = $p.sendPort.nativePort; + _$types[$a] = {}; + _$methods[$a] = {}; + _$types[$a]!["T"] = T; + _$methods[$a]![r"voidCallback(Ljava/lang/String;)V"] = voidCallback; + _$methods[$a]![r"stringCallback(Ljava/lang/String;)Ljava/lang/String;"] = + stringCallback; + _$methods[$a]![r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;"] = + varCallback; + _$methods[$a]![r"manyPrimitives(IZCD)J"] = manyPrimitives; + _$finalizer.attach($x, $p, detach: $x); $p.listen(($m) { - final $i = MethodInvocation.fromMessage($m); - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $c = $i.result; - final $a = $i.args; - if ($d == r"voidCallback(Ljava/lang/String;)V") { - voidCallback( - $a[0].castTo(const jni.JStringType(), deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - jni.nullptr, - ); - return; - } - if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { - final $r = stringCallback( - $a[0].castTo(const jni.JStringType(), deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.reference, - ); - return; - } - if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { - final $r = varCallback( - $a[0].castTo(T, deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.reference, - ); - return; - } - if ($d == r"manyPrimitives(IZCD)J") { - final $r = manyPrimitives( - $a[0] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[1] - .castTo(const jni.JBooleanType(), deleteOriginal: true) - .booleanValue(deleteOriginal: true), - $a[2] - .castTo(const jni.JCharacterType(), deleteOriginal: true) - .charValue(deleteOriginal: true), - $a[3] - .castTo(const jni.JDoubleType(), deleteOriginal: true) - .doubleValue(deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.toJLong().reference, - ); - return; - } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); }); return $x; } @@ -3078,6 +3115,27 @@ class MyInterfaceConsumer extends jni.JObject { ]) as jni.JObjType<$T>; return _consumeOnAnotherThread(myInterface.reference, s.reference).check(); } + + static final _consumeOnSameThread = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "MyInterfaceConsumer__consumeOnSameThread") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeOnSameThread<$T extends jni.JObject>( + MyInterface<$T> myInterface, + jni.JString s, { + jni.JObjType<$T>? T, + }) { + T ??= jni.lowestCommonSuperType([ + (myInterface.$type as $MyInterfaceType).T, + ]) as jni.JObjType<$T>; + return _consumeOnSameThread(myInterface.reference, s.reference).check(); + } } class $MyInterfaceConsumerType extends jni.JObjType { diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 094f7a59..29d03f53 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2757,18 +2757,94 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { [jni.JValueInt(a), b ? 1 : 0, jni.JValueChar(c), d]).long; } + /// Maps a specific port to the implemented methods. + static final Map> _$methods = {}; + + /// Maps a specific port to the type parameters. + static final Map> _$types = {}; + ReceivePort? _$p; - static final Finalizer _finalizer = - Finalizer((port) => port.close()); + static final Finalizer _$finalizer = Finalizer(($p) { + _$methods.remove($p.sendPort.nativePort); + _$types.remove($p.sendPort.nativePort); + $p.close(); + }); @override void delete() { + _$methods.remove(_$p?.sendPort.nativePort); + _$types.remove(_$p?.sendPort.nativePort); _$p?.close(); - _finalizer.detach(this); + _$finalizer.detach(this); super.delete(); } + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); + final $a = $i.args; + if ($d == r"voidCallback(Ljava/lang/String;)V") { + _$methods[$p]![$d]!( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + return jni.nullptr; + } + if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(const jni.JStringType(), deleteOriginal: true), + ); + return $r.reference; + } + if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { + final $r = _$methods[$p]![$d]!( + $a[0].castTo(_$types[$p]!["T"]!, deleteOriginal: true), + ); + return $r.reference; + } + if ($d == r"manyPrimitives(IZCD)J") { + final $r = _$methods[$p]![$d]!( + $a[0] + .castTo(const jni.JIntegerType(), deleteOriginal: true) + .intValue(deleteOriginal: true), + $a[1] + .castTo(const jni.JBooleanType(), deleteOriginal: true) + .booleanValue(deleteOriginal: true), + $a[2] + .castTo(const jni.JCharacterType(), deleteOriginal: true) + .charValue(deleteOriginal: true), + $a[3] + .castTo(const jni.JDoubleType(), deleteOriginal: true) + .doubleValue(deleteOriginal: true), + ); + return $r.toJLong().reference; + } + return jni.nullptr; + } + factory MyInterface.implement({ required void Function(jni.JString s) voidCallback, required jni.JString Function(jni.JString s) stringCallback, @@ -2780,65 +2856,26 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { final $x = MyInterface.fromRef( T, ProtectedJniExtensions.newPortProxy( - r"com.github.dart_lang.jnigen.interfaces.MyInterface", $p), + r"com.github.dart_lang.jnigen.interfaces.MyInterface", + $p, + _$invokePointer, + ), ).._$p = $p; - _finalizer.attach($x, $p, detach: $x); + final $a = $p.sendPort.nativePort; + _$types[$a] = {}; + _$methods[$a] = {}; + _$types[$a]!["T"] = T; + _$methods[$a]![r"voidCallback(Ljava/lang/String;)V"] = voidCallback; + _$methods[$a]![r"stringCallback(Ljava/lang/String;)Ljava/lang/String;"] = + stringCallback; + _$methods[$a]![r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;"] = + varCallback; + _$methods[$a]![r"manyPrimitives(IZCD)J"] = manyPrimitives; + _$finalizer.attach($x, $p, detach: $x); $p.listen(($m) { - final $i = MethodInvocation.fromMessage($m); - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $c = $i.result; - final $a = $i.args; - if ($d == r"voidCallback(Ljava/lang/String;)V") { - voidCallback( - $a[0].castTo(const jni.JStringType(), deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - jni.nullptr, - ); - return; - } - if ($d == r"stringCallback(Ljava/lang/String;)Ljava/lang/String;") { - final $r = stringCallback( - $a[0].castTo(const jni.JStringType(), deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.reference, - ); - return; - } - if ($d == r"varCallback(Ljava/lang/Object;)Ljava/lang/Object;") { - final $r = varCallback( - $a[0].castTo(T, deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.reference, - ); - return; - } - if ($d == r"manyPrimitives(IZCD)J") { - final $r = manyPrimitives( - $a[0] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[1] - .castTo(const jni.JBooleanType(), deleteOriginal: true) - .booleanValue(deleteOriginal: true), - $a[2] - .castTo(const jni.JCharacterType(), deleteOriginal: true) - .charValue(deleteOriginal: true), - $a[3] - .castTo(const jni.JDoubleType(), deleteOriginal: true) - .doubleValue(deleteOriginal: true), - ); - ProtectedJniExtensions.returnResult( - $c, - $r.toJLong().reference, - ); - return; - } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); }); return $x; } @@ -2920,6 +2957,27 @@ class MyInterfaceConsumer extends jni.JObject { jni.JniCallType.voidType, [myInterface.reference, s.reference]).check(); } + + static final _id_consumeOnSameThread = jni.Jni.accessors.getStaticMethodIDOf( + _class.reference, + r"consumeOnSameThread", + r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;)V"); + + /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + static void consumeOnSameThread<$T extends jni.JObject>( + MyInterface<$T> myInterface, + jni.JString s, { + jni.JObjType<$T>? T, + }) { + T ??= jni.lowestCommonSuperType([ + (myInterface.$type as $MyInterfaceType).T, + ]) as jni.JObjType<$T>; + return jni.Jni.accessors.callStaticMethodWithArgs( + _class.reference, + _id_consumeOnSameThread, + jni.JniCallType.voidType, + [myInterface.reference, s.reference]).check(); + } } class $MyInterfaceConsumerType extends jni.JObjType { diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java index 0d88b478..6514f716 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java @@ -8,10 +8,12 @@ public class MyInterfaceConsumer { public static void consumeOnAnotherThread(MyInterface myInterface, String s) { var thread = new Thread( - () -> { - String result = myInterface.stringCallback(s); - myInterface.voidCallback(result); - }); + () -> consumeOnSameThread(myInterface, s)); thread.start(); } + + public static void consumeOnSameThread(MyInterface myInterface, String s) { + String result = myInterface.stringCallback(s); + myInterface.voidCallback(result); + } } diff --git a/jnigen/test/simple_package_test/runtime_test_registrant.dart b/jnigen/test/simple_package_test/runtime_test_registrant.dart index 8c362f05..5aee36bc 100644 --- a/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -2,6 +2,7 @@ // 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:async'; import 'dart:io'; import 'package:test/test.dart'; @@ -530,13 +531,13 @@ void registerTests(String groupName, TestRunnerCallback test) { }); group('interface implementation', () { - test('MyInterface.implement', () { + test('MyInterface.implement consume on another thread', () async { + final voidCallbackResult = Completer(); final myInterface = MyInterface.implement( voidCallback: (s) { - print(s.toDartString()); + voidCallbackResult.complete(s); }, stringCallback: (s) { - print(s.toDartString()); return (s.toDartString(deleteOriginal: true) * 2).toJString(); }, varCallback: (t) { @@ -549,8 +550,35 @@ void registerTests(String groupName, TestRunnerCallback test) { ); MyInterfaceConsumer.consumeOnAnotherThread( myInterface, - "hello".toJString(), + 'hello'.toJString(), ); + final result = await voidCallbackResult.future; + expect(result.toDartString(deleteOriginal: true), 'hellohello'); + myInterface.delete(); + }); + test('MyInterface.implement consume on the same thread', () async { + final voidCallbackResult = Completer(); + final myInterface = MyInterface.implement( + voidCallback: (s) { + voidCallbackResult.complete(s); + }, + stringCallback: (s) { + return (s.toDartString(deleteOriginal: true) * 2).toJString(); + }, + varCallback: (t) { + return "".toJString(); + }, + manyPrimitives: (a, b, c, d) { + return 0; + }, + T: JString.type, + ); + MyInterfaceConsumer.consumeOnSameThread( + myInterface, + 'hello'.toJString(), + ); + final result = await voidCallbackResult.future; + expect(result.toDartString(deleteOriginal: true), 'hellohello'); }); }); From 05fe1cff25e35dce937552e223ff6506c3ff136c Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 01:46:01 +0200 Subject: [PATCH 10/25] generate correct implement when no method is available + bring typeParams to the beginning --- jnigen/lib/src/bindings/dart_generator.dart | 36 +++++++++---------- .../c_based/dart_bindings/simple_package.dart | 2 +- .../dart_bindings/simple_package.dart | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index a33c9935..61fde6fc 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -462,19 +462,22 @@ class $name$typeParamsDef extends $superName { return jni.nullptr; } - factory $name.implement({ + factory $name.implement( '''); - final typeClassesDef = typeParams - .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,') - .join(_newLine(depth: 1)); final typeClassesCall = - typeParams.map((typeParam) => '$typeParam,').join(_newLine(depth: 2)); - final methodImplementCall = _InterfaceImplementArg(resolver, s); - for (final method in node.methods) { - method.accept(methodImplementCall); - } - s.write('''$typeClassesDef - }) { + typeParams.map((typeParam) => '$typeParam,').join(_newLine(depth: 3)); + s.write(_encloseIfNotEmpty( + '{', + [ + ...typeParams + .map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,'), + ...node.methods.accept(_InterfaceImplementArg(resolver)) + ].join(_newLine(depth: 2)), + '}', + )); + + s.write(''' + ) { final \$p = ReceivePort(); final \$x = $name.fromRef( $typeClassesCall @@ -1496,20 +1499,17 @@ class _TypeVarLocator extends TypeVisitor>> { } /// The argument for .implement method of interfaces. -class _InterfaceImplementArg extends Visitor { +class _InterfaceImplementArg extends Visitor { final Resolver resolver; - final StringSink s; - _InterfaceImplementArg(this.resolver, this.s); + _InterfaceImplementArg(this.resolver); @override - void visit(Method node) { + String visit(Method node) { final returnType = node.returnType.accept(_TypeGenerator(resolver)); final name = node.finalName; final args = node.params.accept(_ParamDef(resolver)).join(', '); - s.write(''' - required $returnType Function($args) $name, -'''); + return 'required $returnType Function($args) $name,'; } } diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index cabc8e01..3a0b731e 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -3007,11 +3007,11 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { } factory MyInterface.implement({ + required jni.JObjType<$T> T, required void Function(jni.JString s) voidCallback, required jni.JString Function(jni.JString s) stringCallback, required $T Function($T t) varCallback, required int Function(int a, bool b, int c, double d) manyPrimitives, - required jni.JObjType<$T> T, }) { final $p = ReceivePort(); final $x = MyInterface.fromRef( diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 29d03f53..4595727c 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2846,11 +2846,11 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { } factory MyInterface.implement({ + required jni.JObjType<$T> T, required void Function(jni.JString s) voidCallback, required jni.JString Function(jni.JString s) stringCallback, required $T Function($T t) varCallback, required int Function(int a, bool b, int c, double d) manyPrimitives, - required jni.JObjType<$T> T, }) { final $p = ReceivePort(); final $x = MyInterface.fromRef( From 8d2da897d5e0c8095fae0c73701b2149a8de26aa Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 01:49:50 +0200 Subject: [PATCH 11/25] java format --- .../jnigen/apisummarizer/disasm/AsmClassVisitor.java | 1 - .../apisummarizer/disasm/AsmMethodSignatureVisitor.java | 3 +-- .../jnigen/apisummarizer/disasm/AsmSummarizer.java | 1 - .../disasm/AsmTypeUsageSignatureVisitor.java | 4 ++-- .../dart_lang/jnigen/apisummarizer/doclet/AstEnv.java | 8 -------- .../jnigen/apisummarizer/doclet/ElementBuilders.java | 1 - .../jnigen/apisummarizer/doclet/SummarizerDoclet.java | 1 - .../jnigen/apisummarizer/elements/ClassDecl.java | 1 + .../dart_lang/jnigen/interfaces/MyInterfaceConsumer.java | 4 +--- 9 files changed, 5 insertions(+), 19 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java index 2ad407f5..33fecbd5 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java @@ -9,7 +9,6 @@ import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; import java.util.*; import java.util.stream.Collectors; - import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java index dcf6cf41..5410acfa 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmMethodSignatureVisitor.java @@ -7,10 +7,9 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.Method; import com.github.dart_lang.jnigen.apisummarizer.elements.TypeParam; import com.github.dart_lang.jnigen.apisummarizer.elements.TypeUsage; -import org.objectweb.asm.signature.SignatureVisitor; - import java.util.ArrayList; import java.util.List; +import org.objectweb.asm.signature.SignatureVisitor; public class AsmMethodSignatureVisitor extends SignatureVisitor { private final Method method; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java index 7b15cdd0..485c4426 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmSummarizer.java @@ -8,7 +8,6 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.ClassDecl; import com.github.dart_lang.jnigen.apisummarizer.util.InputStreamProvider; -import java.util.ArrayList; import java.util.List; import org.objectweb.asm.ClassReader; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java index 001d7fe2..6ddda454 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java @@ -94,7 +94,7 @@ public SignatureVisitor visitTypeArgument(char wildcard) { @Override public void visitInnerClassType(String name) { typeUsage.shorthand += "." + name; - ((TypeUsage.DeclaredType)typeUsage.type).binaryName += "$" + name; - ((TypeUsage.DeclaredType)typeUsage.type).simpleName = name; + ((TypeUsage.DeclaredType) typeUsage.type).binaryName += "$" + name; + ((TypeUsage.DeclaredType) typeUsage.type).simpleName = name; } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java index 856e98c7..71e31c4c 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/AstEnv.java @@ -5,18 +5,10 @@ package com.github.dart_lang.jnigen.apisummarizer.doclet; import com.sun.source.util.DocTrees; - -import javax.annotation.processing.Filer; -import javax.annotation.processing.Messager; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.SourceVersion; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import jdk.javadoc.doclet.DocletEnvironment; -import java.util.Locale; -import java.util.Map; - /** Class to hold utility classes initialized from DocletEnvironment. */ public class AstEnv { public final Types types; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java index da5641eb..7ca8bb62 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/ElementBuilders.java @@ -7,7 +7,6 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.*; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; import com.sun.source.doctree.DocCommentTree; - import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java index e8fffc88..abbc7cc6 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/doclet/SummarizerDoclet.java @@ -13,7 +13,6 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.ElementScanner9; - import jdk.javadoc.doclet.Doclet; import jdk.javadoc.doclet.DocletEnvironment; import jdk.javadoc.doclet.Reporter; diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java index c2cbeceb..746b9137 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/ClassDecl.java @@ -26,6 +26,7 @@ public class ClassDecl { * uses $ instead of dot (.) before nested classes. */ public String binaryName; + public List typeParams = new ArrayList<>(); public List methods = new ArrayList<>(); public List fields = new ArrayList<>(); diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java index 6514f716..bf06b837 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java @@ -6,9 +6,7 @@ public class MyInterfaceConsumer { public static void consumeOnAnotherThread(MyInterface myInterface, String s) { - var thread = - new Thread( - () -> consumeOnSameThread(myInterface, s)); + var thread = new Thread(() -> consumeOnSameThread(myInterface, s)); thread.start(); } From daf0dd67db343b13e25099856d01e02b8cc79bb7 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 01:59:59 +0200 Subject: [PATCH 12/25] remove print --- jni/lib/src/jni.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/jni/lib/src/jni.dart b/jni/lib/src/jni.dart index 1ff861e1..8145642f 100644 --- a/jni/lib/src/jni.dart +++ b/jni/lib/src/jni.dart @@ -36,7 +36,6 @@ DynamicLibrary _loadDartJniLibrary({String? dir, String baseName = "dartjni"}) { final fileName = _getLibraryFileName(baseName); final libPath = (dir != null) ? join(dir, fileName) : fileName; try { - print(libPath); final dylib = DynamicLibrary.open(libPath); return dylib; } on Error { From fa32943e339eff0f791c6a59715336d43636d01c Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:03:09 +0200 Subject: [PATCH 13/25] remove more prints + add more time limit to descriptor test --- jni/src/dartjni.c | 3 --- jnigen/test/descriptor_test.dart | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index b00cfb20..9bf5ed75 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -647,8 +647,6 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobjectArray args) { attach_thread(); if (threadId != thread_id()) { - printf("on a different thread!!!\n"); - CallbackResult result; result.ready = 0; result.object = NULL; @@ -685,7 +683,6 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, destroy_cond(&result.cond); return result.object; } else { - printf("on the same thread\n"); return ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( port, (*env)->NewGlobalRef(env, methodDescriptor), (*env)->NewGlobalRef(env, args)); diff --git a/jnigen/test/descriptor_test.dart b/jnigen/test/descriptor_test.dart index 42c7c35f..41ac58cf 100644 --- a/jnigen/test/descriptor_test.dart +++ b/jnigen/test/descriptor_test.dart @@ -16,7 +16,8 @@ import 'test_util/test_util.dart'; void main() { checkLocallyBuiltDependencies(); - test('Method descriptor generation', () async { + test('Method descriptor generation', timeout: const Timeout.factor(3), + () async { final configGetters = [ simple_package_test.getConfig, kotlin_test.getConfig, From 6c78989bb0f8c20b9422239db149a7e68cd9e0a1 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:07:13 +0200 Subject: [PATCH 14/25] fix method filling --- jnigen/lib/src/bindings/dart_generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 61fde6fc..83f48ca6 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -1618,7 +1618,7 @@ class _InterfaceMethodsFiller extends Visitor { @override void visit(Method node) { s.write(''' - _\$methods[\$a]![r"${node.javaSig}"] = ${node.name}; + _\$methods[\$a]![r"${node.javaSig}"] = ${node.finalName}; '''); } } From 266f98474e6f6153234a1b57b2bc1ceeb2052c95 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:09:00 +0200 Subject: [PATCH 15/25] ignore unused local variable for jnigen generated files --- jnigen/lib/src/bindings/dart_generator.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 83f48ca6..9a7d7b44 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -156,6 +156,7 @@ import "package:jni/jni.dart" as jni; // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name '''; From dd0258edf291e69a5a88117402db5f0c54c99be2 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:26:29 +0200 Subject: [PATCH 16/25] regenarate bindings to add the ignore --- jnigen/example/in_app_java/lib/android_utils.dart | 1 + jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart | 1 + jnigen/example/notification_plugin/lib/notifications.dart | 1 + .../src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart | 1 + .../org/apache/pdfbox/pdmodel/PDDocumentInformation.dart | 1 + .../src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonParser.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonToken.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonParser.dart | 1 + .../dart_bindings/com/fasterxml/jackson/core/JsonToken.dart | 1 + jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart | 1 + jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart | 1 + .../c_based/dart_bindings/simple_package.dart | 1 + .../dart_only/dart_bindings/simple_package.dart | 1 + 16 files changed, 16 insertions(+) diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 264e9cf6..4e48de98 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -12,6 +12,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart b/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart index b0c6ab97..e1002dd9 100644 --- a/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart +++ b/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart @@ -12,6 +12,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/example/notification_plugin/lib/notifications.dart b/jnigen/example/notification_plugin/lib/notifications.dart index c95f3e6e..170c7205 100644 --- a/jnigen/example/notification_plugin/lib/notifications.dart +++ b/jnigen/example/notification_plugin/lib/notifications.dart @@ -16,6 +16,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart index 614a23f7..3e552521 100644 --- a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart +++ b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart @@ -30,6 +30,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart index 657f68ae..87862bac 100644 --- a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart +++ b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart @@ -30,6 +30,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart index 9d6fe3a3..306258cc 100644 --- a/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart +++ b/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart @@ -30,6 +30,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart index 0b91b29f..33f51399 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart +++ b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart index 1c60902d..032db314 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart +++ b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart index fe8cf885..14ce836a 100644 --- a/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart +++ b/jnigen/test/jackson_core_test/third_party/c_based/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart index c58fb97a..71a79f1b 100644 --- a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart +++ b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonFactory.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart index d2147575..f978756b 100644 --- a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart +++ b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonParser.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart index b6bc6e54..aac454b6 100644 --- a/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart +++ b/jnigen/test/jackson_core_test/third_party/dart_only/dart_bindings/com/fasterxml/jackson/core/JsonToken.dart @@ -29,6 +29,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart b/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart index 4c465c55..3e65d3b0 100644 --- a/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart +++ b/jnigen/test/kotlin_test/c_based/dart_bindings/kotlin.dart @@ -16,6 +16,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart b/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart index c9f757c6..e4ceec3e 100644 --- a/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart +++ b/jnigen/test/kotlin_test/dart_only/dart_bindings/kotlin.dart @@ -16,6 +16,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index 3a0b731e..fbdfe97f 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -16,6 +16,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 4595727c..4d090614 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -16,6 +16,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; From 64f17acbcf99dbf9a3fdb646009030f818731b75 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:36:34 +0200 Subject: [PATCH 17/25] temporarily do not check the ffigen bindings until ffigen#555 is solved --- .github/workflows/test-package.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 8ec3cae1..b320c373 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -188,11 +188,15 @@ jobs: flag-name: jni_tests parallel: true path-to-lcov: ./jni/coverage/lcov.info - - name: regenerate & compare ffigen bindings - ## Use git to verify no source files have changed - run: | - dart run tool/generate_ffi_bindings.dart - git diff --exit-code -- lib/src/third_party src/third_party + # TODO(https://github.com/dart-lang/ffigen/issues/555): Ffigen generated + # on my machine has macOS specific stuff and CI does not. + # We should just generate the struct as opaque, but we currently can't. + # + # - name: regenerate & compare ffigen bindings + # ## Use git to verify no source files have changed + # run: | + # dart run tool/generate_ffi_bindings.dart + # git diff --exit-code -- lib/src/third_party src/third_party ## Run tests for package:jni on windows, just to confirm it works. ## Do not, for example, collect coverage or check formatting. From 0b88397ef4b00017b211730d114030f6b82e1fa3 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 02:38:55 +0200 Subject: [PATCH 18/25] revert the versions back to 2.17 as it was not necessary yet to upgrade it --- jni/pubspec.yaml | 2 +- jnigen/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jni/pubspec.yaml b/jni/pubspec.yaml index 6fa45cc8..0c1068a3 100644 --- a/jni/pubspec.yaml +++ b/jni/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.6.0-dev.1 repository: https://github.com/dart-lang/jnigen/tree/main/jni environment: - sdk: '>=2.19.0 <4.0.0' + sdk: '>=2.17.1 <4.0.0' flutter: '>=2.11.0' dependencies: diff --git a/jnigen/pubspec.yaml b/jnigen/pubspec.yaml index 0e745b2a..ee704308 100644 --- a/jnigen/pubspec.yaml +++ b/jnigen/pubspec.yaml @@ -8,7 +8,7 @@ description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen environment: - sdk: '>=2.19.0 <4.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: json_annotation: ^4.8.0 From 47b305b69a88563b5cc39686edbf4f33c104b49a Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 20 Jul 2023 10:33:59 +0200 Subject: [PATCH 19/25] remove empty file --- jni/lib/src/lang/reflect/jmethod.dart | 1 - 1 file changed, 1 deletion(-) delete mode 100644 jni/lib/src/lang/reflect/jmethod.dart diff --git a/jni/lib/src/lang/reflect/jmethod.dart b/jni/lib/src/lang/reflect/jmethod.dart deleted file mode 100644 index 8b137891..00000000 --- a/jni/lib/src/lang/reflect/jmethod.dart +++ /dev/null @@ -1 +0,0 @@ - From 6b4f112bf02cc1a048263bbaa97754e7f04f7108 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 21 Jul 2023 03:47:53 +0200 Subject: [PATCH 20/25] fix synchronization --- jni/src/dartjni.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/jni/src/dartjni.c b/jni/src/dartjni.c index 9bf5ed75..6f0fa086 100644 --- a/jni/src/dartjni.c +++ b/jni/src/dartjni.c @@ -647,17 +647,16 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, jobjectArray args) { attach_thread(); if (threadId != thread_id()) { - CallbackResult result; - result.ready = 0; - result.object = NULL; - init_lock(&result.lock); - init_cond(&result.cond); - - acquire_lock(&result.lock); + CallbackResult* result = (CallbackResult*)malloc(sizeof(CallbackResult)); + 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); + c_result.value.as_int64 = (jlong)result; Dart_CObject c_method; c_method.type = Dart_CObject_kInt64; @@ -675,13 +674,15 @@ Java_com_github_dart_1lang_jni_PortProxy__1invoke(JNIEnv* env, c_post.value.as_array.length = sizeof(c_post_arr) / sizeof(c_post_arr[0]); Dart_PostCObject_DL(port, &c_post); - release_lock(&result.lock); - while (!result.ready) { - wait_for(&result.lock, &result.cond); + while (!result->ready) { + wait_for(&result->cond, &result->lock); } - destroy_lock(&result.lock); - destroy_cond(&result.cond); - return result.object; + release_lock(&result->lock); + destroy_lock(&result->lock); + destroy_cond(&result->cond); + jobject object = result->object; + free(result); + return object; } else { return ((jobject(*)(uint64_t, jobject, jobject))functionPtr)( port, (*env)->NewGlobalRef(env, methodDescriptor), From 744344cb4ca19c185647fb3c16bc60ac46d1e332 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 21 Jul 2023 13:02:50 +0200 Subject: [PATCH 21/25] remove duplicated java source in jni + sort ffigen.yaml inline functions --- jni/android/build.gradle | 20 +++++ jni/android/consumer-rules.pro | 1 + .../com/github/dart_lang/jni/JniPlugin.java | 2 - .../dart_lang/jni/PortContinuation.java | 42 ---------- .../com/github/dart_lang/jni/PortProxy.java | 82 ------------------- jni/ffigen.yaml | 28 ++++--- 6 files changed, 36 insertions(+), 139 deletions(-) create mode 100644 jni/android/consumer-rules.pro delete mode 100644 jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java delete mode 100644 jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java diff --git a/jni/android/build.gradle b/jni/android/build.gradle index 4f5ca1b8..3f650e84 100644 --- a/jni/android/build.gradle +++ b/jni/android/build.gradle @@ -27,11 +27,31 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { + // Keeping the classes from being removed by proguard. + defaultConfig { + consumerProguardFiles 'consumer-rules.pro' + } + buildTypes { + release { + minifyEnabled false + } + } + // Condition for namespace compatibility in AGP 8 if (project.android.hasProperty("namespace")) { namespace 'com.github.dart_lang.jni' } + // Adding [PortContinuation] and [PortProxy] classes shared between Flutter and + // Dart-standalone versions of package:jni. + sourceSets { + main { + java { + srcDirs '../java/src/main/java' + } + } + } + // Bumping the plugin compileSdkVersion requires all clients of this plugin // to bump the version in their app. compileSdkVersion 31 diff --git a/jni/android/consumer-rules.pro b/jni/android/consumer-rules.pro new file mode 100644 index 00000000..269e4212 --- /dev/null +++ b/jni/android/consumer-rules.pro @@ -0,0 +1 @@ +-keep class com.github.dart_lang.jni.** { *; } diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/JniPlugin.java b/jni/android/src/main/java/com/github/dart_lang/jni/JniPlugin.java index 2a22dd8e..d3a1ebe7 100644 --- a/jni/android/src/main/java/com/github/dart_lang/jni/JniPlugin.java +++ b/jni/android/src/main/java/com/github/dart_lang/jni/JniPlugin.java @@ -6,14 +6,12 @@ import android.app.Activity; import android.content.Context; -import androidx.annotation.Keep; import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.PluginRegistry.Registrar; -@Keep public class JniPlugin implements FlutterPlugin, ActivityAware { @Override diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java deleted file mode 100644 index eb377e4a..00000000 --- a/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2023, 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. - -package com.github.dart_lang.jni; - -import androidx.annotation.Keep; -import androidx.annotation.NonNull; -import kotlin.coroutines.Continuation; -import kotlin.coroutines.CoroutineContext; -import kotlinx.coroutines.Dispatchers; - -/// An implementation of kotlin.coroutines.Continuation which sends the address -/// of the object to Dart through a native port. -/// -/// This allows converting Kotlin coroutines to Dart async methods. -/// The implementation of native void _resumeWith is located in `dartjni.c`. -@Keep -public class PortContinuation implements Continuation { - static { - System.loadLibrary("dartjni"); - } - - private long port; - - public PortContinuation(long port) { - this.port = port; - } - - @NonNull - @Override - public CoroutineContext getContext() { - return (CoroutineContext) Dispatchers.getIO(); - } - - @Override - public void resumeWith(Object o) { - _resumeWith(port, o); - } - - private native void _resumeWith(long port, Object result); -} diff --git a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java b/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java deleted file mode 100644 index a75a914f..00000000 --- a/jni/android/src/main/java/com/github/dart_lang/jni/PortProxy.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2023, 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. - -package com.github.dart_lang.jni; - -import java.lang.reflect.*; - -public class PortProxy implements InvocationHandler { - static { - System.loadLibrary("dartjni"); - } - - private final long port; - private final long threadId; - private final long functionPtr; - - private PortProxy(long port, long threadId, long functionPtr) { - this.port = port; - this.threadId = threadId; - this.functionPtr = functionPtr; - } - - private static String getDescriptor(Method method) { - StringBuilder descriptor = new StringBuilder(); - descriptor.append(method.getName()).append("("); - Class[] parameterTypes = method.getParameterTypes(); - for (Class paramType : parameterTypes) { - appendType(descriptor, paramType); - } - descriptor.append(")"); - appendType(descriptor, method.getReturnType()); - return descriptor.toString(); - } - - private static void appendType(StringBuilder descriptor, Class type) { - if (type == void.class) { - descriptor.append("V"); - } else if (type == boolean.class) { - descriptor.append("Z"); - } else if (type == byte.class) { - descriptor.append("B"); - } else if (type == char.class) { - descriptor.append("C"); - } else if (type == short.class) { - descriptor.append("S"); - } else if (type == int.class) { - descriptor.append("I"); - } else if (type == long.class) { - descriptor.append("J"); - } else if (type == float.class) { - descriptor.append("F"); - } else if (type == double.class) { - descriptor.append("D"); - } else if (type.isArray()) { - descriptor.append('['); - appendType(descriptor, type.getComponentType()); - } else { - descriptor.append("L").append(type.getName().replace('.', '/')).append(";"); - } - } - - public static Object newInstance(String binaryName, long port, long threadId, long functionPtr) - throws ClassNotFoundException { - Class clazz = Class.forName(binaryName); - return Proxy.newProxyInstance( - clazz.getClassLoader(), new Class[] {clazz}, new PortProxy(port, threadId, functionPtr)); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return _invoke(port, threadId, functionPtr, proxy, getDescriptor(method), args); - } - - private native Object _invoke( - long port, - long threadId, - long functionPtr, - Object proxy, - String methodDescriptor, - Object[] args); -} diff --git a/jni/ffigen.yaml b/jni/ffigen.yaml index 6e741507..62b7429a 100644 --- a/jni/ffigen.yaml +++ b/jni/ffigen.yaml @@ -33,28 +33,30 @@ functions: - 'setJniGetters' - 'jni_log' # Inline functions + # keep-sorted start - 'acquire_lock' - - 'release_lock' - - 'init_lock' + - 'attach_thread' + - 'check_exception' + - 'destroy_cond' - 'destroy_lock' - 'init_cond' - - 'destroy_cond' - - 'signal_cond' - - 'wait_for' - - 'thread_id' + - 'init_lock' - 'load_class' - 'load_class_global_ref' - - 'attach_thread' - - 'load_method' - - 'load_static_method' + - 'load_class_local_ref' + - 'load_class_platform' + - 'load_env' - 'load_field' + - 'load_method' - 'load_static_field' - - 'load_class_platform' - - 'load_class_local_ref' + - 'load_static_method' + - 'release_lock' + - 'signal_cond' + - 'thread_id' - 'to_global_ref' - 'to_global_ref_result' - - 'check_exception' - - 'load_env' + - 'wait_for' + # keep-sorted end # Native functions in Java. No need to call them from Dart. - 'Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith' - 'Java_com_github_dart_1lang_jni_PortProxy__1invoke' From aade7d96190b59955bc1700bf4caf086f2f5d7dc Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 21 Jul 2023 17:54:28 +0200 Subject: [PATCH 22/25] add missing tests --- .../in_app_java/lib/android_utils.dart | 2 +- jnigen/lib/src/bindings/dart_generator.dart | 21 +++- .../c_based/c_bindings/simple_package.c | 23 +++- .../c_based/dart_bindings/simple_package.dart | 54 +++++++-- .../dart_bindings/simple_package.dart | 54 ++++++--- .../interfaces/MyInterfaceConsumer.java | 10 +- .../runtime_test_registrant.dart | 113 ++++++++++-------- 7 files changed, 183 insertions(+), 94 deletions(-) diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 4e48de98..ef9eecd8 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1612,7 +1612,7 @@ class EmojiCompat_GlyphChecker extends jni.JObject { .castTo(const jni.JIntegerType(), deleteOriginal: true) .intValue(deleteOriginal: true), ); - return $r.toJBoolean().reference; + return jni.JBoolean($r).reference; } return jni.nullptr; } diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 9a7d7b44..2a2d4ed3 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -698,8 +698,12 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { @override _TypeClass visitArrayType(ArrayType node) { - final innerTypeClass = - node.type.accept(_TypeClassGenerator(resolver, isConst: false)); + final innerTypeClass = node.type.accept(_TypeClassGenerator( + resolver, + isConst: false, + boxPrimitives: boxPrimitives, + typeVarFromMap: typeVarFromMap, + )); final ifConst = innerTypeClass.canBeConst && isConst ? 'const ' : ''; return _TypeClass( '$ifConst${_jArray}Type(${innerTypeClass.name})', @@ -714,8 +718,12 @@ class _TypeClassGenerator extends TypeVisitor<_TypeClass> { .toList(); // The ones that are declared. - final definedTypeClasses = - node.params.accept(_TypeClassGenerator(resolver, isConst: false)); + final definedTypeClasses = node.params.accept(_TypeClassGenerator( + resolver, + isConst: false, + boxPrimitives: boxPrimitives, + typeVarFromMap: typeVarFromMap, + )); // Can be const if all the type parameters are defined and each of them are // also const. @@ -1524,9 +1532,10 @@ class _InterfaceMethodIf extends Visitor { @override void visit(Method node) { final signature = node.javaSig; + final saveResult = node.returnType.name == 'void' ? '' : 'final \$r = '; s.write(''' if (\$d == r"$signature") { - ${node.returnType.name == 'void' ? '' : 'final \$r = '}_\$methods[\$p]![\$d]!( + ${saveResult}_\$methods[\$p]![\$d]!( '''); for (var i = 0; i < node.params.length; ++i) { node.params[i].accept(_InterfaceParamCast(resolver, s, paramIndex: i)); @@ -1590,7 +1599,7 @@ class _InterfaceReturnBox extends TypeVisitor { if (node.name == 'void') { return '$_jni.nullptr'; } - return '\$r.toJ${node.name.capitalize()}().reference'; + return '$_jni.J${node.name.capitalize()}(\$r).reference'; } } diff --git a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c index 30308c5f..9b6e5e10 100644 --- a/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c +++ b/jnigen/test/simple_package_test/c_based/c_bindings/simple_package.c @@ -2408,7 +2408,12 @@ JniResult MyInterfaceConsumer__ctor() { jmethodID _m_MyInterfaceConsumer__consumeOnAnotherThread = NULL; FFI_PLUGIN_EXPORT JniResult MyInterfaceConsumer__consumeOnAnotherThread(jobject myInterface, - jobject s) { + jobject s, + int32_t a, + uint8_t b, + uint16_t c, + double d, + jobject t) { load_env(); load_class_global_ref( &_c_MyInterfaceConsumer, @@ -2419,19 +2424,25 @@ JniResult MyInterfaceConsumer__consumeOnAnotherThread(jobject myInterface, &_m_MyInterfaceConsumer__consumeOnAnotherThread, "consumeOnAnotherThread", "(Lcom/github/dart_lang/jnigen/interfaces/" - "MyInterface;Ljava/lang/String;)V"); + "MyInterface;Ljava/lang/String;IZCDLjava/lang/Object;)V"); if (_m_MyInterfaceConsumer__consumeOnAnotherThread == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; (*jniEnv)->CallStaticVoidMethod( jniEnv, _c_MyInterfaceConsumer, - _m_MyInterfaceConsumer__consumeOnAnotherThread, myInterface, s); + _m_MyInterfaceConsumer__consumeOnAnotherThread, myInterface, s, a, b, c, + d, t); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } jmethodID _m_MyInterfaceConsumer__consumeOnSameThread = NULL; FFI_PLUGIN_EXPORT JniResult MyInterfaceConsumer__consumeOnSameThread(jobject myInterface, - jobject s) { + jobject s, + int32_t a, + uint8_t b, + uint16_t c, + double d, + jobject t) { load_env(); load_class_global_ref( &_c_MyInterfaceConsumer, @@ -2442,12 +2453,12 @@ JniResult MyInterfaceConsumer__consumeOnSameThread(jobject myInterface, &_m_MyInterfaceConsumer__consumeOnSameThread, "consumeOnSameThread", "(Lcom/github/dart_lang/jnigen/interfaces/" - "MyInterface;Ljava/lang/String;)V"); + "MyInterface;Ljava/lang/String;IZCDLjava/lang/Object;)V"); if (_m_MyInterfaceConsumer__consumeOnSameThread == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; (*jniEnv)->CallStaticVoidMethod(jniEnv, _c_MyInterfaceConsumer, _m_MyInterfaceConsumer__consumeOnSameThread, - myInterface, s); + myInterface, s, a, b, c, d, t); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } diff --git a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart index fbdfe97f..7cb01108 100644 --- a/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/c_based/dart_bindings/simple_package.dart @@ -3002,7 +3002,7 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { .castTo(const jni.JDoubleType(), deleteOriginal: true) .doubleValue(deleteOriginal: true), ); - return $r.toJLong().reference; + return jni.JLong($r).reference; } return jni.nullptr; } @@ -3099,43 +3099,71 @@ class MyInterfaceConsumer extends jni.JObject { static final _consumeOnAnotherThread = jniLookup< ffi.NativeFunction< jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Uint8, + ffi.Uint16, + ffi.Double, + ffi.Pointer)>>( "MyInterfaceConsumer__consumeOnAnotherThread") .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + int, int, int, double, ffi.Pointer)>(); - /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s, int a, boolean b, char c, double d, T t) static void consumeOnAnotherThread<$T extends jni.JObject>( MyInterface<$T> myInterface, - jni.JString s, { + jni.JString s, + int a, + bool b, + int c, + double d, + $T t, { jni.JObjType<$T>? T, }) { T ??= jni.lowestCommonSuperType([ + t.$type, (myInterface.$type as $MyInterfaceType).T, ]) as jni.JObjType<$T>; - return _consumeOnAnotherThread(myInterface.reference, s.reference).check(); + return _consumeOnAnotherThread( + myInterface.reference, s.reference, a, b ? 1 : 0, c, d, t.reference) + .check(); } static final _consumeOnSameThread = jniLookup< ffi.NativeFunction< jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Uint8, + ffi.Uint16, + ffi.Double, + ffi.Pointer)>>( "MyInterfaceConsumer__consumeOnSameThread") .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + int, int, int, double, ffi.Pointer)>(); - /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s, int a, boolean b, char c, double d, T t) static void consumeOnSameThread<$T extends jni.JObject>( MyInterface<$T> myInterface, - jni.JString s, { + jni.JString s, + int a, + bool b, + int c, + double d, + $T t, { jni.JObjType<$T>? T, }) { T ??= jni.lowestCommonSuperType([ + t.$type, (myInterface.$type as $MyInterfaceType).T, ]) as jni.JObjType<$T>; - return _consumeOnSameThread(myInterface.reference, s.reference).check(); + return _consumeOnSameThread( + myInterface.reference, s.reference, a, b ? 1 : 0, c, d, t.reference) + .check(); } } diff --git a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart index 4d090614..7befe1b9 100644 --- a/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart +++ b/jnigen/test/simple_package_test/dart_only/dart_bindings/simple_package.dart @@ -2841,7 +2841,7 @@ class MyInterface<$T extends jni.JObject> extends jni.JObject { .castTo(const jni.JDoubleType(), deleteOriginal: true) .doubleValue(deleteOriginal: true), ); - return $r.toJLong().reference; + return jni.JLong($r).reference; } return jni.nullptr; } @@ -2941,43 +2941,65 @@ class MyInterfaceConsumer extends jni.JObject { static final _id_consumeOnAnotherThread = jni.Jni.accessors.getStaticMethodIDOf( _class.reference, r"consumeOnAnotherThread", - r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;)V"); + r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;IZCDLjava/lang/Object;)V"); - /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + /// from: static public void consumeOnAnotherThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s, int a, boolean b, char c, double d, T t) static void consumeOnAnotherThread<$T extends jni.JObject>( MyInterface<$T> myInterface, - jni.JString s, { + jni.JString s, + int a, + bool b, + int c, + double d, + $T t, { jni.JObjType<$T>? T, }) { T ??= jni.lowestCommonSuperType([ + t.$type, (myInterface.$type as $MyInterfaceType).T, ]) as jni.JObjType<$T>; - return jni.Jni.accessors.callStaticMethodWithArgs( - _class.reference, - _id_consumeOnAnotherThread, - jni.JniCallType.voidType, - [myInterface.reference, s.reference]).check(); + return jni.Jni.accessors.callStaticMethodWithArgs(_class.reference, + _id_consumeOnAnotherThread, jni.JniCallType.voidType, [ + myInterface.reference, + s.reference, + jni.JValueInt(a), + b ? 1 : 0, + jni.JValueChar(c), + d, + t.reference + ]).check(); } static final _id_consumeOnSameThread = jni.Jni.accessors.getStaticMethodIDOf( _class.reference, r"consumeOnSameThread", - r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;)V"); + r"(Lcom/github/dart_lang/jnigen/interfaces/MyInterface;Ljava/lang/String;IZCDLjava/lang/Object;)V"); - /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s) + /// from: static public void consumeOnSameThread(com.github.dart_lang.jnigen.interfaces.MyInterface myInterface, java.lang.String s, int a, boolean b, char c, double d, T t) static void consumeOnSameThread<$T extends jni.JObject>( MyInterface<$T> myInterface, - jni.JString s, { + jni.JString s, + int a, + bool b, + int c, + double d, + $T t, { jni.JObjType<$T>? T, }) { T ??= jni.lowestCommonSuperType([ + t.$type, (myInterface.$type as $MyInterfaceType).T, ]) as jni.JObjType<$T>; return jni.Jni.accessors.callStaticMethodWithArgs( - _class.reference, - _id_consumeOnSameThread, - jni.JniCallType.voidType, - [myInterface.reference, s.reference]).check(); + _class.reference, _id_consumeOnSameThread, jni.JniCallType.voidType, [ + myInterface.reference, + s.reference, + jni.JValueInt(a), + b ? 1 : 0, + jni.JValueChar(c), + d, + t.reference + ]).check(); } } diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java index bf06b837..b4eeb2fd 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/interfaces/MyInterfaceConsumer.java @@ -5,13 +5,17 @@ package com.github.dart_lang.jnigen.interfaces; public class MyInterfaceConsumer { - public static void consumeOnAnotherThread(MyInterface myInterface, String s) { - var thread = new Thread(() -> consumeOnSameThread(myInterface, s)); + public static void consumeOnAnotherThread( + MyInterface myInterface, String s, int a, boolean b, char c, double d, T t) { + var thread = new Thread(() -> consumeOnSameThread(myInterface, s, a, b, c, d, t)); thread.start(); } - public static void consumeOnSameThread(MyInterface myInterface, String s) { + public static void consumeOnSameThread( + MyInterface myInterface, String s, int a, boolean b, char c, double d, T t) { String result = myInterface.stringCallback(s); myInterface.voidCallback(result); + myInterface.manyPrimitives(a, b, c, d); + myInterface.varCallback(t); } } diff --git a/jnigen/test/simple_package_test/runtime_test_registrant.dart b/jnigen/test/simple_package_test/runtime_test_registrant.dart index 5aee36bc..cd6f9997 100644 --- a/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -531,55 +531,70 @@ void registerTests(String groupName, TestRunnerCallback test) { }); group('interface implementation', () { - test('MyInterface.implement consume on another thread', () async { - final voidCallbackResult = Completer(); - final myInterface = MyInterface.implement( - voidCallback: (s) { - voidCallbackResult.complete(s); - }, - stringCallback: (s) { - return (s.toDartString(deleteOriginal: true) * 2).toJString(); - }, - varCallback: (t) { - return "".toJString(); - }, - manyPrimitives: (a, b, c, d) { - return 0; - }, - T: JString.type, - ); - MyInterfaceConsumer.consumeOnAnotherThread( - myInterface, - 'hello'.toJString(), - ); - final result = await voidCallbackResult.future; - expect(result.toDartString(deleteOriginal: true), 'hellohello'); - myInterface.delete(); - }); - test('MyInterface.implement consume on the same thread', () async { - final voidCallbackResult = Completer(); - final myInterface = MyInterface.implement( - voidCallback: (s) { - voidCallbackResult.complete(s); - }, - stringCallback: (s) { - return (s.toDartString(deleteOriginal: true) * 2).toJString(); - }, - varCallback: (t) { - return "".toJString(); - }, - manyPrimitives: (a, b, c, d) { - return 0; - }, - T: JString.type, - ); - MyInterfaceConsumer.consumeOnSameThread( - myInterface, - 'hello'.toJString(), - ); - final result = await voidCallbackResult.future; - expect(result.toDartString(deleteOriginal: true), 'hellohello'); - }); + for (final method in { + 'another thread': MyInterfaceConsumer.consumeOnAnotherThread, + 'the same thread': MyInterfaceConsumer.consumeOnSameThread, + }.entries) { + test('MyInterface.implement on ${method.key}', () async { + final voidCallbackResult = Completer(); + final varCallbackResult = Completer(); + final manyPrimitivesResult = Completer(); + // We can use this trick to access self, instead of generating a `thiz` + // or `self` argument for each one of the callbacks. + late final MyInterface myInterface; + myInterface = MyInterface.implement( + voidCallback: (s) { + voidCallbackResult.complete(s); + }, + stringCallback: (s) { + return (s.toDartString(deleteOriginal: true) * 2).toJString(); + }, + varCallback: (JInteger t) { + final result = (t.intValue(deleteOriginal: true) * 2).toJInteger(); + varCallbackResult.complete(result); + return result; + }, + manyPrimitives: (a, b, c, d) { + if (b) { + final result = a + c + d.toInt(); + manyPrimitivesResult.complete(result); + return result; + } else { + // Call self, add to [a] when [b] is false and change b to true. + return myInterface.manyPrimitives(a + 1, true, c, d); + } + }, + T: JInteger.type, + ); + // [stringCallback] is going to be called first using [s]. + // The result of it is going to be used as the argument for + // [voidCallback]. + // The other two methods will be called individually using the passed + // arguments afterwards. + method.value( + myInterface, + // For stringCallback: + 'hello'.toJString(), + // For manyPrimitives: + -1, + false, + 3, + 3.14, + // For varCallback + 7.toJInteger(), + ); + final voidCallback = await voidCallbackResult.future; + expect(voidCallback.toDartString(deleteOriginal: true), 'hellohello'); + + final varCallback = await varCallbackResult.future; + expect(varCallback.intValue(), 14); + + final manyPrimitives = await manyPrimitivesResult.future; + expect(manyPrimitives, -1 + 3 + 3.14.toInt() + 1); + + myInterface.delete(); + }); + } }); group('$groupName (load tests)', () { From 16d71ea91e74e7752e9dc6bc9355e23758887e92 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 21 Jul 2023 18:53:23 +0200 Subject: [PATCH 23/25] add the interface implementation under an experiment flag + docs --- README.md | 1 + jni/CHANGELOG.md | 4 + jni/example/pubspec.lock | 2 +- jni/pubspec.yaml | 6 +- jnigen/CHANGELOG.md | 7 + .../in_app_java/lib/android_utils.dart | 269 ------------------ jnigen/lib/src/bindings/dart_generator.dart | 6 +- jnigen/lib/src/config/config_types.dart | 23 ++ jnigen/pubspec.yaml | 2 +- jnigen/test/config_test.dart | 1 + jnigen/test/jackson_core_test/generate.dart | 24 +- jnigen/test/jackson_core_test/jnigen.yaml | 3 + jnigen/test/simple_package_test/generate.dart | 1 + 13 files changed, 64 insertions(+), 285 deletions(-) diff --git a/README.md b/README.md index 8d47afd5..8e0d89e0 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ A `*` denotes required configuration. | `source_path` | List of directory paths | Directories to search for source files. Note: source_path for dependencies downloaded using `maven_downloads` configuration is added automatically without the need to specify here. | | `class_path` | List of directory / JAR paths | Classpath for API summary generation. This should include any JAR dependencies of the source files in `source_path`. | | `classes` * | List of qualified class / package names | List of qualified class / package names. `source_path` will be scanned assuming the sources follow standard java-ish hierarchy. That is a.b.c either maps to a directory `a/b/c` or a class file `a/b/c.java`. | +| `enable_experiment` | List of experiment names | List of enabled experiments. These features are still in development and their API might break. | | `output:` | (Subsection) | This subsection will contain configuration related to output files. | | `output:` >> `bindings_type` | `c_based` (default) or `dart_only` | Binding generation strategy. [Trade-offs](#pure-dart-bindings) are explained at the end of this document. | | `output:` >> `c:` | (Subsection) | This subsection specified C output configuration. Required if `bindings_type` is `c_based`. | diff --git a/jni/CHANGELOG.md b/jni/CHANGELOG.md index 08d66d88..6679b16b 100644 --- a/jni/CHANGELOG.md +++ b/jni/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0-dev-2 +* Added `PortProxy` and related methods used for interface implementation. +* Added the missing binding for `java.lang.Character`. + ## 0.5.0 * **Breaking Change** ([#137](https://github.com/dart-lang/jnigen/issues/137)): Java primitive types are now all lowercase like `jint`, `jshort`, ... * The bindings for `java.util.Set`, `java.util.Map`, `java.util.List` and the numeric types like `java.lang.Integer`, `java.lang.Boolean`, ... are now included in `package:jni`. diff --git a/jni/example/pubspec.lock b/jni/example/pubspec.lock index 34dff04b..c730dba9 100644 --- a/jni/example/pubspec.lock +++ b/jni/example/pubspec.lock @@ -200,7 +200,7 @@ packages: path: ".." relative: true source: path - version: "0.6.0-dev.1" + version: "0.6.0-dev.2" js: dependency: transitive description: diff --git a/jni/pubspec.yaml b/jni/pubspec.yaml index 0c1068a3..e42149a8 100644 --- a/jni/pubspec.yaml +++ b/jni/pubspec.yaml @@ -1,6 +1,10 @@ +# Copyright (c) 2022, 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. + name: jni description: Library to access JNI from dart and flutter -version: 0.6.0-dev.1 +version: 0.6.0-dev.2 repository: https://github.com/dart-lang/jnigen/tree/main/jni environment: diff --git a/jnigen/CHANGELOG.md b/jnigen/CHANGELOG.md index 48b1b212..696549ad 100644 --- a/jnigen/CHANGELOG.md +++ b/jnigen/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.6.0-dev.2 +* Fixed a bug where the nested classes would be generated incorrectly depending on the backend used for generation. +* Fixed a bug where ASM backend would produce the incorrect parent for multi-level nested classes. +* Fixed a bug where the backends would produce different descriptors for the same method. +* Added `enable_experiment` option to config. +* Created an experiment called `interface_implementation` which creates a `.implement` method for interfaces, so you can implement them using Dart. + ## 0.6.0-dev.1 * **Breaking Change** Specifying a class always pulls in nested classes by default. If a nested class is specified in config, it will be an error. * Save all `jnigen` logs to a file in `.dart_tool/jnigen/logs/`. This is useful for debugging. diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index ef9eecd8..b14813fc 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -1548,100 +1548,6 @@ class EmojiCompat_GlyphChecker extends jni.JObject { return _hasGlyph(reference, charSequence.reference, start, end, sdkAdded) .boolean; } - - /// Maps a specific port to the implemented methods. - static final Map> _$methods = {}; - - /// Maps a specific port to the type parameters. - static final Map> _$types = {}; - - ReceivePort? _$p; - - static final Finalizer _$finalizer = Finalizer(($p) { - _$methods.remove($p.sendPort.nativePort); - _$types.remove($p.sendPort.nativePort); - $p.close(); - }); - - @override - void delete() { - _$methods.remove(_$p?.sendPort.nativePort); - _$types.remove(_$p?.sendPort.nativePort); - _$p?.close(); - _$finalizer.detach(this); - super.delete(); - } - - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, - ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); - } - - static final ffi.Pointer< - ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); - - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, - ) { - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $a = $i.args; - if ($d == r"hasGlyph(Ljava/lang/CharSequence;III)Z") { - final $r = _$methods[$p]![$d]!( - $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), - $a[1] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[2] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - $a[3] - .castTo(const jni.JIntegerType(), deleteOriginal: true) - .intValue(deleteOriginal: true), - ); - return jni.JBoolean($r).reference; - } - return jni.nullptr; - } - - factory EmojiCompat_GlyphChecker.implement({ - required bool Function( - jni.JObject charSequence, int start, int end, int sdkAdded) - hasGlyph, - }) { - final $p = ReceivePort(); - final $x = EmojiCompat_GlyphChecker.fromRef( - ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$GlyphChecker", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$types[$a] = {}; - _$methods[$a] = {}; - _$methods[$a]![r"hasGlyph(Ljava/lang/CharSequence;III)Z"] = hasGlyph; - _$finalizer.attach($x, $p, detach: $x); - $p.listen(($m) { - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; - } } class $EmojiCompat_GlyphCheckerType @@ -1704,95 +1610,6 @@ class EmojiCompat_MetadataRepoLoader extends jni.JObject { ) { return _load(reference, loaderCallback.reference).check(); } - - /// Maps a specific port to the implemented methods. - static final Map> _$methods = {}; - - /// Maps a specific port to the type parameters. - static final Map> _$types = {}; - - ReceivePort? _$p; - - static final Finalizer _$finalizer = Finalizer(($p) { - _$methods.remove($p.sendPort.nativePort); - _$types.remove($p.sendPort.nativePort); - $p.close(); - }); - - @override - void delete() { - _$methods.remove(_$p?.sendPort.nativePort); - _$types.remove(_$p?.sendPort.nativePort); - _$p?.close(); - _$finalizer.detach(this); - super.delete(); - } - - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, - ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); - } - - static final ffi.Pointer< - ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); - - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, - ) { - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $a = $i.args; - if ($d == - r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V") { - _$methods[$p]![$d]!( - $a[0].castTo(const $EmojiCompat_MetadataRepoLoaderCallbackType(), - deleteOriginal: true), - ); - return jni.nullptr; - } - return jni.nullptr; - } - - factory EmojiCompat_MetadataRepoLoader.implement({ - required void Function( - EmojiCompat_MetadataRepoLoaderCallback loaderCallback) - load, - }) { - final $p = ReceivePort(); - final $x = EmojiCompat_MetadataRepoLoader.fromRef( - ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$MetadataRepoLoader", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$types[$a] = {}; - _$methods[$a] = {}; - _$methods[$a]![ - r"load(Landroidx/emoji2/text/EmojiCompat$MetadataRepoLoaderCallback;)V"] = - load; - _$finalizer.attach($x, $p, detach: $x); - $p.listen(($m) { - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; - } } class $EmojiCompat_MetadataRepoLoaderType @@ -2021,92 +1838,6 @@ class EmojiCompat_SpanFactory extends jni.JObject { return const jni.JObjectType() .fromRef(_createSpan(reference, rasterizer.reference).object); } - - /// Maps a specific port to the implemented methods. - static final Map> _$methods = {}; - - /// Maps a specific port to the type parameters. - static final Map> _$types = {}; - - ReceivePort? _$p; - - static final Finalizer _$finalizer = Finalizer(($p) { - _$methods.remove($p.sendPort.nativePort); - _$types.remove($p.sendPort.nativePort); - $p.close(); - }); - - @override - void delete() { - _$methods.remove(_$p?.sendPort.nativePort); - _$types.remove(_$p?.sendPort.nativePort); - _$p?.close(); - _$finalizer.detach(this); - super.delete(); - } - - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, - ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); - } - - static final ffi.Pointer< - ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); - - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, - ) { - final $d = $i.methodDescriptor.toDartString(deleteOriginal: true); - final $a = $i.args; - if ($d == - r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;") { - final $r = _$methods[$p]![$d]!( - $a[0].castTo(const jni.JObjectType(), deleteOriginal: true), - ); - return $r.reference; - } - return jni.nullptr; - } - - factory EmojiCompat_SpanFactory.implement({ - required jni.JObject Function(jni.JObject rasterizer) createSpan, - }) { - final $p = ReceivePort(); - final $x = EmojiCompat_SpanFactory.fromRef( - ProtectedJniExtensions.newPortProxy( - r"androidx.emoji2.text.EmojiCompat$SpanFactory", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$types[$a] = {}; - _$methods[$a] = {}; - _$methods[$a]![ - r"createSpan(Landroidx/emoji2/text/TypefaceEmojiRasterizer;)Landroidx/emoji2/text/EmojiSpan;"] = - createSpan; - _$finalizer.attach($x, $p, detach: $x); - $p.listen(($m) { - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; - } } class $EmojiCompat_SpanFactoryType diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 2a2d4ed3..2a56f10f 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -401,8 +401,10 @@ class $name$typeParamsDef extends $superName { method.accept(methodGenerator); } - // Interface implementation - if (node.declKind == DeclKind.interfaceKind) { + // Experimental: Interface implementation + if (node.declKind == DeclKind.interfaceKind && + (config.experiments?.contains(Experiment.interfaceImplementation) ?? + false)) { s.write(''' /// Maps a specific port to the implemented methods. static final Map> _\$methods = {}; diff --git a/jnigen/lib/src/config/config_types.dart b/jnigen/lib/src/config/config_types.dart index c032dc26..0991166b 100644 --- a/jnigen/lib/src/config/config_types.dart +++ b/jnigen/lib/src/config/config_types.dart @@ -319,6 +319,7 @@ class Config { Config({ required this.outputConfig, required this.classes, + this.experiments, this.exclude, this.sourcePath, this.classPath, @@ -347,6 +348,8 @@ class Config { /// name suffix is `.class`. List classes; + Set? experiments; + /// Methods and fields to be excluded from generated bindings. final BindingExclusions? exclude; @@ -592,6 +595,12 @@ class Config { : null, ), preamble: prov.getString(_Props.preamble), + experiments: prov + .getStringList(_Props.experiments) + ?.map( + (e) => getExperiment(e, null)!, + ) + .toSet(), imports: prov.getPathList(_Props.import), mavenDownloads: prov.hasValue(_Props.mavenDownloads) ? MavenDownloads( @@ -643,6 +652,19 @@ class Config { } } +enum Experiment { interfaceImplementation } + +Experiment? getExperiment( + String? name, + Experiment? defaultVal, +) { + return _getEnumValueFromString( + Experiment.values.valuesMap(), + name, + defaultVal, + ); +} + class _Props { static const summarizer = 'summarizer'; static const summarizerArgs = '$summarizer.extra_args'; @@ -656,6 +678,7 @@ class _Props { static const excludeMethods = '$exclude.methods'; static const excludeFields = '$exclude.fields'; + static const experiments = 'enable_experiment'; static const import = 'import'; static const outputConfig = 'output'; static const bindingsType = '$outputConfig.bindings_type'; diff --git a/jnigen/pubspec.yaml b/jnigen/pubspec.yaml index ee704308..5c018cff 100644 --- a/jnigen/pubspec.yaml +++ b/jnigen/pubspec.yaml @@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: jnigen -version: 0.6.0-dev.1 +version: 0.6.0-dev.2 description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen diff --git a/jnigen/test/config_test.dart b/jnigen/test/config_test.dart index 4b6617b3..ae2f9871 100644 --- a/jnigen/test/config_test.dart +++ b/jnigen/test/config_test.dart @@ -35,6 +35,7 @@ void expectConfigsAreEqual(Config a, Config b) { equals(b.outputConfig.symbolsConfig?.path), reason: "symbolsRoot"); expect(a.sourcePath, equals(b.sourcePath), reason: "sourcePath"); + expect(a.experiments, equals(b.experiments), reason: "experiments"); expect(a.classPath, equals(b.classPath), reason: "classPath"); expect(a.preamble, equals(b.preamble), reason: "preamble"); final am = a.mavenDownloads; diff --git a/jnigen/test/jackson_core_test/generate.dart b/jnigen/test/jackson_core_test/generate.dart index 9b9b37f9..1b2a7613 100644 --- a/jnigen/test/jackson_core_test/generate.dart +++ b/jnigen/test/jackson_core_test/generate.dart @@ -69,17 +69,19 @@ Config getConfig({ ], logLevel: Level.INFO, exclude: BindingExclusions( - // TODO(#31): Remove field exclusions. - fields: excludeAll([ - ['com.fasterxml.jackson.core.JsonFactory', 'DEFAULT_QUOTE_CHAR'], - ['com.fasterxml.jackson.core.Base64Variant', 'PADDING_CHAR_NONE'], - ['com.fasterxml.jackson.core.base.ParserMinimalBase', 'CHAR_NULL'], - ['com.fasterxml.jackson.core.io.UTF32Reader', 'NC'], - ]), - // TODO(#159): Remove class exclusions. - classes: ClassNameFilter.exclude( - 'com.fasterxml.jackson.core.JsonFactoryBuilder', - )), + // TODO(#31): Remove field exclusions. + fields: excludeAll([ + ['com.fasterxml.jackson.core.JsonFactory', 'DEFAULT_QUOTE_CHAR'], + ['com.fasterxml.jackson.core.Base64Variant', 'PADDING_CHAR_NONE'], + ['com.fasterxml.jackson.core.base.ParserMinimalBase', 'CHAR_NULL'], + ['com.fasterxml.jackson.core.io.UTF32Reader', 'NC'], + ]), + // TODO(#159): Remove class exclusions. + classes: ClassNameFilter.exclude( + 'com.fasterxml.jackson.core.JsonFactoryBuilder', + ), + ), + experiments: {Experiment.interfaceImplementation}, ); return config; } diff --git a/jnigen/test/jackson_core_test/jnigen.yaml b/jnigen/test/jackson_core_test/jnigen.yaml index 6041671c..cf3b4db1 100644 --- a/jnigen/test/jackson_core_test/jnigen.yaml +++ b/jnigen/test/jackson_core_test/jnigen.yaml @@ -43,3 +43,6 @@ preamble: | // limitations under the License. log_level: warning + +enable_experiment: + - interface_implementation diff --git a/jnigen/test/simple_package_test/generate.dart b/jnigen/test/simple_package_test/generate.dart index c156830c..f550f74b 100644 --- a/jnigen/test/simple_package_test/generate.dart +++ b/jnigen/test/simple_package_test/generate.dart @@ -75,6 +75,7 @@ Config getConfig([BindingsType bindingsType = BindingsType.cBased]) { ), ), preamble: preamble, + experiments: {Experiment.interfaceImplementation}, ); return config; } From 8bb8e48b0ab1328faf8d6202b3af53d8f0df84b4 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Fri, 21 Jul 2023 20:50:56 +0200 Subject: [PATCH 24/25] fix primitive boxing --- jnigen/lib/src/bindings/dart_generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 2a56f10f..497e94a7 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -1601,7 +1601,7 @@ class _InterfaceReturnBox extends TypeVisitor { if (node.name == 'void') { return '$_jni.nullptr'; } - return '$_jni.J${node.name.capitalize()}(\$r).reference'; + return '$_jni.J${node.boxedName}(\$r).reference'; } } From a887b907a51723a9f9c710aa2d20be359242f2c6 Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Tue, 25 Jul 2023 11:17:40 +0200 Subject: [PATCH 25/25] address comments --- README.md | 2 +- jni/CHANGELOG.md | 2 +- jni/pubspec.yaml | 2 +- jnigen/CHANGELOG.md | 6 +-- jnigen/lib/src/bindings/dart_generator.dart | 1 + jnigen/lib/src/config/config_types.dart | 16 +------- jnigen/lib/src/config/experiments.dart | 38 +++++++++++++++++++ jnigen/pubspec.yaml | 2 +- jnigen/test/jackson_core_test/generate.dart | 1 + jnigen/test/simple_package_test/generate.dart | 1 + 10 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 jnigen/lib/src/config/experiments.dart diff --git a/README.md b/README.md index 8e0d89e0..d524e0e8 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ A `*` denotes required configuration. | `source_path` | List of directory paths | Directories to search for source files. Note: source_path for dependencies downloaded using `maven_downloads` configuration is added automatically without the need to specify here. | | `class_path` | List of directory / JAR paths | Classpath for API summary generation. This should include any JAR dependencies of the source files in `source_path`. | | `classes` * | List of qualified class / package names | List of qualified class / package names. `source_path` will be scanned assuming the sources follow standard java-ish hierarchy. That is a.b.c either maps to a directory `a/b/c` or a class file `a/b/c.java`. | -| `enable_experiment` | List of experiment names | List of enabled experiments. These features are still in development and their API might break. | +| `enable_experiment` | List of experiment names:
  • `interface_implementation`
| List of enabled experiments. These features are still in development and their API might break. | | `output:` | (Subsection) | This subsection will contain configuration related to output files. | | `output:` >> `bindings_type` | `c_based` (default) or `dart_only` | Binding generation strategy. [Trade-offs](#pure-dart-bindings) are explained at the end of this document. | | `output:` >> `c:` | (Subsection) | This subsection specified C output configuration. Required if `bindings_type` is `c_based`. | diff --git a/jni/CHANGELOG.md b/jni/CHANGELOG.md index 6679b16b..35a09db0 100644 --- a/jni/CHANGELOG.md +++ b/jni/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.6.0-dev-2 +## 0.6.0-wip-2 * Added `PortProxy` and related methods used for interface implementation. * Added the missing binding for `java.lang.Character`. diff --git a/jni/pubspec.yaml b/jni/pubspec.yaml index e42149a8..83442497 100644 --- a/jni/pubspec.yaml +++ b/jni/pubspec.yaml @@ -4,7 +4,7 @@ name: jni description: Library to access JNI from dart and flutter -version: 0.6.0-dev.2 +version: 0.6.0-wip.2 repository: https://github.com/dart-lang/jnigen/tree/main/jni environment: diff --git a/jnigen/CHANGELOG.md b/jnigen/CHANGELOG.md index 696549ad..8d2086e4 100644 --- a/jnigen/CHANGELOG.md +++ b/jnigen/CHANGELOG.md @@ -1,15 +1,15 @@ -## 0.6.0-dev.2 +## 0.6.0-wip.2 * Fixed a bug where the nested classes would be generated incorrectly depending on the backend used for generation. * Fixed a bug where ASM backend would produce the incorrect parent for multi-level nested classes. * Fixed a bug where the backends would produce different descriptors for the same method. * Added `enable_experiment` option to config. * Created an experiment called `interface_implementation` which creates a `.implement` method for interfaces, so you can implement them using Dart. -## 0.6.0-dev.1 +## 0.6.0-wip.1 * **Breaking Change** Specifying a class always pulls in nested classes by default. If a nested class is specified in config, it will be an error. * Save all `jnigen` logs to a file in `.dart_tool/jnigen/logs/`. This is useful for debugging. -## 0.6.0-dev.0 +## 0.6.0-wip.0 * **Breaking Change** Removed `suspend_fun_to_async` flag from the config. It's now happening by default since we read the Kotlin's metadata and reliably identify the `suspend fun`s. ## 0.5.0 diff --git a/jnigen/lib/src/bindings/dart_generator.dart b/jnigen/lib/src/bindings/dart_generator.dart index 497e94a7..3db7c0bc 100644 --- a/jnigen/lib/src/bindings/dart_generator.dart +++ b/jnigen/lib/src/bindings/dart_generator.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:meta/meta.dart'; import '../config/config.dart'; +import '../config/experiments.dart'; import '../elements/elements.dart'; import '../logging/logging.dart'; import '../util/string_util.dart'; diff --git a/jnigen/lib/src/config/config_types.dart b/jnigen/lib/src/config/config_types.dart index 0991166b..8004cee4 100644 --- a/jnigen/lib/src/config/config_types.dart +++ b/jnigen/lib/src/config/config_types.dart @@ -13,6 +13,7 @@ import '../elements/elements.dart'; import '../logging/logging.dart'; import '../util/find_package.dart'; import 'config_exception.dart'; +import 'experiments.dart'; import 'filters.dart'; import 'yaml_reader.dart'; @@ -598,7 +599,7 @@ class Config { experiments: prov .getStringList(_Props.experiments) ?.map( - (e) => getExperiment(e, null)!, + (e) => Experiment.fromString(e), ) .toSet(), imports: prov.getPathList(_Props.import), @@ -652,19 +653,6 @@ class Config { } } -enum Experiment { interfaceImplementation } - -Experiment? getExperiment( - String? name, - Experiment? defaultVal, -) { - return _getEnumValueFromString( - Experiment.values.valuesMap(), - name, - defaultVal, - ); -} - class _Props { static const summarizer = 'summarizer'; static const summarizerArgs = '$summarizer.extra_args'; diff --git a/jnigen/lib/src/config/experiments.dart b/jnigen/lib/src/config/experiments.dart new file mode 100644 index 00000000..d0eb39dd --- /dev/null +++ b/jnigen/lib/src/config/experiments.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2023, 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. + +class Experiment { + static const available = [ + interfaceImplementation, + ]; + + static const interfaceImplementation = Experiment( + name: 'interface_implementation', + description: 'Enables generation of machinery for ' + 'implementing Java interfaces in Dart.', + isExpired: false, + ); + + final String name; + final String description; + final bool isExpired; + + const Experiment({ + required this.name, + required this.description, + required this.isExpired, + }); + + factory Experiment.fromString(String s) { + final search = available.where((element) => element.name == s); + if (search.isEmpty) { + throw 'The experiment $s is not available in this version.'; + } + final result = search.single; + if (result.isExpired) { + throw 'The experiment $s can no longer be used in this version. '; + } + return result; + } +} diff --git a/jnigen/pubspec.yaml b/jnigen/pubspec.yaml index 5c018cff..07be3c81 100644 --- a/jnigen/pubspec.yaml +++ b/jnigen/pubspec.yaml @@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: jnigen -version: 0.6.0-dev.2 +version: 0.6.0-wip.2 description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen diff --git a/jnigen/test/jackson_core_test/generate.dart b/jnigen/test/jackson_core_test/generate.dart index 1b2a7613..796a760c 100644 --- a/jnigen/test/jackson_core_test/generate.dart +++ b/jnigen/test/jackson_core_test/generate.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:jnigen/jnigen.dart'; +import 'package:jnigen/src/config/experiments.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' hide equals; diff --git a/jnigen/test/simple_package_test/generate.dart b/jnigen/test/simple_package_test/generate.dart index f550f74b..ce38009d 100644 --- a/jnigen/test/simple_package_test/generate.dart +++ b/jnigen/test/simple_package_test/generate.dart @@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:jnigen/src/config/experiments.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart'; import 'package:jnigen/jnigen.dart';