From e4f24e77dc6a886444074b7e6f298ae30bb99e35 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:57:05 +0530 Subject: [PATCH 01/22] Fix inner classes --- jnigen/android_test_runner/lib/main.dart | 4 +- .../in_app_java/lib/android_utils.dart | 417 +++++++++++++++++- .../src/android_utils/android_utils.c | 278 ++++++++++++ .../apache/pdfbox/text/PDFTextStripper.dart | 32 +- jnigen/java/.gitignore | 2 + .../dart_lang/jnigen/apisummarizer/Main.java | 17 +- .../apisummarizer/SummarizerOptions.java | 2 +- .../apisummarizer/util/ClassFinder.java | 142 +++--- .../c_based/dart_bindings/simple_package.dart | 136 +++--- 9 files changed, 877 insertions(+), 153 deletions(-) diff --git a/jnigen/android_test_runner/lib/main.dart b/jnigen/android_test_runner/lib/main.dart index da07cdd5..ee6f2d81 100644 --- a/jnigen/android_test_runner/lib/main.dart +++ b/jnigen/android_test_runner/lib/main.dart @@ -37,10 +37,10 @@ class MyHomePage extends StatelessWidget { appBar: AppBar( title: const Text("Integration test runner"), ), - body: const Center( + body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: const [ Text( 'This app should be run as flutter integration test', ), diff --git a/jnigen/example/in_app_java/lib/android_utils.dart b/jnigen/example/in_app_java/lib/android_utils.dart index 77ffac78..028fa429 100644 --- a/jnigen/example/in_app_java/lib/android_utils.dart +++ b/jnigen/example/in_app_java/lib/android_utils.dart @@ -2382,6 +2382,419 @@ class $DefaultEmojiCompatConfig_DefaultEmojiCompatConfigFactoryType extends jni } } +/// from: android.os.Build$Partition +class Build_Partition extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Build_Partition.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $Build_PartitionType(); + + /// from: static public final java.lang.String PARTITION_NAME_SYSTEM + static const PARTITION_NAME_SYSTEM = r"""system"""; + + static final _getName = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer)>>("Build_Partition__getName") + .asFunction)>(); + + /// from: public java.lang.String getName() + /// The returned object must be deleted after use, by calling the `delete` method. + jni.JString getName() { + return const jni.JStringType().fromRef(_getName(reference).object); + } + + static final _getFingerprint = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer)>>("Build_Partition__getFingerprint") + .asFunction)>(); + + /// from: public java.lang.String getFingerprint() + /// The returned object must be deleted after use, by calling the `delete` method. + jni.JString getFingerprint() { + return const jni.JStringType().fromRef(_getFingerprint(reference).object); + } + + static final _getBuildTimeMillis = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>( + "Build_Partition__getBuildTimeMillis") + .asFunction)>(); + + /// from: public long getBuildTimeMillis() + int getBuildTimeMillis() { + return _getBuildTimeMillis(reference).long; + } + + static final _equals1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("Build_Partition__equals1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public boolean equals(java.lang.Object object) + bool equals1( + jni.JObject object, + ) { + return _equals1(reference, object.reference).boolean; + } + + static final _hashCode1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer)>>("Build_Partition__hashCode1") + .asFunction)>(); + + /// from: public int hashCode() + int hashCode1() { + return _hashCode1(reference).integer; + } +} + +class $Build_PartitionType extends jni.JObjType { + const $Build_PartitionType(); + + @override + String get signature => r"Landroid/os/Build$Partition;"; + + @override + Build_Partition fromRef(jni.JObjectPtr ref) => Build_Partition.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($Build_PartitionType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($Build_PartitionType) && + other is $Build_PartitionType; + } +} + +/// from: android.os.Build$VERSION +class Build_VERSION extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Build_VERSION.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $Build_VERSIONType(); + static final _get_BASE_OS = + jniLookup>( + "get_Build_VERSION__BASE_OS") + .asFunction(); + + /// from: static public final java.lang.String BASE_OS + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get BASE_OS => + const jni.JStringType().fromRef(_get_BASE_OS().object); + + static final _get_CODENAME = + jniLookup>( + "get_Build_VERSION__CODENAME") + .asFunction(); + + /// from: static public final java.lang.String CODENAME + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get CODENAME => + const jni.JStringType().fromRef(_get_CODENAME().object); + + static final _get_INCREMENTAL = + jniLookup>( + "get_Build_VERSION__INCREMENTAL") + .asFunction(); + + /// from: static public final java.lang.String INCREMENTAL + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get INCREMENTAL => + const jni.JStringType().fromRef(_get_INCREMENTAL().object); + + static final _get_MEDIA_PERFORMANCE_CLASS = + jniLookup>( + "get_Build_VERSION__MEDIA_PERFORMANCE_CLASS") + .asFunction(); + + /// from: static public final int MEDIA_PERFORMANCE_CLASS + static int get MEDIA_PERFORMANCE_CLASS => + _get_MEDIA_PERFORMANCE_CLASS().integer; + + static final _get_PREVIEW_SDK_INT = + jniLookup>( + "get_Build_VERSION__PREVIEW_SDK_INT") + .asFunction(); + + /// from: static public final int PREVIEW_SDK_INT + static int get PREVIEW_SDK_INT => _get_PREVIEW_SDK_INT().integer; + + static final _get_RELEASE = + jniLookup>( + "get_Build_VERSION__RELEASE") + .asFunction(); + + /// from: static public final java.lang.String RELEASE + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get RELEASE => + const jni.JStringType().fromRef(_get_RELEASE().object); + + static final _get_RELEASE_OR_CODENAME = + jniLookup>( + "get_Build_VERSION__RELEASE_OR_CODENAME") + .asFunction(); + + /// from: static public final java.lang.String RELEASE_OR_CODENAME + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get RELEASE_OR_CODENAME => + const jni.JStringType().fromRef(_get_RELEASE_OR_CODENAME().object); + + static final _get_RELEASE_OR_PREVIEW_DISPLAY = + jniLookup>( + "get_Build_VERSION__RELEASE_OR_PREVIEW_DISPLAY") + .asFunction(); + + /// from: static public final java.lang.String RELEASE_OR_PREVIEW_DISPLAY + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get RELEASE_OR_PREVIEW_DISPLAY => + const jni.JStringType().fromRef(_get_RELEASE_OR_PREVIEW_DISPLAY().object); + + static final _get_SDK = + jniLookup>( + "get_Build_VERSION__SDK") + .asFunction(); + + /// from: static public final java.lang.String SDK + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JString get SDK => + const jni.JStringType().fromRef(_get_SDK().object); + + static final _get_SDK_INT = + jniLookup>( + "get_Build_VERSION__SDK_INT") + .asFunction(); + + /// from: static public final int SDK_INT + static int get SDK_INT => _get_SDK_INT().integer; + + static final _get_SECURITY_PATCH = + jniLookup>( + "get_Build_VERSION__SECURITY_PATCH") + .asFunction(); + + /// from: static public final java.lang.String SECURITY_PATCH + /// The returned object must be deleted after use, by calling the `delete` method. + 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)>(); + + /// from: public void (android.os.Build $parent) + /// 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); + } +} + +class $Build_VERSIONType extends jni.JObjType { + const $Build_VERSIONType(); + + @override + String get signature => r"Landroid/os/Build$VERSION;"; + + @override + Build_VERSION fromRef(jni.JObjectPtr ref) => Build_VERSION.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($Build_VERSIONType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($Build_VERSIONType) && + other is $Build_VERSIONType; + } +} + +/// from: android.os.Build$VERSION_CODES +class Build_VERSION_CODES extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Build_VERSION_CODES.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $Build_VERSION_CODESType(); + + /// from: static public final int BASE + static const BASE = 1; + + /// from: static public final int BASE_1_1 + static const BASE_1_1 = 2; + + /// from: static public final int CUPCAKE + static const CUPCAKE = 3; + + /// from: static public final int CUR_DEVELOPMENT + static const CUR_DEVELOPMENT = 10000; + + /// from: static public final int DONUT + static const DONUT = 4; + + /// from: static public final int ECLAIR + static const ECLAIR = 5; + + /// from: static public final int ECLAIR_0_1 + static const ECLAIR_0_1 = 6; + + /// from: static public final int ECLAIR_MR1 + static const ECLAIR_MR1 = 7; + + /// from: static public final int FROYO + static const FROYO = 8; + + /// from: static public final int GINGERBREAD + static const GINGERBREAD = 9; + + /// from: static public final int GINGERBREAD_MR1 + static const GINGERBREAD_MR1 = 10; + + /// from: static public final int HONEYCOMB + static const HONEYCOMB = 11; + + /// from: static public final int HONEYCOMB_MR1 + static const HONEYCOMB_MR1 = 12; + + /// from: static public final int HONEYCOMB_MR2 + static const HONEYCOMB_MR2 = 13; + + /// from: static public final int ICE_CREAM_SANDWICH + static const ICE_CREAM_SANDWICH = 14; + + /// from: static public final int ICE_CREAM_SANDWICH_MR1 + static const ICE_CREAM_SANDWICH_MR1 = 15; + + /// from: static public final int JELLY_BEAN + static const JELLY_BEAN = 16; + + /// from: static public final int JELLY_BEAN_MR1 + static const JELLY_BEAN_MR1 = 17; + + /// from: static public final int JELLY_BEAN_MR2 + static const JELLY_BEAN_MR2 = 18; + + /// from: static public final int KITKAT + static const KITKAT = 19; + + /// from: static public final int KITKAT_WATCH + static const KITKAT_WATCH = 20; + + /// from: static public final int LOLLIPOP + static const LOLLIPOP = 21; + + /// from: static public final int LOLLIPOP_MR1 + static const LOLLIPOP_MR1 = 22; + + /// from: static public final int M + static const M = 23; + + /// from: static public final int N + static const N = 24; + + /// from: static public final int N_MR1 + static const N_MR1 = 25; + + /// from: static public final int O + static const O = 26; + + /// from: static public final int O_MR1 + static const O_MR1 = 27; + + /// from: static public final int P + static const P = 28; + + /// from: static public final int Q + static const Q = 29; + + /// from: static public final int R + static const R = 30; + + /// from: static public final int S + static const S = 31; + + /// from: static public final int S_V2 + static const S_V2 = 32; + + /// 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)>(); + + /// from: public void (android.os.Build $parent) + /// 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); + } +} + +class $Build_VERSION_CODESType extends jni.JObjType { + const $Build_VERSION_CODESType(); + + @override + String get signature => r"Landroid/os/Build$VERSION_CODES;"; + + @override + Build_VERSION_CODES fromRef(jni.JObjectPtr ref) => + Build_VERSION_CODES.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($Build_VERSION_CODESType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($Build_VERSION_CODESType) && + other is $Build_VERSION_CODESType; + } +} + /// from: android.os.Build class Build extends jni.JObject { @override @@ -2688,8 +3101,8 @@ class Build extends jni.JObject { /// from: static public java.util.List getFingerprintedPartitions() /// The returned object must be deleted after use, by calling the `delete` method. - static jni.JList getFingerprintedPartitions() { - return const jni.JListType(jni.JObjectType()) + static jni.JList getFingerprintedPartitions() { + return const jni.JListType($Build_PartitionType()) .fromRef(_getFingerprintedPartitions().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 6cf0cf28..c41ff6e9 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 @@ -1352,6 +1352,284 @@ JniResult DefaultEmojiCompatConfig_DefaultEmojiCompatConfigFactory__create( return to_global_ref_result(_result); } +// android.os.Build$Partition +jclass _c_Build_Partition = NULL; + +jmethodID _m_Build_Partition__getName = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_Partition__getName(jobject self_) { + load_env(); + load_class_global_ref(&_c_Build_Partition, "android/os/Build$Partition"); + if (_c_Build_Partition == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Build_Partition, &_m_Build_Partition__getName, "getName", + "()Ljava/lang/String;"); + if (_m_Build_Partition__getName == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_Build_Partition__getName); + return to_global_ref_result(_result); +} + +jmethodID _m_Build_Partition__getFingerprint = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_Partition__getFingerprint(jobject self_) { + load_env(); + load_class_global_ref(&_c_Build_Partition, "android/os/Build$Partition"); + if (_c_Build_Partition == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Build_Partition, &_m_Build_Partition__getFingerprint, + "getFingerprint", "()Ljava/lang/String;"); + if (_m_Build_Partition__getFingerprint == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_Build_Partition__getFingerprint); + return to_global_ref_result(_result); +} + +jmethodID _m_Build_Partition__getBuildTimeMillis = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_Partition__getBuildTimeMillis(jobject self_) { + load_env(); + load_class_global_ref(&_c_Build_Partition, "android/os/Build$Partition"); + if (_c_Build_Partition == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Build_Partition, &_m_Build_Partition__getBuildTimeMillis, + "getBuildTimeMillis", "()J"); + if (_m_Build_Partition__getBuildTimeMillis == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = (*jniEnv)->CallLongMethod( + jniEnv, self_, _m_Build_Partition__getBuildTimeMillis); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_Build_Partition__equals1 = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_Partition__equals1(jobject self_, jobject object) { + load_env(); + load_class_global_ref(&_c_Build_Partition, "android/os/Build$Partition"); + if (_c_Build_Partition == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Build_Partition, &_m_Build_Partition__equals1, "equals", + "(Ljava/lang/Object;)Z"); + if (_m_Build_Partition__equals1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod( + jniEnv, self_, _m_Build_Partition__equals1, object); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_Build_Partition__hashCode1 = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_Partition__hashCode1(jobject self_) { + load_env(); + load_class_global_ref(&_c_Build_Partition, "android/os/Build$Partition"); + if (_c_Build_Partition == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Build_Partition, &_m_Build_Partition__hashCode1, "hashCode", + "()I"); + if (_m_Build_Partition__hashCode1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = + (*jniEnv)->CallIntMethod(jniEnv, self_, _m_Build_Partition__hashCode1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +// android.os.Build$VERSION +jclass _c_Build_VERSION = NULL; + +jmethodID _m_Build_VERSION__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_VERSION__ctor(jobject _parent) { + 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"); + 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); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__BASE_OS = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__BASE_OS() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__BASE_OS, "BASE_OS", + "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField(jniEnv, _c_Build_VERSION, + _f_Build_VERSION__BASE_OS); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__CODENAME = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__CODENAME() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__CODENAME, "CODENAME", + "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField(jniEnv, _c_Build_VERSION, + _f_Build_VERSION__CODENAME); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__INCREMENTAL = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__INCREMENTAL() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__INCREMENTAL, + "INCREMENTAL", "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__INCREMENTAL); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__MEDIA_PERFORMANCE_CLASS = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__MEDIA_PERFORMANCE_CLASS() { + 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_static_field(_c_Build_VERSION, + &_f_Build_VERSION__MEDIA_PERFORMANCE_CLASS, + "MEDIA_PERFORMANCE_CLASS", "I"); + int32_t _result = (*jniEnv)->GetStaticIntField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__MEDIA_PERFORMANCE_CLASS); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jfieldID _f_Build_VERSION__PREVIEW_SDK_INT = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__PREVIEW_SDK_INT() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__PREVIEW_SDK_INT, + "PREVIEW_SDK_INT", "I"); + int32_t _result = (*jniEnv)->GetStaticIntField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__PREVIEW_SDK_INT); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jfieldID _f_Build_VERSION__RELEASE = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__RELEASE() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__RELEASE, "RELEASE", + "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField(jniEnv, _c_Build_VERSION, + _f_Build_VERSION__RELEASE); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__RELEASE_OR_CODENAME = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__RELEASE_OR_CODENAME() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__RELEASE_OR_CODENAME, + "RELEASE_OR_CODENAME", "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__RELEASE_OR_CODENAME); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__RELEASE_OR_PREVIEW_DISPLAY = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__RELEASE_OR_PREVIEW_DISPLAY() { + 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_static_field(_c_Build_VERSION, + &_f_Build_VERSION__RELEASE_OR_PREVIEW_DISPLAY, + "RELEASE_OR_PREVIEW_DISPLAY", "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__RELEASE_OR_PREVIEW_DISPLAY); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__SDK = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__SDK() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__SDK, "SDK", + "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField(jniEnv, _c_Build_VERSION, + _f_Build_VERSION__SDK); + return to_global_ref_result(_result); +} + +jfieldID _f_Build_VERSION__SDK_INT = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__SDK_INT() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__SDK_INT, "SDK_INT", + "I"); + int32_t _result = (*jniEnv)->GetStaticIntField(jniEnv, _c_Build_VERSION, + _f_Build_VERSION__SDK_INT); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jfieldID _f_Build_VERSION__SECURITY_PATCH = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Build_VERSION__SECURITY_PATCH() { + 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_static_field(_c_Build_VERSION, &_f_Build_VERSION__SECURITY_PATCH, + "SECURITY_PATCH", "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_Build_VERSION, _f_Build_VERSION__SECURITY_PATCH); + return to_global_ref_result(_result); +} + +// android.os.Build$VERSION_CODES +jclass _c_Build_VERSION_CODES = NULL; + +jmethodID _m_Build_VERSION_CODES__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult Build_VERSION_CODES__ctor(jobject _parent) { + 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"); + 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); + return to_global_ref_result(_result); +} + // android.os.Build jclass _c_Build = NULL; 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..bd59ec65 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 @@ -62,12 +62,12 @@ class PDFTextStripper extends jni.JObject { static final _get_LINE_SEPARATOR = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__LINE_SEPARATOR") + jni.JObjectPtr, + )>>("get_PDFTextStripper__LINE_SEPARATOR") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); /// from: protected final java.lang.String LINE_SEPARATOR /// The returned object must be deleted after use, by calling the `delete` method. @@ -79,12 +79,12 @@ class PDFTextStripper extends jni.JObject { static final _get_charactersByArticle = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__charactersByArticle") + jni.JObjectPtr, + )>>("get_PDFTextStripper__charactersByArticle") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_charactersByArticle = jniLookup< ffi.NativeFunction< @@ -133,12 +133,12 @@ class PDFTextStripper extends jni.JObject { static final _get_document = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__document") + jni.JObjectPtr, + )>>("get_PDFTextStripper__document") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_document = jniLookup< ffi.NativeFunction< @@ -160,12 +160,12 @@ class PDFTextStripper extends jni.JObject { static final _get_output = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__output") + jni.JObjectPtr, + )>>("get_PDFTextStripper__output") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_output = jniLookup< ffi.NativeFunction< diff --git a/jnigen/java/.gitignore b/jnigen/java/.gitignore index fe41dfd1..5ccba179 100644 --- a/jnigen/java/.gitignore +++ b/jnigen/java/.gitignore @@ -8,3 +8,5 @@ target/* .idea/jarRepositories.xml .idea/misc.xml .idea/runConfigurations.xml + +.classpath ## Used for experimenting with JShell REPL diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java index 1eda4600..17e99294 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java @@ -8,10 +8,7 @@ import com.github.dart_lang.jnigen.apisummarizer.doclet.SummarizerDoclet; import com.github.dart_lang.jnigen.apisummarizer.elements.ClassDecl; import com.github.dart_lang.jnigen.apisummarizer.util.*; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.OutputStream; +import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; @@ -31,6 +28,16 @@ public enum Backend { AUTO, } + static PrintStream log; + + static { + try { + log = new PrintStream(new FileOutputStream("summarizer_log.txt", true), true); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + static SummarizerOptions options; public static List runDocletWithClass( @@ -73,7 +80,7 @@ public static void main(String[] args) throws FileNotFoundException { } else { output = new FileOutputStream(options.outputFile); } - + log.printf("sourcepaths=%s, classpaths=%s\n", options.sourcePath, options.classPath); List sourcePaths = options.sourcePath != null ? Arrays.asList(options.sourcePath.split(File.pathSeparator)) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java index ca63a453..d21e4754 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java @@ -19,7 +19,7 @@ public class SummarizerOptions { public static SummarizerOptions fromCommandLine(CommandLine cmd) { var opts = new SummarizerOptions(); - opts.sourcePath = cmd.getOptionValue("sources", "."); + opts.sourcePath = cmd.getOptionValue("sources", null); var backendString = cmd.getOptionValue("backend", "auto"); opts.backend = Main.Backend.valueOf(backendString.toUpperCase()); opts.classPath = cmd.getOptionValue("classes", null); diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 29380224..227c5dd9 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,8 +1,12 @@ package com.github.dart_lang.jnigen.apisummarizer.util; -import java.io.File; -import java.io.FileFilter; -import java.util.*; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; import java.util.function.BiFunction; import java.util.function.Function; import java.util.jar.JarEntry; @@ -13,27 +17,75 @@ import javax.tools.StandardJavaFileManager; public class ClassFinder { + private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { + var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; + if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { + return false; + } + String nested = + pathString.substring( + fqnWithSlashesDollarSign.length(), pathString.length() - suffix.length()); + if (nested.matches("[a-zA-Z0-9_$]*")) { + return true; + } + Log.always("Possibly invalid path - '%s', skipping", pathString); + return false; + } + + public static List getNestedClassesInPathList( + TreeSet paths, String fqnWithSlashes, String suffix) { + return paths.tailSet(Path.of(fqnWithSlashes)).stream() + .takeWhile(path -> isNestedClassOf(path.toString(), fqnWithSlashes, suffix)) + .collect(Collectors.toList()); + } + + public static List getNestedClassesInStringList( + TreeSet paths, String fqnWithSlashes, String suffix) { + return paths.tailSet(fqnWithSlashes).stream() + .takeWhile(path -> isNestedClassOf(path, fqnWithSlashes, suffix)) + .collect(Collectors.toList()); + } + public static void findFilesInPath( - String searchPath, + String searchLocation, String suffix, Map> classes, - Function, List> mapper) { + Function, List> mapper) { + Path searchPath = Path.of(searchLocation); + + TreeSet filePaths; + try (var walk = Files.walk(searchPath)) { + filePaths = walk.filter(Files::isRegularFile).collect(Collectors.toCollection(TreeSet::new)); + } catch (IOException e) { + throw new RuntimeException(e); + } - for (var binaryName : classes.keySet()) { - if (classes.get(binaryName) != null) { + for (var className : classes.keySet()) { + if (classes.get(className) != null) { // Already found by other method of searching continue; } - var s = binaryName.replace(".", File.separator); - var f = new File(searchPath, s + suffix); - if (f.exists() && f.isFile()) { - classes.put(binaryName, mapper.apply(List.of(f))); - } - - var d = new File(searchPath, s); - if (d.exists() && d.isDirectory()) { - var files = recursiveListFiles(d, file -> file.getName().endsWith(suffix)); - classes.put(binaryName, mapper.apply(files)); + var fqnWithSlashes = className.replace(".", File.separator); + var dirPath = Path.of(searchLocation, fqnWithSlashes); + var filePath = Path.of(searchLocation, fqnWithSlashes + suffix); + if (filePaths.contains(filePath)) { + List resultPaths = new ArrayList<>(); + resultPaths.add(filePath); + List nestedClasses = getNestedClassesInPathList(filePaths, fqnWithSlashes, suffix); + resultPaths.addAll(nestedClasses); + classes.put(className, mapper.apply(resultPaths)); + } else if (Files.exists(dirPath) && Files.isDirectory(dirPath)) { + try (var walk = Files.walk(dirPath)) { + List resultPaths = + walk.filter(Files::isRegularFile) + .filter(innerPath -> innerPath.toString().endsWith(suffix)) + .collect(Collectors.toList()); + classes.put(className, mapper.apply(resultPaths)); + } catch (IOException e) { + throw new RuntimeException(e); + } } + // Not found in this search path. + Log.verbose("FQN '%s' not found in '%s'", className, searchLocation); } } @@ -48,16 +100,18 @@ public static void findFilesInJar( if (classes.get(binaryName) != null) { continue; } - var relativePath = binaryName.replace('.', '/'); - - var filePath = relativePath + suffix; + var fqnWithSlashes = binaryName.replace('.', '/'); + var filePath = fqnWithSlashes + suffix; if (entries.contains(filePath)) { - var found = List.of(jar.getEntry(filePath)); + // find nested classes as well + var foundPaths = getNestedClassesInStringList(entries, fqnWithSlashes, suffix); + var found = StreamUtil.map(foundPaths, jar::getEntry); + found.add(jar.getEntry(filePath)); classes.put(binaryName, mapper.apply(jar, found)); } // Obtain set of all strings prefixed with relativePath + '/' - var dirPath = relativePath + '/'; + var dirPath = fqnWithSlashes + '/'; var children = entries.tailSet(dirPath).stream() .takeWhile(e -> e.startsWith(dirPath)) @@ -75,7 +129,7 @@ public static void find( Map> classes, List searchPaths, String suffix, - Function, List> fileMapper, + Function, List> fileMapper, BiFunction, List> entryMapper) { for (var searchPath : searchPaths) { File searchFile = new File(searchPath); @@ -89,8 +143,9 @@ public static void find( } private static List getJavaFileObjectsFromFiles( - List files, StandardJavaFileManager fm) { + List paths, StandardJavaFileManager fm) { var result = new ArrayList(); + var files = StreamUtil.map(paths, Path::toFile); fm.getJavaFileObjectsFromFiles(files).forEach(result::add); return result; } @@ -100,8 +155,8 @@ private static List getJavaFileObjectsFromJar( return StreamUtil.map(entries, (entry) -> new JarEntryFileObject(jarFile, entry)); } - private static List getInputStreamProvidersFromFiles(List files) { - return StreamUtil.map(files, FileInputStreamProvider::new); + private static List getInputStreamProvidersFromFiles(List files) { + return StreamUtil.map(files, (path) -> new FileInputStreamProvider(path.toFile())); } private static List getInputStreamProvidersFromJar( @@ -113,6 +168,7 @@ public static void findJavaSources( Map> classes, List searchPaths, StandardJavaFileManager fm) { + Log.always("searchPaths (sources): %s", searchPaths); find( classes, searchPaths, @@ -123,6 +179,7 @@ public static void findJavaSources( public static void findJavaClasses( Map> classes, List searchPaths) { + Log.always("searchPaths (classes): %s", searchPaths); find( classes, searchPaths, @@ -130,37 +187,4 @@ public static void findJavaClasses( ClassFinder::getInputStreamProvidersFromFiles, ClassFinder::getInputStreamProvidersFromJar); } - - /** - * Lists all files under given directory, which satisfy the condition of filter.
- * The order of listing will be deterministic. - */ - public static List recursiveListFiles(File file, FileFilter filter) { - if (!file.exists()) { - throw new RuntimeException("File not found: " + file.getPath()); - } - - if (!file.isDirectory()) { - return List.of(file); - } - - // List files using a breadth-first traversal. - var files = new ArrayList(); - var queue = new ArrayDeque(); - queue.add(file); - while (!queue.isEmpty()) { - var dir = queue.poll(); - var list = dir.listFiles(entry -> entry.isDirectory() || filter.accept(entry)); - if (list == null) throw new IllegalArgumentException("File.listFiles returned null!"); - Arrays.sort(list); - for (var path : list) { - if (path.isDirectory()) { - queue.add(path); - } else { - files.add(path); - } - } - } - return files; - } } 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 4e16f1e8..79b14dd7 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 @@ -933,12 +933,12 @@ class Fields extends jni.JObject { static final _get_i = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__i") + jni.JObjectPtr, + )>>("get_Fields__i") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_i = jniLookup< ffi.NativeFunction< @@ -959,12 +959,12 @@ class Fields extends jni.JObject { static final _get_trillion = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__trillion") + jni.JObjectPtr, + )>>("get_Fields__trillion") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_trillion = jniLookup< ffi.NativeFunction< @@ -981,12 +981,12 @@ class Fields extends jni.JObject { static final _get_isAchillesDead = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__isAchillesDead") + jni.JObjectPtr, + )>>("get_Fields__isAchillesDead") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_isAchillesDead = jniLookup< ffi.NativeFunction< @@ -1004,12 +1004,12 @@ class Fields extends jni.JObject { static final _get_bestFighterInGreece = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__bestFighterInGreece") + jni.JObjectPtr, + )>>("get_Fields__bestFighterInGreece") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_bestFighterInGreece = jniLookup< ffi.NativeFunction< @@ -1031,12 +1031,12 @@ class Fields extends jni.JObject { static final _get_random = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__random") + jni.JObjectPtr, + )>>("get_Fields__random") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_random = jniLookup< ffi.NativeFunction< @@ -1121,12 +1121,12 @@ class Fields_Nested extends jni.JObject { static final _get_hundred = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields_Nested__hundred") + jni.JObjectPtr, + )>>("get_Fields_Nested__hundred") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_hundred = jniLookup< ffi.NativeFunction< @@ -1341,12 +1341,12 @@ class GrandParent<$T extends jni.JObject> extends jni.JObject { static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent__value") + jni.JObjectPtr, + )>>("get_GrandParent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1521,12 +1521,12 @@ class GrandParent_Parent<$T extends jni.JObject, $S extends jni.JObject> static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -1548,12 +1548,12 @@ class GrandParent_Parent<$T extends jni.JObject, $S extends jni.JObject> static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent__value") + jni.JObjectPtr, + )>>("get_GrandParent_Parent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1668,12 +1668,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_grandParentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__grandParentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__grandParentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_grandParentValue = jniLookup< ffi.NativeFunction< @@ -1695,12 +1695,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -1722,12 +1722,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__value") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1840,12 +1840,12 @@ class GrandParent_StaticParent<$S extends jni.JObject> extends jni.JObject { static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent__value") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1946,12 +1946,12 @@ class GrandParent_StaticParent_Child<$S extends jni.JObject, static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent_Child__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent_Child__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -1973,12 +1973,12 @@ class GrandParent_StaticParent_Child<$S extends jni.JObject, static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent_Child__value") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent_Child__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -2218,12 +2218,12 @@ class MyMap_MyEntry<$K extends jni.JObject, $V extends jni.JObject> static final _get_key = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_MyMap_MyEntry__key") + jni.JObjectPtr, + )>>("get_MyMap_MyEntry__key") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_key = jniLookup< ffi.NativeFunction< @@ -2243,12 +2243,12 @@ class MyMap_MyEntry<$K extends jni.JObject, $V extends jni.JObject> static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_MyMap_MyEntry__value") + jni.JObjectPtr, + )>>("get_MyMap_MyEntry__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< From a015aa779094b0b5eb2fe214923927240d153ba1 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:12:14 +0530 Subject: [PATCH 02/22] Fix nested class finding --- .../dart_lang/jnigen/apisummarizer/Main.java | 20 +++++-------------- .../apisummarizer/util/ClassFinder.java | 16 ++++++++++----- jnigen/test/summary_generation_test.dart | 6 ++++++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java index 17e99294..f2828df8 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java @@ -8,15 +8,16 @@ import com.github.dart_lang.jnigen.apisummarizer.doclet.SummarizerDoclet; import com.github.dart_lang.jnigen.apisummarizer.elements.ClassDecl; import com.github.dart_lang.jnigen.apisummarizer.util.*; +import jdk.javadoc.doclet.Doclet; + +import javax.tools.DocumentationTool; +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; -import javax.tools.DocumentationTool; -import javax.tools.JavaFileObject; -import javax.tools.ToolProvider; -import jdk.javadoc.doclet.Doclet; public class Main { public enum Backend { @@ -28,16 +29,6 @@ public enum Backend { AUTO, } - static PrintStream log; - - static { - try { - log = new PrintStream(new FileOutputStream("summarizer_log.txt", true), true); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - static SummarizerOptions options; public static List runDocletWithClass( @@ -80,7 +71,6 @@ public static void main(String[] args) throws FileNotFoundException { } else { output = new FileOutputStream(options.outputFile); } - log.printf("sourcepaths=%s, classpaths=%s\n", options.sourcePath, options.classPath); List sourcePaths = options.sourcePath != null ? Arrays.asList(options.sourcePath.split(File.pathSeparator)) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 227c5dd9..9052eee8 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,6 +1,9 @@ package com.github.dart_lang.jnigen.apisummarizer.util; -import java.io.*; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -13,11 +16,10 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.zip.ZipEntry; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { + Log.always("path=%s, fqn=%s, suffix=%s", pathString, fqnWithSlashes, suffix); var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { return false; @@ -70,8 +72,12 @@ public static void findFilesInPath( if (filePaths.contains(filePath)) { List resultPaths = new ArrayList<>(); resultPaths.add(filePath); - List nestedClasses = getNestedClassesInPathList(filePaths, fqnWithSlashes, suffix); - resultPaths.addAll(nestedClasses); + var filePathsWithoutSearchPathPrefix = filePaths.stream() + .map(searchPath::relativize) + .collect(Collectors.toCollection(TreeSet::new)); + List nestedClasses = getNestedClassesInPathList(filePathsWithoutSearchPathPrefix, + fqnWithSlashes, suffix); + resultPaths.addAll(StreamUtil.map(nestedClasses, searchPath::resolve)); classes.put(className, mapper.apply(resultPaths)); } else if (Files.exists(dirPath) && Files.isDirectory(dirPath)) { try (var walk = Files.walk(dirPath)) { diff --git a/jnigen/test/summary_generation_test.dart b/jnigen/test/summary_generation_test.dart index 0133bac3..287f66d9 100644 --- a/jnigen/test/summary_generation_test.dart +++ b/jnigen/test/summary_generation_test.dart @@ -21,6 +21,9 @@ import 'package:test/test.dart'; import 'test_util/test_util.dart'; +const nestedClass = + 'com.github.dart_lang.jnigen.simple_package.Example\$Nested'; + void expectSummaryHasAllClasses(Classes? classes) { expect(classes, isNotNull); final decls = classes!.decls; @@ -28,6 +31,9 @@ void expectSummaryHasAllClasses(Classes? classes) { final declNames = decls.keys.toSet(); final expectedClasses = javaClasses.where((name) => !name.contains("annotations.")).toList(); + // Nested classes should be included automatically with parent class. + // change this part if you change this behavior intentionally. + expectedClasses.add(nestedClass); expect(declNames, containsAll(expectedClasses)); } From a04c2e280ac6a359e1ba4869ecbddde3eaff10db Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:00:07 +0530 Subject: [PATCH 03/22] Log to file --- jnigen/lib/src/logging/logging.dart | 63 +++++++++++++++++++-- jnigen/lib/src/summary/summary.dart | 2 + jnigen/lib/src/tools/android_sdk_tools.dart | 3 + 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/jnigen/lib/src/logging/logging.dart b/jnigen/lib/src/logging/logging.dart index 67c605ee..c965cf47 100644 --- a/jnigen/lib/src/logging/logging.dart +++ b/jnigen/lib/src/logging/logging.dart @@ -18,10 +18,55 @@ String _colorize(String message, String colorCode) { return message; } +/// Format [DateTime] for use in filename +String _formatTime(DateTime now) { + return '${now.year}-${now.month}-${now.day}-' + '${now.hour}.${now.minute}.${now.second}'; +} + +// We need to respect logging level for console but log everything to file. +// Hierarchical logging is convoluted. I'm just keeping track of log level. +var _logLevel = Level.INFO; + +final _logDirUri = Directory.current.uri.resolve(".dart_tool/jnigen/logs/"); + +final _logDir = () { + final dir = Directory.fromUri(_logDirUri); + dir.createSync(recursive: true); + return dir; +}(); + +final _logFileUri = + _logDir.uri.resolve("jnigen-${_formatTime(DateTime.now())}.log"); + +final _logStream = File.fromUri(_logFileUri).openWrite(mode: FileMode.append); + +// Maximum number of log files to keep. +const _maxLogFiles = 20; + Logger log = () { + // Delete files except most recent files. + final logFiles = _logDir.listSync().map((f) => File(f.path)).toList(); + // sort in descending order of last modified time. + logFiles + .sort((f1, f2) => f2.lastModifiedSync().compareTo(f1.lastModifiedSync())); + final toDelete = logFiles.length < _maxLogFiles + ? const [] + : logFiles.sublist(_maxLogFiles); + for (final oldLogFile in toDelete) { + oldLogFile.deleteSync(); + } + + // initialize the logger. final jnigenLogger = Logger('jnigen'); - Logger.root.level = Level.INFO; + Logger.root.level = Level.ALL; Logger.root.onRecord.listen((r) { + // Write to file regardless of level. + _logStream.writeln('${r.level} ${r.time}: ${r.message}'); + // write to console only if level is above configured level. + if (r.level < _logLevel) { + return; + } var message = '(${r.loggerName}) ${r.level.name}: ${r.message}'; if ((r.level == Level.SHOUT || r.level == Level.SEVERE)) { message = _colorize(message, _ansiRed); @@ -35,10 +80,8 @@ Logger log = () { /// Set logging level to [level]. void setLoggingLevel(Level level) { - /// This initializes `log` as a side effect, so that level setting we apply - /// is always the last one applied. log.fine('Set log level: $level'); - Logger.root.level = level; + _logLevel = level; } /// Prints [message] without logging information. @@ -55,3 +98,15 @@ extension FatalErrors on Logger { return exit(exitCode); } } + +extension WriteToFile on Logger { + void writeToFile(Object? data) { + _logStream.writeln(data); + } + + void writeSectionToFile(String? sectionName, Object? data) { + _logStream.writeln("==== Begin $sectionName ===="); + _logStream.writeln(data); + _logStream.writeln("==== End $sectionName ===="); + } +} diff --git a/jnigen/lib/src/summary/summary.dart b/jnigen/lib/src/summary/summary.dart index 31473a63..09c13f5c 100644 --- a/jnigen/lib/src/summary/summary.dart +++ b/jnigen/lib/src/summary/summary.dart @@ -187,6 +187,8 @@ Future getSummary(Config config) async { stderrBuffer.toString(), 'Cannot generate summary: $e', ); + } finally { + log.writeSectionToFile("summarizer logs", stderrBuffer.toString()); } if (json == null) { throw SummaryParseException('Expected JSON element from summarizer.'); diff --git a/jnigen/lib/src/tools/android_sdk_tools.dart b/jnigen/lib/src/tools/android_sdk_tools.dart index 466863c3..3de5575f 100644 --- a/jnigen/lib/src/tools/android_sdk_tools.dart +++ b/jnigen/lib/src/tools/android_sdk_tools.dart @@ -209,6 +209,9 @@ task $_gradleGetSourcesTaskName(type: Copy) { 'yet cached. Please run `flutter build apk`$inAndroidProject and try ' 'again\n'); } + // Record both stdout and stderr of gradle. + log.writeSectionToFile("Gradle logs ($stubName)", procRes.stderr); + log.writeSectionToFile("Gradle output ($stubName)", procRes.stdout); final output = procRes.stdout as String; if (output.isEmpty) { printError(procRes.stderr); From 4c519414a7e2248f7de9010b2ca06088fbf208dc Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:20:19 +0530 Subject: [PATCH 04/22] Remove -r and -v options --- .../dart_lang/jnigen/apisummarizer/Main.java | 23 +++++++++--------- .../apisummarizer/SummarizerOptions.java | 24 ++++--------------- .../apisummarizer/util/ClassFinder.java | 11 +++++---- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java index f2828df8..0077fd71 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/Main.java @@ -7,17 +7,22 @@ import com.github.dart_lang.jnigen.apisummarizer.disasm.AsmSummarizer; import com.github.dart_lang.jnigen.apisummarizer.doclet.SummarizerDoclet; import com.github.dart_lang.jnigen.apisummarizer.elements.ClassDecl; -import com.github.dart_lang.jnigen.apisummarizer.util.*; -import jdk.javadoc.doclet.Doclet; - -import javax.tools.DocumentationTool; -import javax.tools.JavaFileObject; -import javax.tools.ToolProvider; -import java.io.*; +import com.github.dart_lang.jnigen.apisummarizer.util.ClassFinder; +import com.github.dart_lang.jnigen.apisummarizer.util.InputStreamProvider; +import com.github.dart_lang.jnigen.apisummarizer.util.JsonWriter; +import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; +import javax.tools.DocumentationTool; +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; +import jdk.javadoc.doclet.Doclet; public class Main { public enum Backend { @@ -36,16 +41,12 @@ public static List runDocletWithClass( Class docletClass, List fileObjects, SummarizerOptions options) { - Log.setVerbose(options.verbose); var fileManager = javaDoc.getStandardFileManager(null, null, null); var cli = new ArrayList(); cli.add((options.useModules ? "--module-" : "--") + "source-path=" + options.sourcePath); if (options.classPath != null) { cli.add("--class-path=" + options.classPath); } - if (options.addDependencies) { - cli.add("--expand-requires=all"); - } cli.addAll(List.of("-encoding", "utf8")); if (options.toolArgs != null) { diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java index d21e4754..8a3e5450 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/SummarizerOptions.java @@ -1,5 +1,6 @@ package com.github.dart_lang.jnigen.apisummarizer; +import java.util.Arrays; import org.apache.commons.cli.*; public class SummarizerOptions { @@ -9,7 +10,6 @@ public class SummarizerOptions { boolean useModules; Main.Backend backend; String modulesList; - boolean addDependencies; String toolArgs; boolean verbose; String outputFile; @@ -25,9 +25,7 @@ public static SummarizerOptions fromCommandLine(CommandLine cmd) { opts.classPath = cmd.getOptionValue("classes", null); opts.useModules = cmd.hasOption("use-modules"); opts.modulesList = cmd.getOptionValue("module-names", null); - opts.addDependencies = cmd.hasOption("recursive"); opts.toolArgs = cmd.getOptionValue("doctool-args", null); - opts.verbose = cmd.hasOption("verbose"); opts.outputFile = cmd.getOptionValue("output-file", null); opts.args = cmd.getArgs(); if (opts.args.length == 0) { @@ -47,28 +45,16 @@ public static SummarizerOptions parseArgs(String[] args) { true, "backend to use for summary generation ('doclet', 'asm' or 'auto' (default))."); Option useModules = new Option("M", "use-modules", false, "use Java modules"); - Option recursive = new Option("r", "recursive", false, "include dependencies of classes"); Option moduleNames = new Option("m", "module-names", true, "comma separated list of module names"); Option doctoolArgs = new Option("D", "doctool-args", true, "arguments to pass to the documentation tool"); - Option verbose = new Option("v", "verbose", false, "enable verbose output"); Option outputFile = new Option("o", "output-file", true, "write JSON to file instead of stdout"); - for (Option opt : - new Option[] { - sources, - classes, - backend, - useModules, - recursive, - moduleNames, - doctoolArgs, - verbose, - outputFile, - }) { - options.addOption(opt); - } + Option[] allOptions = { + sources, classes, backend, useModules, moduleNames, doctoolArgs, outputFile + }; + Arrays.stream(allOptions).forEach(options::addOption); HelpFormatter help = new HelpFormatter(); diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 9052eee8..15a0d78e 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,7 +1,5 @@ package com.github.dart_lang.jnigen.apisummarizer.util; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -16,6 +14,8 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.zip.ZipEntry; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { @@ -72,11 +72,12 @@ public static void findFilesInPath( if (filePaths.contains(filePath)) { List resultPaths = new ArrayList<>(); resultPaths.add(filePath); - var filePathsWithoutSearchPathPrefix = filePaths.stream() + var filePathsWithoutSearchPathPrefix = + filePaths.stream() .map(searchPath::relativize) .collect(Collectors.toCollection(TreeSet::new)); - List nestedClasses = getNestedClassesInPathList(filePathsWithoutSearchPathPrefix, - fqnWithSlashes, suffix); + List nestedClasses = + getNestedClassesInPathList(filePathsWithoutSearchPathPrefix, fqnWithSlashes, suffix); resultPaths.addAll(StreamUtil.map(nestedClasses, searchPath::resolve)); classes.put(className, mapper.apply(resultPaths)); } else if (Files.exists(dirPath) && Files.isDirectory(dirPath)) { From c93e2cbb430e45c2fb75ad6b1f0052f22996fccf Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:46:49 +0530 Subject: [PATCH 05/22] Use JUL for logging in summarizer --- .../doclet/SummarizerDoclet.java | 12 ++++---- .../apisummarizer/util/ClassFinder.java | 8 ++--- .../jnigen/apisummarizer/util/JsonWriter.java | 4 +-- .../jnigen/apisummarizer/util/Log.java | 30 +++++++------------ 4 files changed, 22 insertions(+), 32 deletions(-) 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 b1664e5e..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 @@ -46,7 +46,7 @@ public static List getClasses() { @Override public boolean run(DocletEnvironment docletEnvironment) { - Log.timed("Initializing doclet"); + Log.info("Initializing doclet"); utils = AstEnv.fromEnvironment(docletEnvironment); SummarizingScanner scanner = new SummarizingScanner(); docletEnvironment.getSpecifiedElements().forEach(e -> scanner.scan(e, new SummaryCollector())); @@ -77,7 +77,7 @@ public Void scan(Element e, SummaryCollector collector) { @Override public Void visitPackage(PackageElement e, SummaryCollector collector) { - Log.verbose("Visiting package: %s", e.getQualifiedName()); + Log.info("Visiting package: %s", e.getQualifiedName()); collector.packages.push(new Package()); System.out.println("package: " + e.getQualifiedName()); var result = super.visitPackage(e, collector); @@ -91,7 +91,7 @@ public Void visitType(TypeElement e, SummaryCollector collector) { if (!collector.types.isEmpty()) { return null; } - Log.verbose("Visiting class: %s, %s", e.getQualifiedName(), collector.types); + Log.info("Visiting class: %s, %s", e.getQualifiedName(), collector.types); switch (e.getKind()) { case CLASS: case INTERFACE: @@ -102,11 +102,11 @@ public Void visitType(TypeElement e, SummaryCollector collector) { super.visitType(e, collector); types.add(collector.types.pop()); } catch (SkipException skip) { - Log.always("Skip type: %s", e.getQualifiedName()); + Log.info("Skip type: %s", e.getQualifiedName()); } break; case ANNOTATION_TYPE: - Log.always("Skip annotation type: %s", e.getQualifiedName()); + Log.info("Skip annotation type: %s", e.getQualifiedName()); break; } return null; @@ -149,7 +149,7 @@ public Void visitExecutable(ExecutableElement element, SummaryCollector collecto collector.method = null; cls.methods.add(method); } catch (SkipException skip) { - Log.always("Skip method: %s", element.getSimpleName()); + Log.info("Skip method: %s", element.getSimpleName()); } break; case STATIC_INIT: diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 15a0d78e..f9d46211 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -19,7 +19,7 @@ public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { - Log.always("path=%s, fqn=%s, suffix=%s", pathString, fqnWithSlashes, suffix); + Log.info("path=%s, fqn=%s, suffix=%s", pathString, fqnWithSlashes, suffix); var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { return false; @@ -30,7 +30,7 @@ private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, if (nested.matches("[a-zA-Z0-9_$]*")) { return true; } - Log.always("Possibly invalid path - '%s', skipping", pathString); + Log.warning("Possibly invalid path - '%s', skipping", pathString); return false; } @@ -92,7 +92,7 @@ public static void findFilesInPath( } } // Not found in this search path. - Log.verbose("FQN '%s' not found in '%s'", className, searchLocation); + Log.info("FQN '%s' not found in '%s'", className, searchLocation); } } @@ -175,7 +175,6 @@ public static void findJavaSources( Map> classes, List searchPaths, StandardJavaFileManager fm) { - Log.always("searchPaths (sources): %s", searchPaths); find( classes, searchPaths, @@ -186,7 +185,6 @@ public static void findJavaSources( public static void findJavaClasses( Map> classes, List searchPaths) { - Log.always("searchPaths (classes): %s", searchPaths); find( classes, searchPaths, diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/JsonWriter.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/JsonWriter.java index 873a6be9..112e241f 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/JsonWriter.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/JsonWriter.java @@ -11,7 +11,7 @@ public class JsonWriter { public static void writeJSON(List classes, OutputStream output) { var mapper = new ObjectMapper(); - Log.timed("Writing JSON"); + Log.info("Writing JSON for %d classes", classes.size()); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); try { @@ -19,6 +19,6 @@ public static void writeJSON(List classes, OutputStream output) { } catch (IOException e) { e.printStackTrace(); } - Log.timed("Finished"); + Log.info("Finished"); } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/Log.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/Log.java index b2b2d78b..dfb48e18 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/Log.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/Log.java @@ -4,30 +4,22 @@ package com.github.dart_lang.jnigen.apisummarizer.util; -public class Log { - private static long lastPrinted = System.currentTimeMillis(); - - public static void setVerbose(boolean verbose) { - Log.verboseLogs = verbose; - } +import java.util.logging.Level; +import java.util.logging.Logger; - private static boolean verboseLogs = false; +public class Log { + private static final Logger logger = Logger.getLogger("ApiSummarizer"); - public static void verbose(String format, Object... args) { - if (!verboseLogs) { - return; - } - System.err.printf(format + "\n", args); + private static void log(Level level, String format, Object... args) { + String formatted = String.format(format, args); + logger.log(level, formatted); } - public static void timed(String format, Object... args) { - long now = System.currentTimeMillis(); - System.err.printf("[%6d ms] ", now - lastPrinted); - lastPrinted = now; - System.err.printf(format + "\n", args); + public static void info(String format, Object... args) { + log(Level.INFO, format, args); } - public static void always(String format, Object... args) { - System.err.printf(format + "\n", args); + public static void warning(String format, Object... args) { + log(Level.WARNING, format, args); } } From 6b56ce9c770bf06777e75e412573c16a45deecdd Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Fri, 30 Jun 2023 08:09:16 +0530 Subject: [PATCH 06/22] Add double nested class t o test --- .../apisummarizer/util/ClassFinder.java | 3 - .../c_based/c_bindings/simple_package.c | 53 +++++++++++++++ .../c_based/dart_bindings/simple_package.dart | 66 ++++++++++++++++++ .../dart_bindings/simple_package.dart | 68 +++++++++++++++++++ .../jnigen/simple_package/Example.java | 4 ++ jnigen/test/summary_generation_test.dart | 8 ++- 6 files changed, 196 insertions(+), 6 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index f9d46211..7792f155 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -19,7 +19,6 @@ public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { - Log.info("path=%s, fqn=%s, suffix=%s", pathString, fqnWithSlashes, suffix); var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { return false; @@ -91,8 +90,6 @@ public static void findFilesInPath( throw new RuntimeException(e); } } - // Not found in this search path. - Log.info("FQN '%s' not found in '%s'", className, searchLocation); } } 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 4a170e76..4b1d1197 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 @@ -642,6 +642,59 @@ JniResult Example_Nested__setValue(jobject self_, uint8_t value) { return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } +// com.github.dart_lang.jnigen.simple_package.Example$Nested$NestedTwice +jclass _c_Example_Nested_NestedTwice = NULL; + +jmethodID _m_Example_Nested_NestedTwice__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult Example_Nested_NestedTwice__ctor() { + load_env(); + load_class_global_ref( + &_c_Example_Nested_NestedTwice, + "com/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice"); + if (_c_Example_Nested_NestedTwice == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Example_Nested_NestedTwice, + &_m_Example_Nested_NestedTwice__ctor, "", "()V"); + if (_m_Example_Nested_NestedTwice__ctor == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_Example_Nested_NestedTwice, + _m_Example_Nested_NestedTwice__ctor); + return to_global_ref_result(_result); +} + +jfieldID _f_Example_Nested_NestedTwice__ZERO = NULL; +FFI_PLUGIN_EXPORT +JniResult get_Example_Nested_NestedTwice__ZERO() { + load_env(); + load_class_global_ref( + &_c_Example_Nested_NestedTwice, + "com/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice"); + if (_c_Example_Nested_NestedTwice == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_Example_Nested_NestedTwice, + &_f_Example_Nested_NestedTwice__ZERO, "ZERO", "I"); + int32_t _result = + (*jniEnv)->GetStaticIntField(jniEnv, _c_Example_Nested_NestedTwice, + _f_Example_Nested_NestedTwice__ZERO); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +FFI_PLUGIN_EXPORT +JniResult set_Example_Nested_NestedTwice__ZERO(int32_t value) { + load_env(); + load_class_global_ref( + &_c_Example_Nested_NestedTwice, + "com/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice"); + if (_c_Example_Nested_NestedTwice == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_Example_Nested_NestedTwice, + &_f_Example_Nested_NestedTwice__ZERO, "ZERO", "I"); + (*jniEnv)->SetStaticIntField(jniEnv, _c_Example_Nested_NestedTwice, + _f_Example_Nested_NestedTwice__ZERO, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + // com.github.dart_lang.jnigen.simple_package.Exceptions jclass _c_Exceptions = 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 79b14dd7..8efe648e 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 @@ -610,6 +610,72 @@ class $Example_NestedType extends jni.JObjType { } } +/// from: com.github.dart_lang.jnigen.simple_package.Example$Nested$NestedTwice +class Example_Nested_NestedTwice extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Example_Nested_NestedTwice.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $Example_Nested_NestedTwiceType(); + static final _get_ZERO = + jniLookup>( + "get_Example_Nested_NestedTwice__ZERO") + .asFunction(); + + static final _set_ZERO = + jniLookup>( + "set_Example_Nested_NestedTwice__ZERO") + .asFunction(); + + /// from: static public int ZERO + static int get ZERO => _get_ZERO().integer; + + /// from: static public int ZERO + static set ZERO(int value) => _set_ZERO(value).check(); + + static final _ctor = jniLookup>( + "Example_Nested_NestedTwice__ctor") + .asFunction(); + + /// from: public void () + /// The returned object must be deleted after use, by calling the `delete` method. + factory Example_Nested_NestedTwice() { + return Example_Nested_NestedTwice.fromRef(_ctor().object); + } +} + +class $Example_Nested_NestedTwiceType + extends jni.JObjType { + const $Example_Nested_NestedTwiceType(); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice;"; + + @override + Example_Nested_NestedTwice fromRef(jni.JObjectPtr ref) => + Example_Nested_NestedTwice.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($Example_Nested_NestedTwiceType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($Example_Nested_NestedTwiceType) && + other is $Example_Nested_NestedTwiceType; + } +} + /// from: com.github.dart_lang.jnigen.simple_package.Exceptions class Exceptions 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 fb92e1dd..e719671f 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 @@ -574,6 +574,74 @@ class $Example_NestedType extends jni.JObjType { } } +/// from: com.github.dart_lang.jnigen.simple_package.Example$Nested$NestedTwice +class Example_Nested_NestedTwice extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Example_Nested_NestedTwice.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + static final _class = jni.Jni.findJClass( + r"com/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice"); + + /// The type which includes information such as the signature of this class. + static const type = $Example_Nested_NestedTwiceType(); + static final _id_ZERO = jni.Jni.accessors.getStaticFieldIDOf( + _class.reference, + r"ZERO", + r"I", + ); + + /// from: static public int ZERO + static int get ZERO => jni.Jni.accessors + .getStaticField(_class.reference, _id_ZERO, jni.JniCallType.intType) + .integer; + + /// from: static public int ZERO + static set ZERO(int value) => + jni.Jni.env.SetStaticIntField(_class.reference, _id_ZERO, value); + + 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 Example_Nested_NestedTwice() { + return Example_Nested_NestedTwice.fromRef(jni.Jni.accessors + .newObjectWithArgs(_class.reference, _id_ctor, []).object); + } +} + +class $Example_Nested_NestedTwiceType + extends jni.JObjType { + const $Example_Nested_NestedTwiceType(); + + @override + String get signature => + r"Lcom/github/dart_lang/jnigen/simple_package/Example$Nested$NestedTwice;"; + + @override + Example_Nested_NestedTwice fromRef(jni.JObjectPtr ref) => + Example_Nested_NestedTwice.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($Example_Nested_NestedTwiceType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($Example_Nested_NestedTwiceType) && + other is $Example_Nested_NestedTwiceType; + } +} + /// from: com.github.dart_lang.jnigen.simple_package.Exceptions class Exceptions extends jni.JObject { @override diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java index edb27489..acaf2f95 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java @@ -183,5 +183,9 @@ public boolean getValue() { public void setValue(boolean value) { this.value = value; } + + public static class NestedTwice { + public static int ZERO = 0; + } } } diff --git a/jnigen/test/summary_generation_test.dart b/jnigen/test/summary_generation_test.dart index 287f66d9..2c436f6d 100644 --- a/jnigen/test/summary_generation_test.dart +++ b/jnigen/test/summary_generation_test.dart @@ -21,8 +21,10 @@ import 'package:test/test.dart'; import 'test_util/test_util.dart'; -const nestedClass = - 'com.github.dart_lang.jnigen.simple_package.Example\$Nested'; +const nestedClasses = [ + 'com.github.dart_lang.jnigen.simple_package.Example\$Nested', + 'com.github.dart_lang.jnigen.simple_package.Example\$Nested\$NestedTwice', +]; void expectSummaryHasAllClasses(Classes? classes) { expect(classes, isNotNull); @@ -33,7 +35,7 @@ void expectSummaryHasAllClasses(Classes? classes) { javaClasses.where((name) => !name.contains("annotations.")).toList(); // Nested classes should be included automatically with parent class. // change this part if you change this behavior intentionally. - expectedClasses.add(nestedClass); + expectedClasses.addAll(nestedClasses); expect(declNames, containsAll(expectedClasses)); } From b52956f0e3a62cf35c98ba4e427902b98551ec02 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sat, 1 Jul 2023 18:18:07 +0530 Subject: [PATCH 07/22] Enable logs only when run as jnigen command --- jnigen/bin/jnigen.dart | 1 + jnigen/lib/src/logging/logging.dart | 34 ++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/jnigen/bin/jnigen.dart b/jnigen/bin/jnigen.dart index be04b584..8dc69e37 100644 --- a/jnigen/bin/jnigen.dart +++ b/jnigen/bin/jnigen.dart @@ -6,6 +6,7 @@ import 'package:jnigen/jnigen.dart'; import 'package:jnigen/src/logging/logging.dart'; void main(List args) async { + enableLoggingToFile(); Config config; try { config = Config.parseArgs(args); diff --git a/jnigen/lib/src/logging/logging.dart b/jnigen/lib/src/logging/logging.dart index c965cf47..844bfe63 100644 --- a/jnigen/lib/src/logging/logging.dart +++ b/jnigen/lib/src/logging/logging.dart @@ -36,16 +36,28 @@ final _logDir = () { return dir; }(); -final _logFileUri = +Uri _getDefaultLogFileUri() => _logDir.uri.resolve("jnigen-${_formatTime(DateTime.now())}.log"); -final _logStream = File.fromUri(_logFileUri).openWrite(mode: FileMode.append); +IOSink? _logStream; + +/// Enable saving the logs to a file. +/// +/// This is only meant to be called from an application entry point such as +/// `main`. +void enableLoggingToFile() { + _deleteOldLogFiles(); + if (_logStream != null) { + throw StateError('Log file is already set'); + } + _logStream = File.fromUri(_getDefaultLogFileUri()).openWrite(); +} // Maximum number of log files to keep. -const _maxLogFiles = 20; +const _maxLogFiles = 5; -Logger log = () { - // Delete files except most recent files. +/// Delete log files except most recent [_maxLogFiles] files. +void _deleteOldLogFiles() { final logFiles = _logDir.listSync().map((f) => File(f.path)).toList(); // sort in descending order of last modified time. logFiles @@ -56,13 +68,15 @@ Logger log = () { for (final oldLogFile in toDelete) { oldLogFile.deleteSync(); } +} +Logger log = () { // initialize the logger. final jnigenLogger = Logger('jnigen'); Logger.root.level = Level.ALL; Logger.root.onRecord.listen((r) { // Write to file regardless of level. - _logStream.writeln('${r.level} ${r.time}: ${r.message}'); + _logStream?.writeln('${r.level} ${r.time}: ${r.message}'); // write to console only if level is above configured level. if (r.level < _logLevel) { return; @@ -101,12 +115,12 @@ extension FatalErrors on Logger { extension WriteToFile on Logger { void writeToFile(Object? data) { - _logStream.writeln(data); + _logStream?.writeln(data); } void writeSectionToFile(String? sectionName, Object? data) { - _logStream.writeln("==== Begin $sectionName ===="); - _logStream.writeln(data); - _logStream.writeln("==== End $sectionName ===="); + _logStream?.writeln("==== Begin $sectionName ===="); + _logStream?.writeln(data); + _logStream?.writeln("==== End $sectionName ===="); } } From e96186c5880a9373a5d180bf750542687b6d2bba Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sat, 1 Jul 2023 18:50:13 +0530 Subject: [PATCH 08/22] Log from tests --- jnigen/lib/src/logging/logging.dart | 27 +++++++++++-------- .../generated_files_test.dart | 3 +++ .../kotlin_test/generated_files_test.dart | 2 ++ jnigen/test/regenerate_examples_test.dart | 2 ++ .../generated_files_test.dart | 2 ++ jnigen/test/summary_generation_test.dart | 1 + 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/jnigen/lib/src/logging/logging.dart b/jnigen/lib/src/logging/logging.dart index 844bfe63..a13e02b3 100644 --- a/jnigen/lib/src/logging/logging.dart +++ b/jnigen/lib/src/logging/logging.dart @@ -36,25 +36,30 @@ final _logDir = () { return dir; }(); -Uri _getDefaultLogFileUri() => - _logDir.uri.resolve("jnigen-${_formatTime(DateTime.now())}.log"); +Uri _getDefaultLogFileUri([String? identifier]) { + final suffix = identifier == null ? '' : '-$identifier'; + return _logDir.uri + .resolve("jnigen$suffix-${_formatTime(DateTime.now())}.log"); +} IOSink? _logStream; -/// Enable saving the logs to a file. +/// Creates a log file and enables saving all logs to that file. This file is +/// in .dart_tool/jnigen/logs and the default name is derived from timestamp. /// -/// This is only meant to be called from an application entry point such as -/// `main`. -void enableLoggingToFile() { +/// If an `identifier` is passed, it will be appended to the filename. It's +/// useful to distinguish logs from different tests. +void enableLoggingToFile([String? identifier]) { + // If another `jnigen` task had run on same isolate, close the log file + // associated with it. + _logStream?.close(); _deleteOldLogFiles(); - if (_logStream != null) { - throw StateError('Log file is already set'); - } - _logStream = File.fromUri(_getDefaultLogFileUri()).openWrite(); + _logStream = File.fromUri(_getDefaultLogFileUri(identifier)) + .openWrite(mode: FileMode.append); } // Maximum number of log files to keep. -const _maxLogFiles = 5; +const _maxLogFiles = 20; /// Delete log files except most recent [_maxLogFiles] files. void _deleteOldLogFiles() { diff --git a/jnigen/test/jackson_core_test/generated_files_test.dart b/jnigen/test/jackson_core_test/generated_files_test.dart index 818e5d07..aba716cb 100644 --- a/jnigen/test/jackson_core_test/generated_files_test.dart +++ b/jnigen/test/jackson_core_test/generated_files_test.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 'package:jnigen/src/logging/logging.dart'; import 'package:test/test.dart'; import 'package:jnigen/jnigen.dart'; @@ -12,6 +13,8 @@ import 'generate.dart'; void main() async { await checkLocallyBuiltDependencies(); + enableLoggingToFile('jackson-core-generate-test'); + generateAndCompareBothModes( 'Generate and compare bindings for jackson_core library', getConfig(bindingsType: BindingsType.cBased), diff --git a/jnigen/test/kotlin_test/generated_files_test.dart b/jnigen/test/kotlin_test/generated_files_test.dart index b96c643c..f1ed2600 100644 --- a/jnigen/test/kotlin_test/generated_files_test.dart +++ b/jnigen/test/kotlin_test/generated_files_test.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/logging/logging.dart'; import 'package:test/test.dart'; import 'generate.dart'; @@ -12,6 +13,7 @@ void main() async { // This is not run in setupAll, because we want to exit with one line of // error message, not throw a long exception. await checkLocallyBuiltDependencies(); + enableLoggingToFile('kotlin-generate-test'); generateAndCompareBothModes( 'Generate and compare bindings for kotlin_test', getConfig(BindingsType.cBased), diff --git a/jnigen/test/regenerate_examples_test.dart b/jnigen/test/regenerate_examples_test.dart index 5afeebea..a649e6c4 100644 --- a/jnigen/test/regenerate_examples_test.dart +++ b/jnigen/test/regenerate_examples_test.dart @@ -4,6 +4,7 @@ import 'dart:io'; +import 'package:jnigen/src/logging/logging.dart'; import 'package:test/test.dart'; import 'package:path/path.dart' hide equals; @@ -47,6 +48,7 @@ void testExample(String exampleName, String dartOutput, String? cOutput, void main() async { await checkLocallyBuiltDependencies(); + enableLoggingToFile('regenerate-examples-test'); testExample( 'in_app_java', join('lib', 'android_utils.dart'), diff --git a/jnigen/test/simple_package_test/generated_files_test.dart b/jnigen/test/simple_package_test/generated_files_test.dart index 0f595d2f..ae26e55e 100644 --- a/jnigen/test/simple_package_test/generated_files_test.dart +++ b/jnigen/test/simple_package_test/generated_files_test.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/logging/logging.dart'; import 'package:test/test.dart'; import 'generate.dart'; @@ -10,6 +11,7 @@ import '../test_util/test_util.dart'; void main() async { await checkLocallyBuiltDependencies(); + enableLoggingToFile('simple-package-generate-test'); generateAndCompareBothModes( 'Generate and compare bindings for simple_package java files', diff --git a/jnigen/test/summary_generation_test.dart b/jnigen/test/summary_generation_test.dart index 2c436f6d..a96a36a9 100644 --- a/jnigen/test/summary_generation_test.dart +++ b/jnigen/test/summary_generation_test.dart @@ -153,6 +153,7 @@ void testAllCases({ void main() async { await checkLocallyBuiltDependencies(); + enableLoggingToFile('summary-generation-tests'); final tempDir = getTempDir("jnigen_summary_tests_"); group('Test summary generation from compiled JAR', () { From 38ad41e5c6890c8706777888917570f7e91d5512 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sat, 1 Jul 2023 18:50:28 +0530 Subject: [PATCH 09/22] Revert "Log from tests" This reverts commit e96186c5880a9373a5d180bf750542687b6d2bba. --- jnigen/lib/src/logging/logging.dart | 27 ++++++++----------- .../generated_files_test.dart | 3 --- .../kotlin_test/generated_files_test.dart | 2 -- jnigen/test/regenerate_examples_test.dart | 2 -- .../generated_files_test.dart | 2 -- jnigen/test/summary_generation_test.dart | 1 - 6 files changed, 11 insertions(+), 26 deletions(-) diff --git a/jnigen/lib/src/logging/logging.dart b/jnigen/lib/src/logging/logging.dart index a13e02b3..844bfe63 100644 --- a/jnigen/lib/src/logging/logging.dart +++ b/jnigen/lib/src/logging/logging.dart @@ -36,30 +36,25 @@ final _logDir = () { return dir; }(); -Uri _getDefaultLogFileUri([String? identifier]) { - final suffix = identifier == null ? '' : '-$identifier'; - return _logDir.uri - .resolve("jnigen$suffix-${_formatTime(DateTime.now())}.log"); -} +Uri _getDefaultLogFileUri() => + _logDir.uri.resolve("jnigen-${_formatTime(DateTime.now())}.log"); IOSink? _logStream; -/// Creates a log file and enables saving all logs to that file. This file is -/// in .dart_tool/jnigen/logs and the default name is derived from timestamp. +/// Enable saving the logs to a file. /// -/// If an `identifier` is passed, it will be appended to the filename. It's -/// useful to distinguish logs from different tests. -void enableLoggingToFile([String? identifier]) { - // If another `jnigen` task had run on same isolate, close the log file - // associated with it. - _logStream?.close(); +/// This is only meant to be called from an application entry point such as +/// `main`. +void enableLoggingToFile() { _deleteOldLogFiles(); - _logStream = File.fromUri(_getDefaultLogFileUri(identifier)) - .openWrite(mode: FileMode.append); + if (_logStream != null) { + throw StateError('Log file is already set'); + } + _logStream = File.fromUri(_getDefaultLogFileUri()).openWrite(); } // Maximum number of log files to keep. -const _maxLogFiles = 20; +const _maxLogFiles = 5; /// Delete log files except most recent [_maxLogFiles] files. void _deleteOldLogFiles() { diff --git a/jnigen/test/jackson_core_test/generated_files_test.dart b/jnigen/test/jackson_core_test/generated_files_test.dart index aba716cb..818e5d07 100644 --- a/jnigen/test/jackson_core_test/generated_files_test.dart +++ b/jnigen/test/jackson_core_test/generated_files_test.dart @@ -2,7 +2,6 @@ // 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/logging/logging.dart'; import 'package:test/test.dart'; import 'package:jnigen/jnigen.dart'; @@ -13,8 +12,6 @@ import 'generate.dart'; void main() async { await checkLocallyBuiltDependencies(); - enableLoggingToFile('jackson-core-generate-test'); - generateAndCompareBothModes( 'Generate and compare bindings for jackson_core library', getConfig(bindingsType: BindingsType.cBased), diff --git a/jnigen/test/kotlin_test/generated_files_test.dart b/jnigen/test/kotlin_test/generated_files_test.dart index f1ed2600..b96c643c 100644 --- a/jnigen/test/kotlin_test/generated_files_test.dart +++ b/jnigen/test/kotlin_test/generated_files_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:jnigen/jnigen.dart'; -import 'package:jnigen/src/logging/logging.dart'; import 'package:test/test.dart'; import 'generate.dart'; @@ -13,7 +12,6 @@ void main() async { // This is not run in setupAll, because we want to exit with one line of // error message, not throw a long exception. await checkLocallyBuiltDependencies(); - enableLoggingToFile('kotlin-generate-test'); generateAndCompareBothModes( 'Generate and compare bindings for kotlin_test', getConfig(BindingsType.cBased), diff --git a/jnigen/test/regenerate_examples_test.dart b/jnigen/test/regenerate_examples_test.dart index a649e6c4..5afeebea 100644 --- a/jnigen/test/regenerate_examples_test.dart +++ b/jnigen/test/regenerate_examples_test.dart @@ -4,7 +4,6 @@ import 'dart:io'; -import 'package:jnigen/src/logging/logging.dart'; import 'package:test/test.dart'; import 'package:path/path.dart' hide equals; @@ -48,7 +47,6 @@ void testExample(String exampleName, String dartOutput, String? cOutput, void main() async { await checkLocallyBuiltDependencies(); - enableLoggingToFile('regenerate-examples-test'); testExample( 'in_app_java', join('lib', 'android_utils.dart'), diff --git a/jnigen/test/simple_package_test/generated_files_test.dart b/jnigen/test/simple_package_test/generated_files_test.dart index ae26e55e..0f595d2f 100644 --- a/jnigen/test/simple_package_test/generated_files_test.dart +++ b/jnigen/test/simple_package_test/generated_files_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:jnigen/jnigen.dart'; -import 'package:jnigen/src/logging/logging.dart'; import 'package:test/test.dart'; import 'generate.dart'; @@ -11,7 +10,6 @@ import '../test_util/test_util.dart'; void main() async { await checkLocallyBuiltDependencies(); - enableLoggingToFile('simple-package-generate-test'); generateAndCompareBothModes( 'Generate and compare bindings for simple_package java files', diff --git a/jnigen/test/summary_generation_test.dart b/jnigen/test/summary_generation_test.dart index a96a36a9..2c436f6d 100644 --- a/jnigen/test/summary_generation_test.dart +++ b/jnigen/test/summary_generation_test.dart @@ -153,7 +153,6 @@ void testAllCases({ void main() async { await checkLocallyBuiltDependencies(); - enableLoggingToFile('summary-generation-tests'); final tempDir = getTempDir("jnigen_summary_tests_"); group('Test summary generation from compiled JAR', () { From d8d9e76eae39772008a670f5c4a3847fe34e9bd6 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sat, 1 Jul 2023 19:11:49 +0530 Subject: [PATCH 10/22] Safe delete isPrivate --- .../jnigen/apisummarizer/disasm/AsmClassVisitor.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 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 50bf2f0d..8d4b924f 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 @@ -4,16 +4,14 @@ package com.github.dart_lang.jnigen.apisummarizer.disasm; -import static org.objectweb.asm.Opcodes.ACC_PROTECTED; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; - import com.github.dart_lang.jnigen.apisummarizer.elements.*; import com.github.dart_lang.jnigen.apisummarizer.util.SkipException; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; -import java.util.*; import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; +import java.util.*; + public class AsmClassVisitor extends ClassVisitor implements AsmAnnotatedElementVisitor { private static Param param( Type type, String name, @SuppressWarnings("SameParameterValue") String signature) { @@ -59,10 +57,6 @@ public void visit( super.visit(version, access, name, signature, superName, interfaces); } - private static boolean isPrivate(int access) { - return ((access & ACC_PUBLIC) == 0) && ((access & ACC_PROTECTED) == 0); - } - @Override public FieldVisitor visitField( int access, String name, String descriptor, String signature, Object value) { From eb68812066a97e5ea0479975c6245b6a0fae60d0 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 08:40:17 +0530 Subject: [PATCH 11/22] Update changelog --- jnigen/CHANGELOG.md | 4 ++++ jnigen/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/jnigen/CHANGELOG.md b/jnigen/CHANGELOG.md index 6ed1e844..423a8e0e 100644 --- a/jnigen/CHANGELOG.md +++ b/jnigen/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.0 +* **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 * **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. diff --git a/jnigen/pubspec.yaml b/jnigen/pubspec.yaml index 75be98a7..13e3eac8 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.0 +version: 0.7.0 description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen From c9cc06483c59731d9ff4d4629af380ca6d8f9e81 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:23:13 +0530 Subject: [PATCH 12/22] Add checks for nested classes in config --- jnigen/lib/src/config/config_types.dart | 29 ++++++++++++++++++++++++- jnigen/lib/src/logging/logging.dart | 2 +- jnigen/test/config_test.dart | 4 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/jnigen/lib/src/config/config_types.dart b/jnigen/lib/src/config/config_types.dart index f737b811..c1d166bf 100644 --- a/jnigen/lib/src/config/config_types.dart +++ b/jnigen/lib/src/config/config_types.dart @@ -268,6 +268,29 @@ class BindingExclusions { ClassFilter? classes; } +bool _isCapitalized(String s) { + final firstLetter = s.substring(0, 1); + return firstLetter == firstLetter.toUpperCase(); +} + +void _validateClassName(String className) { + final parts = className.split('.'); + assert(parts.isNotEmpty); + const nestedClassesInfo = + "Nested classes cannot be specified separately. Specifying the " + "parent class will pull the nested classes."; + if (parts.length > 1 && _isCapitalized(parts[parts.length - 2])) { + // Try to detect possible nested classes specified using dot notation eg: + // `com.package.Class.NestedClass` and emit a warning. + log.warning("It appears a nested class $className is specified in the " + "config. $nestedClassesInfo"); + } + if (className.contains('\$')) { + throw ConfigException( + "Nested class $className not allowed. $nestedClassesInfo"); + } +} + /// Configuration for jnigen binding generation. class Config { Config({ @@ -283,7 +306,11 @@ class Config { this.logLevel = Level.INFO, this.dumpJsonTo, this.imports, - }); + }) { + for (final className in classes) { + _validateClassName(className); + } + } /// Output configuration for generated bindings OutputConfig outputConfig; diff --git a/jnigen/lib/src/logging/logging.dart b/jnigen/lib/src/logging/logging.dart index 844bfe63..71db2179 100644 --- a/jnigen/lib/src/logging/logging.dart +++ b/jnigen/lib/src/logging/logging.dart @@ -64,7 +64,7 @@ void _deleteOldLogFiles() { .sort((f1, f2) => f2.lastModifiedSync().compareTo(f1.lastModifiedSync())); final toDelete = logFiles.length < _maxLogFiles ? const [] - : logFiles.sublist(_maxLogFiles); + : logFiles.sublist(_maxLogFiles - 1); for (final oldLogFile in toDelete) { oldLogFile.deleteSync(); } diff --git a/jnigen/test/config_test.dart b/jnigen/test/config_test.dart index 8538ac1c..4b6617b3 100644 --- a/jnigen/test/config_test.dart +++ b/jnigen/test/config_test.dart @@ -132,5 +132,9 @@ void main() async { name: 'Invalid log level', overrides: ['-Dlog_level=inf'], ); + testForErrorChecking( + name: 'Nested class specified', + overrides: ['-Dclasses=com.android.Clock\$Clock'], + ); }); } From 55ad9e3eb6da6faabe34e554f6f51cd0682eeaed Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:25:23 +0530 Subject: [PATCH 13/22] Fix java format --- .../dart_lang/jnigen/apisummarizer/disasm/AsmClassVisitor.java | 3 +-- 1 file changed, 1 insertion(+), 2 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 8d4b924f..6a536d32 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 @@ -7,11 +7,10 @@ import com.github.dart_lang.jnigen.apisummarizer.elements.*; import com.github.dart_lang.jnigen.apisummarizer.util.SkipException; import com.github.dart_lang.jnigen.apisummarizer.util.StreamUtil; +import java.util.*; import org.objectweb.asm.*; import org.objectweb.asm.signature.SignatureReader; -import java.util.*; - public class AsmClassVisitor extends ClassVisitor implements AsmAnnotatedElementVisitor { private static Param param( Type type, String name, @SuppressWarnings("SameParameterValue") String signature) { From 857ead37cc461c7fcd013e0613437af844289cb6 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:28:38 +0530 Subject: [PATCH 14/22] Remove problematic steps from GH action --- .github/workflows/test-package.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index a589b674..c9236b52 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -392,13 +392,6 @@ jobs: sudo apt-get install -y ninja-build libgtk-3-dev clang-format - run: flutter config --enable-linux-desktop - run: dart pub get - - name: Generate bindings - run: | - dart run jnigen -Doutput.c.path=_c/ -Doutput.dart.path=_dart/ --config jnigen.yaml - - name: Compare generated bindings - run: | - diff -r _c src/ - diff -r _dart lib/src/third_party - name: Generate full bindings run: dart run jnigen --config jnigen.yaml --override classes="org.apache.pdfbox" - name: Analyze generated bindings From 1461b0eee3c21bd30751b2e8da97af18b30ffcde Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:52:57 +0530 Subject: [PATCH 15/22] Format changes --- jnigen/android_test_runner/lib/main.dart | 4 +- .../apache/pdfbox/text/PDFTextStripper.dart | 32 ++--- .../c_based/dart_bindings/simple_package.dart | 136 +++++++++--------- 3 files changed, 86 insertions(+), 86 deletions(-) diff --git a/jnigen/android_test_runner/lib/main.dart b/jnigen/android_test_runner/lib/main.dart index ee6f2d81..da07cdd5 100644 --- a/jnigen/android_test_runner/lib/main.dart +++ b/jnigen/android_test_runner/lib/main.dart @@ -37,10 +37,10 @@ class MyHomePage extends StatelessWidget { appBar: AppBar( title: const Text("Integration test runner"), ), - body: Center( + body: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ Text( 'This app should be run as flutter integration test', ), 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 bd59ec65..9d6fe3a3 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 @@ -62,12 +62,12 @@ class PDFTextStripper extends jni.JObject { static final _get_LINE_SEPARATOR = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__LINE_SEPARATOR") + jni.JObjectPtr, + )>>("get_PDFTextStripper__LINE_SEPARATOR") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); /// from: protected final java.lang.String LINE_SEPARATOR /// The returned object must be deleted after use, by calling the `delete` method. @@ -79,12 +79,12 @@ class PDFTextStripper extends jni.JObject { static final _get_charactersByArticle = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__charactersByArticle") + jni.JObjectPtr, + )>>("get_PDFTextStripper__charactersByArticle") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_charactersByArticle = jniLookup< ffi.NativeFunction< @@ -133,12 +133,12 @@ class PDFTextStripper extends jni.JObject { static final _get_document = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__document") + jni.JObjectPtr, + )>>("get_PDFTextStripper__document") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_document = jniLookup< ffi.NativeFunction< @@ -160,12 +160,12 @@ class PDFTextStripper extends jni.JObject { static final _get_output = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_PDFTextStripper__output") + jni.JObjectPtr, + )>>("get_PDFTextStripper__output") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_output = jniLookup< ffi.NativeFunction< 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 8efe648e..95621ce1 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 @@ -999,12 +999,12 @@ class Fields extends jni.JObject { static final _get_i = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__i") + jni.JObjectPtr, + )>>("get_Fields__i") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_i = jniLookup< ffi.NativeFunction< @@ -1025,12 +1025,12 @@ class Fields extends jni.JObject { static final _get_trillion = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__trillion") + jni.JObjectPtr, + )>>("get_Fields__trillion") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_trillion = jniLookup< ffi.NativeFunction< @@ -1047,12 +1047,12 @@ class Fields extends jni.JObject { static final _get_isAchillesDead = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__isAchillesDead") + jni.JObjectPtr, + )>>("get_Fields__isAchillesDead") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_isAchillesDead = jniLookup< ffi.NativeFunction< @@ -1070,12 +1070,12 @@ class Fields extends jni.JObject { static final _get_bestFighterInGreece = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__bestFighterInGreece") + jni.JObjectPtr, + )>>("get_Fields__bestFighterInGreece") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_bestFighterInGreece = jniLookup< ffi.NativeFunction< @@ -1097,12 +1097,12 @@ class Fields extends jni.JObject { static final _get_random = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields__random") + jni.JObjectPtr, + )>>("get_Fields__random") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_random = jniLookup< ffi.NativeFunction< @@ -1187,12 +1187,12 @@ class Fields_Nested extends jni.JObject { static final _get_hundred = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_Fields_Nested__hundred") + jni.JObjectPtr, + )>>("get_Fields_Nested__hundred") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_hundred = jniLookup< ffi.NativeFunction< @@ -1407,12 +1407,12 @@ class GrandParent<$T extends jni.JObject> extends jni.JObject { static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent__value") + jni.JObjectPtr, + )>>("get_GrandParent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1587,12 +1587,12 @@ class GrandParent_Parent<$T extends jni.JObject, $S extends jni.JObject> static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -1614,12 +1614,12 @@ class GrandParent_Parent<$T extends jni.JObject, $S extends jni.JObject> static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent__value") + jni.JObjectPtr, + )>>("get_GrandParent_Parent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1734,12 +1734,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_grandParentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__grandParentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__grandParentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_grandParentValue = jniLookup< ffi.NativeFunction< @@ -1761,12 +1761,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -1788,12 +1788,12 @@ class GrandParent_Parent_Child<$T extends jni.JObject, $S extends jni.JObject, static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_Parent_Child__value") + jni.JObjectPtr, + )>>("get_GrandParent_Parent_Child__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -1906,12 +1906,12 @@ class GrandParent_StaticParent<$S extends jni.JObject> extends jni.JObject { static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent__value") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -2012,12 +2012,12 @@ class GrandParent_StaticParent_Child<$S extends jni.JObject, static final _get_parentValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent_Child__parentValue") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent_Child__parentValue") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_parentValue = jniLookup< ffi.NativeFunction< @@ -2039,12 +2039,12 @@ class GrandParent_StaticParent_Child<$S extends jni.JObject, static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_GrandParent_StaticParent_Child__value") + jni.JObjectPtr, + )>>("get_GrandParent_StaticParent_Child__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< @@ -2284,12 +2284,12 @@ class MyMap_MyEntry<$K extends jni.JObject, $V extends jni.JObject> static final _get_key = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_MyMap_MyEntry__key") + jni.JObjectPtr, + )>>("get_MyMap_MyEntry__key") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_key = jniLookup< ffi.NativeFunction< @@ -2309,12 +2309,12 @@ class MyMap_MyEntry<$K extends jni.JObject, $V extends jni.JObject> static final _get_value = jniLookup< ffi.NativeFunction< jni.JniResult Function( - jni.JObjectPtr, - )>>("get_MyMap_MyEntry__value") + jni.JObjectPtr, + )>>("get_MyMap_MyEntry__value") .asFunction< jni.JniResult Function( - jni.JObjectPtr, - )>(); + jni.JObjectPtr, + )>(); static final _set_value = jniLookup< ffi.NativeFunction< From 1663e4723860287cb1abb67f0386fedb47dff867 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:05:49 +0530 Subject: [PATCH 16/22] Add anonymous inner class example --- .../jnigen/apisummarizer/util/ClassFinder.java | 6 +++--- .../c_based/c_bindings/simple_package.c | 18 ++++++++++++++++++ .../c_based/dart_bindings/simple_package.dart | 11 +++++++++++ .../dart_bindings/simple_package.dart | 9 +++++++++ .../jnigen/simple_package/Example.java | 11 +++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 7792f155..e009ef5b 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,5 +1,7 @@ package com.github.dart_lang.jnigen.apisummarizer.util; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -14,8 +16,6 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.zip.ZipEntry; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { @@ -26,7 +26,7 @@ private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String nested = pathString.substring( fqnWithSlashesDollarSign.length(), pathString.length() - suffix.length()); - if (nested.matches("[a-zA-Z0-9_$]*")) { + if (nested.matches("[a-zA-Z_][a-zA-Z0-9_$]*")) { return true; } Log.warning("Possibly invalid path - '%s', skipping", pathString); 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 4b1d1197..6c0f3e24 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 @@ -607,6 +607,24 @@ JniResult Example_Nested__ctor(uint8_t value) { return to_global_ref_result(_result); } +jmethodID _m_Example_Nested__usesAnonymousInnerClass = NULL; +FFI_PLUGIN_EXPORT +JniResult Example_Nested__usesAnonymousInnerClass(jobject self_) { + load_env(); + load_class_global_ref( + &_c_Example_Nested, + "com/github/dart_lang/jnigen/simple_package/Example$Nested"); + if (_c_Example_Nested == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Example_Nested, &_m_Example_Nested__usesAnonymousInnerClass, + "usesAnonymousInnerClass", "()V"); + if (_m_Example_Nested__usesAnonymousInnerClass == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_Example_Nested__usesAnonymousInnerClass); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + jmethodID _m_Example_Nested__getValue = NULL; FFI_PLUGIN_EXPORT JniResult Example_Nested__getValue(jobject self_) { 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 95621ce1..d0ad47c7 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 @@ -559,6 +559,17 @@ class Example_Nested extends jni.JObject { return Example_Nested.fromRef(_ctor(value ? 1 : 0).object); } + static final _usesAnonymousInnerClass = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>( + "Example_Nested__usesAnonymousInnerClass") + .asFunction)>(); + + /// from: public void usesAnonymousInnerClass() + void usesAnonymousInnerClass() { + return _usesAnonymousInnerClass(reference).check(); + } + static final _getValue = jniLookup< ffi.NativeFunction< jni.JniResult Function( 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 e719671f..3394d3d9 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 @@ -527,6 +527,15 @@ class Example_Nested extends jni.JObject { .newObjectWithArgs(_class.reference, _id_ctor, [value ? 1 : 0]).object); } + static final _id_usesAnonymousInnerClass = jni.Jni.accessors + .getMethodIDOf(_class.reference, r"usesAnonymousInnerClass", r"()V"); + + /// from: public void usesAnonymousInnerClass() + void usesAnonymousInnerClass() { + return jni.Jni.accessors.callMethodWithArgs(reference, + _id_usesAnonymousInnerClass, jni.JniCallType.voidType, []).check(); + } + static final _id_getValue = jni.Jni.accessors.getMethodIDOf(_class.reference, r"getValue", r"()Z"); diff --git a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java index acaf2f95..47fab9d2 100644 --- a/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java +++ b/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java @@ -176,6 +176,17 @@ public Nested(boolean value) { this.value = value; } + public void usesAnonymousInnerClass() { + new Thread( + new Runnable() { + @Override + public void run() { + System.out.println("2982157093127690243"); + } + }) + .start(); + } + public boolean getValue() { return value; } From 503681e9adf00e0224dae3a09cd55937bc7bff3f Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:50:03 +0530 Subject: [PATCH 17/22] Use sorted() for deterministic order --- .../dart_lang/jnigen/apisummarizer/util/ClassFinder.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index e009ef5b..1178dbee 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,7 +1,5 @@ package com.github.dart_lang.jnigen.apisummarizer.util; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -16,6 +14,8 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.zip.ZipEntry; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; public class ClassFinder { private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { @@ -56,7 +56,8 @@ public static void findFilesInPath( TreeSet filePaths; try (var walk = Files.walk(searchPath)) { - filePaths = walk.filter(Files::isRegularFile).collect(Collectors.toCollection(TreeSet::new)); + filePaths = + walk.filter(Files::isRegularFile).sorted().collect(Collectors.toCollection(TreeSet::new)); } catch (IOException e) { throw new RuntimeException(e); } @@ -84,6 +85,7 @@ public static void findFilesInPath( List resultPaths = walk.filter(Files::isRegularFile) .filter(innerPath -> innerPath.toString().endsWith(suffix)) + .sorted() .collect(Collectors.toList()); classes.put(className, mapper.apply(resultPaths)); } catch (IOException e) { From 2a2741ae7b707ad6d482586f3f0897655c587b94 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Tue, 4 Jul 2023 20:04:45 +0530 Subject: [PATCH 18/22] Refactor ClassFinder logic and add Unit test --- .../apisummarizer/util/ClassFinder.java | 139 ++++++++---------- .../apisummarizer/util/ExceptionUtil.java | 17 ++- .../jnigen/apisummarizer/ClassFinderTest.java | 88 +++++++++++ 3 files changed, 167 insertions(+), 77 deletions(-) create mode 100644 jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index 1178dbee..c5469b13 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -1,13 +1,12 @@ package com.github.dart_lang.jnigen.apisummarizer.util; +import static com.github.dart_lang.jnigen.apisummarizer.util.ExceptionUtil.wrapCheckedException; + import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.jar.JarEntry; @@ -18,7 +17,7 @@ import javax.tools.StandardJavaFileManager; public class ClassFinder { - private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { + public static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { return false; @@ -26,25 +25,39 @@ private static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String nested = pathString.substring( fqnWithSlashesDollarSign.length(), pathString.length() - suffix.length()); - if (nested.matches("[a-zA-Z_][a-zA-Z0-9_$]*")) { - return true; - } - Log.warning("Possibly invalid path - '%s', skipping", pathString); - return false; + String[] nestedParts = nested.split("\\$"); + return Arrays.stream(nestedParts).allMatch(part -> part.matches("[a-zA-Z_][a-zA-Z0-9_]*")); } - public static List getNestedClassesInPathList( - TreeSet paths, String fqnWithSlashes, String suffix) { - return paths.tailSet(Path.of(fqnWithSlashes)).stream() - .takeWhile(path -> isNestedClassOf(path.toString(), fqnWithSlashes, suffix)) - .collect(Collectors.toList()); - } + // Finds [fqn] and its children with [suffix] in [entries]. + public static Optional> findClassAndChildren( + TreeSet entries, String fqn, String sep, String suffix) { + String fqnWithSlashes = fqn.replace(".", sep); + String fqnWithSlashesSuffix = fqnWithSlashes + suffix; + String fqnWithSlashesSlash = fqnWithSlashes + sep; + String fqnWithSlashesDollarSign = fqnWithSlashes + "$"; + if (entries.contains(fqnWithSlashesSuffix)) { + List classes = new ArrayList<>(); + // Add nested classes first, because they're alphabetically first + entries.tailSet(fqnWithSlashesDollarSign).stream() + .takeWhile(entry -> entry.startsWith(fqnWithSlashesDollarSign)) + // Note: filter comes after takeWhile, because it can filter out additional elements + // eg: Class$1 - but there may be valid nested classes after Class$1. + .filter(entry -> isNestedClassOf(entry, fqnWithSlashes, suffix)) + .forEach(classes::add); + classes.add(fqnWithSlashesSuffix); + return Optional.of(classes); + } - public static List getNestedClassesInStringList( - TreeSet paths, String fqnWithSlashes, String suffix) { - return paths.tailSet(fqnWithSlashes).stream() - .takeWhile(path -> isNestedClassOf(path, fqnWithSlashes, suffix)) - .collect(Collectors.toList()); + // consider fqnWithSlashes as a directory + List children = + entries.tailSet(fqnWithSlashesSlash).stream() + // takeWhile instead of filter - the difference is O(log n + k) instead of O(n) + // so always use takeWhile when doing a treeSet subset stream. + .takeWhile(entry -> entry.startsWith(fqnWithSlashesSlash)) + .filter(entry -> entry.endsWith(suffix)) + .collect(Collectors.toList()); + return children.isEmpty() ? Optional.empty() : Optional.of(children); } public static void findFilesInPath( @@ -54,10 +67,12 @@ public static void findFilesInPath( Function, List> mapper) { Path searchPath = Path.of(searchLocation); - TreeSet filePaths; + TreeSet filePaths; try (var walk = Files.walk(searchPath)) { filePaths = - walk.filter(Files::isRegularFile).sorted().collect(Collectors.toCollection(TreeSet::new)); + walk.map(searchPath::relativize) + .map(Path::toString) + .collect(Collectors.toCollection(TreeSet::new)); } catch (IOException e) { throw new RuntimeException(e); } @@ -66,69 +81,40 @@ public static void findFilesInPath( if (classes.get(className) != null) { // Already found by other method of searching continue; } - var fqnWithSlashes = className.replace(".", File.separator); - var dirPath = Path.of(searchLocation, fqnWithSlashes); - var filePath = Path.of(searchLocation, fqnWithSlashes + suffix); - if (filePaths.contains(filePath)) { - List resultPaths = new ArrayList<>(); - resultPaths.add(filePath); - var filePathsWithoutSearchPathPrefix = - filePaths.stream() - .map(searchPath::relativize) - .collect(Collectors.toCollection(TreeSet::new)); - List nestedClasses = - getNestedClassesInPathList(filePathsWithoutSearchPathPrefix, fqnWithSlashes, suffix); - resultPaths.addAll(StreamUtil.map(nestedClasses, searchPath::resolve)); - classes.put(className, mapper.apply(resultPaths)); - } else if (Files.exists(dirPath) && Files.isDirectory(dirPath)) { - try (var walk = Files.walk(dirPath)) { - List resultPaths = - walk.filter(Files::isRegularFile) - .filter(innerPath -> innerPath.toString().endsWith(suffix)) - .sorted() - .collect(Collectors.toList()); - classes.put(className, mapper.apply(resultPaths)); - } catch (IOException e) { - throw new RuntimeException(e); - } + var resultPaths = findClassAndChildren(filePaths, className, File.separator, suffix); + if (resultPaths.isPresent()) { + // [filePaths] and [resultPaths] are relativized to searchPath. + // perform opposite operation (resolve) to get full paths. + var fullPaths = + resultPaths.get().stream().map(searchPath::resolve).collect(Collectors.toList()); + classes.put(className, mapper.apply(fullPaths)); } } } - public static void findFilesInJar( + public static boolean findFilesInJar( Map> classes, JarFile jar, String suffix, BiFunction, List> mapper) { - var entries = + + // It appears JAR file entries are always separated by "/" + var jarSeparator = "/"; + var entryNames = jar.stream().map(JarEntry::getName).collect(Collectors.toCollection(TreeSet::new)); - for (var binaryName : classes.keySet()) { - if (classes.get(binaryName) != null) { + boolean foundClassesInThisJar = false; + for (var fqn : classes.keySet()) { + if (classes.get(fqn) != null) { // already found continue; } - var fqnWithSlashes = binaryName.replace('.', '/'); - var filePath = fqnWithSlashes + suffix; - if (entries.contains(filePath)) { - // find nested classes as well - var foundPaths = getNestedClassesInStringList(entries, fqnWithSlashes, suffix); - var found = StreamUtil.map(foundPaths, jar::getEntry); - found.add(jar.getEntry(filePath)); - classes.put(binaryName, mapper.apply(jar, found)); - } - - // Obtain set of all strings prefixed with relativePath + '/' - var dirPath = fqnWithSlashes + '/'; - var children = - entries.tailSet(dirPath).stream() - .takeWhile(e -> e.startsWith(dirPath)) - .filter(e -> e.endsWith(suffix)) - .map(jar::getEntry) - .collect(Collectors.toList()); - if (!children.isEmpty()) { - var mapped = mapper.apply(jar, children); - classes.put(binaryName, mapped); + var resultPaths = findClassAndChildren(entryNames, fqn, jarSeparator, suffix); + if (resultPaths.isPresent()) { + var jarEntries = resultPaths.get().stream().map(jar::getEntry).collect(Collectors.toList()); + classes.put(fqn, mapper.apply(jar, jarEntries)); + foundClassesInThisJar = true; } } + return foundClassesInThisJar; } public static void find( @@ -142,8 +128,11 @@ public static void find( if (searchFile.isDirectory()) { findFilesInPath(searchPath, suffix, classes, fileMapper); } else if (searchFile.isFile() && searchPath.endsWith(".jar")) { - var jarFile = ExceptionUtil.wrapCheckedException(JarFile::new, searchPath); - findFilesInJar(classes, jarFile, suffix, entryMapper); + var jarFile = wrapCheckedException(JarFile::new, searchPath); + var useful = findFilesInJar(classes, jarFile, suffix, entryMapper); + if (!useful) { + wrapCheckedException(jarFile::close); + } } } } diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ExceptionUtil.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ExceptionUtil.java index 3ed92a52..6b4c8bd1 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ExceptionUtil.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ExceptionUtil.java @@ -2,19 +2,32 @@ public class ExceptionUtil { @FunctionalInterface - public interface FunctionThrowingException { + public interface CheckedFunction { R function(T value) throws Exception; } + @FunctionalInterface + public interface CheckedRunnable { + void run() throws Exception; + } + /** * Wraps a function throwing a checked exception and throws all checked exceptions as runtime * exceptions. */ - public static R wrapCheckedException(FunctionThrowingException function, T value) { + public static R wrapCheckedException(CheckedFunction function, T value) { try { return function.function(value); } catch (Exception e) { throw new RuntimeException(e); } } + + public static void wrapCheckedException(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java new file mode 100644 index 00000000..a4b627ee --- /dev/null +++ b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java @@ -0,0 +1,88 @@ +package com.github.dart_lang.jnigen.apisummarizer; + +import static com.github.dart_lang.jnigen.apisummarizer.util.ClassFinder.findClassAndChildren; +import static com.github.dart_lang.jnigen.apisummarizer.util.ClassFinder.isNestedClassOf; +import static org.junit.Assert.*; + +import java.util.*; +import java.util.stream.Collectors; +import org.junit.Test; + +public class ClassFinderTest { + @Test + public void testNestedClassCheck() { + assertTrue( + "Single nested class", + isNestedClassOf("com/abc/Class$Nested.class", "com/abc/Class", ".class")); + assertTrue( + "Nested twice", + isNestedClassOf("com/abc/Class$Nested$Twice.class", "com/abc/Class", ".class")); + assertTrue( + "Single nested class - backslash separator", + isNestedClassOf("com\\abc\\Class$Nested.class", "com\\abc\\Class", ".class")); + assertFalse( + "Anon inner class", isNestedClassOf("com/abc/Class$1.class", "com/abc/Class", ".class")); + assertFalse( + "Anon inner class inside nested class", + isNestedClassOf("com/abc/Class$Nested$1.class", "com/abc/Class", ".class")); + assertFalse( + "Different class name", + isNestedClassOf("com/abc/AClass$Nested.class", "com/abc/Class", ".class")); + } + + private Optional> pathListOf(String sep, String... paths) { + List pathList = + Arrays.stream(paths).map(path -> path.replace("/", sep)).collect(Collectors.toList()); + return Optional.of(pathList); + } + + @Test + public void testFindChildren() { + TreeSet entriesWithSlash = + new TreeSet<>( + List.of( + "random/os/App.class", + "random/os/Process.class", + "random/os/Process$Fork.class", + "random/widget/Dialog.class", + "random/widget/Dialog$Button.class", + "random/widget/Dialog$Button$Color.class", + "random/widget/Dialogue$Button.class", + "random/time/Clock.class", + "random/time/Calendar.class", + "random/time/Calendar$Month.class")); + TreeSet entriesWithBackslash = + entriesWithSlash.stream() + .map(x -> x.replace('/', '\\')) + .collect(Collectors.toCollection(TreeSet::new)); + Map> bySeparater = + Map.of("/", entriesWithSlash, "\\", entriesWithBackslash); + + for (var sep : bySeparater.keySet()) { + var entries = bySeparater.get(sep); + assertEquals( + pathListOf(sep, "random/os/Process$Fork.class", "random/os/Process.class"), + findClassAndChildren(entries, "random.os.Process", sep, ".class")); + assertEquals( + pathListOf( + sep, + "random/time/Calendar$Month.class", + "random/time/Calendar.class", + "random/time/Clock.class"), + findClassAndChildren(entries, "random.time", sep, ".class")); + assertEquals( + pathListOf( + sep, + "random/widget/Dialog$Button$Color.class", + "random/widget/Dialog$Button.class", + "random/widget/Dialog.class"), + findClassAndChildren(entries, "random.widget.Dialog", sep, ".class")); + assertEquals( + pathListOf(sep, "random/os/App.class"), + findClassAndChildren(entries, "random.os.App", sep, ".class")); + assertEquals( + pathListOf(sep, "random/os/App.class"), + findClassAndChildren(entries, "random.os.App", sep, ".class")); + } + } +} From 82881eabb7319ff22e98de43b7368ad61cd911d5 Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Tue, 4 Jul 2023 22:42:06 +0530 Subject: [PATCH 19/22] Filter anonymous classes in summarizer itself --- .../apisummarizer/util/ClassFinder.java | 20 +++++++++++++++++-- .../jnigen/apisummarizer/ClassFinderTest.java | 7 +++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java index c5469b13..23c1ca53 100644 --- a/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java +++ b/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/util/ClassFinder.java @@ -17,6 +17,13 @@ import javax.tools.StandardJavaFileManager; public class ClassFinder { + // If class is A$B$C, simpleName can be B$C or A$B$C. Doesn't matter much, because + // A can't be anonymous class. + private static boolean isNonAnonymousNestedClassName(String simpleName) { + String[] nestedParts = simpleName.split("\\$"); + return Arrays.stream(nestedParts).allMatch(part -> part.matches("[a-zA-Z_][a-zA-Z0-9_]*")); + } + public static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String suffix) { var fqnWithSlashesDollarSign = fqnWithSlashes + "$"; if (!pathString.startsWith(fqnWithSlashesDollarSign) || !pathString.endsWith(suffix)) { @@ -25,8 +32,16 @@ public static boolean isNestedClassOf(String pathString, String fqnWithSlashes, String nested = pathString.substring( fqnWithSlashesDollarSign.length(), pathString.length() - suffix.length()); - String[] nestedParts = nested.split("\\$"); - return Arrays.stream(nestedParts).allMatch(part -> part.matches("[a-zA-Z_][a-zA-Z0-9_]*")); + return isNonAnonymousNestedClassName(nested); + } + + private static boolean isNonAnonymousClassFullPath(String path) { + String[] pathParts = path.split("[/\\\\]"); + String simpleNameWithExt = pathParts[pathParts.length - 1]; + int extIndex = simpleNameWithExt.indexOf('.'); + assert extIndex != -1 : "Should've passed full path with extension to this method"; + String simpleName = simpleNameWithExt.substring(0, extIndex); + return isNonAnonymousNestedClassName(simpleName); } // Finds [fqn] and its children with [suffix] in [entries]. @@ -56,6 +71,7 @@ public static Optional> findClassAndChildren( // so always use takeWhile when doing a treeSet subset stream. .takeWhile(entry -> entry.startsWith(fqnWithSlashesSlash)) .filter(entry -> entry.endsWith(suffix)) + .filter(ClassFinder::isNonAnonymousClassFullPath) .collect(Collectors.toList()); return children.isEmpty() ? Optional.empty() : Optional.of(children); } diff --git a/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java index a4b627ee..4209e3a2 100644 --- a/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java +++ b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/ClassFinderTest.java @@ -42,14 +42,21 @@ public void testFindChildren() { new TreeSet<>( List.of( "random/os/App.class", + "random/os/App$1.class", + "random/os/App$1$3.class", "random/os/Process.class", "random/os/Process$Fork.class", + "random/os/Process$Fork$1.class", + "random/os/Process$Fork$A$B$C$2.class", "random/widget/Dialog.class", "random/widget/Dialog$Button.class", + "random/widget/Dialog$Button$2.class", "random/widget/Dialog$Button$Color.class", "random/widget/Dialogue$Button.class", "random/time/Clock.class", + "random/time/Clock$1.class", "random/time/Calendar.class", + "random/time/Calendar$Month$1.class", "random/time/Calendar$Month.class")); TreeSet entriesWithBackslash = entriesWithSlash.stream() From 1b8afd74d468df7c8528115d85d539a7337989ff Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Tue, 4 Jul 2023 22:55:40 +0530 Subject: [PATCH 20/22] Fix java tests --- .../jnigen/apisummarizer/JSONComparisonTest.java | 12 ++++++++++++ .../java/src/test/resources/exampleClassSummary.json | 4 ---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/JSONComparisonTest.java b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/JSONComparisonTest.java index 4d15ad2e..b02ee15e 100644 --- a/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/JSONComparisonTest.java +++ b/jnigen/java/src/test/java/com/github/dart_lang/jnigen/apisummarizer/JSONComparisonTest.java @@ -1,5 +1,6 @@ package com.github.dart_lang.jnigen.apisummarizer; +import com.github.dart_lang.jnigen.apisummarizer.util.Log; import java.io.File; import java.io.IOException; import org.junit.Assert; @@ -31,11 +32,22 @@ private int gitDiff(String a, String b) throws IOException, InterruptedException @Test public void testExampleSummary() throws IOException, InterruptedException { var tempFile = File.createTempFile("summarizer_test", ".json"); + Log.info("Temporary file: %s", tempFile.getPath()); Main.main( new String[] { "-s", "src/test/resources", "com.example.Example", "-o", tempFile.getPath(), }); int comparison = gitDiff(exampleClassJsonOutput, tempFile); + if (comparison != 0) { + Log.warning("New output (%s) is different than reference output.", tempFile.getPath()); + } + + // Fail test if git diff exited with 1 Assert.assertEquals(0, comparison); + + var deleted = tempFile.delete(); + if (!deleted) { + Log.warning("Cannot delete temp file %s", tempFile.getPath()); + } } } diff --git a/jnigen/java/src/test/resources/exampleClassSummary.json b/jnigen/java/src/test/resources/exampleClassSummary.json index 90a08e41..208e7ae1 100644 --- a/jnigen/java/src/test/resources/exampleClassSummary.json +++ b/jnigen/java/src/test/resources/exampleClassSummary.json @@ -1,9 +1,7 @@ [ { "declKind" : "CLASS", "modifiers" : [ "public" ], - "simpleName" : "Example", "binaryName" : "com.example.Example", - "packageName" : "com.example", "methods" : [ { "modifiers" : [ "public" ], "name" : "", @@ -140,10 +138,8 @@ }, { "declKind" : "CLASS", "modifiers" : [ "static", "public" ], - "simpleName" : "Aux", "binaryName" : "com.example.Example$Aux", "parentName" : "com.example.Example", - "packageName" : "com.example", "methods" : [ { "modifiers" : [ "public" ], "name" : "", From 00670c3c8bdd9dc028e60eb75309509ad25257fb Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:24:40 +0530 Subject: [PATCH 21/22] Update versions of jni, jnigen to 0.6.0-dev.1 --- jni/example/pubspec.lock | 2 +- jni/pubspec.yaml | 2 +- jnigen/CHANGELOG.md | 2 +- jnigen/pubspec.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jni/example/pubspec.lock b/jni/example/pubspec.lock index 3080aea5..34dff04b 100644 --- a/jni/example/pubspec.lock +++ b/jni/example/pubspec.lock @@ -200,7 +200,7 @@ packages: path: ".." relative: true source: path - version: "0.5.0" + version: "0.6.0-dev.1" js: dependency: transitive description: diff --git a/jni/pubspec.yaml b/jni/pubspec.yaml index 4de344fa..0c1068a3 100644 --- a/jni/pubspec.yaml +++ b/jni/pubspec.yaml @@ -1,6 +1,6 @@ name: jni description: Library to access JNI from dart and flutter -version: 0.5.0 +version: 0.6.0-dev.1 repository: https://github.com/dart-lang/jnigen/tree/main/jni environment: diff --git a/jnigen/CHANGELOG.md b/jnigen/CHANGELOG.md index 423a8e0e..48b1b212 100644 --- a/jnigen/CHANGELOG.md +++ b/jnigen/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.7.0 +## 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/pubspec.yaml b/jnigen/pubspec.yaml index 13e3eac8..ee704308 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.7.0 +version: 0.6.0-dev.1 description: Experimental generator for FFI+JNI bindings. repository: https://github.com/dart-lang/jnigen/tree/main/jnigen From 314ee840902141f682da6db3e6c59d28a588fa0d Mon Sep 17 00:00:00 2001 From: Mahesh Hegde <46179734+mahesh-hegde@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:33:13 +0530 Subject: [PATCH 22/22] Add more nested classes to test --- jnigen/test/summary_generation_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jnigen/test/summary_generation_test.dart b/jnigen/test/summary_generation_test.dart index 2c436f6d..e1657f1a 100644 --- a/jnigen/test/summary_generation_test.dart +++ b/jnigen/test/summary_generation_test.dart @@ -24,6 +24,10 @@ import 'test_util/test_util.dart'; const nestedClasses = [ 'com.github.dart_lang.jnigen.simple_package.Example\$Nested', 'com.github.dart_lang.jnigen.simple_package.Example\$Nested\$NestedTwice', + 'com.github.dart_lang.jnigen.generics.GrandParent\$StaticParent', + 'com.github.dart_lang.jnigen.generics.GrandParent\$StaticParent\$Child', + 'com.github.dart_lang.jnigen.generics.GrandParent\$Parent', + 'com.github.dart_lang.jnigen.generics.GrandParent\$Parent\$Child', ]; void expectSummaryHasAllClasses(Classes? classes) {