[jnigen] General cleanup (https://github.com/dart-lang/jnigen/issues/80)
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index d3d44e7..060f8e4 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml
@@ -121,11 +121,18 @@ with: distribution: 'zulu' java-version: '11' + - name: install clang tools + run: | + sudo apt-get update -y + sudo apt-get install -y clang-format - run: flutter pub get - name: Check formatting run: flutter format --output=none --set-exit-if-changed . - name: Run lints run: flutter analyze --fatal-infos + - name: Check C code formatting using clang-format + run: clang-format --dry-run -Werror dartjni.c dartjni.h third_party/*.c third_party/*.h + working-directory: ./pkgs/jni/src test_jni: runs-on: ubuntu-latest @@ -192,7 +199,9 @@ - run: Add-Content $env:GITHUB_PATH "$env:JAVA_HOME\bin\server" - run: dart pub get - run: dart run jni:setup - - run: dart test + ## TODO(#51): -j 1 is required to avoid a rare race condition which is caused by + ## all tests which spawn JVM being run in same process. + - run: dart test -j 1 test_jnigen_windows_minimal: needs: [analyze_jnigen] @@ -362,7 +371,7 @@ diff -qr _c src/ diff -qr _dart lib/third_party - name: Generate full bindings - run: dart run jnigen --config jnigen_full.yaml + run: dart run jnigen --config jnigen.yaml --override classes="org.apache.pdfbox.pdmodel;org.apache.pdfbox.text" - name: Analyze generated bindings run: | flutter pub get # dart-analyze errors on flutter example
diff --git a/pkgs/jni/README.md b/pkgs/jni/README.md index 3f4ba5d..79ce932 100644 --- a/pkgs/jni/README.md +++ b/pkgs/jni/README.md
@@ -17,7 +17,7 @@ __To generate type-safe bindings from Java libraries, use `jnigen`.__ ## SDK Note -Dart standalone is supported, but due to some current limitations of the `pubspec` format, `dart` command must be from Flutter SDK and not dart SDK. See dart-lang/pub#3563. +Dart standalone is supported, but due to some current limitations of the `pubspec` format, `dart` command must be from Flutter SDK and not dart SDK. ## Version note This library is at an early stage of development and we do not provide backwards compatibility of the API at this point.
diff --git a/pkgs/jni/android/README.md b/pkgs/jni/android/README.md new file mode 100644 index 0000000..7d56c69 --- /dev/null +++ b/pkgs/jni/android/README.md
@@ -0,0 +1 @@ +This folder contains the [Android plugin class](src/main/java/com/github/dart_lang/jni/JniPlugin.java) which takes care of initializing JNI on Android.
diff --git a/pkgs/jni/bin/setup.dart b/pkgs/jni/bin/setup.dart index ec62bb1..8f12c6b 100644 --- a/pkgs/jni/bin/setup.dart +++ b/pkgs/jni/bin/setup.dart
@@ -7,111 +7,141 @@ import 'package:args/args.dart'; import 'package:package_config/package_config.dart'; -const _buildDir = "build-dir"; -const _srcDir = "source-dir"; +const ansiRed = '\x1b[31m'; +const ansiDefault = '\x1b[39;49m'; + +const _defaultRelativeBuildPath = "build/jni_libs"; + +const _buildPath = "build-path"; +const _srcPath = "source-path"; const _packageName = 'package-name'; const _verbose = "verbose"; const _cmakeArgs = "cmake-args"; -const _clean = "clean"; -const _cmakeTemporaryFiles = [ - 'CMakeCache.txt', - 'CMakeFiles/', - 'cmake_install.cmake', - 'Makefile' -]; - -void deleteCMakeTemps(Uri buildDir) async { - for (var filename in _cmakeTemporaryFiles) { - if (options.verbose) { - stderr.writeln('remove $filename'); - } - await File(buildDir.resolve(filename).toFilePath()).delete(recursive: true); +Future<void> runCommand( + String exec, List<String> args, String workingDir) async { + // For printing relative path always. + var current = Directory.current.path; + if (!current.endsWith(Platform.pathSeparator)) { + current += Platform.pathSeparator; } -} + if (workingDir.startsWith(current)) { + workingDir.replaceFirst(current, ""); + } -// Sets up input output channels and maintains state. -class CommandRunner { - CommandRunner({this.printCmds = false}); - bool printCmds = false; - int? time; - Future<CommandRunner> run( - String exec, List<String> args, String workingDir) async { - if (printCmds) { - final cmd = "$exec ${args.join(" ")}"; - stderr.writeln("\n+ [$workingDir] $cmd"); - } - final process = await Process.start(exec, args, - workingDirectory: workingDir, - runInShell: true, - mode: ProcessStartMode.inheritStdio); - final exitCode = await process.exitCode; + final cmd = "$exec ${args.join(" ")}"; + stderr.writeln("+ [$workingDir] $cmd"); + int exitCode; + if (options.verbose) { + final process = await Process.start( + exec, args, + workingDirectory: workingDir, + mode: ProcessStartMode.inheritStdio, + // without `runInShell`, sometimes cmake doesn't run on windows. + runInShell: true, + ); + exitCode = await process.exitCode; + } else { + // ProcessStartMode.normal sometimes hangs on windows. No idea why. + final process = await Process.run(exec, args, + runInShell: true, workingDirectory: workingDir); + exitCode = process.exitCode; if (exitCode != 0) { - stderr.writeln("command exited with $exitCode"); + var out = process.stdout; + var err = process.stderr; + if (stdout.supportsAnsiEscapes) { + out = "$ansiRed$out$ansiDefault"; + err = "$ansiRed$err$ansiDefault"; + } + stdout.writeln(out); + stderr.writeln(err); } - return this; + } + if (exitCode != 0) { + stderr.writeln("Command exited with $exitCode."); } } class Options { Options(ArgResults arg) - : buildDir = arg[_buildDir], - srcDir = arg[_srcDir], + : buildPath = arg[_buildPath], + srcPath = arg[_srcPath], packageName = arg[_packageName] ?? 'jni', cmakeArgs = arg[_cmakeArgs], - verbose = arg[_verbose] ?? false, - clean = arg[_clean] ?? false; + verbose = arg[_verbose] ?? false; - String? buildDir, srcDir; + String? buildPath, srcPath; String packageName; List<String> cmakeArgs; - bool verbose, clean; + bool verbose; } late Options options; -void log(String msg) { + +void verboseLog(String msg) { if (options.verbose) { stderr.writeln(msg); } } -/// tries to find package:jni's source folder in pub cache -/// if not possible, returns null. -Future<String?> findSources() async { +/// Find path to C sources in pub cache for package specified by [packageName]. +/// +/// It's assumed C FFI sources are in "src/" relative to package root. +/// If package cannot be found, null is returned. +Future<String?> findSources(String packageName) async { final packageConfig = await findPackageConfig(Directory.current); if (packageConfig == null) { return null; } - final packages = packageConfig.packages; - for (var package in packages) { - if (package.name == options.packageName) { - return package.root.resolve("src/").toFilePath(); + final package = packageConfig[options.packageName]; + if (package == null) { + return null; + } + return package.root.resolve("src").toFilePath(); +} + +/// Returns true if [artifact] does not exist, or any file in [sourceDir] is +/// newer than [artifact]. +bool needsBuild(File artifact, Directory sourceDir) { + if (!artifact.existsSync()) return true; + final fileLastModified = artifact.lastModifiedSync(); + for (final entry in sourceDir.listSync(recursive: true)) { + if (entry.statSync().modified.isAfter(fileLastModified)) { + return true; } } - return null; + return false; +} + +/// Returns the name of file built using sources in [cDir] +String getTargetName(Directory cDir) { + for (final file in cDir.listSync(recursive: true)) { + if (file.path.endsWith(".c")) { + final cFileName = file.uri.pathSegments.last; + final librarySuffix = Platform.isWindows ? "dll" : "so"; + return cFileName.substring(0, cFileName.length - 1) + librarySuffix; + } + } + throw Exception("Could not find a C file in ${cDir.path}"); } void main(List<String> arguments) async { final parser = ArgParser() - ..addOption(_buildDir, - abbr: 'B', help: 'Directory to place built artifacts') - ..addOption(_srcDir, - abbr: 'S', help: 'alternative path to package:jni sources') + ..addOption(_buildPath, + abbr: 'b', help: 'Directory to place built artifacts') + ..addOption(_srcPath, + abbr: 's', help: 'alternative path to package:jni sources') ..addOption(_packageName, abbr: 'p', help: 'package for which native' 'library should be built', defaultsTo: 'jni') ..addFlag(_verbose, abbr: 'v', help: 'Enable verbose output') - ..addFlag(_clean, - negatable: false, - abbr: 'C', - help: 'Clear built artifacts instead of running a build') ..addMultiOption(_cmakeArgs, - abbr: 'm', help: 'additional argument to pass to CMake'); - final cli = parser.parse(arguments); - options = Options(cli); - final rest = cli.rest; + abbr: 'm', help: 'Pass additional argument to CMake'); + final argResults = parser.parse(arguments); + options = Options(argResults); + final rest = argResults.rest; if (rest.isNotEmpty) { stderr.writeln("one or more unrecognized arguments: $rest"); @@ -121,79 +151,69 @@ return; } - final srcPath = options.srcDir ?? await findSources(); + final srcPath = options.srcPath ?? await findSources(options.packageName); if (srcPath == null) { - stderr.writeln("No sources specified and current directory is not a " - "package root."); + stderr.writeln("Cannot find sources for package ${options.packageName} " + "and no sources were manually specified."); exitCode = 1; return; } final srcDir = Directory(srcPath); - if (!await srcDir.exists() && !options.clean) { - throw 'Directory $srcPath does not exist'; + if (!srcDir.existsSync()) { + stderr.writeln('Directory $srcPath does not exist'); + exitCode = 1; + return; } - log("srcPath: $srcPath"); + verboseLog("srcPath: $srcPath"); - final currentDirUri = Uri.file("."); - final buildPath = - options.buildDir ?? currentDirUri.resolve("build/jni_libs").toFilePath(); + final currentDirUri = Uri.directory("."); + final buildPath = options.buildPath ?? + currentDirUri.resolve(_defaultRelativeBuildPath).toFilePath(); final buildDir = Directory(buildPath); await buildDir.create(recursive: true); - log("buildPath: $buildPath"); + verboseLog("buildPath: $buildPath"); if (buildDir.absolute.uri == srcDir.absolute.uri) { stderr.writeln("Please build in a directory different than source."); - exit(2); + exitCode = 1; + return; } - if (options.clean) { - await cleanup(options, srcDir.absolute.path, buildDir.absolute.path); - } else { - // pass srcDir absolute path because it will be passed to CMake as arg - // which will be running in different directory - final jniDirUri = Uri.directory(".dart_tool").resolve("jni"); - final jniDir = Directory.fromUri(jniDirUri); - await jniDir.create(recursive: true); - final tempDir = await jniDir.createTemp("jni_native_build_"); - await build(options, srcDir.absolute.path, tempDir.path); - final dllDirUri = - Platform.isWindows ? tempDir.uri.resolve("Debug") : tempDir.uri; - final dllDir = Directory.fromUri(dllDirUri); - for (var entry in dllDir.listSync()) { - final dllSuffix = Platform.isWindows ? "dll" : "so"; - if (entry.path.endsWith(dllSuffix)) { - final dllName = entry.uri.pathSegments.last; - final target = buildDir.uri.resolve(dllName); - entry.renameSync(target.toFilePath()); - } - } - await tempDir.delete(recursive: true); + final targetFileUri = buildDir.uri.resolve(getTargetName(srcDir)); + final targetFile = File.fromUri(targetFileUri); + if (!needsBuild(targetFile, srcDir)) { + verboseLog("last modified of ${targetFile.path}: " + "${targetFile.lastModifiedSync()}"); + stderr.writeln("target newer than source, skipping build"); + return; } -} -Future<void> build(Options options, String srcPath, String buildPath) async { - final runner = CommandRunner(printCmds: true); + // Note: creating temp dir in .dart_tool/jni instead of SystemTemp + // because latter can fail tests on Windows CI, when system temp is on + // separate drive or something. + final jniDirUri = Uri.directory(".dart_tool").resolve("jni"); + final jniDir = Directory.fromUri(jniDirUri); + await jniDir.create(recursive: true); + final tempDir = await jniDir.createTemp("jni_native_build_"); final cmakeArgs = <String>[]; cmakeArgs.addAll(options.cmakeArgs); - cmakeArgs.add(srcPath); - await runner.run("cmake", cmakeArgs, buildPath); - await runner.run("cmake", ["--build", "."], buildPath); -} - -Future<void> cleanup(Options options, String srcPath, String buildPath) async { - if (srcPath == buildPath) { - stderr.writeln('Error: build path is same as source path.'); + // Pass absolute path of srcDir because cmake command is run in temp dir + cmakeArgs.add(srcDir.absolute.path); + await runCommand("cmake", cmakeArgs, tempDir.path); + await runCommand("cmake", ["--build", "."], tempDir.path); + final dllDirUri = + Platform.isWindows ? tempDir.uri.resolve("Debug") : tempDir.uri; + final dllDir = Directory.fromUri(dllDirUri); + for (var entry in dllDir.listSync()) { + final dllSuffix = Platform.isWindows ? "dll" : "so"; + if (entry.path.endsWith(dllSuffix)) { + final dllName = entry.uri.pathSegments.last; + final target = buildDir.uri.resolve(dllName); + entry.renameSync(target.toFilePath()); + } } - - stderr.writeln("deleting $buildPath"); - - try { - await Directory(buildPath).delete(recursive: true); - } catch (e) { - stderr.writeln("Error: cannot be deleted"); - stderr.writeln(e); - } + await tempDir.delete(recursive: true); }
diff --git a/pkgs/jni/example/integration_test/jni_object_test.dart b/pkgs/jni/example/integration_test/on_device_jni_test.dart similarity index 85% rename from pkgs/jni/example/integration_test/jni_object_test.dart rename to pkgs/jni/example/integration_test/on_device_jni_test.dart index aee60d9..5b0fd84 100644 --- a/pkgs/jni/example/integration_test/jni_object_test.dart +++ b/pkgs/jni/example/integration_test/on_device_jni_test.dart
@@ -2,6 +2,11 @@ // 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. +// This file contains mostly the same tests from package:jni's +// test/jni_object_test.dart. This file can be run on android device using +// flutter test integration_test/ and therefore useful for checking the +// working of JNI on an Android device (or emulator). + import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; @@ -80,6 +85,15 @@ expect(maxLong, equals(32767)); }); + testWidgets("Call method with null argument, expect exception", + (tester) async { + final integerClass = Jni.findJniClass("java/lang/Integer"); + expect( + () => integerClass.callStaticMethodByName<int>( + "parseInt", "(Ljava/lang/String;)I", [nullptr]), + throwsException); + }); + testWidgets("callStaticStringMethod", (t) async { final longClass = Jni.findJniClass("java/lang/Long"); const n = 1223334444;
diff --git a/pkgs/jni/example/lib/main.dart b/pkgs/jni/example/lib/main.dart index aaa5341..69f5933 100644 --- a/pkgs/jni/example/lib/main.dart +++ b/pkgs/jni/example/lib/main.dart
@@ -13,6 +13,7 @@ // An example of calling JNI methods using low level primitives. // GlobalJniEnv is a thin abstraction over JNIEnv in JNI C API. +// // For a more ergonomic API for common use cases of calling methods and // accessing fields, see next examples using JniObject and JniClass. String toJavaStringUsingEnv(int n) => using((arena) { @@ -78,7 +79,7 @@ Jni.getCachedApplicationContext(), "😀", 0, - ]).use((toast) => toast.callMethodByName("show", "()V", [])); + ]).callMethodByName<void>("show", "()V", []); } void main() {
diff --git a/pkgs/jni/example/pubspec.lock b/pkgs/jni/example/pubspec.lock index cb53cc4..129041a 100644 --- a/pkgs/jni/example/pubspec.lock +++ b/pkgs/jni/example/pubspec.lock
@@ -130,7 +130,7 @@ path: ".." relative: true source: path - version: "0.1.0" + version: "0.1.1" lints: dependency: transitive description:
diff --git a/pkgs/jni/ffigen.yaml b/pkgs/jni/ffigen.yaml index 8fdb4f1..3833215 100644 --- a/pkgs/jni/ffigen.yaml +++ b/pkgs/jni/ffigen.yaml
@@ -11,19 +11,17 @@ output: 'lib/src/third_party/jni_bindings_generated.dart' headers: entry-points: - - 'src/global_jni_env.h' + - 'src/dartjni.h' # Exports majority of JNI functions + - 'src/third_party/global_jni_env.h' # Exports GlobalJniEnv type include-directives: - 'src/dartjni.h' - - 'src/global_jni_env.h' - - 'third_party/jni.h' + - 'src/third_party/global_jni_env.h' + - 'third_party/jni.h' # jni.h from Android NDK compiler-opts: - '-Ithird_party/' functions: exclude: # Exclude init functions supposed to be defined in loaded DLL, not JNI - - 'JNI_OnLoad' - - 'JNI_OnUnload' - - 'JNI_OnLoad_L' - - 'JNI_OnUnload_L' + - 'JNI_.*' - 'GetJniContext' - 'setJniGetters' - 'jni_log' @@ -33,6 +31,7 @@ - 'JNIEnv' - '_JNIEnv' - 'JNIInvokeInterface' + - '__va_list_tag' rename: ## opaque struct definitions, base types of jfieldID and jmethodID '_jfieldID': 'jfieldID_' @@ -41,9 +40,6 @@ unions: rename: 'jvalue': 'JValue' -enums: - rename: - 'DartJniLogLevel': 'JniLogLevel' globals: exclude: - 'jni' @@ -51,6 +47,8 @@ - 'context_getter' - 'env_getter' typedefs: + exclude: + - 'va_list' rename: 'JNI(.*)': 'Jni$1' 'jint': 'JInt'
diff --git a/pkgs/jni/lib/internal_helpers_for_jnigen.dart b/pkgs/jni/lib/internal_helpers_for_jnigen.dart index fb0f36c..9f44e7a 100644 --- a/pkgs/jni/lib/internal_helpers_for_jnigen.dart +++ b/pkgs/jni/lib/internal_helpers_for_jnigen.dart
@@ -8,3 +8,4 @@ export 'src/jni.dart' show ProtectedJniExtensions; export 'src/jni_object.dart' show JniReference; +export 'src/accessors.dart';
diff --git a/pkgs/jni/lib/jni.dart b/pkgs/jni/lib/jni.dart index bb5a5ae..7306fd8 100644 --- a/pkgs/jni/lib/jni.dart +++ b/pkgs/jni/lib/jni.dart
@@ -60,7 +60,7 @@ library jni; export 'src/third_party/jni_bindings_generated.dart' - hide JNI_LOG_TAG, JniBindings, JniEnv, JniEnv1; + hide JniBindings, JniEnv, JniEnv1; export 'src/jni.dart' hide ProtectedJniExtensions; export 'src/jvalues.dart' hide JValueArgs, toJValues; export 'src/env_extensions.dart'
diff --git a/pkgs/jni/lib/src/accessors.dart b/pkgs/jni/lib/src/accessors.dart new file mode 100644 index 0000000..030d87c --- /dev/null +++ b/pkgs/jni/lib/src/accessors.dart
@@ -0,0 +1,131 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:ffi'; +import 'package:ffi/ffi.dart' show using; + +import 'package:jni/src/jvalues.dart'; + +import 'third_party/jni_bindings_generated.dart'; +import 'jni.dart'; +import 'env_extensions.dart'; + +Pointer<GlobalJniEnv> env = Jni.env; + +void _check(JThrowable exception) { + if (exception != nullptr) { + env.throwException(exception); + } +} + +extension JniResultMethods on JniResult { + void check() => _check(exception); + + int get byte { + check(); + return result.b; + } + + int get short { + check(); + return result.s; + } + + int get char { + check(); + return result.c; + } + + int get integer { + check(); + return result.i; + } + + int get long { + check(); + return result.j; + } + + double get float { + check(); + return result.f; + } + + double get doubleFloat { + check(); + return result.d; + } + + JObject get object { + check(); + return result.l; + } + + bool get boolean { + check(); + return result.z != 0; + } +} + +extension JniIdLookupResultMethods on JniPointerResult { + JMethodID get methodID { + _check(exception); + return id.cast<jmethodID_>(); + } + + JFieldID get fieldID { + _check(exception); + return id.cast<jfieldID_>(); + } +} + +extension JniClassLookupResultMethods on JniClassLookupResult { + JClass get checkedClassRef { + _check(exception); + return classRef; + } +} + +extension JniAccessorWrappers on Pointer<JniAccessors> { + // TODO(PR): How to name these methods? These only wrap toNativeChars() + // so that generated bindings are less verbose. + JClass getClassOf(String internalName) => + using((arena) => getClass(internalName.toNativeChars(arena))) + .checkedClassRef; + + JMethodID getMethodIDOf(JClass cls, String name, String signature) => + using((arena) => getMethodID( + cls, name.toNativeChars(arena), signature.toNativeChars(arena))) + .methodID; + + JMethodID getStaticMethodIDOf(JClass cls, String name, String signature) => + using((arena) => getStaticMethodID( + cls, name.toNativeChars(arena), signature.toNativeChars(arena))) + .methodID; + + JFieldID getFieldIDOf(JClass cls, String name, String signature) => + using((arena) => getFieldID( + cls, name.toNativeChars(arena), signature.toNativeChars(arena))) + .fieldID; + + JFieldID getStaticFieldIDOf(JClass cls, String name, String signature) => + using((arena) => getStaticFieldID( + cls, name.toNativeChars(arena), signature.toNativeChars(arena))) + .fieldID; + + JniResult newObjectWithArgs(JClass cls, JMethodID ctor, List<dynamic> args) => + using((arena) { + return newObject(cls, ctor, toJValues(args, allocator: arena)); + }); + + JniResult callMethodWithArgs( + JObject obj, JMethodID id, int callType, List<dynamic> args) => + using((arena) => + callMethod(obj, id, callType, toJValues(args, allocator: arena))); + + JniResult callStaticMethodWithArgs( + JClass cls, JMethodID id, int callType, List<dynamic> args) => + using((arena) => callStaticMethod( + cls, id, callType, toJValues(args, allocator: arena))); +}
diff --git a/pkgs/jni/lib/src/env_extensions.dart b/pkgs/jni/lib/src/env_extensions.dart index 63fddd1..c9164ef 100644 --- a/pkgs/jni/lib/src/env_extensions.dart +++ b/pkgs/jni/lib/src/env_extensions.dart
@@ -47,26 +47,25 @@ /// /// If [describe] is true, a description is printed to screen. /// To access actual exception object, use `ExceptionOccurred`. + /// + /// TODO(#64): Do not keep reference to exception, store stack trace & error. void checkException({bool describe = false}) { final exc = ExceptionOccurred(); - if (exc != nullptr) { - // TODO(#13): Doing this every time is expensive. - // Should lookup and cache method reference, - // and keep it alive by keeping a reference to Exception class. - final ecls = GetObjectClass(exc); - final toStr = GetMethodID(ecls, _toString, _toStringSig); - final jstr = CallObjectMethod(exc, toStr); - final dstr = asDartString(jstr); - for (final i in [jstr, ecls]) { - DeleteGlobalRef(i); - } - if (describe) { - ExceptionDescribe(); - } else { - ExceptionClear(); - } - throw JniException(exc, dstr); + throwException(exc); + } + + /// Throw [exception] from JNI in Dart as a [JniException]. + void throwException(JObject exception, {bool describe = false}) { + if (exception == nullptr) { + return; } + final exceptionClass = GetObjectClass(exception); + final toStringMethod = GetMethodID(exceptionClass, _toString, _toStringSig); + final javaDescription = CallObjectMethod(exception, toStringMethod); + final dartDescription = asDartString(javaDescription); + DeleteGlobalRef(javaDescription); + DeleteGlobalRef(exceptionClass); + throw JniException(exception, dartDescription); } /// Calls the printStackTrace on exception object
diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart index 115a74a..2b8d10f 100644 --- a/pkgs/jni/lib/src/jni.dart +++ b/pkgs/jni/lib/src/jni.dart
@@ -13,6 +13,7 @@ import 'jvalues.dart'; import 'jni_exceptions.dart'; import 'jni_object.dart'; +import 'accessors.dart'; String _getLibraryFileName(String base) { if (Platform.isLinux || Platform.isAndroid) { @@ -146,10 +147,11 @@ /// It provides an indirection over [JniEnv] so that it can be used from /// any thread, and always returns global object references. static Pointer<GlobalJniEnv> get env { - _env ??= _fetchGlobalEnv(); - return _env!; + return _env ??= _fetchGlobalEnv(); } + static Pointer<JniAccessors> get accessors => _bindings.GetAccessors(); + /// Returns current application context on Android. static JObject getCachedApplicationContext() { return _bindings.GetApplicationContext(); @@ -167,12 +169,8 @@ /// Returns class reference found through system-specific mechanism static JClass findClass(String qualifiedName) => using((arena) { - final nameChars = qualifiedName.toNativeChars(arena); - final cls = _bindings.LoadClass(nameChars); - if (cls == nullptr) { - env.checkException(); - } - return cls; + final cls = accessors.getClass(qualifiedName.toNativeChars(arena)); + return cls.checkedClassRef; }); /// Returns class for [qualifiedName] found by platform-specific mechanism, @@ -257,7 +255,4 @@ final lookup = dl.lookup; return lookup; } - - /// Checks for and rethrows any pending exception in JNI as a [JniException]. - static void checkException() => Jni.env.checkException(); }
diff --git a/pkgs/jni/lib/src/jni_exceptions.dart b/pkgs/jni/lib/src/jni_exceptions.dart index 0cb5806..939c465 100644 --- a/pkgs/jni/lib/src/jni_exceptions.dart +++ b/pkgs/jni/lib/src/jni_exceptions.dart
@@ -45,7 +45,7 @@ extension JniTypeNames on int { static const _names = { - JniType.boolType: 'bool', + JniType.booleanType: 'bool', JniType.byteType: 'byte', JniType.shortType: 'short', JniType.charType: 'char', @@ -88,5 +88,6 @@ String toString() => "Lookup for helper library $path failed.\n" "Please ensure that `dartjni` shared library is built.\n" "Provided jni:setup script can be used to build the shared library." - "If the library is already built, double check the path."; + "If the library is already built, ensure that the JVM libraries can be " + "loaded from Dart."; }
diff --git a/pkgs/jni/lib/src/jni_object.dart b/pkgs/jni/lib/src/jni_object.dart index fc66c6a..80cf707 100644 --- a/pkgs/jni/lib/src/jni_object.dart +++ b/pkgs/jni/lib/src/jni_object.dart
@@ -10,6 +10,7 @@ import 'jni_exceptions.dart'; import 'jni.dart'; import 'env_extensions.dart'; +import 'accessors.dart'; import 'jvalues.dart'; // This typedef is needed because void is a keyword and cannot be used in @@ -71,89 +72,23 @@ } } -class _CallGetMethods { - _CallGetMethods(this.getField, this.getStaticField, this.callMethod, - this.callStaticMethod); - Function(JObject, JFieldID) getField; - Function(JClass, JFieldID) getStaticField; - Function(JObject, JMethodID, Pointer<JValue>) callMethod; - Function(JClass, JMethodID, Pointer<JValue>) callStaticMethod; -} +final Pointer<JniAccessors> _accessors = Jni.accessors; final Pointer<GlobalJniEnv> _env = Jni.env; -final Map<int, _CallGetMethods> _accessors = { - JniType.boolType: _CallGetMethods( - _env.GetBooleanField, - _env.GetStaticBooleanField, - _env.CallBooleanMethodA, - _env.CallStaticBooleanMethodA, - ), - JniType.byteType: _CallGetMethods( - _env.GetByteField, - _env.GetStaticByteField, - _env.CallByteMethodA, - _env.CallStaticByteMethodA, - ), - JniType.shortType: _CallGetMethods( - _env.GetShortField, - _env.GetStaticShortField, - _env.CallShortMethodA, - _env.CallStaticShortMethodA, - ), - JniType.charType: _CallGetMethods( - _env.GetCharField, - _env.GetStaticCharField, - _env.CallCharMethodA, - _env.CallStaticCharMethodA, - ), - JniType.intType: _CallGetMethods( - _env.GetIntField, - _env.GetStaticIntField, - _env.CallIntMethodA, - _env.CallStaticIntMethodA, - ), - JniType.longType: _CallGetMethods( - _env.GetLongField, - _env.GetStaticLongField, - _env.CallLongMethodA, - _env.CallStaticLongMethodA, - ), - JniType.floatType: _CallGetMethods( - _env.GetFloatField, - _env.GetStaticFloatField, - _env.CallFloatMethodA, - _env.CallStaticFloatMethodA, - ), - JniType.doubleType: _CallGetMethods( - _env.GetDoubleField, - _env.GetStaticDoubleField, - _env.CallDoubleMethodA, - _env.CallStaticDoubleMethodA, - ), - JniType.objectType: _CallGetMethods( - _env.GetObjectField, - _env.GetStaticObjectField, - _env.CallObjectMethodA, - _env.CallStaticObjectMethodA, - ), - JniType.voidType: _CallGetMethods( - (x, y) => throw ArgumentError('void passed as field type'), - (x, y) => throw ArgumentError('void passed as static field type'), - _env.CallVoidMethodA, - _env.CallStaticVoidMethodA, - ), -}; - -T _getID<T>( - T Function(Pointer<Void> ptr, Pointer<Char> name, Pointer<Char> sig) f, +Pointer<T> _getID<T extends NativeType>( + JniPointerResult Function( + Pointer<Void> ptr, Pointer<Char> name, Pointer<Char> sig) + f, Pointer<Void> ptr, String name, String sig) { final result = using( (arena) => f(ptr, name.toNativeChars(arena), sig.toNativeChars(arena))); - _env.checkException(); - return result; + if (result.exception != nullptr) { + env.throwException(result.exception); + } + return result.id.cast<T>(); } int _getCallType(int? callType, int defaultType, Set<int> allowed) { @@ -162,14 +97,14 @@ throw InvalidCallTypeException(callType, allowed); } -T _callOrGet<T>(int? callType, Function(int) f) { +T _callOrGet<T>(int? callType, JniResult Function(int) function) { final int finalCallType; - T result; + late T result; switch (T) { case bool: finalCallType = - _getCallType(callType, JniType.boolType, {JniType.boolType}); - result = (f(finalCallType) as int != 0) as T; + _getCallType(callType, JniType.booleanType, {JniType.booleanType}); + result = function(finalCallType).boolean as T; break; case int: finalCallType = _getCallType(callType, JniType.intType, { @@ -179,22 +114,44 @@ JniType.intType, JniType.longType, }); - result = f(finalCallType) as T; + final jniResult = function(finalCallType); + switch (finalCallType) { + case JniType.byteType: + result = jniResult.byte as T; + break; + case JniType.shortType: + result = jniResult.short as T; + break; + case JniType.charType: + result = jniResult.char as T; + break; + case JniType.intType: + result = jniResult.integer as T; + break; + case JniType.longType: + result = jniResult.long as T; + break; + } break; case double: finalCallType = _getCallType(callType, JniType.doubleType, {JniType.floatType, JniType.doubleType}); - result = f(finalCallType) as T; + final jniResult = function(finalCallType); + switch (finalCallType) { + case JniType.floatType: + result = jniResult.float as T; + break; + case JniType.doubleType: + result = jniResult.doubleFloat as T; + break; + } break; case String: case JniObject: case JniString: finalCallType = _getCallType(callType, JniType.objectType, {JniType.objectType}); - final ref = f(finalCallType) as JObject; - if (ref == nullptr) { - _env.checkException(); - } + final ref = function(finalCallType).object; final ctor = T == String ? (ref) => _env.asDartString(ref, deleteOriginal: true) : (T == JniObject ? JniObject.fromRef : JniString.fromRef); @@ -203,33 +160,27 @@ case _VoidType: finalCallType = _getCallType(callType, JniType.voidType, {JniType.voidType}); - f(finalCallType); + function(finalCallType).check(); result = null as T; break; case dynamic: - result = f(callType ?? JniType.voidType) as T; - break; + throw UnsupportedError("Return type not specified for JNI call"); default: throw UnsupportedError('Unknown type $T'); } return result; } -T _callMethod<T>( - int? callType, List<dynamic> args, Function(int, Pointer<JValue>) f) => +T _callMethod<T>(int? callType, List<dynamic> args, + JniResult Function(int, Pointer<JValue>) f) => using((arena) { final jArgs = JValueArgs(args, arena); - final result = _callOrGet<T>(callType, (ct) => f(ct, jArgs.values)); - jArgs.dispose(); - if (result == 0 || result == 0.0 || result == null) { - _env.checkException(); - } - return result; + arena.onReleaseAll(jArgs.dispose); + return _callOrGet<T>(callType, (ct) => f(ct, jArgs.values)); }); -T _getField<T>(int? callType, Function(int) f) { +T _getField<T>(int? callType, JniResult Function(int) f) { final result = _callOrGet<T>(callType, f); - _env.checkException(); return result; } @@ -243,8 +194,7 @@ JniClass? _jniClass; JniClass get _class { - _jniClass ??= getClass(); - return _jniClass!; + return _jniClass ??= getClass(); } /// Deletes the JNI reference and marks this object as deleted. Any further @@ -270,27 +220,29 @@ /// Get [JFieldID] of instance field identified by [fieldName] & [signature]. JFieldID getFieldID(String fieldName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetFieldID, _class.reference, fieldName, signature); + return _getID( + _accessors.getFieldID, _class.reference, fieldName, signature); } /// Get [JFieldID] of static field identified by [fieldName] & [signature]. JFieldID getStaticFieldID(String fieldName, String signature) { _ensureNotDeleted(); - return _getID( - _env.GetStaticFieldID, _class.reference, fieldName, signature); + return _getID<jfieldID_>( + _accessors.getStaticFieldID, _class.reference, fieldName, signature); } /// Get [JMethodID] of instance method [methodName] with [signature]. JMethodID getMethodID(String methodName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetMethodID, _class.reference, methodName, signature); + return _getID<jmethodID_>( + _accessors.getMethodID, _class.reference, methodName, signature); } /// Get [JMethodID] of static method [methodName] with [signature]. JMethodID getStaticMethodID(String methodName, String signature) { _ensureNotDeleted(); - return _getID( - _env.GetStaticMethodID, _class.reference, methodName, signature); + return _getID<jmethodID_>( + _accessors.getStaticMethodID, _class.reference, methodName, signature); } /// Retrieve the value of the field using [fieldID]. @@ -304,8 +256,11 @@ /// final value is returned. T getField<T>(JFieldID fieldID, [int? callType]) { _ensureNotDeleted(); + if (callType == JniType.voidType) { + throw ArgumentError("void is not a valid field type."); + } return _getField<T>( - callType, (ct) => _accessors[ct]!.getField(reference, fieldID)); + callType, (ct) => _accessors.getField(reference, fieldID, ct)); } /// Get value of the field identified by [name] and [signature]. @@ -320,9 +275,12 @@ /// /// See [getField] for an explanation about [callType] parameter. T getStaticField<T>(JFieldID fieldID, [int? callType]) { + if (callType == JniType.voidType) { + throw ArgumentError("void is not a valid field type."); + } _ensureNotDeleted(); return _getField<T>(callType, - (ct) => _accessors[ct]!.getStaticField(_class.reference, fieldID)); + (ct) => _accessors.getStaticField(_class.reference, fieldID, ct)); } /// Get value of the static field identified by [name] and [signature]. @@ -342,7 +300,7 @@ T callMethod<T>(JMethodID methodID, List<dynamic> args, [int? callType]) { _ensureNotDeleted(); return _callMethod<T>(callType, args, - (ct, jvs) => _accessors[ct]!.callMethod(reference, methodID, jvs)); + (ct, jvs) => _accessors.callMethod(reference, methodID, ct, jvs)); } /// Call instance method identified by [name] and [signature]. @@ -359,11 +317,8 @@ T callStaticMethod<T>(JMethodID methodID, List<dynamic> args, [int? callType]) { _ensureNotDeleted(); - return _callMethod<T>( - callType, - args, - (ct, jvs) => - _accessors[ct]!.callStaticMethod(reference, methodID, jvs)); + return _callMethod<T>(callType, args, + (ct, jvs) => _accessors.callStaticMethod(reference, methodID, ct, jvs)); } /// Call static method identified by [name] and [signature]. @@ -384,25 +339,29 @@ /// Get [JFieldID] of static field [fieldName] with [signature]. JFieldID getStaticFieldID(String fieldName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetStaticFieldID, reference, fieldName, signature); + return _getID<jfieldID_>( + _accessors.getStaticFieldID, reference, fieldName, signature); } /// Get [JMethodID] of static method [methodName] with [signature]. JMethodID getStaticMethodID(String methodName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetStaticMethodID, reference, methodName, signature); + return _getID<jmethodID_>( + _accessors.getStaticMethodID, reference, methodName, signature); } /// Get [JFieldID] of field [fieldName] with [signature]. JFieldID getFieldID(String fieldName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetFieldID, reference, fieldName, signature); + return _getID<jfieldID_>( + _accessors.getFieldID, reference, fieldName, signature); } /// Get [JMethodID] of method [methodName] with [signature]. JMethodID getMethodID(String methodName, String signature) { _ensureNotDeleted(); - return _getID(_env.GetMethodID, reference, methodName, signature); + return _getID<jmethodID_>( + _accessors.getMethodID, reference, methodName, signature); } /// Get [JMethodID] of constructor with [signature]. @@ -412,9 +371,12 @@ /// /// See [JniObject.getField] for more explanation about [callType]. T getStaticField<T>(JFieldID fieldID, [int? callType]) { + if (callType == JniType.voidType) { + throw ArgumentError("void is not a valid field type."); + } _ensureNotDeleted(); return _getField<T>( - callType, (ct) => _accessors[ct]!.getStaticField(reference, fieldID)); + callType, (ct) => _accessors.getStaticField(reference, fieldID, ct)); } /// Get the value of static field identified by [name] and [signature]. @@ -432,11 +394,8 @@ T callStaticMethod<T>(JMethodID methodID, List<dynamic> args, [int? callType]) { _ensureNotDeleted(); - return _callMethod<T>( - callType, - args, - (ct, jvs) => - _accessors[ct]!.callStaticMethod(reference, methodID, jvs)); + return _callMethod<T>(callType, args, + (ct, jvs) => _accessors.callStaticMethod(reference, methodID, ct, jvs)); } /// Call the static method identified by [name] and [signature]. @@ -452,11 +411,8 @@ JniObject newInstance(JMethodID ctor, List<dynamic> args) => using((arena) { _ensureNotDeleted(); final jArgs = JValueArgs(args, arena); - final res = _env.NewObjectA(reference, ctor, jArgs.values); - jArgs.dispose(); - if (res == nullptr) { - _env.checkException(); - } + arena.onReleaseAll(jArgs.dispose); + final res = _accessors.newObject(reference, ctor, jArgs.values).object; return JniObject.fromRef(res); }); }
diff --git a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart index 9b95e21..a93b135 100644 --- a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart +++ b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart
@@ -62,64 +62,15 @@ lookup) : _lookup = lookup; - /// VM initialization functions. - /// - /// Note these are the only symbols exported for JNI by the VM. - int JNI_GetDefaultJavaVMInitArgs( - ffi.Pointer<ffi.Void> arg0, - ) { - return _JNI_GetDefaultJavaVMInitArgs( - arg0, - ); + ffi.Pointer<JniAccessors> GetAccessors() { + return _GetAccessors(); } - late final _JNI_GetDefaultJavaVMInitArgsPtr = - _lookup<ffi.NativeFunction<JInt Function(ffi.Pointer<ffi.Void>)>>( - 'JNI_GetDefaultJavaVMInitArgs'); - late final _JNI_GetDefaultJavaVMInitArgs = _JNI_GetDefaultJavaVMInitArgsPtr - .asFunction<int Function(ffi.Pointer<ffi.Void>)>(); - - int JNI_CreateJavaVM( - ffi.Pointer<ffi.Pointer<JavaVM>> arg0, - ffi.Pointer<ffi.Pointer<JniEnv>> arg1, - ffi.Pointer<ffi.Void> arg2, - ) { - return _JNI_CreateJavaVM( - arg0, - arg1, - arg2, - ); - } - - late final _JNI_CreateJavaVMPtr = _lookup< - ffi.NativeFunction< - JInt Function( - ffi.Pointer<ffi.Pointer<JavaVM>>, - ffi.Pointer<ffi.Pointer<JniEnv>>, - ffi.Pointer<ffi.Void>)>>('JNI_CreateJavaVM'); - late final _JNI_CreateJavaVM = _JNI_CreateJavaVMPtr.asFunction< - int Function(ffi.Pointer<ffi.Pointer<JavaVM>>, - ffi.Pointer<ffi.Pointer<JniEnv>>, ffi.Pointer<ffi.Void>)>(); - - int JNI_GetCreatedJavaVMs( - ffi.Pointer<ffi.Pointer<JavaVM>> arg0, - int arg1, - ffi.Pointer<JSize> arg2, - ) { - return _JNI_GetCreatedJavaVMs( - arg0, - arg1, - arg2, - ); - } - - late final _JNI_GetCreatedJavaVMsPtr = _lookup< - ffi.NativeFunction< - JInt Function(ffi.Pointer<ffi.Pointer<JavaVM>>, JSize, - ffi.Pointer<JSize>)>>('JNI_GetCreatedJavaVMs'); - late final _JNI_GetCreatedJavaVMs = _JNI_GetCreatedJavaVMsPtr.asFunction< - int Function( - ffi.Pointer<ffi.Pointer<JavaVM>>, int, ffi.Pointer<JSize>)>(); + late final _GetAccessorsPtr = + _lookup<ffi.NativeFunction<ffi.Pointer<JniAccessors> Function()>>( + 'GetAccessors'); + late final _GetAccessors = + _GetAccessorsPtr.asFunction<ffi.Pointer<JniAccessors> Function()>(); ffi.Pointer<JavaVM> GetJavaVM() { return _GetJavaVM(); @@ -130,14 +81,6 @@ late final _GetJavaVM = _GetJavaVMPtr.asFunction<ffi.Pointer<JavaVM> Function()>(); - int DestroyJavaVM() { - return _DestroyJavaVM(); - } - - late final _DestroyJavaVMPtr = - _lookup<ffi.NativeFunction<ffi.Int Function()>>('DestroyJavaVM'); - late final _DestroyJavaVM = _DestroyJavaVMPtr.asFunction<int Function()>(); - ffi.Pointer<JniEnv> GetJniEnv() { return _GetJniEnv(); } @@ -203,11 +146,6 @@ late final _GetCurrentActivity = _GetCurrentActivityPtr.asFunction<JObject Function()>(); - late final ffi.Pointer<GlobalJniEnv> _globalEnv = - _lookup<GlobalJniEnv>('globalEnv'); - - GlobalJniEnv get globalEnv => _globalEnv.ref; - ffi.Pointer<GlobalJniEnv> GetGlobalEnv() { return _GetGlobalEnv(); } @@ -470,10 +408,7 @@ ffi.NativeFunction< JObject Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> NewObject; - external ffi.Pointer< - ffi.NativeFunction< - JObject Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _NewObjectV; + external ffi.Pointer<ffi.Void> _NewObjectV; external ffi.Pointer< ffi.NativeFunction< @@ -499,10 +434,7 @@ JObject Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallObjectMethod; - external ffi.Pointer< - ffi.NativeFunction< - JObject Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallObjectMethodV; + external ffi.Pointer<ffi.Void> _CallObjectMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -514,10 +446,7 @@ JBoolean Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallBooleanMethod; - external ffi.Pointer< - ffi.NativeFunction< - JBoolean Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallBooleanMethodV; + external ffi.Pointer<ffi.Void> _CallBooleanMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -529,10 +458,7 @@ JByte Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallByteMethod; - external ffi.Pointer< - ffi.NativeFunction< - JByte Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallByteMethodV; + external ffi.Pointer<ffi.Void> _CallByteMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -544,10 +470,7 @@ JChar Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallCharMethod; - external ffi.Pointer< - ffi.NativeFunction< - JChar Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallCharMethodV; + external ffi.Pointer<ffi.Void> _CallCharMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -559,10 +482,7 @@ JShort Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallShortMethod; - external ffi.Pointer< - ffi.NativeFunction< - JShort Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallShortMethodV; + external ffi.Pointer<ffi.Void> _CallShortMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -574,10 +494,7 @@ JInt Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallIntMethod; - external ffi.Pointer< - ffi.NativeFunction< - JInt Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallIntMethodV; + external ffi.Pointer<ffi.Void> _CallIntMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -589,10 +506,7 @@ JLong Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallLongMethod; - external ffi.Pointer< - ffi.NativeFunction< - JLong Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallLongMethodV; + external ffi.Pointer<ffi.Void> _CallLongMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -604,10 +518,7 @@ JFloat Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallFloatMethod; - external ffi.Pointer< - ffi.NativeFunction< - JFloat Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallFloatMethodV; + external ffi.Pointer<ffi.Void> _CallFloatMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -619,10 +530,7 @@ JDouble Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallDoubleMethod; - external ffi.Pointer< - ffi.NativeFunction< - JDouble Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallDoubleMethodV; + external ffi.Pointer<ffi.Void> _CallDoubleMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -634,10 +542,7 @@ ffi.Void Function(ffi.Pointer<JniEnv1>, JObject, JMethodID)>> CallVoidMethod; - external ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<JniEnv1>, JObject, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallVoidMethodV; + external ffi.Pointer<ffi.Void> _CallVoidMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -650,10 +555,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualObjectMethod; - external ffi.Pointer< - ffi.NativeFunction< - JObject Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualObjectMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualObjectMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -666,10 +568,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualBooleanMethod; - external ffi.Pointer< - ffi.NativeFunction< - JBoolean Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualBooleanMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualBooleanMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -681,10 +580,7 @@ JByte Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualByteMethod; - external ffi.Pointer< - ffi.NativeFunction< - JByte Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualByteMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualByteMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -696,10 +592,7 @@ JChar Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualCharMethod; - external ffi.Pointer< - ffi.NativeFunction< - JChar Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualCharMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualCharMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -712,10 +605,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualShortMethod; - external ffi.Pointer< - ffi.NativeFunction< - JShort Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualShortMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualShortMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -727,10 +617,7 @@ JInt Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualIntMethod; - external ffi.Pointer< - ffi.NativeFunction< - JInt Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualIntMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualIntMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -742,10 +629,7 @@ JLong Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualLongMethod; - external ffi.Pointer< - ffi.NativeFunction< - JLong Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualLongMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualLongMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -758,10 +642,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualFloatMethod; - external ffi.Pointer< - ffi.NativeFunction< - JFloat Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualFloatMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualFloatMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -774,10 +655,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualDoubleMethod; - external ffi.Pointer< - ffi.NativeFunction< - JDouble Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualDoubleMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualDoubleMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -790,10 +668,7 @@ ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID)>> CallNonvirtualVoidMethod; - external ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<JniEnv1>, JObject, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallNonvirtualVoidMethodV; + external ffi.Pointer<ffi.Void> _CallNonvirtualVoidMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -907,10 +782,7 @@ JObject Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticObjectMethod; - external ffi.Pointer< - ffi.NativeFunction< - JObject Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticObjectMethodV; + external ffi.Pointer<ffi.Void> _CallStaticObjectMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -922,10 +794,7 @@ JBoolean Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticBooleanMethod; - external ffi.Pointer< - ffi.NativeFunction< - JBoolean Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticBooleanMethodV; + external ffi.Pointer<ffi.Void> _CallStaticBooleanMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -937,10 +806,7 @@ JByte Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticByteMethod; - external ffi.Pointer< - ffi.NativeFunction< - JByte Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticByteMethodV; + external ffi.Pointer<ffi.Void> _CallStaticByteMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -952,10 +818,7 @@ JChar Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticCharMethod; - external ffi.Pointer< - ffi.NativeFunction< - JChar Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticCharMethodV; + external ffi.Pointer<ffi.Void> _CallStaticCharMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -967,10 +830,7 @@ JShort Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticShortMethod; - external ffi.Pointer< - ffi.NativeFunction< - JShort Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticShortMethodV; + external ffi.Pointer<ffi.Void> _CallStaticShortMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -982,10 +842,7 @@ JInt Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticIntMethod; - external ffi.Pointer< - ffi.NativeFunction< - JInt Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticIntMethodV; + external ffi.Pointer<ffi.Void> _CallStaticIntMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -997,10 +854,7 @@ JLong Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticLongMethod; - external ffi.Pointer< - ffi.NativeFunction< - JLong Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticLongMethodV; + external ffi.Pointer<ffi.Void> _CallStaticLongMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -1012,10 +866,7 @@ JFloat Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticFloatMethod; - external ffi.Pointer< - ffi.NativeFunction< - JFloat Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticFloatMethodV; + external ffi.Pointer<ffi.Void> _CallStaticFloatMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -1027,10 +878,7 @@ JDouble Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticDoubleMethod; - external ffi.Pointer< - ffi.NativeFunction< - JDouble Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticDoubleMethodV; + external ffi.Pointer<ffi.Void> _CallStaticDoubleMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -1042,10 +890,7 @@ ffi.Void Function(ffi.Pointer<JniEnv1>, JClass, JMethodID)>> CallStaticVoidMethod; - external ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer<JniEnv1>, JClass, JMethodID, - ffi.Pointer<__va_list_tag>)>> _CallStaticVoidMethodV; + external ffi.Pointer<ffi.Void> _CallStaticVoidMethodV; external ffi.Pointer< ffi.NativeFunction< @@ -1517,19 +1362,6 @@ typedef JMethodID = ffi.Pointer<jmethodID_>; typedef JFieldID = ffi.Pointer<jfieldID_>; typedef JThrowable = JObject; - -class __va_list_tag extends ffi.Struct { - @ffi.UnsignedInt() - external int gp_offset; - - @ffi.UnsignedInt() - external int fp_offset; - - external ffi.Pointer<ffi.Void> overflow_arg_area; - - external ffi.Pointer<ffi.Void> reg_save_area; -} - typedef JString = JObject; typedef JArray = JObject; typedef JObjectArray = JArray; @@ -1577,16 +1409,9 @@ external int ignoreUnrecognized; } -abstract class JniLogLevel { - static const int JNI_VERBOSE = 2; - static const int JNI_DEBUG = 3; - static const int JNI_INFO = 4; - static const int JNI_WARN = 5; - static const int JNI_ERROR = 6; -} - +/// Types used by JNI API to distinguish between primitive types. abstract class JniType { - static const int boolType = 0; + static const int booleanType = 0; static const int byteType = 1; static const int shortType = 2; static const int charType = 3; @@ -1598,6 +1423,158 @@ static const int voidType = 9; } +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +class JniResult extends ffi.Struct { + external JValue result; + + external JThrowable exception; +} + +/// Similar to [JniResult] but for class lookups. +class JniClassLookupResult extends ffi.Struct { + external JClass classRef; + + external JThrowable exception; +} + +/// Similar to [JniResult] but for method/field ID lookups. +class JniPointerResult extends ffi.Struct { + external ffi.Pointer<ffi.Void> id; + + external JThrowable exception; +} + +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +class JniAccessors extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction< + JniClassLookupResult Function(ffi.Pointer<ffi.Char>)>> getClass; + + external ffi.Pointer< + ffi.NativeFunction< + JniPointerResult Function( + JClass, ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>> + getFieldID; + + external ffi.Pointer< + ffi.NativeFunction< + JniPointerResult Function( + JClass, ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>> + getStaticFieldID; + + external ffi.Pointer< + ffi.NativeFunction< + JniPointerResult Function( + JClass, ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>> + getMethodID; + + external ffi.Pointer< + ffi.NativeFunction< + JniPointerResult Function( + JClass, ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>> + getStaticMethodID; + + external ffi.Pointer< + ffi.NativeFunction< + JniResult Function(JClass, JMethodID, ffi.Pointer<JValue>)>> + newObject; + + external ffi.Pointer< + ffi.NativeFunction< + JniResult Function( + JObject, JMethodID, ffi.Int, ffi.Pointer<JValue>)>> callMethod; + + external ffi.Pointer< + ffi.NativeFunction< + JniResult Function( + JClass, JMethodID, ffi.Int, ffi.Pointer<JValue>)>> + callStaticMethod; + + external ffi.Pointer< + ffi.NativeFunction<JniResult Function(JObject, JFieldID, ffi.Int)>> + getField; + + external ffi.Pointer< + ffi.NativeFunction<JniResult Function(JClass, JFieldID, ffi.Int)>> + getStaticField; +} + +extension JniAccessorsExtension on ffi.Pointer<JniAccessors> { + JniClassLookupResult getClass(ffi.Pointer<ffi.Char> internalName) { + return ref.getClass + .asFunction<JniClassLookupResult Function(ffi.Pointer<ffi.Char>)>()( + internalName); + } + + JniPointerResult getFieldID(JClass cls, ffi.Pointer<ffi.Char> fieldName, + ffi.Pointer<ffi.Char> signature) { + return ref.getFieldID.asFunction< + JniPointerResult Function(JClass, ffi.Pointer<ffi.Char>, + ffi.Pointer<ffi.Char>)>()(cls, fieldName, signature); + } + + JniPointerResult getStaticFieldID(JClass cls, ffi.Pointer<ffi.Char> fieldName, + ffi.Pointer<ffi.Char> signature) { + return ref.getStaticFieldID.asFunction< + JniPointerResult Function(JClass, ffi.Pointer<ffi.Char>, + ffi.Pointer<ffi.Char>)>()(cls, fieldName, signature); + } + + JniPointerResult getMethodID(JClass cls, ffi.Pointer<ffi.Char> methodName, + ffi.Pointer<ffi.Char> signature) { + return ref.getMethodID.asFunction< + JniPointerResult Function(JClass, ffi.Pointer<ffi.Char>, + ffi.Pointer<ffi.Char>)>()(cls, methodName, signature); + } + + JniPointerResult getStaticMethodID(JClass cls, + ffi.Pointer<ffi.Char> methodName, ffi.Pointer<ffi.Char> signature) { + return ref.getStaticMethodID.asFunction< + JniPointerResult Function(JClass, ffi.Pointer<ffi.Char>, + ffi.Pointer<ffi.Char>)>()(cls, methodName, signature); + } + + JniResult newObject(JClass cls, JMethodID ctor, ffi.Pointer<JValue> args) { + return ref.newObject.asFunction< + JniResult Function( + JClass, JMethodID, ffi.Pointer<JValue>)>()(cls, ctor, args); + } + + JniResult callMethod( + JObject obj, JMethodID methodID, int callType, ffi.Pointer<JValue> args) { + return ref.callMethod.asFunction< + JniResult Function(JObject, JMethodID, int, ffi.Pointer<JValue>)>()( + obj, methodID, callType, args); + } + + JniResult callStaticMethod( + JClass cls, JMethodID methodID, int callType, ffi.Pointer<JValue> args) { + return ref.callStaticMethod.asFunction< + JniResult Function(JClass, JMethodID, int, ffi.Pointer<JValue>)>()( + cls, methodID, callType, args); + } + + JniResult getField(JObject obj, JFieldID fieldID, int callType) { + return ref.getField + .asFunction<JniResult Function(JObject, JFieldID, int)>()( + obj, fieldID, callType); + } + + JniResult getStaticField(JClass cls, JFieldID fieldID, int callType) { + return ref.getStaticField + .asFunction<JniResult Function(JClass, JFieldID, int)>()( + cls, fieldID, callType); + } +} + /// Wrapper over JNIEnv in the JNI API, which can be used from multiple Dart /// Threads. /// @@ -3542,5 +3519,3 @@ const int JNI_COMMIT = 1; const int JNI_ABORT = 2; - -const String JNI_LOG_TAG = 'Dart-JNI';
diff --git a/pkgs/jni/src/.clang-format b/pkgs/jni/src/.clang-format new file mode 100644 index 0000000..a256c2f --- /dev/null +++ b/pkgs/jni/src/.clang-format
@@ -0,0 +1,15 @@ +# From dart SDK: https://github.com/dart-lang/sdk/blob/main/.clang-format + +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium + +# clang-format doesn't seem to do a good job of this for longer comments. +ReflowComments: 'false' + +# We have lots of these. Though we need to put them all in curly braces, +# clang-format can't do that. +AllowShortIfStatementsOnASingleLine: 'true' + +# Put escaped newlines into the rightmost column. +AlignEscapedNewlinesLeft: false
diff --git a/pkgs/jni/src/CMakeLists.txt b/pkgs/jni/src/CMakeLists.txt index 3b4739e..23ac23b 100644 --- a/pkgs/jni/src/CMakeLists.txt +++ b/pkgs/jni/src/CMakeLists.txt
@@ -7,7 +7,7 @@ add_library(jni SHARED "dartjni.c" - "global_jni_env.c" + "third_party/global_jni_env.c" ) set_target_properties(jni PROPERTIES
diff --git a/pkgs/jni/src/README.md b/pkgs/jni/src/README.md index c0c3119..6516f65 100644 --- a/pkgs/jni/src/README.md +++ b/pkgs/jni/src/README.md
@@ -1,27 +1,5 @@ +Native code for jni library. + ## LSP instructions -If using an LSP based editor plugin and the syntax highlighting / code completion is not working, there are 2 ways to fix that. - -* __Create a compile_flags.txt with following content__: - -`-I<path_to_folder_containing_jni_headers>` - -This might need the OS specific include folder as well, so that transitively included headers can be found. Example: - -``` --I/usr/lib/jvm/java-11-openjdk-amd64/include --I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -``` - -Note that this file should contain one compilation flag per line. - -* __create a compilation database by prefixing `bear` to your cmake build__. - -Run `cmake --build` command from your source, prefixed with `bear`. - -`bear -- cmake --build <build_dir>` - -On some distro versions of `bear` command, the `--` needs to be omitted. - -`bear cmake --build <build_dir>` - +see https://releases.llvm.org/8.0.0/tools/clang/tools/extra/docs/clangd/Installation.html#project-setup for creating compile_commands.json for use with LSP based editor plugins (in VS Code, VIM etc..). \ No newline at end of file
diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c index d30689d..36af423 100644 --- a/pkgs/jni/src/dartjni.c +++ b/pkgs/jni/src/dartjni.c
@@ -3,39 +3,21 @@ // BSD-style license that can be found in the LICENSE file. #include <jni.h> +#include <stdarg.h> #include <stdint.h> #include "dartjni.h" +// Context and shared global state. Initialized once or if thread-local, initialized once in a thread. JniContext jni = {NULL, NULL, NULL, NULL, NULL}; -thread_local JNIEnv *jniEnv = NULL; - -FFI_PLUGIN_EXPORT JniContext GetJniContext() { return jni; } +thread_local JNIEnv* jniEnv = NULL; /// Get JVM associated with current process. /// Returns NULL if no JVM is running. FFI_PLUGIN_EXPORT -JavaVM *GetJavaVM() { return jni.jvm; } - -/// Destroys the JVM. -/// -/// Returns 0 on success and appropriate error code on failure. -FFI_PLUGIN_EXPORT -int DestroyJavaVM() { - int result = (*jni.jvm)->DestroyJavaVM(jni.jvm); - jni.jvm = NULL; - return result; -} - -/// Returns Application classLoader (on Android), -/// which can be used to load application and platform classes. -/// ... -/// On other platforms, NULL is returned. -FFI_PLUGIN_EXPORT -jobject GetClassLoader() { - attach_thread(); - return (*jniEnv)->NewLocalRef(jniEnv, jni.classLoader); +JavaVM* GetJavaVM() { + return jni.jvm; } /// Load class through platform-specific mechanism. @@ -43,20 +25,23 @@ /// Currently uses application classloader on android, /// and JNIEnv->FindClass on other platforms. FFI_PLUGIN_EXPORT -jclass LoadClass(const char *name) { - jclass cls = NULL; - attach_thread(); - load_class(&cls, name); - return to_global_ref(cls); +jclass LoadClass(const char* name) { + jclass cls = NULL; + attach_thread(); + load_class(&cls, name); + return to_global_ref(cls); }; +// Android specifics + +/// Returns Application classLoader (on Android), +/// which can be used to load application and platform classes. +/// ... +/// On other platforms, NULL is returned. FFI_PLUGIN_EXPORT -JNIEnv *GetJniEnv() { - if (jni.jvm == NULL) { - return NULL; - } - attach_thread(); - return jniEnv; +jobject GetClassLoader() { + attach_thread(); + return (*jniEnv)->NewGlobalRef(jniEnv, jni.classLoader); } /// Returns application context on Android. @@ -64,64 +49,330 @@ /// On other platforms, NULL is returned. FFI_PLUGIN_EXPORT jobject GetApplicationContext() { - attach_thread(); - return (*jniEnv)->NewGlobalRef(jniEnv, jni.appContext); + attach_thread(); + return (*jniEnv)->NewGlobalRef(jniEnv, jni.appContext); } /// Returns current activity of the app FFI_PLUGIN_EXPORT jobject GetCurrentActivity() { - attach_thread(); - return (*jniEnv)->NewGlobalRef(jniEnv, jni.currentActivity); + attach_thread(); + return (*jniEnv)->NewGlobalRef(jniEnv, jni.currentActivity); } +// JNI Initialization + #ifdef __ANDROID__ -JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_JniPlugin_initializeJni( - JNIEnv *env, jobject obj, jobject appContext, jobject classLoader) { - jniEnv = env; - (*env)->GetJavaVM(env, &jni.jvm); - jni.classLoader = (*env)->NewGlobalRef(env, classLoader); - jni.appContext = (*env)->NewGlobalRef(env, appContext); - jclass classLoaderClass = (*env)->GetObjectClass(env, classLoader); - jni.loadClassMethod = - (*env)->GetMethodID(env, classLoaderClass, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_JniPlugin_initializeJni(JNIEnv* env, + jobject obj, + jobject appContext, + jobject classLoader) { + jniEnv = env; + (*env)->GetJavaVM(env, &jni.jvm); + jni.classLoader = (*env)->NewGlobalRef(env, classLoader); + jni.appContext = (*env)->NewGlobalRef(env, appContext); + jclass classLoaderClass = (*env)->GetObjectClass(env, classLoader); + jni.loadClassMethod = + (*env)->GetMethodID(env, classLoaderClass, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); } -JNIEXPORT void JNICALL Java_com_github_dart_1lang_jni_JniPlugin_setJniActivity(JNIEnv *env, jobject obj, jobject activity, jobject context) { - jniEnv = env; - if (jni.currentActivity != NULL) { - (*env)->DeleteGlobalRef(env, jni.currentActivity); - } - jni.currentActivity = (*env)->NewGlobalRef(env, activity); - if (jni.appContext != NULL) { - (*env)->DeleteGlobalRef(env, jni.appContext); - } - jni.appContext = (*env)->NewGlobalRef(env, context); +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_JniPlugin_setJniActivity(JNIEnv* env, + jobject obj, + jobject activity, + jobject context) { + jniEnv = env; + if (jni.currentActivity != NULL) { + (*env)->DeleteGlobalRef(env, jni.currentActivity); + } + jni.currentActivity = (*env)->NewGlobalRef(env, activity); + if (jni.appContext != NULL) { + (*env)->DeleteGlobalRef(env, jni.appContext); + } + jni.appContext = (*env)->NewGlobalRef(env, context); } // Sometimes you may get linker error trying to link JNI_CreateJavaVM APIs // on Android NDK. So IFDEF is required. #else FFI_PLUGIN_EXPORT -JNIEnv *SpawnJvm(JavaVMInitArgs *initArgs) { - JavaVMOption jvmopt[1]; - char class_path[] = "-Djava.class.path=."; - jvmopt[0].optionString = class_path; - JavaVMInitArgs vmArgs; - if (!initArgs) { - vmArgs.version = JNI_VERSION_1_2; - vmArgs.nOptions = 1; - vmArgs.options = jvmopt; - vmArgs.ignoreUnrecognized = JNI_TRUE; - initArgs = &vmArgs; - } - const long flag = - JNI_CreateJavaVM(&jni.jvm, __ENVP_CAST &jniEnv, initArgs); - if (flag == JNI_ERR) { - return NULL; - } - return jniEnv; +JNIEnv* SpawnJvm(JavaVMInitArgs* initArgs) { + JavaVMOption jvmopt[1]; + char class_path[] = "-Djava.class.path=."; + jvmopt[0].optionString = class_path; + JavaVMInitArgs vmArgs; + if (!initArgs) { + vmArgs.version = JNI_VERSION_1_6; + vmArgs.nOptions = 1; + vmArgs.options = jvmopt; + vmArgs.ignoreUnrecognized = JNI_TRUE; + initArgs = &vmArgs; + } + const long flag = JNI_CreateJavaVM(&jni.jvm, __ENVP_CAST & jniEnv, initArgs); + if (flag == JNI_ERR) { + return NULL; + } + return jniEnv; } #endif +// accessors - a bunch of functions which are directly called by jnigen generated bindings +// and also package:jni reflective method access. + +jthrowable exceptionCheck() { + jthrowable exception = (*jniEnv)->ExceptionOccurred(jniEnv); + if (exception != NULL) (*jniEnv)->ExceptionClear(jniEnv); + if (exception == NULL) return NULL; + return to_global_ref(exception); +} + +JniClassLookupResult getClass(char* internalName) { + JniClassLookupResult result = {NULL, NULL}; + result.classRef = LoadClass(internalName); + result.exception = exceptionCheck(); + return result; +} + +static inline JniPointerResult _getId( + void* (*getter)(JNIEnv*, jclass, char*, char*), + jclass cls, + char* name, + char* sig) { + JniPointerResult result = {NULL, NULL}; + attach_thread(); + result.id = getter(jniEnv, cls, name, sig); + result.exception = exceptionCheck(); + return result; +} + +JniPointerResult getMethodID(jclass cls, char* name, char* sig) { + return _getId((*jniEnv)->GetMethodID, cls, name, sig); +} + +JniPointerResult getStaticMethodID(jclass cls, char* name, char* sig) { + return _getId((*jniEnv)->GetStaticMethodID, cls, name, sig); +} + +JniPointerResult getFieldID(jclass cls, char* name, char* sig) { + return _getId((*jniEnv)->GetFieldID, cls, name, sig); +} + +JniPointerResult getStaticFieldID(jclass cls, char* name, char* sig) { + return _getId((*jniEnv)->GetStaticFieldID, cls, name, sig); +} + +JniResult callMethod(jobject obj, + jmethodID fieldID, + int callType, + jvalue* args) { + attach_thread(); + jvalue result = {.j = 0}; + switch (callType) { + case booleanType: + result.z = (*jniEnv)->CallBooleanMethodA(jniEnv, obj, fieldID, args); + break; + case byteType: + result.b = (*jniEnv)->CallByteMethodA(jniEnv, obj, fieldID, args); + break; + case shortType: + result.s = (*jniEnv)->CallShortMethodA(jniEnv, obj, fieldID, args); + break; + case charType: + result.c = (*jniEnv)->CallCharMethodA(jniEnv, obj, fieldID, args); + break; + case intType: + result.i = (*jniEnv)->CallIntMethodA(jniEnv, obj, fieldID, args); + break; + case longType: + result.j = (*jniEnv)->CallLongMethodA(jniEnv, obj, fieldID, args); + break; + case floatType: + result.f = (*jniEnv)->CallFloatMethodA(jniEnv, obj, fieldID, args); + break; + case doubleType: + result.d = (*jniEnv)->CallDoubleMethodA(jniEnv, obj, fieldID, args); + break; + case objectType: + result.l = to_global_ref( + (*jniEnv)->CallObjectMethodA(jniEnv, obj, fieldID, args)); + break; + case voidType: + (*jniEnv)->CallVoidMethodA(jniEnv, obj, fieldID, args); + break; + } + JniResult jniResult = {.result = result, .exception = NULL}; + jniResult.exception = exceptionCheck(); + return jniResult; +} + +// TODO(#60): Any way to reduce this boilerplate? +JniResult callStaticMethod(jclass cls, + jmethodID methodID, + int callType, + jvalue* args) { + attach_thread(); + jvalue result = {.j = 0}; + switch (callType) { + case booleanType: + result.z = + (*jniEnv)->CallStaticBooleanMethodA(jniEnv, cls, methodID, args); + break; + case byteType: + result.b = (*jniEnv)->CallStaticByteMethodA(jniEnv, cls, methodID, args); + break; + case shortType: + result.s = (*jniEnv)->CallStaticShortMethodA(jniEnv, cls, methodID, args); + break; + case charType: + result.c = (*jniEnv)->CallStaticCharMethodA(jniEnv, cls, methodID, args); + break; + case intType: + result.i = (*jniEnv)->CallStaticIntMethodA(jniEnv, cls, methodID, args); + break; + case longType: + result.j = (*jniEnv)->CallStaticLongMethodA(jniEnv, cls, methodID, args); + break; + case floatType: + result.f = (*jniEnv)->CallStaticFloatMethodA(jniEnv, cls, methodID, args); + break; + case doubleType: + result.d = + (*jniEnv)->CallStaticDoubleMethodA(jniEnv, cls, methodID, args); + break; + case objectType: + result.l = to_global_ref( + (*jniEnv)->CallStaticObjectMethodA(jniEnv, cls, methodID, args)); + break; + case voidType: + (*jniEnv)->CallStaticVoidMethodA(jniEnv, cls, methodID, args); + break; + } + JniResult jniResult = {.result = result, .exception = NULL}; + jniResult.exception = exceptionCheck(); + return jniResult; +} + +JniResult getField(jobject obj, jfieldID fieldID, int callType) { + attach_thread(); + jvalue result = {.j = 0}; + switch (callType) { + case booleanType: + result.z = (*jniEnv)->GetBooleanField(jniEnv, obj, fieldID); + break; + case byteType: + result.b = (*jniEnv)->GetByteField(jniEnv, obj, fieldID); + break; + case shortType: + result.s = (*jniEnv)->GetShortField(jniEnv, obj, fieldID); + break; + case charType: + result.c = (*jniEnv)->GetCharField(jniEnv, obj, fieldID); + break; + case intType: + result.i = (*jniEnv)->GetIntField(jniEnv, obj, fieldID); + break; + case longType: + result.j = (*jniEnv)->GetLongField(jniEnv, obj, fieldID); + break; + case floatType: + result.f = (*jniEnv)->GetFloatField(jniEnv, obj, fieldID); + break; + case doubleType: + result.d = (*jniEnv)->GetDoubleField(jniEnv, obj, fieldID); + break; + case objectType: + result.l = to_global_ref((*jniEnv)->GetObjectField(jniEnv, obj, fieldID)); + break; + case voidType: + // This error should have been handled in Dart. + break; + } + JniResult jniResult = {.result = result, .exception = NULL}; + jniResult.exception = exceptionCheck(); + return jniResult; +} + +FFI_PLUGIN_EXPORT +JniResult getStaticField(jclass cls, jfieldID fieldID, int callType) { + attach_thread(); + jvalue result = {.j = 0}; + switch (callType) { + case booleanType: + result.z = (*jniEnv)->GetStaticBooleanField(jniEnv, cls, fieldID); + break; + case byteType: + result.b = (*jniEnv)->GetStaticByteField(jniEnv, cls, fieldID); + break; + case shortType: + result.s = (*jniEnv)->GetStaticShortField(jniEnv, cls, fieldID); + break; + case charType: + result.c = (*jniEnv)->GetStaticCharField(jniEnv, cls, fieldID); + break; + case intType: + result.i = (*jniEnv)->GetStaticIntField(jniEnv, cls, fieldID); + break; + case longType: + result.j = (*jniEnv)->GetStaticLongField(jniEnv, cls, fieldID); + break; + case floatType: + result.f = (*jniEnv)->GetStaticFloatField(jniEnv, cls, fieldID); + break; + case doubleType: + result.d = (*jniEnv)->GetStaticDoubleField(jniEnv, cls, fieldID); + break; + case objectType: + result.l = + to_global_ref((*jniEnv)->GetStaticObjectField(jniEnv, cls, fieldID)); + break; + case voidType: + // This error should have been handled in dart. + // is there a way to mark this as unreachable? + // or throw exception in Dart using Dart's C API. + break; + } + JniResult jniResult = {.result = result, .exception = NULL}; + jniResult.exception = exceptionCheck(); + return jniResult; +} + +JniResult newObject(jclass cls, jmethodID ctor, jvalue* args) { + attach_thread(); + JniResult jniResult; + jniResult.result.l = + to_global_ref((*jniEnv)->NewObjectA(jniEnv, cls, ctor, args)); + jniResult.exception = exceptionCheck(); + return jniResult; +} + +JniAccessors accessors = { + .getClass = getClass, + .getFieldID = getFieldID, + .getStaticFieldID = getStaticFieldID, + .getMethodID = getMethodID, + .getStaticMethodID = getStaticMethodID, + .newObject = newObject, + .callMethod = callMethod, + .callStaticMethod = callStaticMethod, + .getField = getField, + .getStaticField = getStaticField, +}; + +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors() { + return &accessors; +} + +// These will not be required after migrating to Dart-only bindings. +FFI_PLUGIN_EXPORT JniContext GetJniContext() { + return jni; +} + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv() { + if (jni.jvm == NULL) { + return NULL; + } + attach_thread(); + return jniEnv; +}
diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jni/src/dartjni.h +++ b/pkgs/jni/src/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jni/src/global_jni_env.c b/pkgs/jni/src/global_jni_env.c deleted file mode 100644 index d7da0af..0000000 --- a/pkgs/jni/src/global_jni_env.c +++ /dev/null
@@ -1,1188 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "global_jni_env.h" -jint globalEnv_GetVersion() { - attach_thread(); - return (*jniEnv)->GetVersion(jniEnv); -} - -jclass globalEnv_DefineClass(const char * name, jobject loader, const jbyte * buf, jsize bufLen) { - attach_thread(); - return to_global_ref((*jniEnv)->DefineClass(jniEnv, name, loader, buf, bufLen)); -} - -jclass globalEnv_FindClass(const char * name) { - attach_thread(); - return to_global_ref((*jniEnv)->FindClass(jniEnv, name)); -} - -jmethodID globalEnv_FromReflectedMethod(jobject method) { - attach_thread(); - return (*jniEnv)->FromReflectedMethod(jniEnv, method); -} - -jfieldID globalEnv_FromReflectedField(jobject field) { - attach_thread(); - return (*jniEnv)->FromReflectedField(jniEnv, field); -} - -jobject globalEnv_ToReflectedMethod(jclass cls, jmethodID methodId, jboolean isStatic) { - attach_thread(); - return to_global_ref((*jniEnv)->ToReflectedMethod(jniEnv, cls, methodId, isStatic)); -} - -jclass globalEnv_GetSuperclass(jclass clazz) { - attach_thread(); - return to_global_ref((*jniEnv)->GetSuperclass(jniEnv, clazz)); -} - -jboolean globalEnv_IsAssignableFrom(jclass clazz1, jclass clazz2) { - attach_thread(); - return (*jniEnv)->IsAssignableFrom(jniEnv, clazz1, clazz2); -} - -jobject globalEnv_ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { - attach_thread(); - return to_global_ref((*jniEnv)->ToReflectedField(jniEnv, cls, fieldID, isStatic)); -} - -jint globalEnv_Throw(jthrowable obj) { - attach_thread(); - return (*jniEnv)->Throw(jniEnv, obj); -} - -jint globalEnv_ThrowNew(jclass clazz, const char * message) { - attach_thread(); - return (*jniEnv)->ThrowNew(jniEnv, clazz, message); -} - -jthrowable globalEnv_ExceptionOccurred() { - attach_thread(); - return to_global_ref((*jniEnv)->ExceptionOccurred(jniEnv)); -} - -void globalEnv_ExceptionDescribe() { - attach_thread(); - (*jniEnv)->ExceptionDescribe(jniEnv); -} - -void globalEnv_ExceptionClear() { - attach_thread(); - (*jniEnv)->ExceptionClear(jniEnv); -} - -void globalEnv_FatalError(const char * msg) { - attach_thread(); - (*jniEnv)->FatalError(jniEnv, msg); -} - -jint globalEnv_PushLocalFrame(jint capacity) { - attach_thread(); - return (*jniEnv)->PushLocalFrame(jniEnv, capacity); -} - -jobject globalEnv_PopLocalFrame(jobject result) { - attach_thread(); - return to_global_ref((*jniEnv)->PopLocalFrame(jniEnv, result)); -} - -jobject globalEnv_NewGlobalRef(jobject obj) { - attach_thread(); - return to_global_ref((*jniEnv)->NewGlobalRef(jniEnv, obj)); -} - -void globalEnv_DeleteGlobalRef(jobject globalRef) { - attach_thread(); - (*jniEnv)->DeleteGlobalRef(jniEnv, globalRef); -} - -jboolean globalEnv_IsSameObject(jobject ref1, jobject ref2) { - attach_thread(); - return (*jniEnv)->IsSameObject(jniEnv, ref1, ref2); -} - -jint globalEnv_EnsureLocalCapacity(jint capacity) { - attach_thread(); - return (*jniEnv)->EnsureLocalCapacity(jniEnv, capacity); -} - -jobject globalEnv_AllocObject(jclass clazz) { - attach_thread(); - return to_global_ref((*jniEnv)->AllocObject(jniEnv, clazz)); -} - -jobject globalEnv_NewObject(jclass arg1, jmethodID arg2) { - attach_thread(); - return to_global_ref((*jniEnv)->NewObject(jniEnv, arg1, arg2)); -} - -jobject globalEnv_NewObjectA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return to_global_ref((*jniEnv)->NewObjectA(jniEnv, clazz, methodID, args)); -} - -jclass globalEnv_GetObjectClass(jobject obj) { - attach_thread(); - return to_global_ref((*jniEnv)->GetObjectClass(jniEnv, obj)); -} - -jboolean globalEnv_IsInstanceOf(jobject obj, jclass clazz) { - attach_thread(); - return (*jniEnv)->IsInstanceOf(jniEnv, obj, clazz); -} - -jmethodID globalEnv_GetMethodID(jclass clazz, const char * name, const char * sig) { - attach_thread(); - return (*jniEnv)->GetMethodID(jniEnv, clazz, name, sig); -} - -jobject globalEnv_CallObjectMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return to_global_ref((*jniEnv)->CallObjectMethod(jniEnv, arg1, arg2)); -} - -jobject globalEnv_CallObjectMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return to_global_ref((*jniEnv)->CallObjectMethodA(jniEnv, obj, methodID, args)); -} - -jboolean globalEnv_CallBooleanMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallBooleanMethod(jniEnv, arg1, arg2); -} - -jboolean globalEnv_CallBooleanMethodA(jobject obj, jmethodID methodId, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallBooleanMethodA(jniEnv, obj, methodId, args); -} - -jbyte globalEnv_CallByteMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallByteMethod(jniEnv, arg1, arg2); -} - -jbyte globalEnv_CallByteMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallByteMethodA(jniEnv, obj, methodID, args); -} - -jchar globalEnv_CallCharMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallCharMethod(jniEnv, arg1, arg2); -} - -jchar globalEnv_CallCharMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallCharMethodA(jniEnv, obj, methodID, args); -} - -jshort globalEnv_CallShortMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallShortMethod(jniEnv, arg1, arg2); -} - -jshort globalEnv_CallShortMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallShortMethodA(jniEnv, obj, methodID, args); -} - -jint globalEnv_CallIntMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallIntMethod(jniEnv, arg1, arg2); -} - -jint globalEnv_CallIntMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallIntMethodA(jniEnv, obj, methodID, args); -} - -jlong globalEnv_CallLongMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallLongMethod(jniEnv, arg1, arg2); -} - -jlong globalEnv_CallLongMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallLongMethodA(jniEnv, obj, methodID, args); -} - -jfloat globalEnv_CallFloatMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallFloatMethod(jniEnv, arg1, arg2); -} - -jfloat globalEnv_CallFloatMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallFloatMethodA(jniEnv, obj, methodID, args); -} - -jdouble globalEnv_CallDoubleMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallDoubleMethod(jniEnv, arg1, arg2); -} - -jdouble globalEnv_CallDoubleMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallDoubleMethodA(jniEnv, obj, methodID, args); -} - -void globalEnv_CallVoidMethod(jobject arg1, jmethodID arg2) { - attach_thread(); - (*jniEnv)->CallVoidMethod(jniEnv, arg1, arg2); -} - -void globalEnv_CallVoidMethodA(jobject obj, jmethodID methodID, const jvalue * args) { - attach_thread(); - (*jniEnv)->CallVoidMethodA(jniEnv, obj, methodID, args); -} - -jobject globalEnv_CallNonvirtualObjectMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return to_global_ref((*jniEnv)->CallNonvirtualObjectMethod(jniEnv, arg1, arg2, arg3)); -} - -jobject globalEnv_CallNonvirtualObjectMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return to_global_ref((*jniEnv)->CallNonvirtualObjectMethodA(jniEnv, obj, clazz, methodID, args)); -} - -jboolean globalEnv_CallNonvirtualBooleanMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualBooleanMethod(jniEnv, arg1, arg2, arg3); -} - -jboolean globalEnv_CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualBooleanMethodA(jniEnv, obj, clazz, methodID, args); -} - -jbyte globalEnv_CallNonvirtualByteMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualByteMethod(jniEnv, arg1, arg2, arg3); -} - -jbyte globalEnv_CallNonvirtualByteMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualByteMethodA(jniEnv, obj, clazz, methodID, args); -} - -jchar globalEnv_CallNonvirtualCharMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualCharMethod(jniEnv, arg1, arg2, arg3); -} - -jchar globalEnv_CallNonvirtualCharMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualCharMethodA(jniEnv, obj, clazz, methodID, args); -} - -jshort globalEnv_CallNonvirtualShortMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualShortMethod(jniEnv, arg1, arg2, arg3); -} - -jshort globalEnv_CallNonvirtualShortMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualShortMethodA(jniEnv, obj, clazz, methodID, args); -} - -jint globalEnv_CallNonvirtualIntMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualIntMethod(jniEnv, arg1, arg2, arg3); -} - -jint globalEnv_CallNonvirtualIntMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualIntMethodA(jniEnv, obj, clazz, methodID, args); -} - -jlong globalEnv_CallNonvirtualLongMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualLongMethod(jniEnv, arg1, arg2, arg3); -} - -jlong globalEnv_CallNonvirtualLongMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualLongMethodA(jniEnv, obj, clazz, methodID, args); -} - -jfloat globalEnv_CallNonvirtualFloatMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualFloatMethod(jniEnv, arg1, arg2, arg3); -} - -jfloat globalEnv_CallNonvirtualFloatMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualFloatMethodA(jniEnv, obj, clazz, methodID, args); -} - -jdouble globalEnv_CallNonvirtualDoubleMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - return (*jniEnv)->CallNonvirtualDoubleMethod(jniEnv, arg1, arg2, arg3); -} - -jdouble globalEnv_CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallNonvirtualDoubleMethodA(jniEnv, obj, clazz, methodID, args); -} - -void globalEnv_CallNonvirtualVoidMethod(jobject arg1, jclass arg2, jmethodID arg3) { - attach_thread(); - (*jniEnv)->CallNonvirtualVoidMethod(jniEnv, arg1, arg2, arg3); -} - -void globalEnv_CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - (*jniEnv)->CallNonvirtualVoidMethodA(jniEnv, obj, clazz, methodID, args); -} - -jfieldID globalEnv_GetFieldID(jclass clazz, const char * name, const char * sig) { - attach_thread(); - return (*jniEnv)->GetFieldID(jniEnv, clazz, name, sig); -} - -jobject globalEnv_GetObjectField(jobject obj, jfieldID fieldID) { - attach_thread(); - return to_global_ref((*jniEnv)->GetObjectField(jniEnv, obj, fieldID)); -} - -jboolean globalEnv_GetBooleanField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetBooleanField(jniEnv, obj, fieldID); -} - -jbyte globalEnv_GetByteField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetByteField(jniEnv, obj, fieldID); -} - -jchar globalEnv_GetCharField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetCharField(jniEnv, obj, fieldID); -} - -jshort globalEnv_GetShortField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetShortField(jniEnv, obj, fieldID); -} - -jint globalEnv_GetIntField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetIntField(jniEnv, obj, fieldID); -} - -jlong globalEnv_GetLongField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetLongField(jniEnv, obj, fieldID); -} - -jfloat globalEnv_GetFloatField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetFloatField(jniEnv, obj, fieldID); -} - -jdouble globalEnv_GetDoubleField(jobject obj, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetDoubleField(jniEnv, obj, fieldID); -} - -void globalEnv_SetObjectField(jobject obj, jfieldID fieldID, jobject val) { - attach_thread(); - (*jniEnv)->SetObjectField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetBooleanField(jobject obj, jfieldID fieldID, jboolean val) { - attach_thread(); - (*jniEnv)->SetBooleanField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetByteField(jobject obj, jfieldID fieldID, jbyte val) { - attach_thread(); - (*jniEnv)->SetByteField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetCharField(jobject obj, jfieldID fieldID, jchar val) { - attach_thread(); - (*jniEnv)->SetCharField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetShortField(jobject obj, jfieldID fieldID, jshort val) { - attach_thread(); - (*jniEnv)->SetShortField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetIntField(jobject obj, jfieldID fieldID, jint val) { - attach_thread(); - (*jniEnv)->SetIntField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetLongField(jobject obj, jfieldID fieldID, jlong val) { - attach_thread(); - (*jniEnv)->SetLongField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetFloatField(jobject obj, jfieldID fieldID, jfloat val) { - attach_thread(); - (*jniEnv)->SetFloatField(jniEnv, obj, fieldID, val); -} - -void globalEnv_SetDoubleField(jobject obj, jfieldID fieldID, jdouble val) { - attach_thread(); - (*jniEnv)->SetDoubleField(jniEnv, obj, fieldID, val); -} - -jmethodID globalEnv_GetStaticMethodID(jclass clazz, const char * name, const char * sig) { - attach_thread(); - return (*jniEnv)->GetStaticMethodID(jniEnv, clazz, name, sig); -} - -jobject globalEnv_CallStaticObjectMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return to_global_ref((*jniEnv)->CallStaticObjectMethod(jniEnv, arg1, arg2)); -} - -jobject globalEnv_CallStaticObjectMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return to_global_ref((*jniEnv)->CallStaticObjectMethodA(jniEnv, clazz, methodID, args)); -} - -jboolean globalEnv_CallStaticBooleanMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticBooleanMethod(jniEnv, arg1, arg2); -} - -jboolean globalEnv_CallStaticBooleanMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticBooleanMethodA(jniEnv, clazz, methodID, args); -} - -jbyte globalEnv_CallStaticByteMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticByteMethod(jniEnv, arg1, arg2); -} - -jbyte globalEnv_CallStaticByteMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticByteMethodA(jniEnv, clazz, methodID, args); -} - -jchar globalEnv_CallStaticCharMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticCharMethod(jniEnv, arg1, arg2); -} - -jchar globalEnv_CallStaticCharMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticCharMethodA(jniEnv, clazz, methodID, args); -} - -jshort globalEnv_CallStaticShortMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticShortMethod(jniEnv, arg1, arg2); -} - -jshort globalEnv_CallStaticShortMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticShortMethodA(jniEnv, clazz, methodID, args); -} - -jint globalEnv_CallStaticIntMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticIntMethod(jniEnv, arg1, arg2); -} - -jint globalEnv_CallStaticIntMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticIntMethodA(jniEnv, clazz, methodID, args); -} - -jlong globalEnv_CallStaticLongMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticLongMethod(jniEnv, arg1, arg2); -} - -jlong globalEnv_CallStaticLongMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticLongMethodA(jniEnv, clazz, methodID, args); -} - -jfloat globalEnv_CallStaticFloatMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticFloatMethod(jniEnv, arg1, arg2); -} - -jfloat globalEnv_CallStaticFloatMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticFloatMethodA(jniEnv, clazz, methodID, args); -} - -jdouble globalEnv_CallStaticDoubleMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - return (*jniEnv)->CallStaticDoubleMethod(jniEnv, arg1, arg2); -} - -jdouble globalEnv_CallStaticDoubleMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - return (*jniEnv)->CallStaticDoubleMethodA(jniEnv, clazz, methodID, args); -} - -void globalEnv_CallStaticVoidMethod(jclass arg1, jmethodID arg2) { - attach_thread(); - (*jniEnv)->CallStaticVoidMethod(jniEnv, arg1, arg2); -} - -void globalEnv_CallStaticVoidMethodA(jclass clazz, jmethodID methodID, const jvalue * args) { - attach_thread(); - (*jniEnv)->CallStaticVoidMethodA(jniEnv, clazz, methodID, args); -} - -jfieldID globalEnv_GetStaticFieldID(jclass clazz, const char * name, const char * sig) { - attach_thread(); - return (*jniEnv)->GetStaticFieldID(jniEnv, clazz, name, sig); -} - -jobject globalEnv_GetStaticObjectField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return to_global_ref((*jniEnv)->GetStaticObjectField(jniEnv, clazz, fieldID)); -} - -jboolean globalEnv_GetStaticBooleanField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticBooleanField(jniEnv, clazz, fieldID); -} - -jbyte globalEnv_GetStaticByteField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticByteField(jniEnv, clazz, fieldID); -} - -jchar globalEnv_GetStaticCharField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticCharField(jniEnv, clazz, fieldID); -} - -jshort globalEnv_GetStaticShortField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticShortField(jniEnv, clazz, fieldID); -} - -jint globalEnv_GetStaticIntField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticIntField(jniEnv, clazz, fieldID); -} - -jlong globalEnv_GetStaticLongField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticLongField(jniEnv, clazz, fieldID); -} - -jfloat globalEnv_GetStaticFloatField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticFloatField(jniEnv, clazz, fieldID); -} - -jdouble globalEnv_GetStaticDoubleField(jclass clazz, jfieldID fieldID) { - attach_thread(); - return (*jniEnv)->GetStaticDoubleField(jniEnv, clazz, fieldID); -} - -void globalEnv_SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject val) { - attach_thread(); - (*jniEnv)->SetStaticObjectField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean val) { - attach_thread(); - (*jniEnv)->SetStaticBooleanField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte val) { - attach_thread(); - (*jniEnv)->SetStaticByteField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticCharField(jclass clazz, jfieldID fieldID, jchar val) { - attach_thread(); - (*jniEnv)->SetStaticCharField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticShortField(jclass clazz, jfieldID fieldID, jshort val) { - attach_thread(); - (*jniEnv)->SetStaticShortField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticIntField(jclass clazz, jfieldID fieldID, jint val) { - attach_thread(); - (*jniEnv)->SetStaticIntField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticLongField(jclass clazz, jfieldID fieldID, jlong val) { - attach_thread(); - (*jniEnv)->SetStaticLongField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat val) { - attach_thread(); - (*jniEnv)->SetStaticFloatField(jniEnv, clazz, fieldID, val); -} - -void globalEnv_SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble val) { - attach_thread(); - (*jniEnv)->SetStaticDoubleField(jniEnv, clazz, fieldID, val); -} - -jstring globalEnv_NewString(const jchar * unicodeChars, jsize len) { - attach_thread(); - return to_global_ref((*jniEnv)->NewString(jniEnv, unicodeChars, len)); -} - -jsize globalEnv_GetStringLength(jstring string) { - attach_thread(); - return (*jniEnv)->GetStringLength(jniEnv, string); -} - -const jchar * globalEnv_GetStringChars(jstring string, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetStringChars(jniEnv, string, isCopy); -} - -void globalEnv_ReleaseStringChars(jstring string, const jchar * isCopy) { - attach_thread(); - (*jniEnv)->ReleaseStringChars(jniEnv, string, isCopy); -} - -jstring globalEnv_NewStringUTF(const char * bytes) { - attach_thread(); - return to_global_ref((*jniEnv)->NewStringUTF(jniEnv, bytes)); -} - -jsize globalEnv_GetStringUTFLength(jstring string) { - attach_thread(); - return (*jniEnv)->GetStringUTFLength(jniEnv, string); -} - -const char * globalEnv_GetStringUTFChars(jstring string, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetStringUTFChars(jniEnv, string, isCopy); -} - -void globalEnv_ReleaseStringUTFChars(jstring string, const char * utf) { - attach_thread(); - (*jniEnv)->ReleaseStringUTFChars(jniEnv, string, utf); -} - -jsize globalEnv_GetArrayLength(jarray array) { - attach_thread(); - return (*jniEnv)->GetArrayLength(jniEnv, array); -} - -jobjectArray globalEnv_NewObjectArray(jsize length, jclass elementClass, jobject initialElement) { - attach_thread(); - return to_global_ref((*jniEnv)->NewObjectArray(jniEnv, length, elementClass, initialElement)); -} - -jobject globalEnv_GetObjectArrayElement(jobjectArray array, jsize index) { - attach_thread(); - return to_global_ref((*jniEnv)->GetObjectArrayElement(jniEnv, array, index)); -} - -void globalEnv_SetObjectArrayElement(jobjectArray array, jsize index, jobject val) { - attach_thread(); - (*jniEnv)->SetObjectArrayElement(jniEnv, array, index, val); -} - -jbooleanArray globalEnv_NewBooleanArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewBooleanArray(jniEnv, length)); -} - -jbyteArray globalEnv_NewByteArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewByteArray(jniEnv, length)); -} - -jcharArray globalEnv_NewCharArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewCharArray(jniEnv, length)); -} - -jshortArray globalEnv_NewShortArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewShortArray(jniEnv, length)); -} - -jintArray globalEnv_NewIntArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewIntArray(jniEnv, length)); -} - -jlongArray globalEnv_NewLongArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewLongArray(jniEnv, length)); -} - -jfloatArray globalEnv_NewFloatArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewFloatArray(jniEnv, length)); -} - -jdoubleArray globalEnv_NewDoubleArray(jsize length) { - attach_thread(); - return to_global_ref((*jniEnv)->NewDoubleArray(jniEnv, length)); -} - -jboolean * globalEnv_GetBooleanArrayElements(jbooleanArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetBooleanArrayElements(jniEnv, array, isCopy); -} - -jbyte * globalEnv_GetByteArrayElements(jbyteArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetByteArrayElements(jniEnv, array, isCopy); -} - -jchar * globalEnv_GetCharArrayElements(jcharArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetCharArrayElements(jniEnv, array, isCopy); -} - -jshort * globalEnv_GetShortArrayElements(jshortArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetShortArrayElements(jniEnv, array, isCopy); -} - -jint * globalEnv_GetIntArrayElements(jintArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetIntArrayElements(jniEnv, array, isCopy); -} - -jlong * globalEnv_GetLongArrayElements(jlongArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetLongArrayElements(jniEnv, array, isCopy); -} - -jfloat * globalEnv_GetFloatArrayElements(jfloatArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetFloatArrayElements(jniEnv, array, isCopy); -} - -jdouble * globalEnv_GetDoubleArrayElements(jdoubleArray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetDoubleArrayElements(jniEnv, array, isCopy); -} - -void globalEnv_ReleaseBooleanArrayElements(jbooleanArray array, jboolean * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseBooleanArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseByteArrayElements(jbyteArray array, jbyte * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseByteArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseCharArrayElements(jcharArray array, jchar * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseCharArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseShortArrayElements(jshortArray array, jshort * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseShortArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseIntArrayElements(jintArray array, jint * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseIntArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseLongArrayElements(jlongArray array, jlong * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseLongArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseFloatArrayElements(jfloatArray array, jfloat * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseFloatArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_ReleaseDoubleArrayElements(jdoubleArray array, jdouble * elems, jint mode) { - attach_thread(); - (*jniEnv)->ReleaseDoubleArrayElements(jniEnv, array, elems, mode); -} - -void globalEnv_GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean * buf) { - attach_thread(); - (*jniEnv)->GetBooleanArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte * buf) { - attach_thread(); - (*jniEnv)->GetByteArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar * buf) { - attach_thread(); - (*jniEnv)->GetCharArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort * buf) { - attach_thread(); - (*jniEnv)->GetShortArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetIntArrayRegion(jintArray array, jsize start, jsize len, jint * buf) { - attach_thread(); - (*jniEnv)->GetIntArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong * buf) { - attach_thread(); - (*jniEnv)->GetLongArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat * buf) { - attach_thread(); - (*jniEnv)->GetFloatArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble * buf) { - attach_thread(); - (*jniEnv)->GetDoubleArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean * buf) { - attach_thread(); - (*jniEnv)->SetBooleanArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte * buf) { - attach_thread(); - (*jniEnv)->SetByteArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar * buf) { - attach_thread(); - (*jniEnv)->SetCharArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort * buf) { - attach_thread(); - (*jniEnv)->SetShortArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint * buf) { - attach_thread(); - (*jniEnv)->SetIntArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong * buf) { - attach_thread(); - (*jniEnv)->SetLongArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat * buf) { - attach_thread(); - (*jniEnv)->SetFloatArrayRegion(jniEnv, array, start, len, buf); -} - -void globalEnv_SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble * buf) { - attach_thread(); - (*jniEnv)->SetDoubleArrayRegion(jniEnv, array, start, len, buf); -} - -jint globalEnv_RegisterNatives(jclass clazz, const JNINativeMethod * methods, jint nMethods) { - attach_thread(); - return (*jniEnv)->RegisterNatives(jniEnv, clazz, methods, nMethods); -} - -jint globalEnv_UnregisterNatives(jclass clazz) { - attach_thread(); - return (*jniEnv)->UnregisterNatives(jniEnv, clazz); -} - -jint globalEnv_MonitorEnter(jobject obj) { - attach_thread(); - return (*jniEnv)->MonitorEnter(jniEnv, obj); -} - -jint globalEnv_MonitorExit(jobject obj) { - attach_thread(); - return (*jniEnv)->MonitorExit(jniEnv, obj); -} - -jint globalEnv_GetJavaVM(JavaVM ** vm) { - attach_thread(); - return (*jniEnv)->GetJavaVM(jniEnv, vm); -} - -void globalEnv_GetStringRegion(jstring str, jsize start, jsize len, jchar * buf) { - attach_thread(); - (*jniEnv)->GetStringRegion(jniEnv, str, start, len, buf); -} - -void globalEnv_GetStringUTFRegion(jstring str, jsize start, jsize len, char * buf) { - attach_thread(); - (*jniEnv)->GetStringUTFRegion(jniEnv, str, start, len, buf); -} - -void * globalEnv_GetPrimitiveArrayCritical(jarray array, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetPrimitiveArrayCritical(jniEnv, array, isCopy); -} - -void globalEnv_ReleasePrimitiveArrayCritical(jarray array, void * carray, jint mode) { - attach_thread(); - (*jniEnv)->ReleasePrimitiveArrayCritical(jniEnv, array, carray, mode); -} - -const jchar * globalEnv_GetStringCritical(jstring str, jboolean * isCopy) { - attach_thread(); - return (*jniEnv)->GetStringCritical(jniEnv, str, isCopy); -} - -void globalEnv_ReleaseStringCritical(jstring str, const jchar * carray) { - attach_thread(); - (*jniEnv)->ReleaseStringCritical(jniEnv, str, carray); -} - -jweak globalEnv_NewWeakGlobalRef(jobject obj) { - attach_thread(); - return to_global_ref((*jniEnv)->NewWeakGlobalRef(jniEnv, obj)); -} - -void globalEnv_DeleteWeakGlobalRef(jweak obj) { - attach_thread(); - (*jniEnv)->DeleteWeakGlobalRef(jniEnv, obj); -} - -jboolean globalEnv_ExceptionCheck() { - attach_thread(); - return (*jniEnv)->ExceptionCheck(jniEnv); -} - -jobject globalEnv_NewDirectByteBuffer(void * address, jlong capacity) { - attach_thread(); - return to_global_ref((*jniEnv)->NewDirectByteBuffer(jniEnv, address, capacity)); -} - -void * globalEnv_GetDirectBufferAddress(jobject buf) { - attach_thread(); - return (*jniEnv)->GetDirectBufferAddress(jniEnv, buf); -} - -jlong globalEnv_GetDirectBufferCapacity(jobject buf) { - attach_thread(); - return (*jniEnv)->GetDirectBufferCapacity(jniEnv, buf); -} - -jobjectRefType globalEnv_GetObjectRefType(jobject obj) { - attach_thread(); - return (*jniEnv)->GetObjectRefType(jniEnv, obj); -} - -struct GlobalJniEnv globalEnv = { - .GetVersion = globalEnv_GetVersion, - .DefineClass = globalEnv_DefineClass, - .FindClass = globalEnv_FindClass, - .FromReflectedMethod = globalEnv_FromReflectedMethod, - .FromReflectedField = globalEnv_FromReflectedField, - .ToReflectedMethod = globalEnv_ToReflectedMethod, - .GetSuperclass = globalEnv_GetSuperclass, - .IsAssignableFrom = globalEnv_IsAssignableFrom, - .ToReflectedField = globalEnv_ToReflectedField, - .Throw = globalEnv_Throw, - .ThrowNew = globalEnv_ThrowNew, - .ExceptionOccurred = globalEnv_ExceptionOccurred, - .ExceptionDescribe = globalEnv_ExceptionDescribe, - .ExceptionClear = globalEnv_ExceptionClear, - .FatalError = globalEnv_FatalError, - .PushLocalFrame = globalEnv_PushLocalFrame, - .PopLocalFrame = globalEnv_PopLocalFrame, - .NewGlobalRef = globalEnv_NewGlobalRef, - .DeleteGlobalRef = globalEnv_DeleteGlobalRef, - .IsSameObject = globalEnv_IsSameObject, - .EnsureLocalCapacity = globalEnv_EnsureLocalCapacity, - .AllocObject = globalEnv_AllocObject, - .NewObject = globalEnv_NewObject, - .NewObjectA = globalEnv_NewObjectA, - .GetObjectClass = globalEnv_GetObjectClass, - .IsInstanceOf = globalEnv_IsInstanceOf, - .GetMethodID = globalEnv_GetMethodID, - .CallObjectMethod = globalEnv_CallObjectMethod, - .CallObjectMethodA = globalEnv_CallObjectMethodA, - .CallBooleanMethod = globalEnv_CallBooleanMethod, - .CallBooleanMethodA = globalEnv_CallBooleanMethodA, - .CallByteMethod = globalEnv_CallByteMethod, - .CallByteMethodA = globalEnv_CallByteMethodA, - .CallCharMethod = globalEnv_CallCharMethod, - .CallCharMethodA = globalEnv_CallCharMethodA, - .CallShortMethod = globalEnv_CallShortMethod, - .CallShortMethodA = globalEnv_CallShortMethodA, - .CallIntMethod = globalEnv_CallIntMethod, - .CallIntMethodA = globalEnv_CallIntMethodA, - .CallLongMethod = globalEnv_CallLongMethod, - .CallLongMethodA = globalEnv_CallLongMethodA, - .CallFloatMethod = globalEnv_CallFloatMethod, - .CallFloatMethodA = globalEnv_CallFloatMethodA, - .CallDoubleMethod = globalEnv_CallDoubleMethod, - .CallDoubleMethodA = globalEnv_CallDoubleMethodA, - .CallVoidMethod = globalEnv_CallVoidMethod, - .CallVoidMethodA = globalEnv_CallVoidMethodA, - .CallNonvirtualObjectMethod = globalEnv_CallNonvirtualObjectMethod, - .CallNonvirtualObjectMethodA = globalEnv_CallNonvirtualObjectMethodA, - .CallNonvirtualBooleanMethod = globalEnv_CallNonvirtualBooleanMethod, - .CallNonvirtualBooleanMethodA = globalEnv_CallNonvirtualBooleanMethodA, - .CallNonvirtualByteMethod = globalEnv_CallNonvirtualByteMethod, - .CallNonvirtualByteMethodA = globalEnv_CallNonvirtualByteMethodA, - .CallNonvirtualCharMethod = globalEnv_CallNonvirtualCharMethod, - .CallNonvirtualCharMethodA = globalEnv_CallNonvirtualCharMethodA, - .CallNonvirtualShortMethod = globalEnv_CallNonvirtualShortMethod, - .CallNonvirtualShortMethodA = globalEnv_CallNonvirtualShortMethodA, - .CallNonvirtualIntMethod = globalEnv_CallNonvirtualIntMethod, - .CallNonvirtualIntMethodA = globalEnv_CallNonvirtualIntMethodA, - .CallNonvirtualLongMethod = globalEnv_CallNonvirtualLongMethod, - .CallNonvirtualLongMethodA = globalEnv_CallNonvirtualLongMethodA, - .CallNonvirtualFloatMethod = globalEnv_CallNonvirtualFloatMethod, - .CallNonvirtualFloatMethodA = globalEnv_CallNonvirtualFloatMethodA, - .CallNonvirtualDoubleMethod = globalEnv_CallNonvirtualDoubleMethod, - .CallNonvirtualDoubleMethodA = globalEnv_CallNonvirtualDoubleMethodA, - .CallNonvirtualVoidMethod = globalEnv_CallNonvirtualVoidMethod, - .CallNonvirtualVoidMethodA = globalEnv_CallNonvirtualVoidMethodA, - .GetFieldID = globalEnv_GetFieldID, - .GetObjectField = globalEnv_GetObjectField, - .GetBooleanField = globalEnv_GetBooleanField, - .GetByteField = globalEnv_GetByteField, - .GetCharField = globalEnv_GetCharField, - .GetShortField = globalEnv_GetShortField, - .GetIntField = globalEnv_GetIntField, - .GetLongField = globalEnv_GetLongField, - .GetFloatField = globalEnv_GetFloatField, - .GetDoubleField = globalEnv_GetDoubleField, - .SetObjectField = globalEnv_SetObjectField, - .SetBooleanField = globalEnv_SetBooleanField, - .SetByteField = globalEnv_SetByteField, - .SetCharField = globalEnv_SetCharField, - .SetShortField = globalEnv_SetShortField, - .SetIntField = globalEnv_SetIntField, - .SetLongField = globalEnv_SetLongField, - .SetFloatField = globalEnv_SetFloatField, - .SetDoubleField = globalEnv_SetDoubleField, - .GetStaticMethodID = globalEnv_GetStaticMethodID, - .CallStaticObjectMethod = globalEnv_CallStaticObjectMethod, - .CallStaticObjectMethodA = globalEnv_CallStaticObjectMethodA, - .CallStaticBooleanMethod = globalEnv_CallStaticBooleanMethod, - .CallStaticBooleanMethodA = globalEnv_CallStaticBooleanMethodA, - .CallStaticByteMethod = globalEnv_CallStaticByteMethod, - .CallStaticByteMethodA = globalEnv_CallStaticByteMethodA, - .CallStaticCharMethod = globalEnv_CallStaticCharMethod, - .CallStaticCharMethodA = globalEnv_CallStaticCharMethodA, - .CallStaticShortMethod = globalEnv_CallStaticShortMethod, - .CallStaticShortMethodA = globalEnv_CallStaticShortMethodA, - .CallStaticIntMethod = globalEnv_CallStaticIntMethod, - .CallStaticIntMethodA = globalEnv_CallStaticIntMethodA, - .CallStaticLongMethod = globalEnv_CallStaticLongMethod, - .CallStaticLongMethodA = globalEnv_CallStaticLongMethodA, - .CallStaticFloatMethod = globalEnv_CallStaticFloatMethod, - .CallStaticFloatMethodA = globalEnv_CallStaticFloatMethodA, - .CallStaticDoubleMethod = globalEnv_CallStaticDoubleMethod, - .CallStaticDoubleMethodA = globalEnv_CallStaticDoubleMethodA, - .CallStaticVoidMethod = globalEnv_CallStaticVoidMethod, - .CallStaticVoidMethodA = globalEnv_CallStaticVoidMethodA, - .GetStaticFieldID = globalEnv_GetStaticFieldID, - .GetStaticObjectField = globalEnv_GetStaticObjectField, - .GetStaticBooleanField = globalEnv_GetStaticBooleanField, - .GetStaticByteField = globalEnv_GetStaticByteField, - .GetStaticCharField = globalEnv_GetStaticCharField, - .GetStaticShortField = globalEnv_GetStaticShortField, - .GetStaticIntField = globalEnv_GetStaticIntField, - .GetStaticLongField = globalEnv_GetStaticLongField, - .GetStaticFloatField = globalEnv_GetStaticFloatField, - .GetStaticDoubleField = globalEnv_GetStaticDoubleField, - .SetStaticObjectField = globalEnv_SetStaticObjectField, - .SetStaticBooleanField = globalEnv_SetStaticBooleanField, - .SetStaticByteField = globalEnv_SetStaticByteField, - .SetStaticCharField = globalEnv_SetStaticCharField, - .SetStaticShortField = globalEnv_SetStaticShortField, - .SetStaticIntField = globalEnv_SetStaticIntField, - .SetStaticLongField = globalEnv_SetStaticLongField, - .SetStaticFloatField = globalEnv_SetStaticFloatField, - .SetStaticDoubleField = globalEnv_SetStaticDoubleField, - .NewString = globalEnv_NewString, - .GetStringLength = globalEnv_GetStringLength, - .GetStringChars = globalEnv_GetStringChars, - .ReleaseStringChars = globalEnv_ReleaseStringChars, - .NewStringUTF = globalEnv_NewStringUTF, - .GetStringUTFLength = globalEnv_GetStringUTFLength, - .GetStringUTFChars = globalEnv_GetStringUTFChars, - .ReleaseStringUTFChars = globalEnv_ReleaseStringUTFChars, - .GetArrayLength = globalEnv_GetArrayLength, - .NewObjectArray = globalEnv_NewObjectArray, - .GetObjectArrayElement = globalEnv_GetObjectArrayElement, - .SetObjectArrayElement = globalEnv_SetObjectArrayElement, - .NewBooleanArray = globalEnv_NewBooleanArray, - .NewByteArray = globalEnv_NewByteArray, - .NewCharArray = globalEnv_NewCharArray, - .NewShortArray = globalEnv_NewShortArray, - .NewIntArray = globalEnv_NewIntArray, - .NewLongArray = globalEnv_NewLongArray, - .NewFloatArray = globalEnv_NewFloatArray, - .NewDoubleArray = globalEnv_NewDoubleArray, - .GetBooleanArrayElements = globalEnv_GetBooleanArrayElements, - .GetByteArrayElements = globalEnv_GetByteArrayElements, - .GetCharArrayElements = globalEnv_GetCharArrayElements, - .GetShortArrayElements = globalEnv_GetShortArrayElements, - .GetIntArrayElements = globalEnv_GetIntArrayElements, - .GetLongArrayElements = globalEnv_GetLongArrayElements, - .GetFloatArrayElements = globalEnv_GetFloatArrayElements, - .GetDoubleArrayElements = globalEnv_GetDoubleArrayElements, - .ReleaseBooleanArrayElements = globalEnv_ReleaseBooleanArrayElements, - .ReleaseByteArrayElements = globalEnv_ReleaseByteArrayElements, - .ReleaseCharArrayElements = globalEnv_ReleaseCharArrayElements, - .ReleaseShortArrayElements = globalEnv_ReleaseShortArrayElements, - .ReleaseIntArrayElements = globalEnv_ReleaseIntArrayElements, - .ReleaseLongArrayElements = globalEnv_ReleaseLongArrayElements, - .ReleaseFloatArrayElements = globalEnv_ReleaseFloatArrayElements, - .ReleaseDoubleArrayElements = globalEnv_ReleaseDoubleArrayElements, - .GetBooleanArrayRegion = globalEnv_GetBooleanArrayRegion, - .GetByteArrayRegion = globalEnv_GetByteArrayRegion, - .GetCharArrayRegion = globalEnv_GetCharArrayRegion, - .GetShortArrayRegion = globalEnv_GetShortArrayRegion, - .GetIntArrayRegion = globalEnv_GetIntArrayRegion, - .GetLongArrayRegion = globalEnv_GetLongArrayRegion, - .GetFloatArrayRegion = globalEnv_GetFloatArrayRegion, - .GetDoubleArrayRegion = globalEnv_GetDoubleArrayRegion, - .SetBooleanArrayRegion = globalEnv_SetBooleanArrayRegion, - .SetByteArrayRegion = globalEnv_SetByteArrayRegion, - .SetCharArrayRegion = globalEnv_SetCharArrayRegion, - .SetShortArrayRegion = globalEnv_SetShortArrayRegion, - .SetIntArrayRegion = globalEnv_SetIntArrayRegion, - .SetLongArrayRegion = globalEnv_SetLongArrayRegion, - .SetFloatArrayRegion = globalEnv_SetFloatArrayRegion, - .SetDoubleArrayRegion = globalEnv_SetDoubleArrayRegion, - .RegisterNatives = globalEnv_RegisterNatives, - .UnregisterNatives = globalEnv_UnregisterNatives, - .MonitorEnter = globalEnv_MonitorEnter, - .MonitorExit = globalEnv_MonitorExit, - .GetJavaVM = globalEnv_GetJavaVM, - .GetStringRegion = globalEnv_GetStringRegion, - .GetStringUTFRegion = globalEnv_GetStringUTFRegion, - .GetPrimitiveArrayCritical = globalEnv_GetPrimitiveArrayCritical, - .ReleasePrimitiveArrayCritical = globalEnv_ReleasePrimitiveArrayCritical, - .GetStringCritical = globalEnv_GetStringCritical, - .ReleaseStringCritical = globalEnv_ReleaseStringCritical, - .NewWeakGlobalRef = globalEnv_NewWeakGlobalRef, - .DeleteWeakGlobalRef = globalEnv_DeleteWeakGlobalRef, - .ExceptionCheck = globalEnv_ExceptionCheck, - .NewDirectByteBuffer = globalEnv_NewDirectByteBuffer, - .GetDirectBufferAddress = globalEnv_GetDirectBufferAddress, - .GetDirectBufferCapacity = globalEnv_GetDirectBufferCapacity, - .GetObjectRefType = globalEnv_GetObjectRefType, -}; - -FFI_PLUGIN_EXPORT struct GlobalJniEnv *GetGlobalEnv(void) { - if (jni.jvm == NULL) return NULL; - return &globalEnv; -}
diff --git a/pkgs/jni/src/global_jni_env.h b/pkgs/jni/src/global_jni_env.h deleted file mode 100644 index befaf4b..0000000 --- a/pkgs/jni/src/global_jni_env.h +++ /dev/null
@@ -1,219 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "dartjni.h" -/// Wrapper over JNIEnv in the JNI API, which can be used from multiple Dart -/// Threads. -/// -/// It consists of wrappers to JNIEnv methods which manage the thread-local -/// JNIEnv pointer in C code. Additionally, any returned local reference value -/// is converted to global reference. -/// -/// For the documentation on methods themselves, see the JNI Specification at -/// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html -/// -/// Apart from the specification, the Android NDK's JNI page consists of useful -/// information about using the JNI: -/// https://developer.android.com/training/articles/perf-jni -typedef struct GlobalJniEnv { - jint (*GetVersion)(); - jclass (*DefineClass)(const char * name, jobject loader, const jbyte * buf, jsize bufLen); - jclass (*FindClass)(const char * name); - jmethodID (*FromReflectedMethod)(jobject method); - jfieldID (*FromReflectedField)(jobject field); - jobject (*ToReflectedMethod)(jclass cls, jmethodID methodId, jboolean isStatic); - jclass (*GetSuperclass)(jclass clazz); - jboolean (*IsAssignableFrom)(jclass clazz1, jclass clazz2); - jobject (*ToReflectedField)(jclass cls, jfieldID fieldID, jboolean isStatic); - jint (*Throw)(jthrowable obj); - jint (*ThrowNew)(jclass clazz, const char * message); - jthrowable (*ExceptionOccurred)(); - void (*ExceptionDescribe)(); - void (*ExceptionClear)(); - void (*FatalError)(const char * msg); - jint (*PushLocalFrame)(jint capacity); - jobject (*PopLocalFrame)(jobject result); - jobject (*NewGlobalRef)(jobject obj); - void (*DeleteGlobalRef)(jobject globalRef); - jboolean (*IsSameObject)(jobject ref1, jobject ref2); - jint (*EnsureLocalCapacity)(jint capacity); - jobject (*AllocObject)(jclass clazz); - jobject (*NewObject)(jclass arg1, jmethodID arg2); - jobject (*NewObjectA)(jclass clazz, jmethodID methodID, const jvalue * args); - jclass (*GetObjectClass)(jobject obj); - jboolean (*IsInstanceOf)(jobject obj, jclass clazz); - jmethodID (*GetMethodID)(jclass clazz, const char * name, const char * sig); - jobject (*CallObjectMethod)(jobject arg1, jmethodID arg2); - jobject (*CallObjectMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jboolean (*CallBooleanMethod)(jobject arg1, jmethodID arg2); - jboolean (*CallBooleanMethodA)(jobject obj, jmethodID methodId, const jvalue * args); - jbyte (*CallByteMethod)(jobject arg1, jmethodID arg2); - jbyte (*CallByteMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jchar (*CallCharMethod)(jobject arg1, jmethodID arg2); - jchar (*CallCharMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jshort (*CallShortMethod)(jobject arg1, jmethodID arg2); - jshort (*CallShortMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jint (*CallIntMethod)(jobject arg1, jmethodID arg2); - jint (*CallIntMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jlong (*CallLongMethod)(jobject arg1, jmethodID arg2); - jlong (*CallLongMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jfloat (*CallFloatMethod)(jobject arg1, jmethodID arg2); - jfloat (*CallFloatMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jdouble (*CallDoubleMethod)(jobject arg1, jmethodID arg2); - jdouble (*CallDoubleMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - void (*CallVoidMethod)(jobject arg1, jmethodID arg2); - void (*CallVoidMethodA)(jobject obj, jmethodID methodID, const jvalue * args); - jobject (*CallNonvirtualObjectMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jobject (*CallNonvirtualObjectMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jboolean (*CallNonvirtualBooleanMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jboolean (*CallNonvirtualBooleanMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jbyte (*CallNonvirtualByteMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jbyte (*CallNonvirtualByteMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jchar (*CallNonvirtualCharMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jchar (*CallNonvirtualCharMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jshort (*CallNonvirtualShortMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jshort (*CallNonvirtualShortMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jint (*CallNonvirtualIntMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jint (*CallNonvirtualIntMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jlong (*CallNonvirtualLongMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jlong (*CallNonvirtualLongMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jfloat (*CallNonvirtualFloatMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jfloat (*CallNonvirtualFloatMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jdouble (*CallNonvirtualDoubleMethod)(jobject arg1, jclass arg2, jmethodID arg3); - jdouble (*CallNonvirtualDoubleMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - void (*CallNonvirtualVoidMethod)(jobject arg1, jclass arg2, jmethodID arg3); - void (*CallNonvirtualVoidMethodA)(jobject obj, jclass clazz, jmethodID methodID, const jvalue * args); - jfieldID (*GetFieldID)(jclass clazz, const char * name, const char * sig); - jobject (*GetObjectField)(jobject obj, jfieldID fieldID); - jboolean (*GetBooleanField)(jobject obj, jfieldID fieldID); - jbyte (*GetByteField)(jobject obj, jfieldID fieldID); - jchar (*GetCharField)(jobject obj, jfieldID fieldID); - jshort (*GetShortField)(jobject obj, jfieldID fieldID); - jint (*GetIntField)(jobject obj, jfieldID fieldID); - jlong (*GetLongField)(jobject obj, jfieldID fieldID); - jfloat (*GetFloatField)(jobject obj, jfieldID fieldID); - jdouble (*GetDoubleField)(jobject obj, jfieldID fieldID); - void (*SetObjectField)(jobject obj, jfieldID fieldID, jobject val); - void (*SetBooleanField)(jobject obj, jfieldID fieldID, jboolean val); - void (*SetByteField)(jobject obj, jfieldID fieldID, jbyte val); - void (*SetCharField)(jobject obj, jfieldID fieldID, jchar val); - void (*SetShortField)(jobject obj, jfieldID fieldID, jshort val); - void (*SetIntField)(jobject obj, jfieldID fieldID, jint val); - void (*SetLongField)(jobject obj, jfieldID fieldID, jlong val); - void (*SetFloatField)(jobject obj, jfieldID fieldID, jfloat val); - void (*SetDoubleField)(jobject obj, jfieldID fieldID, jdouble val); - jmethodID (*GetStaticMethodID)(jclass clazz, const char * name, const char * sig); - jobject (*CallStaticObjectMethod)(jclass arg1, jmethodID arg2); - jobject (*CallStaticObjectMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jboolean (*CallStaticBooleanMethod)(jclass arg1, jmethodID arg2); - jboolean (*CallStaticBooleanMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jbyte (*CallStaticByteMethod)(jclass arg1, jmethodID arg2); - jbyte (*CallStaticByteMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jchar (*CallStaticCharMethod)(jclass arg1, jmethodID arg2); - jchar (*CallStaticCharMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jshort (*CallStaticShortMethod)(jclass arg1, jmethodID arg2); - jshort (*CallStaticShortMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jint (*CallStaticIntMethod)(jclass arg1, jmethodID arg2); - jint (*CallStaticIntMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jlong (*CallStaticLongMethod)(jclass arg1, jmethodID arg2); - jlong (*CallStaticLongMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jfloat (*CallStaticFloatMethod)(jclass arg1, jmethodID arg2); - jfloat (*CallStaticFloatMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jdouble (*CallStaticDoubleMethod)(jclass arg1, jmethodID arg2); - jdouble (*CallStaticDoubleMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - void (*CallStaticVoidMethod)(jclass arg1, jmethodID arg2); - void (*CallStaticVoidMethodA)(jclass clazz, jmethodID methodID, const jvalue * args); - jfieldID (*GetStaticFieldID)(jclass clazz, const char * name, const char * sig); - jobject (*GetStaticObjectField)(jclass clazz, jfieldID fieldID); - jboolean (*GetStaticBooleanField)(jclass clazz, jfieldID fieldID); - jbyte (*GetStaticByteField)(jclass clazz, jfieldID fieldID); - jchar (*GetStaticCharField)(jclass clazz, jfieldID fieldID); - jshort (*GetStaticShortField)(jclass clazz, jfieldID fieldID); - jint (*GetStaticIntField)(jclass clazz, jfieldID fieldID); - jlong (*GetStaticLongField)(jclass clazz, jfieldID fieldID); - jfloat (*GetStaticFloatField)(jclass clazz, jfieldID fieldID); - jdouble (*GetStaticDoubleField)(jclass clazz, jfieldID fieldID); - void (*SetStaticObjectField)(jclass clazz, jfieldID fieldID, jobject val); - void (*SetStaticBooleanField)(jclass clazz, jfieldID fieldID, jboolean val); - void (*SetStaticByteField)(jclass clazz, jfieldID fieldID, jbyte val); - void (*SetStaticCharField)(jclass clazz, jfieldID fieldID, jchar val); - void (*SetStaticShortField)(jclass clazz, jfieldID fieldID, jshort val); - void (*SetStaticIntField)(jclass clazz, jfieldID fieldID, jint val); - void (*SetStaticLongField)(jclass clazz, jfieldID fieldID, jlong val); - void (*SetStaticFloatField)(jclass clazz, jfieldID fieldID, jfloat val); - void (*SetStaticDoubleField)(jclass clazz, jfieldID fieldID, jdouble val); - jstring (*NewString)(const jchar * unicodeChars, jsize len); - jsize (*GetStringLength)(jstring string); - const jchar * (*GetStringChars)(jstring string, jboolean * isCopy); - void (*ReleaseStringChars)(jstring string, const jchar * isCopy); - jstring (*NewStringUTF)(const char * bytes); - jsize (*GetStringUTFLength)(jstring string); - const char * (*GetStringUTFChars)(jstring string, jboolean * isCopy); - void (*ReleaseStringUTFChars)(jstring string, const char * utf); - jsize (*GetArrayLength)(jarray array); - jobjectArray (*NewObjectArray)(jsize length, jclass elementClass, jobject initialElement); - jobject (*GetObjectArrayElement)(jobjectArray array, jsize index); - void (*SetObjectArrayElement)(jobjectArray array, jsize index, jobject val); - jbooleanArray (*NewBooleanArray)(jsize length); - jbyteArray (*NewByteArray)(jsize length); - jcharArray (*NewCharArray)(jsize length); - jshortArray (*NewShortArray)(jsize length); - jintArray (*NewIntArray)(jsize length); - jlongArray (*NewLongArray)(jsize length); - jfloatArray (*NewFloatArray)(jsize length); - jdoubleArray (*NewDoubleArray)(jsize length); - jboolean * (*GetBooleanArrayElements)(jbooleanArray array, jboolean * isCopy); - jbyte * (*GetByteArrayElements)(jbyteArray array, jboolean * isCopy); - jchar * (*GetCharArrayElements)(jcharArray array, jboolean * isCopy); - jshort * (*GetShortArrayElements)(jshortArray array, jboolean * isCopy); - jint * (*GetIntArrayElements)(jintArray array, jboolean * isCopy); - jlong * (*GetLongArrayElements)(jlongArray array, jboolean * isCopy); - jfloat * (*GetFloatArrayElements)(jfloatArray array, jboolean * isCopy); - jdouble * (*GetDoubleArrayElements)(jdoubleArray array, jboolean * isCopy); - void (*ReleaseBooleanArrayElements)(jbooleanArray array, jboolean * elems, jint mode); - void (*ReleaseByteArrayElements)(jbyteArray array, jbyte * elems, jint mode); - void (*ReleaseCharArrayElements)(jcharArray array, jchar * elems, jint mode); - void (*ReleaseShortArrayElements)(jshortArray array, jshort * elems, jint mode); - void (*ReleaseIntArrayElements)(jintArray array, jint * elems, jint mode); - void (*ReleaseLongArrayElements)(jlongArray array, jlong * elems, jint mode); - void (*ReleaseFloatArrayElements)(jfloatArray array, jfloat * elems, jint mode); - void (*ReleaseDoubleArrayElements)(jdoubleArray array, jdouble * elems, jint mode); - void (*GetBooleanArrayRegion)(jbooleanArray array, jsize start, jsize len, jboolean * buf); - void (*GetByteArrayRegion)(jbyteArray array, jsize start, jsize len, jbyte * buf); - void (*GetCharArrayRegion)(jcharArray array, jsize start, jsize len, jchar * buf); - void (*GetShortArrayRegion)(jshortArray array, jsize start, jsize len, jshort * buf); - void (*GetIntArrayRegion)(jintArray array, jsize start, jsize len, jint * buf); - void (*GetLongArrayRegion)(jlongArray array, jsize start, jsize len, jlong * buf); - void (*GetFloatArrayRegion)(jfloatArray array, jsize start, jsize len, jfloat * buf); - void (*GetDoubleArrayRegion)(jdoubleArray array, jsize start, jsize len, jdouble * buf); - void (*SetBooleanArrayRegion)(jbooleanArray array, jsize start, jsize len, const jboolean * buf); - void (*SetByteArrayRegion)(jbyteArray array, jsize start, jsize len, const jbyte * buf); - void (*SetCharArrayRegion)(jcharArray array, jsize start, jsize len, const jchar * buf); - void (*SetShortArrayRegion)(jshortArray array, jsize start, jsize len, const jshort * buf); - void (*SetIntArrayRegion)(jintArray array, jsize start, jsize len, const jint * buf); - void (*SetLongArrayRegion)(jlongArray array, jsize start, jsize len, const jlong * buf); - void (*SetFloatArrayRegion)(jfloatArray array, jsize start, jsize len, const jfloat * buf); - void (*SetDoubleArrayRegion)(jdoubleArray array, jsize start, jsize len, const jdouble * buf); - jint (*RegisterNatives)(jclass clazz, const JNINativeMethod * methods, jint nMethods); - jint (*UnregisterNatives)(jclass clazz); - jint (*MonitorEnter)(jobject obj); - jint (*MonitorExit)(jobject obj); - jint (*GetJavaVM)(JavaVM ** vm); - void (*GetStringRegion)(jstring str, jsize start, jsize len, jchar * buf); - void (*GetStringUTFRegion)(jstring str, jsize start, jsize len, char * buf); - void * (*GetPrimitiveArrayCritical)(jarray array, jboolean * isCopy); - void (*ReleasePrimitiveArrayCritical)(jarray array, void * carray, jint mode); - const jchar * (*GetStringCritical)(jstring str, jboolean * isCopy); - void (*ReleaseStringCritical)(jstring str, const jchar * carray); - jweak (*NewWeakGlobalRef)(jobject obj); - void (*DeleteWeakGlobalRef)(jweak obj); - jboolean (*ExceptionCheck)(); - jobject (*NewDirectByteBuffer)(void * address, jlong capacity); - void * (*GetDirectBufferAddress)(jobject buf); - jlong (*GetDirectBufferCapacity)(jobject buf); - jobjectRefType (*GetObjectRefType)(jobject obj); -} GlobalJniEnv; - -extern GlobalJniEnv globalEnv; -FFI_PLUGIN_EXPORT struct GlobalJniEnv *GetGlobalEnv(void);
diff --git a/pkgs/jni/src/third_party/global_jni_env.c b/pkgs/jni/src/third_party/global_jni_env.c new file mode 100644 index 0000000..961f16f --- /dev/null +++ b/pkgs/jni/src/third_party/global_jni_env.c
@@ -0,0 +1,1417 @@ +// Generated from an annotated version of jni.h provided in Android NDK. +// (NDK Version 23.1.7779620) +// The license for original file is provided below: + +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "global_jni_env.h" +#include "../dartjni.h" + +jint globalEnv_GetVersion() { + attach_thread(); + return (*jniEnv)->GetVersion(jniEnv); +} + +jclass globalEnv_DefineClass(const char* name, + jobject loader, + const jbyte* buf, + jsize bufLen) { + attach_thread(); + return to_global_ref( + (*jniEnv)->DefineClass(jniEnv, name, loader, buf, bufLen)); +} + +jclass globalEnv_FindClass(const char* name) { + attach_thread(); + return to_global_ref((*jniEnv)->FindClass(jniEnv, name)); +} + +jmethodID globalEnv_FromReflectedMethod(jobject method) { + attach_thread(); + return (*jniEnv)->FromReflectedMethod(jniEnv, method); +} + +jfieldID globalEnv_FromReflectedField(jobject field) { + attach_thread(); + return (*jniEnv)->FromReflectedField(jniEnv, field); +} + +jobject globalEnv_ToReflectedMethod(jclass cls, + jmethodID methodId, + jboolean isStatic) { + attach_thread(); + return to_global_ref( + (*jniEnv)->ToReflectedMethod(jniEnv, cls, methodId, isStatic)); +} + +jclass globalEnv_GetSuperclass(jclass clazz) { + attach_thread(); + return to_global_ref((*jniEnv)->GetSuperclass(jniEnv, clazz)); +} + +jboolean globalEnv_IsAssignableFrom(jclass clazz1, jclass clazz2) { + attach_thread(); + return (*jniEnv)->IsAssignableFrom(jniEnv, clazz1, clazz2); +} + +jobject globalEnv_ToReflectedField(jclass cls, + jfieldID fieldID, + jboolean isStatic) { + attach_thread(); + return to_global_ref( + (*jniEnv)->ToReflectedField(jniEnv, cls, fieldID, isStatic)); +} + +jint globalEnv_Throw(jthrowable obj) { + attach_thread(); + return (*jniEnv)->Throw(jniEnv, obj); +} + +jint globalEnv_ThrowNew(jclass clazz, const char* message) { + attach_thread(); + return (*jniEnv)->ThrowNew(jniEnv, clazz, message); +} + +jthrowable globalEnv_ExceptionOccurred() { + attach_thread(); + return to_global_ref((*jniEnv)->ExceptionOccurred(jniEnv)); +} + +void globalEnv_ExceptionDescribe() { + attach_thread(); + (*jniEnv)->ExceptionDescribe(jniEnv); +} + +void globalEnv_ExceptionClear() { + attach_thread(); + (*jniEnv)->ExceptionClear(jniEnv); +} + +void globalEnv_FatalError(const char* msg) { + attach_thread(); + (*jniEnv)->FatalError(jniEnv, msg); +} + +jint globalEnv_PushLocalFrame(jint capacity) { + attach_thread(); + return (*jniEnv)->PushLocalFrame(jniEnv, capacity); +} + +jobject globalEnv_PopLocalFrame(jobject result) { + attach_thread(); + return to_global_ref((*jniEnv)->PopLocalFrame(jniEnv, result)); +} + +jobject globalEnv_NewGlobalRef(jobject obj) { + attach_thread(); + return to_global_ref((*jniEnv)->NewGlobalRef(jniEnv, obj)); +} + +void globalEnv_DeleteGlobalRef(jobject globalRef) { + attach_thread(); + (*jniEnv)->DeleteGlobalRef(jniEnv, globalRef); +} + +jboolean globalEnv_IsSameObject(jobject ref1, jobject ref2) { + attach_thread(); + return (*jniEnv)->IsSameObject(jniEnv, ref1, ref2); +} + +jint globalEnv_EnsureLocalCapacity(jint capacity) { + attach_thread(); + return (*jniEnv)->EnsureLocalCapacity(jniEnv, capacity); +} + +jobject globalEnv_AllocObject(jclass clazz) { + attach_thread(); + return to_global_ref((*jniEnv)->AllocObject(jniEnv, clazz)); +} + +jobject globalEnv_NewObject(jclass arg1, jmethodID arg2) { + attach_thread(); + return to_global_ref((*jniEnv)->NewObject(jniEnv, arg1, arg2)); +} + +jobject globalEnv_NewObjectA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return to_global_ref((*jniEnv)->NewObjectA(jniEnv, clazz, methodID, args)); +} + +jclass globalEnv_GetObjectClass(jobject obj) { + attach_thread(); + return to_global_ref((*jniEnv)->GetObjectClass(jniEnv, obj)); +} + +jboolean globalEnv_IsInstanceOf(jobject obj, jclass clazz) { + attach_thread(); + return (*jniEnv)->IsInstanceOf(jniEnv, obj, clazz); +} + +jmethodID globalEnv_GetMethodID(jclass clazz, + const char* name, + const char* sig) { + attach_thread(); + return (*jniEnv)->GetMethodID(jniEnv, clazz, name, sig); +} + +jobject globalEnv_CallObjectMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return to_global_ref((*jniEnv)->CallObjectMethod(jniEnv, arg1, arg2)); +} + +jobject globalEnv_CallObjectMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return to_global_ref( + (*jniEnv)->CallObjectMethodA(jniEnv, obj, methodID, args)); +} + +jboolean globalEnv_CallBooleanMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallBooleanMethod(jniEnv, arg1, arg2); +} + +jboolean globalEnv_CallBooleanMethodA(jobject obj, + jmethodID methodId, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallBooleanMethodA(jniEnv, obj, methodId, args); +} + +jbyte globalEnv_CallByteMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallByteMethod(jniEnv, arg1, arg2); +} + +jbyte globalEnv_CallByteMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallByteMethodA(jniEnv, obj, methodID, args); +} + +jchar globalEnv_CallCharMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallCharMethod(jniEnv, arg1, arg2); +} + +jchar globalEnv_CallCharMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallCharMethodA(jniEnv, obj, methodID, args); +} + +jshort globalEnv_CallShortMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallShortMethod(jniEnv, arg1, arg2); +} + +jshort globalEnv_CallShortMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallShortMethodA(jniEnv, obj, methodID, args); +} + +jint globalEnv_CallIntMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallIntMethod(jniEnv, arg1, arg2); +} + +jint globalEnv_CallIntMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallIntMethodA(jniEnv, obj, methodID, args); +} + +jlong globalEnv_CallLongMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallLongMethod(jniEnv, arg1, arg2); +} + +jlong globalEnv_CallLongMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallLongMethodA(jniEnv, obj, methodID, args); +} + +jfloat globalEnv_CallFloatMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallFloatMethod(jniEnv, arg1, arg2); +} + +jfloat globalEnv_CallFloatMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallFloatMethodA(jniEnv, obj, methodID, args); +} + +jdouble globalEnv_CallDoubleMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallDoubleMethod(jniEnv, arg1, arg2); +} + +jdouble globalEnv_CallDoubleMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallDoubleMethodA(jniEnv, obj, methodID, args); +} + +void globalEnv_CallVoidMethod(jobject arg1, jmethodID arg2) { + attach_thread(); + (*jniEnv)->CallVoidMethod(jniEnv, arg1, arg2); +} + +void globalEnv_CallVoidMethodA(jobject obj, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + (*jniEnv)->CallVoidMethodA(jniEnv, obj, methodID, args); +} + +jobject globalEnv_CallNonvirtualObjectMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return to_global_ref( + (*jniEnv)->CallNonvirtualObjectMethod(jniEnv, arg1, arg2, arg3)); +} + +jobject globalEnv_CallNonvirtualObjectMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return to_global_ref((*jniEnv)->CallNonvirtualObjectMethodA( + jniEnv, obj, clazz, methodID, args)); +} + +jboolean globalEnv_CallNonvirtualBooleanMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualBooleanMethod(jniEnv, arg1, arg2, arg3); +} + +jboolean globalEnv_CallNonvirtualBooleanMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualBooleanMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jbyte globalEnv_CallNonvirtualByteMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualByteMethod(jniEnv, arg1, arg2, arg3); +} + +jbyte globalEnv_CallNonvirtualByteMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualByteMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jchar globalEnv_CallNonvirtualCharMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualCharMethod(jniEnv, arg1, arg2, arg3); +} + +jchar globalEnv_CallNonvirtualCharMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualCharMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jshort globalEnv_CallNonvirtualShortMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualShortMethod(jniEnv, arg1, arg2, arg3); +} + +jshort globalEnv_CallNonvirtualShortMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualShortMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jint globalEnv_CallNonvirtualIntMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualIntMethod(jniEnv, arg1, arg2, arg3); +} + +jint globalEnv_CallNonvirtualIntMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualIntMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jlong globalEnv_CallNonvirtualLongMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualLongMethod(jniEnv, arg1, arg2, arg3); +} + +jlong globalEnv_CallNonvirtualLongMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualLongMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jfloat globalEnv_CallNonvirtualFloatMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualFloatMethod(jniEnv, arg1, arg2, arg3); +} + +jfloat globalEnv_CallNonvirtualFloatMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualFloatMethodA(jniEnv, obj, clazz, methodID, + args); +} + +jdouble globalEnv_CallNonvirtualDoubleMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + return (*jniEnv)->CallNonvirtualDoubleMethod(jniEnv, arg1, arg2, arg3); +} + +jdouble globalEnv_CallNonvirtualDoubleMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallNonvirtualDoubleMethodA(jniEnv, obj, clazz, methodID, + args); +} + +void globalEnv_CallNonvirtualVoidMethod(jobject arg1, + jclass arg2, + jmethodID arg3) { + attach_thread(); + (*jniEnv)->CallNonvirtualVoidMethod(jniEnv, arg1, arg2, arg3); +} + +void globalEnv_CallNonvirtualVoidMethodA(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + (*jniEnv)->CallNonvirtualVoidMethodA(jniEnv, obj, clazz, methodID, args); +} + +jfieldID globalEnv_GetFieldID(jclass clazz, const char* name, const char* sig) { + attach_thread(); + return (*jniEnv)->GetFieldID(jniEnv, clazz, name, sig); +} + +jobject globalEnv_GetObjectField(jobject obj, jfieldID fieldID) { + attach_thread(); + return to_global_ref((*jniEnv)->GetObjectField(jniEnv, obj, fieldID)); +} + +jboolean globalEnv_GetBooleanField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetBooleanField(jniEnv, obj, fieldID); +} + +jbyte globalEnv_GetByteField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetByteField(jniEnv, obj, fieldID); +} + +jchar globalEnv_GetCharField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetCharField(jniEnv, obj, fieldID); +} + +jshort globalEnv_GetShortField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetShortField(jniEnv, obj, fieldID); +} + +jint globalEnv_GetIntField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetIntField(jniEnv, obj, fieldID); +} + +jlong globalEnv_GetLongField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetLongField(jniEnv, obj, fieldID); +} + +jfloat globalEnv_GetFloatField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetFloatField(jniEnv, obj, fieldID); +} + +jdouble globalEnv_GetDoubleField(jobject obj, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetDoubleField(jniEnv, obj, fieldID); +} + +void globalEnv_SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + attach_thread(); + (*jniEnv)->SetObjectField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetBooleanField(jobject obj, jfieldID fieldID, jboolean val) { + attach_thread(); + (*jniEnv)->SetBooleanField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetByteField(jobject obj, jfieldID fieldID, jbyte val) { + attach_thread(); + (*jniEnv)->SetByteField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetCharField(jobject obj, jfieldID fieldID, jchar val) { + attach_thread(); + (*jniEnv)->SetCharField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetShortField(jobject obj, jfieldID fieldID, jshort val) { + attach_thread(); + (*jniEnv)->SetShortField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetIntField(jobject obj, jfieldID fieldID, jint val) { + attach_thread(); + (*jniEnv)->SetIntField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetLongField(jobject obj, jfieldID fieldID, jlong val) { + attach_thread(); + (*jniEnv)->SetLongField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetFloatField(jobject obj, jfieldID fieldID, jfloat val) { + attach_thread(); + (*jniEnv)->SetFloatField(jniEnv, obj, fieldID, val); +} + +void globalEnv_SetDoubleField(jobject obj, jfieldID fieldID, jdouble val) { + attach_thread(); + (*jniEnv)->SetDoubleField(jniEnv, obj, fieldID, val); +} + +jmethodID globalEnv_GetStaticMethodID(jclass clazz, + const char* name, + const char* sig) { + attach_thread(); + return (*jniEnv)->GetStaticMethodID(jniEnv, clazz, name, sig); +} + +jobject globalEnv_CallStaticObjectMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return to_global_ref((*jniEnv)->CallStaticObjectMethod(jniEnv, arg1, arg2)); +} + +jobject globalEnv_CallStaticObjectMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return to_global_ref( + (*jniEnv)->CallStaticObjectMethodA(jniEnv, clazz, methodID, args)); +} + +jboolean globalEnv_CallStaticBooleanMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticBooleanMethod(jniEnv, arg1, arg2); +} + +jboolean globalEnv_CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticBooleanMethodA(jniEnv, clazz, methodID, args); +} + +jbyte globalEnv_CallStaticByteMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticByteMethod(jniEnv, arg1, arg2); +} + +jbyte globalEnv_CallStaticByteMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticByteMethodA(jniEnv, clazz, methodID, args); +} + +jchar globalEnv_CallStaticCharMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticCharMethod(jniEnv, arg1, arg2); +} + +jchar globalEnv_CallStaticCharMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticCharMethodA(jniEnv, clazz, methodID, args); +} + +jshort globalEnv_CallStaticShortMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticShortMethod(jniEnv, arg1, arg2); +} + +jshort globalEnv_CallStaticShortMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticShortMethodA(jniEnv, clazz, methodID, args); +} + +jint globalEnv_CallStaticIntMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticIntMethod(jniEnv, arg1, arg2); +} + +jint globalEnv_CallStaticIntMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticIntMethodA(jniEnv, clazz, methodID, args); +} + +jlong globalEnv_CallStaticLongMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticLongMethod(jniEnv, arg1, arg2); +} + +jlong globalEnv_CallStaticLongMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticLongMethodA(jniEnv, clazz, methodID, args); +} + +jfloat globalEnv_CallStaticFloatMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticFloatMethod(jniEnv, arg1, arg2); +} + +jfloat globalEnv_CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticFloatMethodA(jniEnv, clazz, methodID, args); +} + +jdouble globalEnv_CallStaticDoubleMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + return (*jniEnv)->CallStaticDoubleMethod(jniEnv, arg1, arg2); +} + +jdouble globalEnv_CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + return (*jniEnv)->CallStaticDoubleMethodA(jniEnv, clazz, methodID, args); +} + +void globalEnv_CallStaticVoidMethod(jclass arg1, jmethodID arg2) { + attach_thread(); + (*jniEnv)->CallStaticVoidMethod(jniEnv, arg1, arg2); +} + +void globalEnv_CallStaticVoidMethodA(jclass clazz, + jmethodID methodID, + const jvalue* args) { + attach_thread(); + (*jniEnv)->CallStaticVoidMethodA(jniEnv, clazz, methodID, args); +} + +jfieldID globalEnv_GetStaticFieldID(jclass clazz, + const char* name, + const char* sig) { + attach_thread(); + return (*jniEnv)->GetStaticFieldID(jniEnv, clazz, name, sig); +} + +jobject globalEnv_GetStaticObjectField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return to_global_ref((*jniEnv)->GetStaticObjectField(jniEnv, clazz, fieldID)); +} + +jboolean globalEnv_GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticBooleanField(jniEnv, clazz, fieldID); +} + +jbyte globalEnv_GetStaticByteField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticByteField(jniEnv, clazz, fieldID); +} + +jchar globalEnv_GetStaticCharField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticCharField(jniEnv, clazz, fieldID); +} + +jshort globalEnv_GetStaticShortField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticShortField(jniEnv, clazz, fieldID); +} + +jint globalEnv_GetStaticIntField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticIntField(jniEnv, clazz, fieldID); +} + +jlong globalEnv_GetStaticLongField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticLongField(jniEnv, clazz, fieldID); +} + +jfloat globalEnv_GetStaticFloatField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticFloatField(jniEnv, clazz, fieldID); +} + +jdouble globalEnv_GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + attach_thread(); + return (*jniEnv)->GetStaticDoubleField(jniEnv, clazz, fieldID); +} + +void globalEnv_SetStaticObjectField(jclass clazz, + jfieldID fieldID, + jobject val) { + attach_thread(); + (*jniEnv)->SetStaticObjectField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticBooleanField(jclass clazz, + jfieldID fieldID, + jboolean val) { + attach_thread(); + (*jniEnv)->SetStaticBooleanField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte val) { + attach_thread(); + (*jniEnv)->SetStaticByteField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticCharField(jclass clazz, jfieldID fieldID, jchar val) { + attach_thread(); + (*jniEnv)->SetStaticCharField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticShortField(jclass clazz, jfieldID fieldID, jshort val) { + attach_thread(); + (*jniEnv)->SetStaticShortField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticIntField(jclass clazz, jfieldID fieldID, jint val) { + attach_thread(); + (*jniEnv)->SetStaticIntField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticLongField(jclass clazz, jfieldID fieldID, jlong val) { + attach_thread(); + (*jniEnv)->SetStaticLongField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat val) { + attach_thread(); + (*jniEnv)->SetStaticFloatField(jniEnv, clazz, fieldID, val); +} + +void globalEnv_SetStaticDoubleField(jclass clazz, + jfieldID fieldID, + jdouble val) { + attach_thread(); + (*jniEnv)->SetStaticDoubleField(jniEnv, clazz, fieldID, val); +} + +jstring globalEnv_NewString(const jchar* unicodeChars, jsize len) { + attach_thread(); + return to_global_ref((*jniEnv)->NewString(jniEnv, unicodeChars, len)); +} + +jsize globalEnv_GetStringLength(jstring string) { + attach_thread(); + return (*jniEnv)->GetStringLength(jniEnv, string); +} + +const jchar* globalEnv_GetStringChars(jstring string, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetStringChars(jniEnv, string, isCopy); +} + +void globalEnv_ReleaseStringChars(jstring string, const jchar* isCopy) { + attach_thread(); + (*jniEnv)->ReleaseStringChars(jniEnv, string, isCopy); +} + +jstring globalEnv_NewStringUTF(const char* bytes) { + attach_thread(); + return to_global_ref((*jniEnv)->NewStringUTF(jniEnv, bytes)); +} + +jsize globalEnv_GetStringUTFLength(jstring string) { + attach_thread(); + return (*jniEnv)->GetStringUTFLength(jniEnv, string); +} + +const char* globalEnv_GetStringUTFChars(jstring string, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetStringUTFChars(jniEnv, string, isCopy); +} + +void globalEnv_ReleaseStringUTFChars(jstring string, const char* utf) { + attach_thread(); + (*jniEnv)->ReleaseStringUTFChars(jniEnv, string, utf); +} + +jsize globalEnv_GetArrayLength(jarray array) { + attach_thread(); + return (*jniEnv)->GetArrayLength(jniEnv, array); +} + +jobjectArray globalEnv_NewObjectArray(jsize length, + jclass elementClass, + jobject initialElement) { + attach_thread(); + return to_global_ref( + (*jniEnv)->NewObjectArray(jniEnv, length, elementClass, initialElement)); +} + +jobject globalEnv_GetObjectArrayElement(jobjectArray array, jsize index) { + attach_thread(); + return to_global_ref((*jniEnv)->GetObjectArrayElement(jniEnv, array, index)); +} + +void globalEnv_SetObjectArrayElement(jobjectArray array, + jsize index, + jobject val) { + attach_thread(); + (*jniEnv)->SetObjectArrayElement(jniEnv, array, index, val); +} + +jbooleanArray globalEnv_NewBooleanArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewBooleanArray(jniEnv, length)); +} + +jbyteArray globalEnv_NewByteArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewByteArray(jniEnv, length)); +} + +jcharArray globalEnv_NewCharArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewCharArray(jniEnv, length)); +} + +jshortArray globalEnv_NewShortArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewShortArray(jniEnv, length)); +} + +jintArray globalEnv_NewIntArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewIntArray(jniEnv, length)); +} + +jlongArray globalEnv_NewLongArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewLongArray(jniEnv, length)); +} + +jfloatArray globalEnv_NewFloatArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewFloatArray(jniEnv, length)); +} + +jdoubleArray globalEnv_NewDoubleArray(jsize length) { + attach_thread(); + return to_global_ref((*jniEnv)->NewDoubleArray(jniEnv, length)); +} + +jboolean* globalEnv_GetBooleanArrayElements(jbooleanArray array, + jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetBooleanArrayElements(jniEnv, array, isCopy); +} + +jbyte* globalEnv_GetByteArrayElements(jbyteArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetByteArrayElements(jniEnv, array, isCopy); +} + +jchar* globalEnv_GetCharArrayElements(jcharArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetCharArrayElements(jniEnv, array, isCopy); +} + +jshort* globalEnv_GetShortArrayElements(jshortArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetShortArrayElements(jniEnv, array, isCopy); +} + +jint* globalEnv_GetIntArrayElements(jintArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetIntArrayElements(jniEnv, array, isCopy); +} + +jlong* globalEnv_GetLongArrayElements(jlongArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetLongArrayElements(jniEnv, array, isCopy); +} + +jfloat* globalEnv_GetFloatArrayElements(jfloatArray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetFloatArrayElements(jniEnv, array, isCopy); +} + +jdouble* globalEnv_GetDoubleArrayElements(jdoubleArray array, + jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetDoubleArrayElements(jniEnv, array, isCopy); +} + +void globalEnv_ReleaseBooleanArrayElements(jbooleanArray array, + jboolean* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseBooleanArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseByteArrayElements(jbyteArray array, + jbyte* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseByteArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseCharArrayElements(jcharArray array, + jchar* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseCharArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseShortArrayElements(jshortArray array, + jshort* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseShortArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseIntArrayElements(jintArray array, + jint* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseIntArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseLongArrayElements(jlongArray array, + jlong* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseLongArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseFloatArrayElements(jfloatArray array, + jfloat* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseFloatArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_ReleaseDoubleArrayElements(jdoubleArray array, + jdouble* elems, + jint mode) { + attach_thread(); + (*jniEnv)->ReleaseDoubleArrayElements(jniEnv, array, elems, mode); +} + +void globalEnv_GetBooleanArrayRegion(jbooleanArray array, + jsize start, + jsize len, + jboolean* buf) { + attach_thread(); + (*jniEnv)->GetBooleanArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetByteArrayRegion(jbyteArray array, + jsize start, + jsize len, + jbyte* buf) { + attach_thread(); + (*jniEnv)->GetByteArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetCharArrayRegion(jcharArray array, + jsize start, + jsize len, + jchar* buf) { + attach_thread(); + (*jniEnv)->GetCharArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetShortArrayRegion(jshortArray array, + jsize start, + jsize len, + jshort* buf) { + attach_thread(); + (*jniEnv)->GetShortArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetIntArrayRegion(jintArray array, + jsize start, + jsize len, + jint* buf) { + attach_thread(); + (*jniEnv)->GetIntArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetLongArrayRegion(jlongArray array, + jsize start, + jsize len, + jlong* buf) { + attach_thread(); + (*jniEnv)->GetLongArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetFloatArrayRegion(jfloatArray array, + jsize start, + jsize len, + jfloat* buf) { + attach_thread(); + (*jniEnv)->GetFloatArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_GetDoubleArrayRegion(jdoubleArray array, + jsize start, + jsize len, + jdouble* buf) { + attach_thread(); + (*jniEnv)->GetDoubleArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetBooleanArrayRegion(jbooleanArray array, + jsize start, + jsize len, + const jboolean* buf) { + attach_thread(); + (*jniEnv)->SetBooleanArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetByteArrayRegion(jbyteArray array, + jsize start, + jsize len, + const jbyte* buf) { + attach_thread(); + (*jniEnv)->SetByteArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetCharArrayRegion(jcharArray array, + jsize start, + jsize len, + const jchar* buf) { + attach_thread(); + (*jniEnv)->SetCharArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetShortArrayRegion(jshortArray array, + jsize start, + jsize len, + const jshort* buf) { + attach_thread(); + (*jniEnv)->SetShortArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetIntArrayRegion(jintArray array, + jsize start, + jsize len, + const jint* buf) { + attach_thread(); + (*jniEnv)->SetIntArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetLongArrayRegion(jlongArray array, + jsize start, + jsize len, + const jlong* buf) { + attach_thread(); + (*jniEnv)->SetLongArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetFloatArrayRegion(jfloatArray array, + jsize start, + jsize len, + const jfloat* buf) { + attach_thread(); + (*jniEnv)->SetFloatArrayRegion(jniEnv, array, start, len, buf); +} + +void globalEnv_SetDoubleArrayRegion(jdoubleArray array, + jsize start, + jsize len, + const jdouble* buf) { + attach_thread(); + (*jniEnv)->SetDoubleArrayRegion(jniEnv, array, start, len, buf); +} + +jint globalEnv_RegisterNatives(jclass clazz, + const JNINativeMethod* methods, + jint nMethods) { + attach_thread(); + return (*jniEnv)->RegisterNatives(jniEnv, clazz, methods, nMethods); +} + +jint globalEnv_UnregisterNatives(jclass clazz) { + attach_thread(); + return (*jniEnv)->UnregisterNatives(jniEnv, clazz); +} + +jint globalEnv_MonitorEnter(jobject obj) { + attach_thread(); + return (*jniEnv)->MonitorEnter(jniEnv, obj); +} + +jint globalEnv_MonitorExit(jobject obj) { + attach_thread(); + return (*jniEnv)->MonitorExit(jniEnv, obj); +} + +jint globalEnv_GetJavaVM(JavaVM** vm) { + attach_thread(); + return (*jniEnv)->GetJavaVM(jniEnv, vm); +} + +void globalEnv_GetStringRegion(jstring str, + jsize start, + jsize len, + jchar* buf) { + attach_thread(); + (*jniEnv)->GetStringRegion(jniEnv, str, start, len, buf); +} + +void globalEnv_GetStringUTFRegion(jstring str, + jsize start, + jsize len, + char* buf) { + attach_thread(); + (*jniEnv)->GetStringUTFRegion(jniEnv, str, start, len, buf); +} + +void* globalEnv_GetPrimitiveArrayCritical(jarray array, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetPrimitiveArrayCritical(jniEnv, array, isCopy); +} + +void globalEnv_ReleasePrimitiveArrayCritical(jarray array, + void* carray, + jint mode) { + attach_thread(); + (*jniEnv)->ReleasePrimitiveArrayCritical(jniEnv, array, carray, mode); +} + +const jchar* globalEnv_GetStringCritical(jstring str, jboolean* isCopy) { + attach_thread(); + return (*jniEnv)->GetStringCritical(jniEnv, str, isCopy); +} + +void globalEnv_ReleaseStringCritical(jstring str, const jchar* carray) { + attach_thread(); + (*jniEnv)->ReleaseStringCritical(jniEnv, str, carray); +} + +jweak globalEnv_NewWeakGlobalRef(jobject obj) { + attach_thread(); + return to_global_ref((*jniEnv)->NewWeakGlobalRef(jniEnv, obj)); +} + +void globalEnv_DeleteWeakGlobalRef(jweak obj) { + attach_thread(); + (*jniEnv)->DeleteWeakGlobalRef(jniEnv, obj); +} + +jboolean globalEnv_ExceptionCheck() { + attach_thread(); + return (*jniEnv)->ExceptionCheck(jniEnv); +} + +jobject globalEnv_NewDirectByteBuffer(void* address, jlong capacity) { + attach_thread(); + return to_global_ref( + (*jniEnv)->NewDirectByteBuffer(jniEnv, address, capacity)); +} + +void* globalEnv_GetDirectBufferAddress(jobject buf) { + attach_thread(); + return (*jniEnv)->GetDirectBufferAddress(jniEnv, buf); +} + +jlong globalEnv_GetDirectBufferCapacity(jobject buf) { + attach_thread(); + return (*jniEnv)->GetDirectBufferCapacity(jniEnv, buf); +} + +jobjectRefType globalEnv_GetObjectRefType(jobject obj) { + attach_thread(); + return (*jniEnv)->GetObjectRefType(jniEnv, obj); +} + +GlobalJniEnv globalJniEnv = { + .GetVersion = globalEnv_GetVersion, + .DefineClass = globalEnv_DefineClass, + .FindClass = globalEnv_FindClass, + .FromReflectedMethod = globalEnv_FromReflectedMethod, + .FromReflectedField = globalEnv_FromReflectedField, + .ToReflectedMethod = globalEnv_ToReflectedMethod, + .GetSuperclass = globalEnv_GetSuperclass, + .IsAssignableFrom = globalEnv_IsAssignableFrom, + .ToReflectedField = globalEnv_ToReflectedField, + .Throw = globalEnv_Throw, + .ThrowNew = globalEnv_ThrowNew, + .ExceptionOccurred = globalEnv_ExceptionOccurred, + .ExceptionDescribe = globalEnv_ExceptionDescribe, + .ExceptionClear = globalEnv_ExceptionClear, + .FatalError = globalEnv_FatalError, + .PushLocalFrame = globalEnv_PushLocalFrame, + .PopLocalFrame = globalEnv_PopLocalFrame, + .NewGlobalRef = globalEnv_NewGlobalRef, + .DeleteGlobalRef = globalEnv_DeleteGlobalRef, + .IsSameObject = globalEnv_IsSameObject, + .EnsureLocalCapacity = globalEnv_EnsureLocalCapacity, + .AllocObject = globalEnv_AllocObject, + .NewObject = globalEnv_NewObject, + .NewObjectA = globalEnv_NewObjectA, + .GetObjectClass = globalEnv_GetObjectClass, + .IsInstanceOf = globalEnv_IsInstanceOf, + .GetMethodID = globalEnv_GetMethodID, + .CallObjectMethod = globalEnv_CallObjectMethod, + .CallObjectMethodA = globalEnv_CallObjectMethodA, + .CallBooleanMethod = globalEnv_CallBooleanMethod, + .CallBooleanMethodA = globalEnv_CallBooleanMethodA, + .CallByteMethod = globalEnv_CallByteMethod, + .CallByteMethodA = globalEnv_CallByteMethodA, + .CallCharMethod = globalEnv_CallCharMethod, + .CallCharMethodA = globalEnv_CallCharMethodA, + .CallShortMethod = globalEnv_CallShortMethod, + .CallShortMethodA = globalEnv_CallShortMethodA, + .CallIntMethod = globalEnv_CallIntMethod, + .CallIntMethodA = globalEnv_CallIntMethodA, + .CallLongMethod = globalEnv_CallLongMethod, + .CallLongMethodA = globalEnv_CallLongMethodA, + .CallFloatMethod = globalEnv_CallFloatMethod, + .CallFloatMethodA = globalEnv_CallFloatMethodA, + .CallDoubleMethod = globalEnv_CallDoubleMethod, + .CallDoubleMethodA = globalEnv_CallDoubleMethodA, + .CallVoidMethod = globalEnv_CallVoidMethod, + .CallVoidMethodA = globalEnv_CallVoidMethodA, + .CallNonvirtualObjectMethod = globalEnv_CallNonvirtualObjectMethod, + .CallNonvirtualObjectMethodA = globalEnv_CallNonvirtualObjectMethodA, + .CallNonvirtualBooleanMethod = globalEnv_CallNonvirtualBooleanMethod, + .CallNonvirtualBooleanMethodA = globalEnv_CallNonvirtualBooleanMethodA, + .CallNonvirtualByteMethod = globalEnv_CallNonvirtualByteMethod, + .CallNonvirtualByteMethodA = globalEnv_CallNonvirtualByteMethodA, + .CallNonvirtualCharMethod = globalEnv_CallNonvirtualCharMethod, + .CallNonvirtualCharMethodA = globalEnv_CallNonvirtualCharMethodA, + .CallNonvirtualShortMethod = globalEnv_CallNonvirtualShortMethod, + .CallNonvirtualShortMethodA = globalEnv_CallNonvirtualShortMethodA, + .CallNonvirtualIntMethod = globalEnv_CallNonvirtualIntMethod, + .CallNonvirtualIntMethodA = globalEnv_CallNonvirtualIntMethodA, + .CallNonvirtualLongMethod = globalEnv_CallNonvirtualLongMethod, + .CallNonvirtualLongMethodA = globalEnv_CallNonvirtualLongMethodA, + .CallNonvirtualFloatMethod = globalEnv_CallNonvirtualFloatMethod, + .CallNonvirtualFloatMethodA = globalEnv_CallNonvirtualFloatMethodA, + .CallNonvirtualDoubleMethod = globalEnv_CallNonvirtualDoubleMethod, + .CallNonvirtualDoubleMethodA = globalEnv_CallNonvirtualDoubleMethodA, + .CallNonvirtualVoidMethod = globalEnv_CallNonvirtualVoidMethod, + .CallNonvirtualVoidMethodA = globalEnv_CallNonvirtualVoidMethodA, + .GetFieldID = globalEnv_GetFieldID, + .GetObjectField = globalEnv_GetObjectField, + .GetBooleanField = globalEnv_GetBooleanField, + .GetByteField = globalEnv_GetByteField, + .GetCharField = globalEnv_GetCharField, + .GetShortField = globalEnv_GetShortField, + .GetIntField = globalEnv_GetIntField, + .GetLongField = globalEnv_GetLongField, + .GetFloatField = globalEnv_GetFloatField, + .GetDoubleField = globalEnv_GetDoubleField, + .SetObjectField = globalEnv_SetObjectField, + .SetBooleanField = globalEnv_SetBooleanField, + .SetByteField = globalEnv_SetByteField, + .SetCharField = globalEnv_SetCharField, + .SetShortField = globalEnv_SetShortField, + .SetIntField = globalEnv_SetIntField, + .SetLongField = globalEnv_SetLongField, + .SetFloatField = globalEnv_SetFloatField, + .SetDoubleField = globalEnv_SetDoubleField, + .GetStaticMethodID = globalEnv_GetStaticMethodID, + .CallStaticObjectMethod = globalEnv_CallStaticObjectMethod, + .CallStaticObjectMethodA = globalEnv_CallStaticObjectMethodA, + .CallStaticBooleanMethod = globalEnv_CallStaticBooleanMethod, + .CallStaticBooleanMethodA = globalEnv_CallStaticBooleanMethodA, + .CallStaticByteMethod = globalEnv_CallStaticByteMethod, + .CallStaticByteMethodA = globalEnv_CallStaticByteMethodA, + .CallStaticCharMethod = globalEnv_CallStaticCharMethod, + .CallStaticCharMethodA = globalEnv_CallStaticCharMethodA, + .CallStaticShortMethod = globalEnv_CallStaticShortMethod, + .CallStaticShortMethodA = globalEnv_CallStaticShortMethodA, + .CallStaticIntMethod = globalEnv_CallStaticIntMethod, + .CallStaticIntMethodA = globalEnv_CallStaticIntMethodA, + .CallStaticLongMethod = globalEnv_CallStaticLongMethod, + .CallStaticLongMethodA = globalEnv_CallStaticLongMethodA, + .CallStaticFloatMethod = globalEnv_CallStaticFloatMethod, + .CallStaticFloatMethodA = globalEnv_CallStaticFloatMethodA, + .CallStaticDoubleMethod = globalEnv_CallStaticDoubleMethod, + .CallStaticDoubleMethodA = globalEnv_CallStaticDoubleMethodA, + .CallStaticVoidMethod = globalEnv_CallStaticVoidMethod, + .CallStaticVoidMethodA = globalEnv_CallStaticVoidMethodA, + .GetStaticFieldID = globalEnv_GetStaticFieldID, + .GetStaticObjectField = globalEnv_GetStaticObjectField, + .GetStaticBooleanField = globalEnv_GetStaticBooleanField, + .GetStaticByteField = globalEnv_GetStaticByteField, + .GetStaticCharField = globalEnv_GetStaticCharField, + .GetStaticShortField = globalEnv_GetStaticShortField, + .GetStaticIntField = globalEnv_GetStaticIntField, + .GetStaticLongField = globalEnv_GetStaticLongField, + .GetStaticFloatField = globalEnv_GetStaticFloatField, + .GetStaticDoubleField = globalEnv_GetStaticDoubleField, + .SetStaticObjectField = globalEnv_SetStaticObjectField, + .SetStaticBooleanField = globalEnv_SetStaticBooleanField, + .SetStaticByteField = globalEnv_SetStaticByteField, + .SetStaticCharField = globalEnv_SetStaticCharField, + .SetStaticShortField = globalEnv_SetStaticShortField, + .SetStaticIntField = globalEnv_SetStaticIntField, + .SetStaticLongField = globalEnv_SetStaticLongField, + .SetStaticFloatField = globalEnv_SetStaticFloatField, + .SetStaticDoubleField = globalEnv_SetStaticDoubleField, + .NewString = globalEnv_NewString, + .GetStringLength = globalEnv_GetStringLength, + .GetStringChars = globalEnv_GetStringChars, + .ReleaseStringChars = globalEnv_ReleaseStringChars, + .NewStringUTF = globalEnv_NewStringUTF, + .GetStringUTFLength = globalEnv_GetStringUTFLength, + .GetStringUTFChars = globalEnv_GetStringUTFChars, + .ReleaseStringUTFChars = globalEnv_ReleaseStringUTFChars, + .GetArrayLength = globalEnv_GetArrayLength, + .NewObjectArray = globalEnv_NewObjectArray, + .GetObjectArrayElement = globalEnv_GetObjectArrayElement, + .SetObjectArrayElement = globalEnv_SetObjectArrayElement, + .NewBooleanArray = globalEnv_NewBooleanArray, + .NewByteArray = globalEnv_NewByteArray, + .NewCharArray = globalEnv_NewCharArray, + .NewShortArray = globalEnv_NewShortArray, + .NewIntArray = globalEnv_NewIntArray, + .NewLongArray = globalEnv_NewLongArray, + .NewFloatArray = globalEnv_NewFloatArray, + .NewDoubleArray = globalEnv_NewDoubleArray, + .GetBooleanArrayElements = globalEnv_GetBooleanArrayElements, + .GetByteArrayElements = globalEnv_GetByteArrayElements, + .GetCharArrayElements = globalEnv_GetCharArrayElements, + .GetShortArrayElements = globalEnv_GetShortArrayElements, + .GetIntArrayElements = globalEnv_GetIntArrayElements, + .GetLongArrayElements = globalEnv_GetLongArrayElements, + .GetFloatArrayElements = globalEnv_GetFloatArrayElements, + .GetDoubleArrayElements = globalEnv_GetDoubleArrayElements, + .ReleaseBooleanArrayElements = globalEnv_ReleaseBooleanArrayElements, + .ReleaseByteArrayElements = globalEnv_ReleaseByteArrayElements, + .ReleaseCharArrayElements = globalEnv_ReleaseCharArrayElements, + .ReleaseShortArrayElements = globalEnv_ReleaseShortArrayElements, + .ReleaseIntArrayElements = globalEnv_ReleaseIntArrayElements, + .ReleaseLongArrayElements = globalEnv_ReleaseLongArrayElements, + .ReleaseFloatArrayElements = globalEnv_ReleaseFloatArrayElements, + .ReleaseDoubleArrayElements = globalEnv_ReleaseDoubleArrayElements, + .GetBooleanArrayRegion = globalEnv_GetBooleanArrayRegion, + .GetByteArrayRegion = globalEnv_GetByteArrayRegion, + .GetCharArrayRegion = globalEnv_GetCharArrayRegion, + .GetShortArrayRegion = globalEnv_GetShortArrayRegion, + .GetIntArrayRegion = globalEnv_GetIntArrayRegion, + .GetLongArrayRegion = globalEnv_GetLongArrayRegion, + .GetFloatArrayRegion = globalEnv_GetFloatArrayRegion, + .GetDoubleArrayRegion = globalEnv_GetDoubleArrayRegion, + .SetBooleanArrayRegion = globalEnv_SetBooleanArrayRegion, + .SetByteArrayRegion = globalEnv_SetByteArrayRegion, + .SetCharArrayRegion = globalEnv_SetCharArrayRegion, + .SetShortArrayRegion = globalEnv_SetShortArrayRegion, + .SetIntArrayRegion = globalEnv_SetIntArrayRegion, + .SetLongArrayRegion = globalEnv_SetLongArrayRegion, + .SetFloatArrayRegion = globalEnv_SetFloatArrayRegion, + .SetDoubleArrayRegion = globalEnv_SetDoubleArrayRegion, + .RegisterNatives = globalEnv_RegisterNatives, + .UnregisterNatives = globalEnv_UnregisterNatives, + .MonitorEnter = globalEnv_MonitorEnter, + .MonitorExit = globalEnv_MonitorExit, + .GetJavaVM = globalEnv_GetJavaVM, + .GetStringRegion = globalEnv_GetStringRegion, + .GetStringUTFRegion = globalEnv_GetStringUTFRegion, + .GetPrimitiveArrayCritical = globalEnv_GetPrimitiveArrayCritical, + .ReleasePrimitiveArrayCritical = globalEnv_ReleasePrimitiveArrayCritical, + .GetStringCritical = globalEnv_GetStringCritical, + .ReleaseStringCritical = globalEnv_ReleaseStringCritical, + .NewWeakGlobalRef = globalEnv_NewWeakGlobalRef, + .DeleteWeakGlobalRef = globalEnv_DeleteWeakGlobalRef, + .ExceptionCheck = globalEnv_ExceptionCheck, + .NewDirectByteBuffer = globalEnv_NewDirectByteBuffer, + .GetDirectBufferAddress = globalEnv_GetDirectBufferAddress, + .GetDirectBufferCapacity = globalEnv_GetDirectBufferCapacity, + .GetObjectRefType = globalEnv_GetObjectRefType, +}; + +FFI_PLUGIN_EXPORT +GlobalJniEnv* GetGlobalEnv() { + if (jni.jvm == NULL) { + return NULL; + } + return &globalJniEnv; +} \ No newline at end of file
diff --git a/pkgs/jni/src/third_party/global_jni_env.h b/pkgs/jni/src/third_party/global_jni_env.h new file mode 100644 index 0000000..e4229c6 --- /dev/null +++ b/pkgs/jni/src/third_party/global_jni_env.h
@@ -0,0 +1,375 @@ +// Generated from an annotated version of jni.h provided in Android NDK. +// (NDK Version 23.1.7779620) +// The license for original file is provided below: + +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <jni.h> +/// Wrapper over JNIEnv in the JNI API, which can be used from multiple Dart +/// Threads. +/// +/// It consists of wrappers to JNIEnv methods which manage the thread-local +/// JNIEnv pointer in C code. Additionally, any returned local reference value +/// is converted to global reference. +/// +/// For the documentation on methods themselves, see the JNI Specification at +/// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html +/// +/// Apart from the specification, the Android NDK's JNI page consists of useful +/// information about using the JNI: +/// https://developer.android.com/training/articles/perf-jni +typedef struct GlobalJniEnv { + jint (*GetVersion)(); + jclass (*DefineClass)(const char* name, + jobject loader, + const jbyte* buf, + jsize bufLen); + jclass (*FindClass)(const char* name); + jmethodID (*FromReflectedMethod)(jobject method); + jfieldID (*FromReflectedField)(jobject field); + jobject (*ToReflectedMethod)(jclass cls, + jmethodID methodId, + jboolean isStatic); + jclass (*GetSuperclass)(jclass clazz); + jboolean (*IsAssignableFrom)(jclass clazz1, jclass clazz2); + jobject (*ToReflectedField)(jclass cls, jfieldID fieldID, jboolean isStatic); + jint (*Throw)(jthrowable obj); + jint (*ThrowNew)(jclass clazz, const char* message); + jthrowable (*ExceptionOccurred)(); + void (*ExceptionDescribe)(); + void (*ExceptionClear)(); + void (*FatalError)(const char* msg); + jint (*PushLocalFrame)(jint capacity); + jobject (*PopLocalFrame)(jobject result); + jobject (*NewGlobalRef)(jobject obj); + void (*DeleteGlobalRef)(jobject globalRef); + jboolean (*IsSameObject)(jobject ref1, jobject ref2); + jint (*EnsureLocalCapacity)(jint capacity); + jobject (*AllocObject)(jclass clazz); + jobject (*NewObject)(jclass arg1, jmethodID arg2); + jobject (*NewObjectA)(jclass clazz, jmethodID methodID, const jvalue* args); + jclass (*GetObjectClass)(jobject obj); + jboolean (*IsInstanceOf)(jobject obj, jclass clazz); + jmethodID (*GetMethodID)(jclass clazz, const char* name, const char* sig); + jobject (*CallObjectMethod)(jobject arg1, jmethodID arg2); + jobject (*CallObjectMethodA)(jobject obj, + jmethodID methodID, + const jvalue* args); + jboolean (*CallBooleanMethod)(jobject arg1, jmethodID arg2); + jboolean (*CallBooleanMethodA)(jobject obj, + jmethodID methodId, + const jvalue* args); + jbyte (*CallByteMethod)(jobject arg1, jmethodID arg2); + jbyte (*CallByteMethodA)(jobject obj, jmethodID methodID, const jvalue* args); + jchar (*CallCharMethod)(jobject arg1, jmethodID arg2); + jchar (*CallCharMethodA)(jobject obj, jmethodID methodID, const jvalue* args); + jshort (*CallShortMethod)(jobject arg1, jmethodID arg2); + jshort (*CallShortMethodA)(jobject obj, + jmethodID methodID, + const jvalue* args); + jint (*CallIntMethod)(jobject arg1, jmethodID arg2); + jint (*CallIntMethodA)(jobject obj, jmethodID methodID, const jvalue* args); + jlong (*CallLongMethod)(jobject arg1, jmethodID arg2); + jlong (*CallLongMethodA)(jobject obj, jmethodID methodID, const jvalue* args); + jfloat (*CallFloatMethod)(jobject arg1, jmethodID arg2); + jfloat (*CallFloatMethodA)(jobject obj, + jmethodID methodID, + const jvalue* args); + jdouble (*CallDoubleMethod)(jobject arg1, jmethodID arg2); + jdouble (*CallDoubleMethodA)(jobject obj, + jmethodID methodID, + const jvalue* args); + void (*CallVoidMethod)(jobject arg1, jmethodID arg2); + void (*CallVoidMethodA)(jobject obj, jmethodID methodID, const jvalue* args); + jobject (*CallNonvirtualObjectMethod)(jobject arg1, + jclass arg2, + jmethodID arg3); + jobject (*CallNonvirtualObjectMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jboolean (*CallNonvirtualBooleanMethod)(jobject arg1, + jclass arg2, + jmethodID arg3); + jboolean (*CallNonvirtualBooleanMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jbyte (*CallNonvirtualByteMethod)(jobject arg1, jclass arg2, jmethodID arg3); + jbyte (*CallNonvirtualByteMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jchar (*CallNonvirtualCharMethod)(jobject arg1, jclass arg2, jmethodID arg3); + jchar (*CallNonvirtualCharMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jshort (*CallNonvirtualShortMethod)(jobject arg1, + jclass arg2, + jmethodID arg3); + jshort (*CallNonvirtualShortMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jint (*CallNonvirtualIntMethod)(jobject arg1, jclass arg2, jmethodID arg3); + jint (*CallNonvirtualIntMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jlong (*CallNonvirtualLongMethod)(jobject arg1, jclass arg2, jmethodID arg3); + jlong (*CallNonvirtualLongMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jfloat (*CallNonvirtualFloatMethod)(jobject arg1, + jclass arg2, + jmethodID arg3); + jfloat (*CallNonvirtualFloatMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jdouble (*CallNonvirtualDoubleMethod)(jobject arg1, + jclass arg2, + jmethodID arg3); + jdouble (*CallNonvirtualDoubleMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + void (*CallNonvirtualVoidMethod)(jobject arg1, jclass arg2, jmethodID arg3); + void (*CallNonvirtualVoidMethodA)(jobject obj, + jclass clazz, + jmethodID methodID, + const jvalue* args); + jfieldID (*GetFieldID)(jclass clazz, const char* name, const char* sig); + jobject (*GetObjectField)(jobject obj, jfieldID fieldID); + jboolean (*GetBooleanField)(jobject obj, jfieldID fieldID); + jbyte (*GetByteField)(jobject obj, jfieldID fieldID); + jchar (*GetCharField)(jobject obj, jfieldID fieldID); + jshort (*GetShortField)(jobject obj, jfieldID fieldID); + jint (*GetIntField)(jobject obj, jfieldID fieldID); + jlong (*GetLongField)(jobject obj, jfieldID fieldID); + jfloat (*GetFloatField)(jobject obj, jfieldID fieldID); + jdouble (*GetDoubleField)(jobject obj, jfieldID fieldID); + void (*SetObjectField)(jobject obj, jfieldID fieldID, jobject val); + void (*SetBooleanField)(jobject obj, jfieldID fieldID, jboolean val); + void (*SetByteField)(jobject obj, jfieldID fieldID, jbyte val); + void (*SetCharField)(jobject obj, jfieldID fieldID, jchar val); + void (*SetShortField)(jobject obj, jfieldID fieldID, jshort val); + void (*SetIntField)(jobject obj, jfieldID fieldID, jint val); + void (*SetLongField)(jobject obj, jfieldID fieldID, jlong val); + void (*SetFloatField)(jobject obj, jfieldID fieldID, jfloat val); + void (*SetDoubleField)(jobject obj, jfieldID fieldID, jdouble val); + jmethodID (*GetStaticMethodID)(jclass clazz, + const char* name, + const char* sig); + jobject (*CallStaticObjectMethod)(jclass arg1, jmethodID arg2); + jobject (*CallStaticObjectMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jboolean (*CallStaticBooleanMethod)(jclass arg1, jmethodID arg2); + jboolean (*CallStaticBooleanMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jbyte (*CallStaticByteMethod)(jclass arg1, jmethodID arg2); + jbyte (*CallStaticByteMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jchar (*CallStaticCharMethod)(jclass arg1, jmethodID arg2); + jchar (*CallStaticCharMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jshort (*CallStaticShortMethod)(jclass arg1, jmethodID arg2); + jshort (*CallStaticShortMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jint (*CallStaticIntMethod)(jclass arg1, jmethodID arg2); + jint (*CallStaticIntMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jlong (*CallStaticLongMethod)(jclass arg1, jmethodID arg2); + jlong (*CallStaticLongMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jfloat (*CallStaticFloatMethod)(jclass arg1, jmethodID arg2); + jfloat (*CallStaticFloatMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jdouble (*CallStaticDoubleMethod)(jclass arg1, jmethodID arg2); + jdouble (*CallStaticDoubleMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + void (*CallStaticVoidMethod)(jclass arg1, jmethodID arg2); + void (*CallStaticVoidMethodA)(jclass clazz, + jmethodID methodID, + const jvalue* args); + jfieldID (*GetStaticFieldID)(jclass clazz, const char* name, const char* sig); + jobject (*GetStaticObjectField)(jclass clazz, jfieldID fieldID); + jboolean (*GetStaticBooleanField)(jclass clazz, jfieldID fieldID); + jbyte (*GetStaticByteField)(jclass clazz, jfieldID fieldID); + jchar (*GetStaticCharField)(jclass clazz, jfieldID fieldID); + jshort (*GetStaticShortField)(jclass clazz, jfieldID fieldID); + jint (*GetStaticIntField)(jclass clazz, jfieldID fieldID); + jlong (*GetStaticLongField)(jclass clazz, jfieldID fieldID); + jfloat (*GetStaticFloatField)(jclass clazz, jfieldID fieldID); + jdouble (*GetStaticDoubleField)(jclass clazz, jfieldID fieldID); + void (*SetStaticObjectField)(jclass clazz, jfieldID fieldID, jobject val); + void (*SetStaticBooleanField)(jclass clazz, jfieldID fieldID, jboolean val); + void (*SetStaticByteField)(jclass clazz, jfieldID fieldID, jbyte val); + void (*SetStaticCharField)(jclass clazz, jfieldID fieldID, jchar val); + void (*SetStaticShortField)(jclass clazz, jfieldID fieldID, jshort val); + void (*SetStaticIntField)(jclass clazz, jfieldID fieldID, jint val); + void (*SetStaticLongField)(jclass clazz, jfieldID fieldID, jlong val); + void (*SetStaticFloatField)(jclass clazz, jfieldID fieldID, jfloat val); + void (*SetStaticDoubleField)(jclass clazz, jfieldID fieldID, jdouble val); + jstring (*NewString)(const jchar* unicodeChars, jsize len); + jsize (*GetStringLength)(jstring string); + const jchar* (*GetStringChars)(jstring string, jboolean* isCopy); + void (*ReleaseStringChars)(jstring string, const jchar* isCopy); + jstring (*NewStringUTF)(const char* bytes); + jsize (*GetStringUTFLength)(jstring string); + const char* (*GetStringUTFChars)(jstring string, jboolean* isCopy); + void (*ReleaseStringUTFChars)(jstring string, const char* utf); + jsize (*GetArrayLength)(jarray array); + jobjectArray (*NewObjectArray)(jsize length, + jclass elementClass, + jobject initialElement); + jobject (*GetObjectArrayElement)(jobjectArray array, jsize index); + void (*SetObjectArrayElement)(jobjectArray array, jsize index, jobject val); + jbooleanArray (*NewBooleanArray)(jsize length); + jbyteArray (*NewByteArray)(jsize length); + jcharArray (*NewCharArray)(jsize length); + jshortArray (*NewShortArray)(jsize length); + jintArray (*NewIntArray)(jsize length); + jlongArray (*NewLongArray)(jsize length); + jfloatArray (*NewFloatArray)(jsize length); + jdoubleArray (*NewDoubleArray)(jsize length); + jboolean* (*GetBooleanArrayElements)(jbooleanArray array, jboolean* isCopy); + jbyte* (*GetByteArrayElements)(jbyteArray array, jboolean* isCopy); + jchar* (*GetCharArrayElements)(jcharArray array, jboolean* isCopy); + jshort* (*GetShortArrayElements)(jshortArray array, jboolean* isCopy); + jint* (*GetIntArrayElements)(jintArray array, jboolean* isCopy); + jlong* (*GetLongArrayElements)(jlongArray array, jboolean* isCopy); + jfloat* (*GetFloatArrayElements)(jfloatArray array, jboolean* isCopy); + jdouble* (*GetDoubleArrayElements)(jdoubleArray array, jboolean* isCopy); + void (*ReleaseBooleanArrayElements)(jbooleanArray array, + jboolean* elems, + jint mode); + void (*ReleaseByteArrayElements)(jbyteArray array, jbyte* elems, jint mode); + void (*ReleaseCharArrayElements)(jcharArray array, jchar* elems, jint mode); + void (*ReleaseShortArrayElements)(jshortArray array, + jshort* elems, + jint mode); + void (*ReleaseIntArrayElements)(jintArray array, jint* elems, jint mode); + void (*ReleaseLongArrayElements)(jlongArray array, jlong* elems, jint mode); + void (*ReleaseFloatArrayElements)(jfloatArray array, + jfloat* elems, + jint mode); + void (*ReleaseDoubleArrayElements)(jdoubleArray array, + jdouble* elems, + jint mode); + void (*GetBooleanArrayRegion)(jbooleanArray array, + jsize start, + jsize len, + jboolean* buf); + void (*GetByteArrayRegion)(jbyteArray array, + jsize start, + jsize len, + jbyte* buf); + void (*GetCharArrayRegion)(jcharArray array, + jsize start, + jsize len, + jchar* buf); + void (*GetShortArrayRegion)(jshortArray array, + jsize start, + jsize len, + jshort* buf); + void (*GetIntArrayRegion)(jintArray array, jsize start, jsize len, jint* buf); + void (*GetLongArrayRegion)(jlongArray array, + jsize start, + jsize len, + jlong* buf); + void (*GetFloatArrayRegion)(jfloatArray array, + jsize start, + jsize len, + jfloat* buf); + void (*GetDoubleArrayRegion)(jdoubleArray array, + jsize start, + jsize len, + jdouble* buf); + void (*SetBooleanArrayRegion)(jbooleanArray array, + jsize start, + jsize len, + const jboolean* buf); + void (*SetByteArrayRegion)(jbyteArray array, + jsize start, + jsize len, + const jbyte* buf); + void (*SetCharArrayRegion)(jcharArray array, + jsize start, + jsize len, + const jchar* buf); + void (*SetShortArrayRegion)(jshortArray array, + jsize start, + jsize len, + const jshort* buf); + void (*SetIntArrayRegion)(jintArray array, + jsize start, + jsize len, + const jint* buf); + void (*SetLongArrayRegion)(jlongArray array, + jsize start, + jsize len, + const jlong* buf); + void (*SetFloatArrayRegion)(jfloatArray array, + jsize start, + jsize len, + const jfloat* buf); + void (*SetDoubleArrayRegion)(jdoubleArray array, + jsize start, + jsize len, + const jdouble* buf); + jint (*RegisterNatives)(jclass clazz, + const JNINativeMethod* methods, + jint nMethods); + jint (*UnregisterNatives)(jclass clazz); + jint (*MonitorEnter)(jobject obj); + jint (*MonitorExit)(jobject obj); + jint (*GetJavaVM)(JavaVM** vm); + void (*GetStringRegion)(jstring str, jsize start, jsize len, jchar* buf); + void (*GetStringUTFRegion)(jstring str, jsize start, jsize len, char* buf); + void* (*GetPrimitiveArrayCritical)(jarray array, jboolean* isCopy); + void (*ReleasePrimitiveArrayCritical)(jarray array, void* carray, jint mode); + const jchar* (*GetStringCritical)(jstring str, jboolean* isCopy); + void (*ReleaseStringCritical)(jstring str, const jchar* carray); + jweak (*NewWeakGlobalRef)(jobject obj); + void (*DeleteWeakGlobalRef)(jweak obj); + jboolean (*ExceptionCheck)(); + jobject (*NewDirectByteBuffer)(void* address, jlong capacity); + void* (*GetDirectBufferAddress)(jobject buf); + jlong (*GetDirectBufferCapacity)(jobject buf); + jobjectRefType (*GetObjectRefType)(jobject obj); +} GlobalJniEnv; + +// Note(Mahesh): Redoing some work here, because I want these 2 files separate. +// including dartjni.h for FFI_PLUGIN_EXPORT leads to more issues. + +#if _WIN32 +__declspec(dllexport) GlobalJniEnv* GetGlobalEnv(); +#else +GlobalJniEnv* GetGlobalEnv(); +#endif \ No newline at end of file
diff --git a/pkgs/jni/test/jni_object_test.dart b/pkgs/jni/test/jni_object_test.dart index 1cca591..2a86dd4 100644 --- a/pkgs/jni/test/jni_object_test.dart +++ b/pkgs/jni/test/jni_object_test.dart
@@ -122,7 +122,7 @@ }); // One-off access of static field in single call. - test("retrieve_", () { + test("Get static field directly", () { final maxLong = Jni.retrieveStaticField<int>( "java/lang/Short", "MAX_VALUE", "S", JniType.shortType); expect(maxLong, equals(32767)); @@ -142,15 +142,18 @@ // you can also pass Dart strings, apart from range of types // allowed by Jni.jvalues // They will be converted automatically. - test("Passing strings in arguments", () { - final out = Jni.retrieveStaticField<JniObject>( - "java/lang/System", "out", "Ljava/io/PrintStream;"); - // uncomment next line to see output - // (\n because test runner prints first char at end of the line) - //out.callMethodByName<Null>( - // "println", "(Ljava/lang/Object;)V", ["\nWorks (Apparently)"]); - out.delete(); - }); + test( + "Passing strings in arguments", + () { + final out = Jni.retrieveStaticField<JniObject>( + "java/lang/System", "out", "Ljava/io/PrintStream;"); + // uncomment next line to see output + // (\n because test runner prints first char at end of the line) + //out.callMethodByName<Null>( + // "println", "(Ljava/lang/Object;)V", ["\nWorks (Apparently)"]); + out.delete(); + }, + ); test("Passing strings in arguments 2", () { final twelve = Jni.invokeStaticMethod<int>("java/lang/Byte", "parseByte", @@ -195,15 +198,15 @@ } void doSomeWorkInIsolate(Void? _) { - // On standalone target, make sure to call load - // when doing getInstance first time in a new isolate. + // On standalone target, make sure to call [setDylibDir] before accessing + // any JNI function. // // otherwise getInstance will throw a "library not found" exception. Jni.setDylibDir(dylibDir: "build/jni_libs"); final random = Jni.newInstance("java/util/Random", "()V", []); - // final r = random.callIntMethodByName("nextInt", "(I)I", [256]); + // final r = random.callMethodByName<int>("nextInt", "(I)I", [256]); // expect(r, lessThan(256)); - // Expect throws an OutsideTestException + // Expect throws an [OutsideTestException] // but you can uncomment below print and see it works // print("\n$r"); random.delete();
diff --git a/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/compound.dart b/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/compound.dart index 66592e2..3d477e8 100644 --- a/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/compound.dart +++ b/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/compound.dart
@@ -15,7 +15,7 @@ // Y is extension class if it contains function pointer fields which are // otherwise equivalent to normal functions, and just packed in a structure // for convenience. -const extensionClasses = {'GlobalJniEnv', 'JniNativeExtensions'}; +const extensionClasses = {'GlobalJniEnv', 'JniAccessors'}; const methodNameRenames = {"throw": "throwException"}; @@ -139,6 +139,8 @@ if (dartDoc != null) { s.write(makeDartDoc(dartDoc!)); } + final voidPointer = + "${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.Void>"; /// Adding [enclosingClassName] because dart doesn't allow class member /// to have the same name as the class. @@ -186,8 +188,7 @@ final fnType = nf.type as FunctionType; if (hasVarArgListParam) { - s.write( - '${depth}external ${m.type.getDartType(w)} _${m.name};\n\n'); + s.write('${depth}external $voidPointer _${m.name};\n\n'); continue; } @@ -228,7 +229,9 @@ es.write(");\n$depth}\n\n"); } else { final memberName = hasVarArgListParam ? '_${m.name}' : m.name; - s.write('${depth}external ${m.type.getDartType(w)} $memberName;\n\n'); + final memberType = + hasVarArgListParam ? voidPointer : m.type.getDartType(w); + s.write('${depth}external $memberType $memberName;\n\n'); } } }
diff --git a/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/writer.dart b/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/writer.dart index 77e9e83..0bacf55 100644 --- a/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/writer.dart +++ b/pkgs/jni/third_party/ffigen_patch_jni/lib/src/code_generator/writer.dart
@@ -239,7 +239,9 @@ /// Write [noLookUpBindings]. for (final b in noLookUpBindings) { - s.write(b.toBindingString(this).string); + if (b.name != '__va_list_tag') { + s.write(b.toBindingString(this).string); + } } // Write neccesary imports.
diff --git a/pkgs/jnigen/.gitignore b/pkgs/jnigen/.gitignore index 2c67603..c66a8b6 100644 --- a/pkgs/jnigen/.gitignore +++ b/pkgs/jnigen/.gitignore
@@ -40,3 +40,7 @@ # Vim .*.swp +## Downloaded files and build artifacts +*.jar +*.class +*.exe
diff --git a/pkgs/jnigen/README.md b/pkgs/jnigen/README.md index 642bcf1..f8b6d44 100644 --- a/pkgs/jnigen/README.md +++ b/pkgs/jnigen/README.md
@@ -9,16 +9,19 @@ | [jni](jni/) | Ergonomic C bindings to JNI C API and several helper methods | | [jnigen](jnigen/) | Tool to generate Dart bindings to Java code using FFI | -This is a work-in-progress project under Google Summer of Code 2022 program. +This project is a work-in-progress and highly experimental. Users can check out and experiment with the examples. We do not guarantee stability of the API or commands at this point. ## SDK Requirements Dart standalone target is supported, but due to some problems with pubspec, the `dart` command must be from Flutter SDK and not Dart SDK. See [dart-lang/pub#3563](https://github.com/dart-lang/pub/issues/3563). +Along with JDK, maven (`mvn` command) is required. On Windows, it can be installed using a package manager such as `chocolatey` or `scoop`. + On windows, you need to append the path of `jvm.dll` in your JDK installation to PATH. For example, on Powershell: -``` +```powershell $env:Path += ";${env:JAVA_HOME}\bin\server". ``` + (If JAVA_HOME not set, find the `java.exe` executable and set the environment variable in Control Panel).
diff --git a/pkgs/jnigen/bin/setup.dart b/pkgs/jnigen/bin/setup.dart index c2937bf..54df277 100644 --- a/pkgs/jnigen/bin/setup.dart +++ b/pkgs/jnigen/bin/setup.dart
@@ -4,10 +4,13 @@ import 'dart:io'; +import 'package:jnigen/src/logging/logging.dart'; import 'package:jnigen/src/tools/tools.dart'; +import 'package:logging/logging.dart'; void main(List<String> args) async { bool force = false; + setLoggingLevel(Level.INFO); if (args.isNotEmpty) { if (args.length != 1 || args[0] != '-f') { stderr.writeln('usage: dart run jnigen:setup [-f]');
diff --git a/pkgs/jnigen/cmake/README.md b/pkgs/jnigen/cmake/README.md new file mode 100644 index 0000000..65f9951 --- /dev/null +++ b/pkgs/jnigen/cmake/README.md
@@ -0,0 +1,3 @@ +This directory contains a CMakeLists.txt.tmpl file which will be used to generate build configuration for generated C code. + +`{{LIBRARY_NAME}}` in this template will be replaced by `library_name` configuration parameter. \ No newline at end of file
diff --git a/pkgs/jnigen/example/in_app_java/android/build.gradle b/pkgs/jnigen/example/in_app_java/android/build.gradle index e42825f..88265a9 100644 --- a/pkgs/jnigen/example/in_app_java/android/build.gradle +++ b/pkgs/jnigen/example/in_app_java/android/build.gradle
@@ -30,4 +30,3 @@ task clean(type: Delete) { delete rootProject.buildDir } -
diff --git a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h +++ b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jnigen/example/notification_plugin/example/pubspec.yaml b/pkgs/jnigen/example/notification_plugin/example/pubspec.yaml index fad3bdb..127c7dc 100644 --- a/pkgs/jnigen/example/notification_plugin/example/pubspec.yaml +++ b/pkgs/jnigen/example/notification_plugin/example/pubspec.yaml
@@ -20,7 +20,7 @@ version: 1.0.0+1 environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=2.17.6 <3.0.0' dependencies: flutter:
diff --git a/pkgs/jnigen/example/notification_plugin/pubspec.yaml b/pkgs/jnigen/example/notification_plugin/pubspec.yaml index 77cc904..f059a55 100644 --- a/pkgs/jnigen/example/notification_plugin/pubspec.yaml +++ b/pkgs/jnigen/example/notification_plugin/pubspec.yaml
@@ -5,7 +5,7 @@ homepage: https://github.com/dart-lang/jnigen environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=2.17.6 <3.0.0' flutter: ">=2.11.0" dependencies:
diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jnigen/example/notification_plugin/src/dartjni.h +++ b/pkgs/jnigen/example/notification_plugin/src/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jnigen/example/pdfbox_plugin/.gitignore b/pkgs/jnigen/example/pdfbox_plugin/.gitignore index b3f33fc..a117acd 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/.gitignore +++ b/pkgs/jnigen/example/pdfbox_plugin/.gitignore
@@ -35,3 +35,8 @@ ## Sometimes generated by maven. ## TODO(#34) This is unwanted. /target/ + +# Builds & downloads +*.pdf +*.jar +*.exe
diff --git a/pkgs/jnigen/example/pdfbox_plugin/dart_example/bin/pdf_info.dart b/pkgs/jnigen/example/pdfbox_plugin/dart_example/bin/pdf_info.dart index c16ac20..b3affc5 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/dart_example/bin/pdf_info.dart +++ b/pkgs/jnigen/example/pdfbox_plugin/dart_example/bin/pdf_info.dart
@@ -12,7 +12,6 @@ void writeInfo(String file) { final inputFile = Jni.newInstance( "java/io/FileInputStream", "(Ljava/lang/String;)V", [file]); - final pdDoc = PDDocument.load7(inputFile); int pages = pdDoc.getNumberOfPages(); final info = pdDoc.getDocumentInformation();
diff --git a/pkgs/jnigen/example/pdfbox_plugin/jnigen_full.yaml b/pkgs/jnigen/example/pdfbox_plugin/jnigen_full.yaml deleted file mode 100644 index 2652756..0000000 --- a/pkgs/jnigen/example/pdfbox_plugin/jnigen_full.yaml +++ /dev/null
@@ -1,63 +0,0 @@ -## Name of the generated library, this is required and used as the name of -## shared library which contains C bindings -library_name: pdfbox_plugin - -## String to be pasted verbatim into generated bindings -preamble: | - // Generated from Apache PDFBox library which is licensed under the Apache License 2.0. - // The following copyright from the original authors applies. - // - // Licensed to the Apache Software Foundation (ASF) under one or more - // contributor license agreements. See the NOTICE file distributed with - // this work for additional information regarding copyright ownership. - // The ASF licenses this file to You under the Apache License, Version 2.0 - // (the "License"); you may not use this file except in compliance with - // the License. You may obtain a copy of the License at - // - // http://www.apache.org/licenses/LICENSE-2.0 - // - // Unless required by applicable law or agreed to in writing, software - // distributed under the License is distributed on an "AS IS" BASIS, - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - // See the License for the specific language governing permissions and - // limitations under the License. - -## Root for generated C bindings. -c_root: 'src/' - -## C files can be stored in a different sub-directory inside c_root. -## -## We have a guideline to keep all generated code in third_party/ since original -## project's license applies to generated code. So we specify third_party/ as -## c_subdir while keeping generated CMakeLists.txt in src/. -c_subdir: 'third_party/' - -## Root for generated Dart bindings. -dart_root: 'lib/third_party/' - -## Classes / packages for which bindings need to be generated. -classes: - - 'org.apache.pdfbox.pdmodel' - - 'org.apache.pdfbox.text' - -## Dependencies to be downloaded using Maven (Invokes `mvn` command). These -## dependencies are always downloaded along with their transitive dependencies. -## -## Dependencies should be specified using groupID:artifactID:version format -## The downloaded dependencies are automatically added to classpath of the -## Java API scanning process. There's no need to specify these again in -## sourcepath and classpath. -## -## Note that when maven based tooling is used, the first run often has to fetch -## all the dependencies and might take some time. However, maven caches most -## artifacts in local repository, thus subsequent runs will be faster. -maven_downloads: - ## For these dependencies, both source and JARs are downloaded. - source_deps: - - 'org.apache.pdfbox:pdfbox:2.0.26' - ## Runtime dependencies for which bindings aren't generated directly. - ## Only JARs are downloaded. - jar_only_deps: - - 'org.bouncycastle:bcmail-jdk15on:1.70' - - 'org.bouncycastle:bcprov-jdk15on:1.70' -
diff --git a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h +++ b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jnigen/example/pdfbox_plugin/tool/generate_bindings.dart b/pkgs/jnigen/example/pdfbox_plugin/tool/generate_bindings.dart deleted file mode 100644 index 615b404..0000000 --- a/pkgs/jnigen/example/pdfbox_plugin/tool/generate_bindings.dart +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:jnigen/jnigen.dart'; - -const preamble = ''' -// Generated from Apache PDFBox library which is licensed under the Apache License 2.0. -// The following copyright from the original authors applies. -// -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -'''; - -void main(List<String> arguments) async { - final config = Config( - mavenDownloads: MavenDownloads( - sourceDeps: ['org.apache.pdfbox:pdfbox:2.0.26'], - jarOnlyDeps: [ - 'org.bouncycastle:bcmail-jdk15on:1.70', - 'org.bouncycastle:bcprov-jdk15on:1.70', - ], - ), - classes: ['org.apache.pdfbox.pdmodel', 'org.apache.pdfbox.text'], - cRoot: Uri.directory('src/'), - cSubdir: 'third_party', - preamble: preamble, - dartRoot: Uri.directory('lib/third_party'), - libraryName: 'pdfbox_plugin', - ); - await generateJniBindings(config); -}
diff --git a/pkgs/jnigen/lib/src/bindings/c_bindings.dart b/pkgs/jnigen/lib/src/bindings/c_bindings.dart index dda1cd0..bdf20bd 100644 --- a/pkgs/jnigen/lib/src/bindings/c_bindings.dart +++ b/pkgs/jnigen/lib/src/bindings/c_bindings.dart
@@ -7,6 +7,9 @@ import 'common.dart'; +// TODO(#52): Do not use long names in C bindings. Use conflict-renamed short +// name as class name; Use `__` between class & method name. + // fullName / mangled name = // binaryName with replace('.', '_'), replace('$', '__'); @@ -14,8 +17,9 @@ static const _classVarPrefix = '_c'; static const _methodVarPrefix = '_m'; static const _fieldVarPrefix = '_f'; - static const _indent = ' '; + static final indent = ' ' * 4; + // These should be avoided in parameter names. static const _cTypeKeywords = { 'short', 'char', @@ -25,19 +29,16 @@ 'double', }; - String _cParamRename(String paramName) => + String _renameCParam(String paramName) => _cTypeKeywords.contains(paramName) ? '${paramName}0' : paramName; CBindingGenerator(this.config); Config config; - String generateBinding(ClassDecl c) { - return _class(c); - } + String generateBinding(ClassDecl c) => _class(c); String _class(ClassDecl c) { final s = StringBuffer(); - final fullName = mangledClassName(c); // global variable in C that holds the reference to class @@ -74,50 +75,47 @@ final s = StringBuffer(); final name = m.finalName; - final methodVar = '${_methodVarPrefix}_${cClassName}_$name'; - s.write('jmethodID $methodVar = NULL;\n'); + final methodID = '${_methodVarPrefix}_${cClassName}_$name'; + s.write('jmethodID $methodID = NULL;\n'); final returnType = isCtor(m) ? 'jobject' : m.returnType.name; - final cReturnType = cType(returnType); + final cReturnType = getCType(returnType); final cMethodName = '${cClassName}_$name'; final cParams = _formalArgs(m); s.write('FFI_PLUGIN_EXPORT\n'); s.write('$cReturnType $cMethodName($cParams) {\n'); final classVar = '${_classVarPrefix}_$cClassName'; - final signature = _signature(m); + final jniSignature = getJniSignature(m); final ifError = '($cReturnType)0'; s.write(_loadEnvCall); - s.write(_loadClassCall(classVar, _internalName(c.binaryName), ifError)); + s.write(_loadClassCall(classVar, getInternalName(c.binaryName), ifError)); final ifStatic = isStatic ? 'static_' : ''; - s.write('${_indent}load_${ifStatic}method($classVar, ' - '&$methodVar, "${m.name}", "$signature");\n'); - s.write('${_indent}if ($methodVar == NULL) return $ifError;\n'); - - s.write(_initParams(m)); + s.write('${indent}load_${ifStatic}method($classVar, ' + '&$methodID, "${m.name}", "$jniSignature");\n'); + s.write('${indent}if ($methodID == NULL) return $ifError;\n'); var returnTypeName = m.returnType.name; if (isACtor) { returnTypeName = c.binaryName; } - s.write(_indent); + s.write(indent); if (returnTypeName != 'void') { - s.write('${cType(returnTypeName)} _result = '); + s.write('${getCType(returnTypeName)} _result = '); } final callType = _typeNameAtCallSite(m.returnType); - final callArgs = _callArgs(m, classVar, methodVar); + final callArgs = _callArgs(m, classVar, methodID); if (isACtor) { s.write('(*jniEnv)->NewObject($callArgs);\n'); } else { final ifStatic = isStatic ? 'Static' : ''; s.write('(*jniEnv)->Call$ifStatic${callType}Method($callArgs);\n'); } - s.write(_destroyParams(m)); if (returnTypeName != 'void') { s.write(_result(m)); } @@ -144,23 +142,24 @@ final classVar = '${_classVarPrefix}_$cClassName'; void writeAccessor({bool isSetter = false}) { - final ct = isSetter ? 'void' : cType(f.type.name); + final ct = isSetter ? 'void' : getCType(f.type.name); // Getter final prefix = isSetter ? 'set' : 'get'; s.write('FFI_PLUGIN_EXPORT\n'); s.write('$ct ${prefix}_${memberNameInC(c, fieldName)}('); final formalArgs = <String>[ if (!isStatic) 'jobject self_', - if (isSetter) '${cType(f.type.name)} value', + if (isSetter) '${getCType(f.type.name)} value', ]; s.write(formalArgs.join(', ')); s.write(') {\n'); s.write(_loadEnvCall); - s.write(_loadClassCall(classVar, _internalName(c.binaryName), '($ct)0')); + s.write( + _loadClassCall(classVar, getInternalName(c.binaryName), '($ct)0')); var ifStatic = isStatic ? 'static_' : ''; s.write( - '${_indent}load_${ifStatic}field($classVar, &$fieldVar, "$fieldName",' + '${indent}load_${ifStatic}field($classVar, &$fieldVar, "$fieldName",' '"${_fieldSignature(f)}");\n'); ifStatic = isStatic ? 'Static' : ''; @@ -168,14 +167,13 @@ final acc = isSetter ? 'Set' : 'Get'; final ret = isSetter ? '' : 'return '; final conv = !isSetter && !isPrimitive(f.type) ? 'to_global_ref' : ''; - s.write('$_indent$ret$conv((*jniEnv)->$acc$ifStatic${callType}Field'); + s.write('$indent$ret$conv((*jniEnv)->$acc$ifStatic${callType}Field'); final secondArg = isStatic ? classVar : 'self_'; s.write('(jniEnv, $secondArg, $fieldVar'); if (isSetter) { s.write(', value'); } s.write('));\n'); - // TODO(#25): Check Exceptions. s.write('}\n\n'); } @@ -187,12 +185,12 @@ return s.toString(); } - final String _loadEnvCall = '${_indent}load_env();\n'; + final String _loadEnvCall = '${indent}load_env();\n'; String _loadClassCall(String classVar, String internalName, String ifError) { - return '${_indent}load_class_gr(&$classVar, ' + return '${indent}load_class_gr(&$classVar, ' '"$internalName");\n' - '${_indent}if ($classVar == NULL) return $ifError;\n'; + '${indent}if ($classVar == NULL) return $ifError;\n'; } String _formalArgs(Method m) { @@ -204,19 +202,14 @@ } for (var param in m.params) { - final paramName = _cParamRename(param.name); - args.add('${cType(param.type.name)} $paramName'); + final paramName = _renameCParam(param.name); + args.add('${getCType(param.type.name)} $paramName'); } return args.join(", "); } - bool _needsTemporaries(String binaryName) { - // currently no type needs temporaries. - return false; - } - - // arguments at call site + // Returns arguments at call site, concatenated by `,`. String _callArgs(Method m, String classVar, String methodVar) { final args = ['jniEnv']; if (hasSelfParam(m)) { @@ -226,117 +219,34 @@ } args.add(methodVar); for (var param in m.params) { - final paramName = _cParamRename(param.name); - if (_needsTemporaries(param.type.name)) { - args.add('_$paramName'); - } else { - args.add(paramName); - } + final paramName = _renameCParam(param.name); + args.add(paramName); } return args.join(', '); } - String _initParams(Method m) { - // currently no type needs temporaries, but in future we may add - // some options that require temporaries. - return ''; - } - - String _destroyParams(Method m) { - final s = StringBuffer(); - for (var param in m.params) { - final paramName = _cParamRename(param.name); - if (_needsTemporaries(param.type.name)) { - s.write('$_indent(*jniEnv)->DeleteLocalRef(jniEnv, _$paramName);\n'); - } - } - return s.toString(); - } - String _result(Method m) { - final cReturnType = cType(m.returnType.name); + final cReturnType = getCType(m.returnType.name); if (cReturnType == 'jobject' || isCtor(m)) { - return '${_indent}return to_global_ref(_result);\n'; + return '${indent}return to_global_ref(_result);\n'; } else { - return '${_indent}return _result;\n'; + return '${indent}return _result;\n'; } } String _fieldSignature(Field f) { - final iname = _internalNameOf(f.type); - if (iname.length == 1) { - return iname; + final internalName = getInternalNameOfUsage(f.type); + if (internalName.length == 1) { + return internalName; } - return 'L$iname;'; + return 'L$internalName;'; } - String _internalName(String binaryName) { - switch (binaryName) { - case "void": - return "V"; - case "byte": - return "B"; - case "char": - return "C"; - case "double": - return "D"; - case "float": - return "F"; - case "int": - return "I"; - case "long": - return "J"; - case "short": - return "S"; - case "boolean": - return "Z"; - default: - return binaryName.replaceAll(".", "/"); - } - } - - String _internalNameOf(TypeUsage usage) { - switch (usage.kind) { - case Kind.declared: - return _internalName((usage.type as DeclaredType).binaryName); - case Kind.primitive: - return _internalName((usage.type as PrimitiveType).name); - case Kind.typeVariable: - // It should be possible to compute the erasure of a type - // in parser itself. - // TODO(#23): Use erasure of the type variable here. - // This is just a (wrong) placeholder - return "java/lang/Object"; - case Kind.array: - final inner = _internalNameOf((usage.type as ArrayType).type); - return "[$inner"; - case Kind.wildcard: - final extendsBound = (usage.type as Wildcard).extendsBound; - if (extendsBound != null) { - return _internalNameOf(extendsBound); - } - return 'java/lang/Object'; - } - } - - /// Returns the JNI signature of the method. - String _signature(Method m) { - final s = StringBuffer(); - s.write('('); - for (var param in m.params) { - final type = _internalNameOf(param.type); - s.write(type.length == 1 ? type : 'L$type;'); - } - s.write(')'); - final returnType = _internalNameOf(m.returnType); - s.write(returnType.length == 1 ? returnType : 'L$returnType;'); - return s.toString(); - } - - // For call<type>Method or get<type>field calls in JNI. - String _typeNameAtCallSite(TypeUsage t) { - if (isPrimitive(t)) { - return t.name.substring(0, 1).toUpperCase() + t.name.substring(1); + /// Returns capitalized java type name to be used as in call${type}Method + /// or get${type}Field etc.. + String _typeNameAtCallSite(TypeUsage type) { + if (isPrimitive(type)) { + return type.name.substring(0, 1).toUpperCase() + type.name.substring(1); } return "Object"; }
diff --git a/pkgs/jnigen/lib/src/bindings/common.dart b/pkgs/jnigen/lib/src/bindings/common.dart index 69e19b1..a8ecccb 100644 --- a/pkgs/jnigen/lib/src/bindings/common.dart +++ b/pkgs/jnigen/lib/src/bindings/common.dart
@@ -3,14 +3,208 @@ // BSD-style license that can be found in the LICENSE file. import 'package:jnigen/src/elements/elements.dart'; +import 'package:jnigen/src/util/rename_conflict.dart'; +import 'symbol_resolver.dart'; + +/// Implements the methods used by both Pure dart & C+Dart generators. +/// +/// These methods are in a superclass since they usually require access +/// to resolver, or for consistency with similar methods which require +/// the resolver. +class BindingsGenerator { + SymbolResolver resolver; + BindingsGenerator(this.resolver); + // Name for reference in base class. + static const selfPointer = 'reference'; + static final indent = ' ' * 2; + + // import prefixes + static const ffi = 'ffi.'; + static const jni = 'jni.'; + + static const String voidPointer = '${ffi}Pointer<${ffi}Void>'; + + static const String ffiVoidType = '${ffi}Void'; + + static const String jniObjectType = '${jni}JniObject'; + + /// Formal parameters list of the generated function. + /// + /// This is the signature seen by the user. + String formalArgs(Method m) { + final List<String> args = []; + for (var param in m.params) { + args.add('${dartOuterType(param.type)} ${kwRename(param.name)}'); + } + return args.join(', '); + } + + /// Actual arguments passed to native call. + String actualArgs(Method m) { + final List<String> args = [if (hasSelfParam(m)) selfPointer]; + for (var param in m.params) { + final paramName = kwRename(param.name); + args.add(toNativeArg(paramName, param.type)); + } + return args.join(', '); + } + + String dartSigForField(Field f, + {bool isSetter = false, required bool isFfiSig}) { + final conv = isFfiSig ? dartFfiType : dartInnerType; + final voidType = isFfiSig ? ffiVoidType : 'void'; + final ref = f.modifiers.contains('static') ? '' : '$voidPointer, '; + if (isSetter) { + return '$voidType Function($ref${conv(f.type)})'; + } + return '${conv(f.type)} Function($ref)'; + } + + String dartSigForMethod(Method m, {required bool isFfiSig}) { + final conv = isFfiSig ? dartFfiType : dartInnerType; + final argTypes = [if (hasSelfParam(m)) voidPointer]; + for (var param in m.params) { + argTypes.add(conv(param.type)); + } + final retType = isCtor(m) ? voidPointer : conv(m.returnType); + return '$retType Function (${argTypes.join(", ")})'; + } + + String _dartType(TypeUsage t, {SymbolResolver? resolver}) { + // if resolver == null, looking for inner fn type, type of fn reference + // else looking for outer fn type, that's what user of the library sees. + const primitives = { + 'byte': 'int', + 'short': 'int', + 'char': 'int', + 'int': 'int', + 'long': 'int', + 'float': 'double', + 'double': 'double', + 'void': 'void', + 'boolean': 'bool', + }; + switch (t.kind) { + case Kind.primitive: + if (t.name == 'boolean' && resolver == null) return 'int'; + return primitives[(t.type as PrimitiveType).name]!; + case Kind.typeVariable: + case Kind.wildcard: + throw SkipException('Not supported: generics'); + case Kind.array: + if (resolver != null) { + return jniObjectType; + } + return voidPointer; + case Kind.declared: + if (resolver != null) { + return resolver.resolve((t.type as DeclaredType).binaryName) ?? + jniObjectType; + } + return voidPointer; + } + } + + // Type for FFI Function signature + String dartFfiType(TypeUsage t) { + const primitives = { + 'byte': 'Int8', + 'short': 'Int16', + 'char': 'Int16', + 'int': 'Int32', + 'long': 'Int64', + 'float': 'Float', + 'double': 'Double', + 'void': 'Void', + 'boolean': 'Uint8', + }; + switch (t.kind) { + case Kind.primitive: + return ffi + primitives[(t.type as PrimitiveType).name]!; + case Kind.typeVariable: + case Kind.wildcard: + throw SkipException( + 'Generic type parameters are not supported', t.toJson()); + case Kind.array: + case Kind.declared: + return voidPointer; + } + } + + String dartInnerType(TypeUsage t) => _dartType(t); + String dartOuterType(TypeUsage t) => _dartType(t, resolver: resolver); + + String literal(dynamic value) { + if (value is String) { + // TODO(#31): escape string literal. + return '"$value"'; + } + if (value is int || value is double || value is bool) { + return value.toString(); + } + throw SkipException('Not a constant of a known type.'); + } + + String originalFieldDecl(Field f) { + final declStmt = '${f.type.shorthand} ${f.name}'; + return [...f.modifiers, declStmt].join(' '); + } + + String originalMethodHeader(Method m) { + final args = <String>[]; + for (var p in m.params) { + args.add('${p.type.shorthand} ${p.name}'); + } + final declStmt = '${m.returnType.shorthand} ${m.name}' + '(${args.join(', ')})'; + return [...m.modifiers, declStmt].join(' '); + } + + String toNativeArg(String name, TypeUsage type) { + if (isPrimitive(type)) { + return type.name == 'boolean' ? '$name ? 1 : 0' : name; + } + return '$name.$selfPointer'; + } + + String toDartResult(String expr, TypeUsage type, String dartType) { + if (isPrimitive(type)) { + return type.name == 'boolean' ? '$expr != 0' : expr; + } + return '$dartType.fromRef($expr)'; + } + + static final deleteInstruction = + '$indent/// The returned object must be deleted after use, ' + 'by calling the `delete` method.\n'; +} + +String breakDocComment(JavaDocComment? javadoc, {String depth = ' '}) { + final link = RegExp('{@link ([^{}]+)}'); + if (javadoc == null) return ''; + final comment = javadoc.comment + .replaceAllMapped(link, (match) => match.group(1) ?? '') + .replaceAll('#', '\\#') + .replaceAll('<p>', '') + .replaceAll('</p>', '\n') + .replaceAll('<b>', '__') + .replaceAll('</b>', '__') + .replaceAll('<em>', '_') + .replaceAll('</em>', '_'); + return '$depth///\n' + '$depth/// ${comment.replaceAll('\n', '\n$depth///')}\n'; +} + +/// class name canonicalized for C bindings, by replacing "." with "_" and +/// "$" with "__". String mangledClassName(ClassDecl decl) => decl.binaryName.replaceAll('.', '_').replaceAll('\$', '__'); String memberNameInC(ClassDecl decl, String name) => "${mangledClassName(decl)}_$name"; -String cType(String binaryName) { +String getCType(String binaryName) { switch (binaryName) { case "void": return "void"; @@ -35,6 +229,55 @@ } } +String getInternalName(String binaryName) { + switch (binaryName) { + case "void": + return "V"; + case "byte": + return "B"; + case "char": + return "C"; + case "double": + return "D"; + case "float": + return "F"; + case "int": + return "I"; + case "long": + return "J"; + case "short": + return "S"; + case "boolean": + return "Z"; + default: + return binaryName.replaceAll(".", "/"); + } +} + +String getInternalNameOfUsage(TypeUsage usage) { + switch (usage.kind) { + case Kind.declared: + return getInternalName((usage.type as DeclaredType).binaryName); + case Kind.primitive: + return getInternalName((usage.type as PrimitiveType).name); + case Kind.typeVariable: + // It should be possible to compute the erasure of a type + // in parser itself. + // TODO(#23): Use erasure of the type variable here. + // This is just a (wrong) placeholder + return "java/lang/Object"; + case Kind.array: + final inner = getInternalNameOfUsage((usage.type as ArrayType).type); + return "[$inner"; + case Kind.wildcard: + final extendsBound = (usage.type as Wildcard).extendsBound; + if (extendsBound != null) { + return getInternalNameOfUsage(extendsBound); + } + return 'java/lang/Object'; + } +} + bool isPrimitive(TypeUsage t) => t.kind == Kind.primitive; bool isVoid(TypeUsage t) => isPrimitive(t) && t.name == 'void'; @@ -50,8 +293,11 @@ bool isObjectField(Field f) => !isPrimitive(f.type); bool isObjectMethod(Method m) => !isPrimitive(m.returnType); -const ctorNameC = 'new'; -const ctorNameDart = 'ctor'; +/// Returns class name as useful in dart. +/// +/// Eg -> a.b.X.Y -> X_Y +String getSimplifiedClassName(String binaryName) => + binaryName.split('.').last.replaceAll('\$', '_'); // Marker exception when a method or class cannot be translated // The inner functions may not know how much context has to be skipped in case @@ -66,3 +312,54 @@ return '$message;'; } } + +String getTypeNameAtCallSite(TypeUsage t) { + if (isPrimitive(t)) { + return t.name.substring(0, 1).toUpperCase() + t.name.substring(1); + } + return "Object"; +} + +String getCallType(TypeUsage returnType) { + if (isPrimitive(returnType)) { + return "jni.JniType.${returnType.name}Type"; + } + return "jni.JniType.objectType"; +} + +String getResultGetterName(TypeUsage returnType) { + final primitives = { + 'boolean': 'boolean', + 'byte': 'byte', + 'short': 'short', + 'char': 'char', + 'int': 'integer', + 'long': 'long', + 'float': 'float', + 'double': 'doubleFloat', + 'void': 'check()', + }; + return primitives[returnType.name] ?? 'object'; +} + +/// Returns the JNI signature of the method. +String getJniSignature(Method m) { + final s = StringBuffer(); + s.write('('); + for (var param in m.params) { + final type = getInternalNameOfUsage(param.type); + s.write(type.length == 1 ? type : 'L$type;'); + } + s.write(')'); + final returnType = getInternalNameOfUsage(m.returnType); + s.write(returnType.length == 1 ? returnType : 'L$returnType;'); + return s.toString(); +} + +String getJniSignatureForField(Field f) { + final internalName = getInternalNameOfUsage(f.type); + if (internalName.length == 1) { + return internalName; + } + return 'L$internalName;'; +}
diff --git a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart index 20c058b..d656745 100644 --- a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart +++ b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
@@ -5,32 +5,28 @@ import 'package:jnigen/src/elements/elements.dart'; import 'package:jnigen/src/config/config.dart'; import 'package:jnigen/src/logging/logging.dart'; -import 'package:jnigen/src/util/rename_conflict.dart'; import 'symbol_resolver.dart'; import 'common.dart'; -final _indent = ' ' * 2; - -class DartBindingsGenerator { - // Name for reference in base class. - static const _self = 'reference'; +class DartBindingsGenerator extends BindingsGenerator { // symbol lookup function for generated code. + static const selfPointer = BindingsGenerator.selfPointer; static const _jniLookup = 'jniLookup'; + static final indent = ' ' * 2; // import prefixes - static const ffi = 'ffi.'; - static const jni = 'jni.'; + static const ffi = BindingsGenerator.ffi; + static const jni = BindingsGenerator.jni; - static const String _voidPtr = '${ffi}Pointer<${ffi}Void>'; + static const voidPointer = BindingsGenerator.voidPointer; - static const String _void = '${ffi}Void'; + static const ffiVoidType = BindingsGenerator.ffiVoidType; - static const String _jniObject = '${jni}JniObject'; + static const jniObjectType = BindingsGenerator.jniObjectType; - DartBindingsGenerator(this.config, this.resolver); + DartBindingsGenerator(this.config, SymbolResolver resolver) : super(resolver); Config config; - SymbolResolver resolver; String generateBinding(ClassDecl decl) { if (!decl.isPreprocessed) { @@ -49,18 +45,18 @@ final s = StringBuffer(); s.write('/// from: ${decl.binaryName}\n'); - s.write(_breakDocComment(decl.javadoc, depth: '')); + s.write(breakDocComment(decl.javadoc, depth: '')); final name = _getSimpleName(decl.binaryName); - var superName = _jniObject; + var superName = jniObjectType; if (decl.superclass != null) { superName = resolver .resolve((decl.superclass!.type as DeclaredType).binaryName) ?? - _jniObject; + jniObjectType; } s.write('class $name extends $superName {\n'); - s.write('$_indent$name.fromRef($_voidPtr ref) : ' + s.write('$indent$name.fromRef($voidPointer ref) : ' 'super.fromRef(ref);\n'); s.writeln(); @@ -94,10 +90,6 @@ return s.toString(); } - static final _deleteInstruction = - '$_indent/// The returned object must be deleted after use, ' - 'by calling the `delete` method.\n'; - String _method(ClassDecl c, Method m) { final name = m.finalName; final cName = memberNameInC(c, name); @@ -105,74 +97,57 @@ final sym = '_$name'; final ffiSig = dartSigForMethod(m, isFfiSig: true); final dartSig = dartSigForMethod(m, isFfiSig: false); - s.write('${_indent}static final $sym = $_jniLookup' + s.write('${indent}static final $sym = $_jniLookup' '<${ffi}NativeFunction<$ffiSig>>("$cName")\n' '.asFunction<$dartSig>();\n'); // Different logic for constructor and method; // For constructor, we want return type to be new object. final returnType = dartOuterType(m.returnType); - s.write('$_indent/// from: ${_originalMethodHeader(m)}\n'); + s.write('$indent/// from: ${originalMethodHeader(m)}\n'); if (!isPrimitive(m.returnType)) { - s.write(_deleteInstruction); + s.write(BindingsGenerator.deleteInstruction); } - s.write(_breakDocComment(m.javadoc)); - s.write(_indent); + s.write(breakDocComment(m.javadoc)); + s.write(indent); if (isStaticMethod(m)) { s.write('static '); } if (isCtor(m)) { - final wrapperExpr = '$sym(${_actualArgs(m)})'; + final wrapperExpr = '$sym(${actualArgs(m)})'; final className = _getSimpleName(c.binaryName); final ctorFnName = name == 'ctor' ? className : '$className.$name'; - s.write('$ctorFnName(${_formalArgs(m)}) : ' + s.write('$ctorFnName(${formalArgs(m)}) : ' 'super.fromRef($wrapperExpr) { jni.Jni.env.checkException(); }\n'); return s.toString(); } - var wrapperExpr = '$sym(${_actualArgs(m)})'; - wrapperExpr = _toDartResult(wrapperExpr, m.returnType, returnType); - final depth = '$_indent$_indent'; - s.write('$returnType $name(${_formalArgs(m)}) {'); + var wrapperExpr = '$sym(${actualArgs(m)})'; + wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnType); + final depth = '$indent$indent'; + s.write('$returnType $name(${formalArgs(m)}) {'); s.write('${depth}final result__ = $wrapperExpr;'); s.write('${depth}jni.Jni.env.checkException();'); - s.write('${depth}return result__;\n$_indent}'); + s.write('${depth}return result__;\n$indent}'); return s.toString(); } - String _formalArgs(Method m) { - final List<String> args = []; - for (var param in m.params) { - args.add('${dartOuterType(param.type)} ${kwRename(param.name)}'); - } - return args.join(', '); - } - - String _actualArgs(Method m) { - final List<String> args = [if (hasSelfParam(m)) _self]; - for (var param in m.params) { - final paramName = kwRename(param.name); - args.add(_toCArg(paramName, param.type)); - } - return args.join(', '); - } - String _field(ClassDecl c, Field f) { final name = f.finalName; final s = StringBuffer(); void writeDocs({bool writeDeleteInstruction = true}) { - s.write('$_indent/// from: ${_originalFieldDecl(f)}\n'); + s.write('$indent/// from: ${originalFieldDecl(f)}\n'); if (!isPrimitive(f.type) && writeDeleteInstruction) { - s.write(_deleteInstruction); + s.write(BindingsGenerator.deleteInstruction); } - s.write(_breakDocComment(f.javadoc)); + s.write(breakDocComment(f.javadoc)); } if (isStaticField(f) && isFinalField(f) && f.defaultValue != null) { writeDocs(writeDeleteInstruction: false); - s.write('${_indent}static const $name = ${_literal(f.defaultValue)};\n'); + s.write('${indent}static const $name = ${literal(f.defaultValue)};\n'); return s.toString(); } final cName = memberNameInC(c, name); @@ -182,26 +157,26 @@ final sym = '_${symPrefix}_$name'; final ffiSig = dartSigForField(f, isSetter: isSetter, isFfiSig: true); final dartSig = dartSigForField(f, isSetter: isSetter, isFfiSig: false); - s.write('${_indent}static final $sym = $_jniLookup' + s.write('${indent}static final $sym = $_jniLookup' '<${ffi}NativeFunction<$ffiSig>>("${symPrefix}_$cName")\n' '.asFunction<$dartSig>();\n'); // write original type writeDocs(); - s.write(_indent); + s.write(indent); if (isStaticField(f)) s.write('static '); if (isSetter) { s.write('set $name(${dartOuterType(f.type)} value) => $sym('); if (!isStaticField(f)) { - s.write('$_self, '); + s.write('$selfPointer, '); } - s.write(_toCArg('value', f.type)); + s.write(toNativeArg('value', f.type)); s.write(');\n'); } else { // getter - final self = isStaticField(f) ? '' : _self; + final self = isStaticField(f) ? '' : selfPointer; final outer = dartOuterType(f.type); final callExpr = '$sym($self)'; - final resultExpr = _toDartResult(callExpr, f.type, outer); + final resultExpr = toDartResult(callExpr, f.type, outer); s.write('$outer get $name => $resultExpr;\n'); } } @@ -216,150 +191,6 @@ return components.last.replaceAll("\$", "_"); } - String dartSigForField(Field f, - {bool isSetter = false, required bool isFfiSig}) { - final conv = isFfiSig ? dartFfiType : dartInnerType; - final voidType = isFfiSig ? _void : 'void'; - final ref = f.modifiers.contains('static') ? '' : '$_voidPtr, '; - if (isSetter) { - return '$voidType Function($ref${conv(f.type)})'; - } - return '${conv(f.type)} Function($ref)'; - } - - String dartSigForMethod(Method m, {required bool isFfiSig}) { - final conv = isFfiSig ? dartFfiType : dartInnerType; - final argTypes = [if (hasSelfParam(m)) _voidPtr]; - for (var param in m.params) { - argTypes.add(conv(param.type)); - } - final retType = isCtor(m) ? _voidPtr : conv(m.returnType); - return '$retType Function (${argTypes.join(", ")})'; - } - - // Type for FFI Function signature - String dartFfiType(TypeUsage t) { - const primitives = { - 'byte': 'Int8', - 'short': 'Int16', - 'char': 'Int16', - 'int': 'Int32', - 'long': 'Int64', - 'float': 'Float', - 'double': 'Double', - 'void': 'Void', - 'boolean': 'Uint8', - }; - switch (t.kind) { - case Kind.primitive: - return ffi + primitives[(t.type as PrimitiveType).name]!; - case Kind.typeVariable: - case Kind.wildcard: - throw SkipException( - 'Generic type parameters are not supported', t.toJson()); - case Kind.array: - case Kind.declared: - return _voidPtr; - } - } - - String _dartType(TypeUsage t, {SymbolResolver? resolver}) { - // if resolver == null, looking for inner fn type, type of fn reference - // else looking for outer fn type, that's what user of the library sees. - const primitives = { - 'byte': 'int', - 'short': 'int', - 'char': 'int', - 'int': 'int', - 'long': 'int', - 'float': 'double', - 'double': 'double', - 'void': 'void', - 'boolean': 'bool', - }; - switch (t.kind) { - case Kind.primitive: - if (t.name == 'boolean' && resolver == null) return 'int'; - return primitives[(t.type as PrimitiveType).name]!; - case Kind.typeVariable: - case Kind.wildcard: - throw SkipException('Not supported: generics'); - case Kind.array: - if (resolver != null) { - return _jniObject; - } - return _voidPtr; - case Kind.declared: - if (resolver != null) { - return resolver.resolve((t.type as DeclaredType).binaryName) ?? - _jniObject; - } - return _voidPtr; - } - } - - String dartInnerType(TypeUsage t) => _dartType(t); - String dartOuterType(TypeUsage t) => _dartType(t, resolver: resolver); - - String _literal(dynamic value) { - if (value is String) { - // TODO(#31): escape string literal. - return '"$value"'; - } - if (value is int || value is double || value is bool) { - return value.toString(); - } - throw SkipException('Not a constant of a known type.'); - } - - String _originalFieldDecl(Field f) { - final declStmt = '${f.type.shorthand} ${f.name}'; - return [...f.modifiers, declStmt].join(' '); - } - - String _originalMethodHeader(Method m) { - final args = <String>[]; - for (var p in m.params) { - args.add('${p.type.shorthand} ${p.name}'); - } - final declStmt = '${m.returnType.shorthand} ${m.name}' - '(${args.join(', ')})'; - return [...m.modifiers, declStmt].join(' '); - } - - String _toCArg(String name, TypeUsage type) { - if (isPrimitive(type)) { - return type.name == 'boolean' ? '$name ? 1 : 0' : name; - } - return '$name.$_self'; - } - - String _toDartResult(String expr, TypeUsage type, String dartType) { - if (isPrimitive(type)) { - return type.name == 'boolean' ? '$expr != 0' : expr; - } - return '$dartType.fromRef($expr)'; - } - - static String _breakDocComment(JavaDocComment? javadoc, - {String depth = ' '}) { - final link = RegExp('{@link ([^{}]+)}'); - if (javadoc == null) return ''; - final comment = javadoc.comment - .replaceAllMapped(link, (match) => match.group(1) ?? '') - .replaceAll('#', '\\#') - .replaceAll('<p>', '') - .replaceAll('</p>', '\n') - .replaceAll('<b>', '__') - .replaceAll('</b>', '__') - .replaceAll('<em>', '_') - .replaceAll('</em>', '_'); - return '$depth///\n' - '$depth/// ${comment.replaceAll('\n', '\n$depth///')}\n'; - } -} - -class DartPreludes { static String initFile(String libraryName) => 'import "dart:ffi";\n' 'import "package:jni/internal_helpers_for_jnigen.dart";\n' '\n'
diff --git a/pkgs/jnigen/lib/src/bindings/preprocessor.dart b/pkgs/jnigen/lib/src/bindings/preprocessor.dart index 61c2141..8ddff1d 100644 --- a/pkgs/jnigen/lib/src/bindings/preprocessor.dart +++ b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
@@ -10,9 +10,17 @@ import 'common.dart'; /// Preprocessor which fills information needed by both Dart and C generators. -class ApiPreprocessor { - static void preprocessAll(Map<String, ClassDecl> classes, Config config) { +abstract class ApiPreprocessor { + static void preprocessAll(Map<String, ClassDecl> classes, Config config, + {bool renameClasses = false}) { + final Map<String, int> classNameCounts = {}; for (var c in classes.values) { + final className = getSimplifiedClassName(c.binaryName); + if (renameClasses) { + c.finalName = renameConflict(classNameCounts, className); + } else { + c.finalName = className; + } _preprocess(c, classes, config); } } @@ -22,7 +30,7 @@ if (decl.isPreprocessed) return; if (!_isClassIncluded(decl, config)) { decl.isIncluded = false; - log.info('exclude class ${decl.binaryName}'); + log.fine('exclude class ${decl.binaryName}'); decl.isPreprocessed = true; return; } @@ -42,7 +50,7 @@ for (var field in decl.fields) { if (!_isFieldIncluded(decl, field, config)) { field.isIncluded = false; - log.info('exclude ${decl.binaryName}#${field.name}'); + log.fine('exclude ${decl.binaryName}#${field.name}'); continue; } field.finalName = renameConflict(decl.nameCounts, field.name); @@ -51,7 +59,7 @@ for (var method in decl.methods) { if (!_isMethodIncluded(decl, method, config)) { method.isIncluded = false; - log.info('exclude method ${decl.binaryName}#${method.name}'); + log.fine('exclude method ${decl.binaryName}#${method.name}'); continue; } var realName = method.name; @@ -62,10 +70,10 @@ // if method already in super class, assign its number, overriding it. final superNum = superclass?.methodNumsAfterRenaming[sig]; if (superNum != null) { - // don't rename if superNum == 0 - final superNumText = superNum == 0 ? '' : '$superNum'; - // well, unless the method name is a keyword & superNum == 0. // TODO(#29): this logic would better live in a dedicated renamer class. + // don't rename if superNum == 0 + // unless the method name is a keyword. + final superNumText = superNum == 0 ? '' : '$superNum'; final methodName = superNum == 0 ? kwRename(realName) : realName; method.finalName = '$methodName$superNumText'; decl.methodNumsAfterRenaming[sig] = superNum; @@ -77,7 +85,7 @@ } } decl.isPreprocessed = true; - log.finest('preprocessed ${decl.binaryName}'); + log.fine('preprocessed ${decl.binaryName}'); } static bool _isFieldIncluded(ClassDecl decl, Field field, Config config) =>
diff --git a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart b/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart index 45b9e1a..adf065b 100644 --- a/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart +++ b/pkgs/jnigen/lib/src/bindings/symbol_resolver.dart
@@ -2,147 +2,12 @@ // 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. -// A symbol resolver is useful mainly to convert a fully qualified name to -// a locally meaningful name, when creating dart bindings - -import 'dart:math'; - -import 'package:jnigen/src/logging/logging.dart'; - -import 'package:jnigen/src/util/name_utils.dart'; - +/// Resolves types referred to in method signatures etc.. and provides +/// appropriate imports for them. abstract class SymbolResolver { /// Resolve the binary name to a String which can be used in dart code. String? resolve(String binaryName); + + /// Get all imports for types so far resolved through this resolver. List<String> getImportStrings(); } - -class PackagePathResolver implements SymbolResolver { - PackagePathResolver(this.importMap, this.currentPackage, this.inputClassNames, - {this.predefined = const {}}); - - final String currentPackage; - final Map<String, String> importMap; - final Map<String, String> predefined; - final Set<String> inputClassNames; - - final List<String> importStrings = []; - - final Set<String> relativeImportedPackages = {}; - - final Map<String, String> _importedNameToPackage = {}; - final Map<String, String> _packageToImportedName = {}; - - /// Returns the dart name of the [binaryName] in current translation context, - /// or `null` if the name cannot be resolved. - @override - String? resolve(String binaryName) { - if (predefined.containsKey(binaryName)) { - return predefined[binaryName]; - } - final parts = cutFromLast(binaryName, '.'); - final package = parts[0]; - final typename = parts[1]; - final simpleTypeName = typename.replaceAll('\$', '_'); - - if (package == currentPackage && inputClassNames.contains(binaryName)) { - return simpleTypeName; - } - - if (_packageToImportedName.containsKey(package)) { - // This package was already resolved - // but we still need to check if it was a relative import, in which case - // the class not in inputClassNames cannot be mapped here. - if (!relativeImportedPackages.contains(package) || - inputClassNames.contains(binaryName)) { - final importedName = _packageToImportedName[package]; - return '$importedName.$simpleTypeName'; - } - } - - final packageImport = getImport(package, binaryName); - log.finest('$package resolved to $packageImport for $binaryName'); - if (packageImport == null) { - return null; - } - - final pkgName = cutFromLast(package, '.')[1]; - if (pkgName.isEmpty) { - throw UnsupportedError('No package could be deduced from ' - 'qualified binaryName'); - } - - // We always name imports with an underscore suffix, so that they can be - // never shadowed by a parameter or local variable. - var importedName = '${pkgName}_'; - int suffix = 0; - while (_importedNameToPackage.containsKey(importedName)) { - suffix++; - importedName = '$pkgName${suffix}_'; - } - - _importedNameToPackage[importedName] = package; - _packageToImportedName[package] = importedName; - importStrings.add('import "$packageImport" as $importedName;\n'); - return '$importedName.$simpleTypeName'; - } - - /// Returns import string, or `null` if package not found. - String? getImport(String packageToResolve, String binaryName) { - final right = <String>[]; - var prefix = packageToResolve; - - if (prefix.isEmpty) { - throw UnsupportedError('unexpected: empty package name.'); - } - - final dest = packageToResolve.split('.'); - final src = currentPackage.split('.'); - if (inputClassNames.contains(binaryName)) { - int common = 0; - for (int i = 0; i < src.length && i < dest.length; i++) { - if (src[i] == dest[i]) { - common++; - } - } - // a.b.c => a/b/c.dart - // from there - // a/b.dart => ../b.dart - // a.b.d => d.dart - // a.b.c.d => c/d.dart - var pathToCommon = ''; - if (common < src.length) { - pathToCommon = '../' * (src.length - common); - } - final pathToPackage = dest.skip(max(common - 1, 0)).join('/'); - relativeImportedPackages.add(packageToResolve); - return '$pathToCommon$pathToPackage.dart'; - } - - while (prefix.isNotEmpty) { - final split = cutFromLast(prefix, '.'); - final left = split[0]; - right.add(split[1]); - // eg: packages[org.apache.pdfbox]/org/apache/pdfbox.dart - if (importMap.containsKey(prefix)) { - final sub = packageToResolve.replaceAll('.', '/'); - final pkg = _suffix(importMap[prefix]!, '/'); - return '$pkg$sub.dart'; - } - prefix = left; - } - return null; - } - - String _suffix(String str, String suffix) { - if (str.endsWith(suffix)) { - return str; - } - return str + suffix; - } - - @override - List<String> getImportStrings() { - return importStrings; - } -}
diff --git a/pkgs/jnigen/lib/src/config/config.dart b/pkgs/jnigen/lib/src/config/config.dart index 136845f..12c32fe 100644 --- a/pkgs/jnigen/lib/src/config/config.dart +++ b/pkgs/jnigen/lib/src/config/config.dart
@@ -65,6 +65,10 @@ this.addGradleDeps = false, this.androidExample, }) { + if (versions != null && sdkRoot == null) { + throw ArgumentError("No SDK Root specified for finding Android SDK " + "from version priority list $versions"); + } if (versions == null && !addGradleDeps) { throw ArgumentError('Neither any SDK versions nor `addGradleDeps` ' 'is specified. Unable to find Android libraries.'); @@ -128,13 +132,30 @@ ClassFilter? classes; } +enum BindingsType { + cBased, // C+Dart bindings + singleFile, + packageStructured, +} + +BindingsType getBindingsType(String? type, BindingsType defaultType) { + const names = { + 'c_based': BindingsType.cBased, + 'single_file': BindingsType.singleFile, + 'package_structured': BindingsType.packageStructured, + }; + return names[type] ?? defaultType; +} + /// Configuration for jnigen binding generation. class Config { Config({ required this.classes, - required this.libraryName, - required this.cRoot, - required this.dartRoot, + this.bindingsType = BindingsType.cBased, + this.outputPath, + this.libraryName, + this.cRoot, + this.dartRoot, this.cSubdir, this.exclude, this.sourcePath, @@ -146,7 +167,16 @@ this.summarizerOptions, this.logLevel = Level.INFO, this.dumpJsonTo, - }); + }) { + if (bindingsType == BindingsType.cBased) { + if (cRoot == null || dartRoot == null || libraryName == null) { + throw ArgumentError("In c_based mode these values must be specified: " + "c_root, dart_root, library_name"); + } + } else { + throw UnimplementedError("BindingsType not yet supported: $bindingsType"); + } + } /// List of classes or packages for which bindings have to be generated. /// @@ -157,27 +187,33 @@ /// name suffix is `.class`. List<String> classes; + /// Type of bindings to generate. + final BindingsType bindingsType; + /// Name of generated library in CMakeLists.txt configuration. /// /// This will also determine the name of shared object file. - String libraryName; + final String? libraryName; - /// Directory to write JNI C Bindings. + /// Directory to write JNI C Bindings, in C+Dart mode. /// /// Strictly speaking, this is the root to place the `CMakeLists.txt` file /// for the generated C bindings. It may be desirable to use the [cSubdir] /// options to write C files to a subdirectory of [cRoot]. For instance, /// when generated code is required to be in `third_party` directory. - Uri cRoot; + Uri? cRoot; - /// Directory to write Dart bindings. - Uri dartRoot; + /// Directory to write Dart bindings, in C + Dart mode. + Uri? dartRoot; - /// Subdirectory relative to [cRoot] to write generated C code. + /// Subfolder relative to [cRoot] to write generated C code. String? cSubdir; + /// Output file or folder in non-legacy modes + Uri? outputPath; + /// Methods and fields to be excluded from generated bindings. - BindingExclusions? exclude; + final BindingExclusions? exclude; /// Paths to search for java source files. /// @@ -193,32 +229,30 @@ List<Uri>? classPath; /// Common text to be pasted on top of generated C and Dart files. - String? preamble; + final String? preamble; /// Additional java package -> dart package mappings (Experimental). - Map<String, String>? importMap; + final Map<String, String>? importMap; /// Configuration to search for Android SDK libraries (Experimental). - AndroidSdkConfig? androidSdkConfig; + final AndroidSdkConfig? androidSdkConfig; /// Configuration for auto-downloading JAR / source packages using maven, /// along with their transitive dependencies. - MavenDownloads? mavenDownloads; + final MavenDownloads? mavenDownloads; /// Additional options for the summarizer component - SummarizerOptions? summarizerOptions; + final SummarizerOptions? summarizerOptions; /// Log verbosity. The possible values in decreasing order of verbosity /// are verbose > debug > info > warning > error. Defaults to [LogLevel.info] Level logLevel = Level.INFO; /// File to which JSON summary is written before binding generation. - String? dumpJsonTo; + final String? dumpJsonTo; static final _levels = Map.fromEntries( Level.LEVELS.map((l) => MapEntry(l.name.toLowerCase(), l))); - static Uri? _toDirUri(String? path) => - path != null ? Uri.directory(path) : null; static List<Uri>? _toUris(List<String>? paths) => paths?.map(Uri.file).toList(); @@ -234,6 +268,10 @@ return res; } + Uri? fileUri(String? path) => path != null ? Uri.file(path) : null; + Uri? directoryUri(String? path) => + path != null ? Uri.directory(path) : null; + MemberFilter<T>? regexFilter<T extends ClassMember>(String property) { final exclusions = prov.getStringList(property); if (exclusions == null) return null; @@ -252,13 +290,9 @@ return CombinedMemberFilter<T>(filters); } - String getSdkRoot() { + String? getSdkRoot() { final root = prov.getString(_Props.androidSdkRoot) ?? Platform.environment['ANDROID_SDK_ROOT']; - if (root == null) { - missingValues.add(_Props.androidSdkRoot); - return '?'; - } return root; } @@ -279,14 +313,17 @@ extraArgs: prov.getStringList(_Props.summarizerArgs) ?? const [], backend: prov.getString(_Props.backend), workingDirectory: - _toDirUri(prov.getString(_Props.summarizerWorkingDir)), + directoryUri(prov.getString(_Props.summarizerWorkingDir)), ), exclude: BindingExclusions( methods: regexFilter<Method>(_Props.excludeMethods), fields: regexFilter<Field>(_Props.excludeFields), ), - cRoot: Uri.directory(must(prov.getString, '', _Props.cRoot)), - dartRoot: Uri.directory(must(prov.getString, '', _Props.dartRoot)), + bindingsType: getBindingsType( + prov.getString(_Props.bindingsType), BindingsType.cBased), + cRoot: directoryUri(prov.getString(_Props.cRoot)), + dartRoot: directoryUri(prov.getString(_Props.dartRoot)), + outputPath: fileUri(prov.getString(_Props.outputPath)), cSubdir: prov.getString(_Props.cSubdir), preamble: prov.getString(_Props.preamble), libraryName: must(prov.getString, '', _Props.libraryName), @@ -317,7 +354,7 @@ logLevel: logLevelFromString( prov.getOneOf( _Props.logLevel, - {'error', 'warning', 'info', 'debug', 'verbose'}, + _levels.keys.toSet(), ), ), ); @@ -353,6 +390,8 @@ static const excludeFields = '$exclude.fields'; static const importMap = 'import_map'; + static const outputPath = 'output_path'; + static const bindingsType = 'bindings_type'; static const cRoot = 'c_root'; static const cSubdir = 'c_subdir'; static const dartRoot = 'dart_root';
diff --git a/pkgs/jnigen/lib/src/config/yaml_reader.dart b/pkgs/jnigen/lib/src/config/yaml_reader.dart index 6765d9d..8f3aa67 100644 --- a/pkgs/jnigen/lib/src/config/yaml_reader.dart +++ b/pkgs/jnigen/lib/src/config/yaml_reader.dart
@@ -63,7 +63,7 @@ stderr.writeln('cannot read $configFile: $e'); } } - final regex = RegExp('([a-z-_.]+)=(.*)'); + final regex = RegExp('([a-z-_.]+)=(.+)'); final properties = <String, String>{}; for (var prop in results['override']) { final match = regex.matchAsPrefix(prop as String); @@ -98,7 +98,7 @@ } List<String>? getStringList(String property) { - final configValue = cli[property]?.split(',') ?? + final configValue = cli[property]?.split(';') ?? getYamlValue<YamlList>(property)?.cast<String>(); return configValue; }
diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart index f7773fa..879fade 100644 --- a/pkgs/jnigen/lib/src/elements/elements.dart +++ b/pkgs/jnigen/lib/src/elements/elements.dart
@@ -91,6 +91,8 @@ _$ClassDeclFromJson(json); Map<String, dynamic> toJson() => _$ClassDeclToJson(this); + String get internalName => binaryName.replaceAll(".", "/"); + // synthesized attributes @JsonKey(ignore: true) late String finalName;
diff --git a/pkgs/jnigen/lib/src/generate_bindings.dart b/pkgs/jnigen/lib/src/generate_bindings.dart index c841d80..1c51b68 100644 --- a/pkgs/jnigen/lib/src/generate_bindings.dart +++ b/pkgs/jnigen/lib/src/generate_bindings.dart
@@ -5,11 +5,13 @@ import 'dart:io'; import 'dart:convert'; +import 'package:jnigen/src/util/command_output.dart'; + import 'elements/elements.dart'; import 'summary/summary.dart'; import 'config/config.dart'; import 'tools/tools.dart'; -import 'writers/writers.dart'; +import 'writers/files_writer.dart'; import 'logging/logging.dart'; Future<void> generateJniBindings(Config config) async { @@ -26,6 +28,7 @@ backend: config.summarizerOptions?.backend, ); + // Additional sources added using maven downloads and gradle trickery. final extraSources = <Uri>[]; final extraJars = <Uri>[]; final mavenDl = config.mavenDownloads; @@ -70,18 +73,23 @@ summarizer.addSourcePaths(extraSources); summarizer.addClassPaths(extraJars); + Process process; Stream<List<int>> input; try { - input = await summarizer.getInputStream(); + process = await summarizer.runProcess(); + input = process.stdout; } on Exception catch (e) { log.fatal('Cannot obtain API summary: $e'); return; } + final errorLog = StringBuffer(); + collectOutputStream(process.stderr, errorLog); final stream = JsonDecoder().bind(Utf8Decoder().bind(input)); dynamic json; try { json = await stream.single; } on Exception catch (e) { + printError(errorLog); log.fatal('Cannot parse summary: $e'); return; }
diff --git a/pkgs/jnigen/lib/src/logging/logging.dart b/pkgs/jnigen/lib/src/logging/logging.dart index 526981e..db8b713 100644 --- a/pkgs/jnigen/lib/src/logging/logging.dart +++ b/pkgs/jnigen/lib/src/logging/logging.dart
@@ -21,6 +21,13 @@ }); } +void printError(Object? message) { + if (stderr.supportsAnsiEscapes) { + message = '$_ansiRed$message$_ansiDefault'; + } + stderr.writeln(message); +} + extension FatalErrors on Logger { void fatal(Object? message, {int exitCode = 2}) { message = '${_ansiRed}Fatal: $message$_ansiDefault';
diff --git a/pkgs/jnigen/lib/src/summary/summary.dart b/pkgs/jnigen/lib/src/summary/summary.dart index ab3fdcf..900f7f3 100644 --- a/pkgs/jnigen/lib/src/summary/summary.dart +++ b/pkgs/jnigen/lib/src/summary/summary.dart
@@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:io'; import 'package:jnigen/src/logging/logging.dart'; -import 'package:jnigen/src/util/command_output.dart'; /// A command based summary source which calls the ApiSummarizer command. /// [sourcePaths] and [classPaths] can be provided for the summarizer to find @@ -68,7 +67,7 @@ } } - Future<Stream<List<int>>> getInputStream() async { + Future<Process> runProcess() async { final commandSplit = command.split(" "); final exec = commandSplit[0]; final List<String> args = commandSplit.sublist(1); @@ -80,13 +79,9 @@ } args.addAll(extraArgs); args.addAll(classes); - log.info('execute $exec ${args.join(' ')}'); final proc = await Process.start(exec, args, workingDirectory: workingDirectory?.toFilePath() ?? '.'); - // ignore: unawaited_futures - prefixedCommandOutputStream('[ApiSummarizer]', proc.stderr) - .forEach(stderr.writeln); - return proc.stdout; + return proc; } }
diff --git a/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart b/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart index 72fda52..e62d1e8 100644 --- a/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart +++ b/pkgs/jnigen/lib/src/tools/android_sdk_tools.dart
@@ -11,9 +11,8 @@ /// get path for android API sources static Future<String?> _getVersionDir( String relative, String? sdkRoot, List<int> versionOrder) async { - sdkRoot ??= Platform.environment['ANDROID_SDK_ROOT']; if (sdkRoot == null) { - throw ArgumentError('SDK Root not provided and ANDROID_SDK_ROOT not set'); + throw ArgumentError('SDK Root not provided'); } final parent = join(sdkRoot, relative); for (var version in versionOrder) { @@ -84,11 +83,16 @@ log.finer('Writing temporary gradle script with stub function...'); origBuild.writeAsStringSync('$script\n$_gradleListDepsFunction\n'); log.finer('Running gradle wrapper...'); - final procRes = Process.runSync('./gradlew', ['-q', 'listDependencies'], - workingDirectory: android); - log.finer('Restoring build scripts'); - origBuild.writeAsStringSync(script); - File(buildGradleOld).deleteSync(); + final gradleCommand = Platform.isWindows ? '.\\gradlew.bat' : './gradlew'; + ProcessResult procRes; + try { + procRes = Process.runSync(gradleCommand, ['-q', 'listDependencies'], + workingDirectory: android, runInShell: true); + } finally { + log.finer('Restoring build scripts'); + origBuild.writeAsStringSync(script); + File(buildGradleOld).deleteSync(); + } if (procRes.exitCode != 0) { final inAndroidProject = (androidProject == '.') ? '' : ' in $androidProject'; @@ -96,7 +100,9 @@ 'This can be related to a known issue with gradle. Please run ' '`flutter build apk`$inAndroidProject and try again\n'); } - final classpaths = (procRes.stdout as String).trim().split('\n'); + final classpaths = (procRes.stdout as String) + .trim() + .split(Platform.isWindows ? '\r\n' : '\n'); log.info('Found release build classpath with ${classpaths.length} entries'); return classpaths; }
diff --git a/pkgs/jnigen/lib/src/tools/build_summarizer.dart b/pkgs/jnigen/lib/src/tools/build_summarizer.dart index 1a1033b..d2dd22d 100644 --- a/pkgs/jnigen/lib/src/tools/build_summarizer.dart +++ b/pkgs/jnigen/lib/src/tools/build_summarizer.dart
@@ -29,17 +29,28 @@ pom.toFilePath(), 'assembly:assembly' ]; - log.info('execute mvn $mvnArgs'); - final mvnProc = await Process.start('mvn', mvnArgs, - workingDirectory: toolPath, - runInShell: true, - mode: ProcessStartMode.inheritStdio); - await mvnProc.exitCode; - await File(targetJarFile).rename(jarFile); - await Directory(mvnTargetDir).delete(recursive: true); + log.info('execute mvn ${mvnArgs.join(" ")}'); + try { + final mvnProc = await Process.run('mvn', mvnArgs, + workingDirectory: toolPath, runInShell: true); + final exitCode = mvnProc.exitCode; + if (exitCode == 0) { + await File(targetJarFile).rename(jarFile); + } else { + printError(mvnProc.stdout); + printError(mvnProc.stderr); + printError("maven exited with $exitCode"); + } + } finally { + await Directory(mvnTargetDir).delete(recursive: true); + } } Future<void> buildSummarizerIfNotExists({bool force = false}) async { + // TODO(#43): This function cannot be invoked concurrently because 2 processes + // will start building summarizer at once. Introduce a locking mechnanism so + // that when one process is building summarizer JAR, other process waits using + // exponential backoff. final jarExists = await File(jarFile).exists(); final isJarStale = jarExists && await isPackageModifiedAfter(
diff --git a/pkgs/jnigen/lib/src/tools/maven_tools.dart b/pkgs/jnigen/lib/src/tools/maven_tools.dart index 80d2c6f..8d1ccd9 100644 --- a/pkgs/jnigen/lib/src/tools/maven_tools.dart +++ b/pkgs/jnigen/lib/src/tools/maven_tools.dart
@@ -34,7 +34,7 @@ log.finer('using POM stub:\n$pom'); await File(tempPom).writeAsString(pom); await Directory(tempTarget).create(); - await _runCmd('mvn', ['-f', tempPom, ...mvnArgs]); + await _runCmd('mvn', ['-q', '-f', tempPom, ...mvnArgs]); await File(tempPom).delete(); await Directory(tempTarget).delete(recursive: true); }
diff --git a/pkgs/jnigen/lib/src/util/command_output.dart b/pkgs/jnigen/lib/src/util/command_output.dart index ba9366a..64deac2 100644 --- a/pkgs/jnigen/lib/src/util/command_output.dart +++ b/pkgs/jnigen/lib/src/util/command_output.dart
@@ -11,3 +11,6 @@ Stream<String> prefixedCommandOutputStream( String prefix, Stream<List<int>> input) => commandOutputStream((line) => '$prefix $line', input); + +void collectOutputStream(Stream<List<int>> stream, StringBuffer buffer) => + stream.transform(Utf8Decoder()).forEach(buffer.write);
diff --git a/pkgs/jnigen/lib/src/util/name_utils.dart b/pkgs/jnigen/lib/src/util/name_utils.dart index 38d980b..96d0c83 100644 --- a/pkgs/jnigen/lib/src/util/name_utils.dart +++ b/pkgs/jnigen/lib/src/util/name_utils.dart
@@ -2,10 +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/elements/elements.dart'; - -String getPackageName(String binaryName) => cutFromLast(binaryName, '.')[0]; - /// splits [str] into 2 from last occurence of [sep] List<String> cutFromLast(String str, String sep) { final li = str.lastIndexOf(sep); @@ -14,17 +10,3 @@ } return [str.substring(0, li), str.substring(li + 1)]; } - -String getLastName(String binaryName) => binaryName.split('.').last; - -String getSimpleNameOf(ClassDecl cls) => cls.simpleName; - -/// Returns class name as useful in dart. -/// -/// Eg -> a.b.X.Y -> X_Y -String simplifiedClassName(String binaryName) => - getLastName(binaryName).replaceAll('\$', '_'); - -// Utilities to operate on package names. - -List<String> getComponents(String packageName) => packageName.split('.');
diff --git a/pkgs/jnigen/lib/src/writers/bindings_writer.dart b/pkgs/jnigen/lib/src/writers/bindings_writer.dart new file mode 100644 index 0000000..f437492 --- /dev/null +++ b/pkgs/jnigen/lib/src/writers/bindings_writer.dart
@@ -0,0 +1,24 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:jnigen/jnigen.dart'; +import 'package:jnigen/src/logging/logging.dart'; + +abstract class BindingsWriter { + Future<void> writeBindings(Iterable<ClassDecl> classes); + + /// Run dart format command on [path]. + static Future<void> runDartFormat(String path) async { + log.info('Running dart format...'); + final formatRes = await Process.run('dart', ['format', path]); + // if negative exit code, likely due to an interrupt. + if (formatRes.exitCode > 0) { + log.fatal('Dart format completed with exit code ${formatRes.exitCode} ' + 'This usually means there\'s a syntax error in bindings.\n' + 'Please look at the generated files and report a bug.'); + } + } +}
diff --git a/pkgs/jnigen/lib/src/writers/files_writer.dart b/pkgs/jnigen/lib/src/writers/files_writer.dart new file mode 100644 index 0000000..f8e724a --- /dev/null +++ b/pkgs/jnigen/lib/src/writers/files_writer.dart
@@ -0,0 +1,279 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'dart:math'; + +import 'package:jnigen/src/bindings/bindings.dart'; +import 'package:jnigen/src/logging/logging.dart'; +import 'package:jnigen/src/elements/elements.dart'; +import 'package:jnigen/src/config/config.dart'; +import 'package:jnigen/src/util/find_package.dart'; +import 'package:jnigen/src/util/name_utils.dart'; +import 'package:jnigen/src/writers/bindings_writer.dart'; + +/// Resolver for file-per-package mapping, in which the java package hierarchy +/// is mirrored. +class PackagePathResolver implements SymbolResolver { + PackagePathResolver(this.importMap, this.currentPackage, this.inputClassNames, + {this.predefined = const {}}); + + final String currentPackage; + final Map<String, String> importMap; + final Map<String, String> predefined; + final Set<String> inputClassNames; + + final List<String> importStrings = []; + + final Set<String> relativeImportedPackages = {}; + + final Map<String, String> _importedNameToPackage = {}; + final Map<String, String> _packageToImportedName = {}; + + /// Returns the dart name of the [binaryName] in current translation context, + /// or `null` if the name cannot be resolved. + @override + String? resolve(String binaryName) { + if (predefined.containsKey(binaryName)) { + return predefined[binaryName]; + } + final parts = cutFromLast(binaryName, '.'); + final package = parts[0]; + final typename = parts[1]; + final simpleTypeName = typename.replaceAll('\$', '_'); + + if (package == currentPackage && inputClassNames.contains(binaryName)) { + return simpleTypeName; + } + + if (_packageToImportedName.containsKey(package)) { + // This package was already resolved + // but we still need to check if it was a relative import, in which case + // the class not in inputClassNames cannot be mapped here. + if (!relativeImportedPackages.contains(package) || + inputClassNames.contains(binaryName)) { + final importedName = _packageToImportedName[package]; + return '$importedName.$simpleTypeName'; + } + } + + final packageImport = getImport(package, binaryName); + log.finest('$package resolved to $packageImport for $binaryName'); + if (packageImport == null) { + return null; + } + + final pkgName = cutFromLast(package, '.')[1]; + if (pkgName.isEmpty) { + throw UnsupportedError('No package could be deduced from ' + 'qualified binaryName'); + } + + // We always name imports with an underscore suffix, so that they can be + // never shadowed by a parameter or local variable. + var importedName = '${pkgName}_'; + int suffix = 0; + while (_importedNameToPackage.containsKey(importedName)) { + suffix++; + importedName = '$pkgName${suffix}_'; + } + + _importedNameToPackage[importedName] = package; + _packageToImportedName[package] = importedName; + importStrings.add('import "$packageImport" as $importedName;\n'); + return '$importedName.$simpleTypeName'; + } + + /// Returns import string, or `null` if package not found. + String? getImport(String packageToResolve, String binaryName) { + final right = <String>[]; + var prefix = packageToResolve; + + if (prefix.isEmpty) { + throw UnsupportedError('unexpected: empty package name.'); + } + + final dest = packageToResolve.split('.'); + final src = currentPackage.split('.'); + if (inputClassNames.contains(binaryName)) { + int common = 0; + for (int i = 0; i < src.length && i < dest.length; i++) { + if (src[i] == dest[i]) { + common++; + } + } + // a.b.c => a/b/c.dart + // from there + // a/b.dart => ../b.dart + // a.b.d => d.dart + // a.b.c.d => c/d.dart + var pathToCommon = ''; + if (common < src.length) { + pathToCommon = '../' * (src.length - common); + } + final pathToPackage = dest.skip(max(common - 1, 0)).join('/'); + relativeImportedPackages.add(packageToResolve); + return '$pathToCommon$pathToPackage.dart'; + } + + while (prefix.isNotEmpty) { + final split = cutFromLast(prefix, '.'); + final left = split[0]; + right.add(split[1]); + // eg: packages[org.apache.pdfbox]/org/apache/pdfbox.dart + if (importMap.containsKey(prefix)) { + final sub = packageToResolve.replaceAll('.', '/'); + final pkg = _suffix(importMap[prefix]!, '/'); + return '$pkg$sub.dart'; + } + prefix = left; + } + return null; + } + + String _suffix(String str, String suffix) { + if (str.endsWith(suffix)) { + return str; + } + return str + suffix; + } + + @override + List<String> getImportStrings() { + return importStrings; + } +} + +/// Writer which executes custom callback on passed class elements. +/// +/// This class is provided for debugging purposes. +class CallbackWriter implements BindingsWriter { + CallbackWriter(this.callback); + Future<void> Function(Iterable<ClassDecl>) callback; + @override + Future<void> writeBindings(Iterable<ClassDecl> classes) async { + await callback(classes); + } +} + +/// Writer which takes writes C and Dart bindings to specified directories. +/// +/// The structure of dart files is determined by package structure of java. +/// One dart file corresponds to one java package, and it's path is decided by +/// fully qualified name of the package. +/// +/// Example: +/// `android.os` -> `$dartWrappersRoot`/`android/os.dart` +class FilesWriter extends BindingsWriter { + static const _initFileName = '_init.dart'; + + FilesWriter(this.config); + Config config; + + @override + Future<void> writeBindings(Iterable<ClassDecl> classes) async { + final preamble = config.preamble; + final Map<String, List<ClassDecl>> packages = {}; + final Map<String, ClassDecl> classesByName = {}; + for (var c in classes) { + classesByName.putIfAbsent(c.binaryName, () => c); + packages.putIfAbsent(c.packageName!, () => <ClassDecl>[]); + packages[c.packageName!]!.add(c); + } + final classNames = classesByName.keys.toSet(); + + if (config.bindingsType == BindingsType.packageStructured) { + throw UnimplementedError( + "Package structured bindings are not yet implemented"); + } + + final cRoot = config.cRoot!; + log.info("Using c root = $cRoot"); + final dartRoot = config.dartRoot!; + log.info("Using dart root = $dartRoot"); + final libraryName = config.libraryName!; + + log.info('Creating dart init file ...'); + final initFileUri = dartRoot.resolve(_initFileName); + final initFile = await File.fromUri(initFileUri).create(recursive: true); + var initCode = DartBindingsGenerator.initFile(libraryName); + if (preamble != null) { + initCode = '$preamble\n$initCode'; + } + await initFile.writeAsString(initCode, flush: true); + final subdir = config.cSubdir ?? '.'; + final cFileRelativePath = '$subdir/$libraryName.c'; + final cFile = await File.fromUri(cRoot.resolve(cFileRelativePath)) + .create(recursive: true); + final cFileStream = cFile.openWrite(); + if (preamble != null) { + cFileStream.writeln(preamble); + } + cFileStream.write(CPreludes.prelude); + ApiPreprocessor.preprocessAll(classesByName, config); + for (var packageName in packages.keys) { + final relativeFileName = '${packageName.replaceAll('.', '/')}.dart'; + final dartFileUri = dartRoot.resolve(relativeFileName); + log.fine('Writing bindings for $packageName...'); + final dartFile = await File.fromUri(dartFileUri).create(recursive: true); + final resolver = PackagePathResolver( + config.importMap ?? const {}, packageName, classNames, + predefined: {'java.lang.String': 'jni.JniString'}); + final cgen = CBindingGenerator(config); + final dgen = DartBindingsGenerator(config, resolver); + + final package = packages[packageName]!; + final cBindings = package.map(cgen.generateBinding).toList(); + final dartBindings = package.map(dgen.generateBinding).toList(); + // write imports from bindings + final dartFileStream = dartFile.openWrite(); + final initImportPath = ('../' * + relativeFileName.codeUnits + .where((cu) => '/'.codeUnitAt(0) == cu) + .length) + + _initFileName; + if (preamble != null) { + dartFileStream.writeln(preamble); + } + dartFileStream + ..write(DartBindingsGenerator.bindingFileHeaders) + ..write(resolver.getImportStrings().join('\n')) + ..write('import "$initImportPath" show jniLookup;\n\n'); + // write dart bindings only after all imports are figured out + dartBindings.forEach(dartFileStream.write); + cBindings.forEach(cFileStream.write); + await dartFileStream.close(); + } + await cFileStream.close(); + await BindingsWriter.runDartFormat(dartRoot.toFilePath()); + log.info('Copying auxiliary files...'); + await _copyFileFromPackage( + 'jni', 'src/dartjni.h', cRoot.resolve('$subdir/dartjni.h')); + await _copyFileFromPackage( + 'jnigen', 'cmake/CMakeLists.txt.tmpl', cRoot.resolve('CMakeLists.txt'), + transform: (s) { + return s + .replaceAll('{{LIBRARY_NAME}}', libraryName) + .replaceAll('{{SUBDIR}}', subdir); + }); + log.info('Completed.'); + } + + Future<void> _copyFileFromPackage(String package, String relPath, Uri target, + {String Function(String)? transform}) async { + final packagePath = await findPackageRoot(package); + if (packagePath != null) { + final sourceFile = File.fromUri(packagePath.resolve(relPath)); + final targetFile = await File.fromUri(target).create(recursive: true); + var source = await sourceFile.readAsString(); + if (transform != null) { + source = transform(source); + } + await targetFile.writeAsString(source); + } else { + log.warning('package $package not found! ' + 'skipped copying ${target.toFilePath()}'); + } + } +}
diff --git a/pkgs/jnigen/lib/src/writers/writers.dart b/pkgs/jnigen/lib/src/writers/writers.dart deleted file mode 100644 index e6e82fd..0000000 --- a/pkgs/jnigen/lib/src/writers/writers.dart +++ /dev/null
@@ -1,153 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'dart:io'; - -import 'package:jnigen/src/bindings/bindings.dart'; - -import 'package:jnigen/src/elements/elements.dart'; -import 'package:jnigen/src/config/config.dart'; -import 'package:jnigen/src/logging/logging.dart'; -import 'package:jnigen/src/util/find_package.dart'; - -abstract class BindingsWriter { - Future<void> writeBindings(Iterable<ClassDecl> classes); -} - -/// Writer which executes custom callback on passed class elements. -/// -/// This class is provided for debugging purposes. -class CallbackWriter implements BindingsWriter { - CallbackWriter(this.callback); - Future<void> Function(Iterable<ClassDecl>) callback; - @override - Future<void> writeBindings(Iterable<ClassDecl> classes) async { - await callback(classes); - } -} - -/// Writer which takes writes C and Dart bindings to specified directories. -/// -/// The structure of dart files is determined by package structure of java. -/// One dart file corresponds to one java package, and it's path is decided by -/// fully qualified name of the package. -/// -/// Example: -/// `android.os` -> `$dartWrappersRoot`/`android/os.dart` -class FilesWriter extends BindingsWriter { - static const _initFileName = '_init.dart'; - - FilesWriter(this.config); - Config config; - - @override - Future<void> writeBindings(Iterable<ClassDecl> classes) async { - // If the file already exists, show warning. - // sort classes so that all classes get written at once. - final cRoot = config.cRoot; - final dartRoot = config.dartRoot; - final libraryName = config.libraryName; - final preamble = config.preamble; - - final Map<String, List<ClassDecl>> packages = {}; - final Map<String, ClassDecl> classesByName = {}; - for (var c in classes) { - classesByName.putIfAbsent(c.binaryName, () => c); - packages.putIfAbsent(c.packageName!, () => <ClassDecl>[]); - packages[c.packageName!]!.add(c); - } - final classNames = classesByName.keys.toSet(); - - log.info('Creating dart init file ...'); - final initFileUri = dartRoot.resolve(_initFileName); - final initFile = await File.fromUri(initFileUri).create(recursive: true); - var initCode = DartPreludes.initFile(config.libraryName); - if (preamble != null) { - initCode = '$preamble\n$initCode'; - } - await initFile.writeAsString(initCode, flush: true); - final subdir = config.cSubdir ?? '.'; - final cFileRelativePath = '$subdir/$libraryName.c'; - final cFile = await File.fromUri(cRoot.resolve(cFileRelativePath)) - .create(recursive: true); - final cFileStream = cFile.openWrite(); - if (preamble != null) { - cFileStream.writeln(preamble); - } - cFileStream.write(CPreludes.prelude); - ApiPreprocessor.preprocessAll(classesByName, config); - for (var packageName in packages.keys) { - final relativeFileName = '${packageName.replaceAll('.', '/')}.dart'; - final dartFileUri = dartRoot.resolve(relativeFileName); - log.fine('Writing bindings for $packageName...'); - final dartFile = await File.fromUri(dartFileUri).create(recursive: true); - final resolver = PackagePathResolver( - config.importMap ?? const {}, packageName, classNames, - predefined: {'java.lang.String': 'jni.JniString'}); - final cgen = CBindingGenerator(config); - final dgen = DartBindingsGenerator(config, resolver); - - final package = packages[packageName]!; - final cBindings = package.map(cgen.generateBinding).toList(); - final dartBindings = package.map(dgen.generateBinding).toList(); - // write imports from bindings - final dartFileStream = dartFile.openWrite(); - final initImportPath = ('../' * - relativeFileName.codeUnits - .where((cu) => '/'.codeUnitAt(0) == cu) - .length) + - _initFileName; - if (preamble != null) { - dartFileStream.writeln(preamble); - } - dartFileStream - ..write(DartPreludes.bindingFileHeaders) - ..write(resolver.getImportStrings().join('\n')) - ..write('import "$initImportPath" show jniLookup;\n\n'); - // write dart bindings only after all imports are figured out - dartBindings.forEach(dartFileStream.write); - cBindings.forEach(cFileStream.write); - await dartFileStream.close(); - } - await cFileStream.close(); - log.info('Running dart format...'); - final formatRes = - await Process.run('dart', ['format', dartRoot.toFilePath()]); - // if negative exit code, likely due to an interrupt. - if (formatRes.exitCode > 0) { - log.fatal('Dart format completed with exit code ${formatRes.exitCode} ' - 'This usually means there\'s a syntax error in bindings.\n' - 'Please look at the generated files and report a bug.'); - } - - log.info('Copying auxiliary files...'); - await _copyFileFromPackage( - 'jni', 'src/dartjni.h', cRoot.resolve('$subdir/dartjni.h')); - await _copyFileFromPackage( - 'jnigen', 'cmake/CMakeLists.txt.tmpl', cRoot.resolve('CMakeLists.txt'), - transform: (s) { - return s - .replaceAll('{{LIBRARY_NAME}}', libraryName) - .replaceAll('{{SUBDIR}}', subdir); - }); - log.info('Completed.'); - } - - Future<void> _copyFileFromPackage(String package, String relPath, Uri target, - {String Function(String)? transform}) async { - final packagePath = await findPackageRoot(package); - if (packagePath != null) { - final sourceFile = File.fromUri(packagePath.resolve(relPath)); - final targetFile = await File.fromUri(target).create(recursive: true); - var source = await sourceFile.readAsString(); - if (transform != null) { - source = transform(source); - } - await targetFile.writeAsString(source); - } else { - log.warning('package $package not found! ' - 'skipped copying ${target.toFilePath()}'); - } - } -}
diff --git a/pkgs/jnigen/test/README.md b/pkgs/jnigen/test/README.md new file mode 100644 index 0000000..0911337 --- /dev/null +++ b/pkgs/jnigen/test/README.md
@@ -0,0 +1,11 @@ +Some notes about tests in this directory: + +* [jackson_core_test](jackson_core_test/) is an end-to-end test which generates bindings for `jackson_core` library using the whole jnigen pipeline and compares the generated bindings with expected bindings. [simple_package_test](simple_package_test/) is similar but instead of using a Java library from maven, uses a stub java class. +* [bindings_test.dart](bindings_test.dart) runs the generated bindings to make sure they work. There are only a few tests here right now. +* [test_util/](test_util/) directory contains some code common to both `jackson_core_test` and `simple_package_test`. (Especially the functions `generateAndCompareBindings` and `generateAndAnalyzeBindings`). +* [yaml_config_test.dart](yaml_config_test.dart) runs the same `jackson_core` configuration but through YAML and makes sure it generates the same bindings. +* The other files contain unit tests for some error-prone components. + +Note: Tests fail if summarizer is not previously built and 2 tests try to build it concurrently. We have to address it using a lock file and exponential backoff (#43). Temporarily, run `dart run jnigen:setup` before running tests for the first time. + +TODO(#62): Add some unit & integration tests in the java portion.
diff --git a/pkgs/jnigen/test/bindings_test.dart b/pkgs/jnigen/test/bindings_test.dart index 7d2c3b0..f8d4cd3 100644 --- a/pkgs/jnigen/test/bindings_test.dart +++ b/pkgs/jnigen/test/bindings_test.dart
@@ -22,37 +22,37 @@ import 'test_util/test_util.dart'; -final simplePackagePath = join('test', 'simple_package_test'); -final jacksonCorePath = join('test', 'jackson_core_test'); -final simplePackageJavaPath = join(simplePackagePath, 'java'); +final simplePackageTest = join('test', 'simple_package_test'); +final jacksonCoreTest = join('test', 'jackson_core_test'); +final simplePackageTestJava = join(simplePackageTest, 'java'); Future<void> setupDylibsAndClasses() async { - await runCmd('dart', ['run', 'jni:setup']); - await runCmd( - 'dart', ['run', 'jni:setup', '-S', join(simplePackagePath, 'src')]); - await runCmd('dart', - ['run', 'jni:setup', '-S', join(jacksonCorePath, 'third_party', 'src')]); + await runCommand('dart', ['run', 'jni:setup']); + await runCommand( + 'dart', ['run', 'jni:setup', '-s', join(simplePackageTest, 'src')]); + await runCommand('dart', + ['run', 'jni:setup', '-s', join(jacksonCoreTest, 'third_party', 'src')]); final group = join('com', 'github', 'dart_lang', 'jnigen'); - await runCmd( + await runCommand( 'javac', [ join(group, 'simple_package', 'Example.java'), join(group, 'pkg2', 'C2.java') ], - workingDirectory: simplePackageJavaPath); - await runCmd('dart', [ + workingDirectory: simplePackageTestJava); + await runCommand('dart', [ 'run', 'jnigen:download_maven_jars', '--config', - join(jacksonCorePath, 'jnigen.yaml') + join(jacksonCoreTest, 'jnigen.yaml') ]); - final jacksonJars = await getJarPaths(join(jacksonCorePath, 'third_party')); + final jacksonJars = await getJarPaths(join(jacksonCoreTest, 'third_party')); if (!Platform.isAndroid) { Jni.spawn( - dylibDir: 'build/jni_libs', - classPath: [simplePackageJavaPath, ...jacksonJars]); + dylibDir: join('build', 'jni_libs'), + classPath: [simplePackageTestJava, ...jacksonJars]); } } @@ -87,12 +87,10 @@ aux.delete(); ex.delete(); }); - test('exceptions', () { - expect(() => Example.throwException(), throwsA(isA<JniException>())); - }); test('simple json parsing test', () { final json = JniString.fromString('[1, true, false, 2, 4]'); - final factory = JsonFactory(); + JsonFactory factory; + factory = JsonFactory(); final parser = factory.createParser6(json); final values = <bool>[]; while (!parser.isClosed()) { @@ -113,4 +111,7 @@ expect(() => erroneous.nextToken(), throwsA(isA<JniException>())); }); }); + test('exceptions', () { + expect(() => Example.throwException(), throwsException); + }); }
diff --git a/pkgs/jnigen/test/config_test.dart b/pkgs/jnigen/test/config_test.dart index f1ed780..352c378 100644 --- a/pkgs/jnigen/test/config_test.dart +++ b/pkgs/jnigen/test/config_test.dart
@@ -20,44 +20,50 @@ /// Compares 2 [Config] objects using [expect] to give useful errors when /// two fields are not equal. void expectConfigsAreEqual(Config a, Config b) { - expect(a.classes, equals(b.classes)); - expect(a.libraryName, equals(b.libraryName)); - expect(a.cRoot, equals(b.cRoot)); - expect(a.dartRoot, equals(b.dartRoot)); - expect(a.sourcePath, equals(b.sourcePath)); - expect(a.classPath, equals(b.classPath)); - expect(a.preamble, equals(b.preamble)); - expect(a.importMap, equals(b.importMap)); + expect(a.classes, equals(b.classes), reason: "classes"); + expect(a.libraryName, equals(b.libraryName), reason: "libraryName"); + expect(a.cRoot, equals(b.cRoot), reason: "cRoot"); + expect(a.dartRoot, equals(b.dartRoot), reason: "dartRoot"); + expect(a.sourcePath, equals(b.sourcePath), reason: "sourcePath"); + expect(a.classPath, equals(b.classPath), reason: "classPath"); + expect(a.preamble, equals(b.preamble), reason: "preamble"); + expect(a.importMap, equals(b.importMap), reason: "importMap"); final am = a.mavenDownloads; final bm = b.mavenDownloads; if (am != null) { expect(bm, isNotNull); - expect(am.sourceDeps, bm!.sourceDeps); - expect(path.equals(am.sourceDir, bm.sourceDir), isTrue); - expect(am.jarOnlyDeps, bm.jarOnlyDeps); - expect(path.equals(am.jarDir, bm.jarDir), isTrue); + expect(am.sourceDeps, bm!.sourceDeps, reason: "mavenDownloads.sourceDeps"); + expect(path.equals(am.sourceDir, bm.sourceDir), isTrue, + reason: "mavenDownloads.sourceDir"); + expect(am.jarOnlyDeps, bm.jarOnlyDeps, + reason: "mavenDownloads.jarOnlyDeps"); + expect(path.equals(am.jarDir, bm.jarDir), isTrue, + reason: "mavenDownloads.jarDir"); } else { - expect(bm, isNull); + expect(bm, isNull, reason: "mavenDownloads"); } final aa = a.androidSdkConfig; final ba = b.androidSdkConfig; if (aa != null) { - expect(ba, isNotNull); - expect(aa.versions, ba!.versions); - expect(aa.sdkRoot, ba.sdkRoot); - expect(aa.includeSources, ba.includeSources); + expect(ba, isNotNull, reason: "androidSdkConfig"); + expect(aa.versions, ba!.versions, reason: "androidSdkConfig.versions"); + expect(aa.sdkRoot, ba.sdkRoot, reason: "androidSdkConfig.sdkRoot"); + expect(aa.includeSources, ba.includeSources, + reason: "androidSdkConfig.includeSources"); } else { - expect(ba, isNull); + expect(ba, isNull, reason: "androidSdkConfig"); } final aso = a.summarizerOptions; final bso = b.summarizerOptions; if (aso != null) { - expect(bso, isNotNull); - expect(aso.extraArgs, bso!.extraArgs); - expect(aso.workingDirectory, bso.workingDirectory); - expect(aso.backend, bso.backend); + expect(bso, isNotNull, reason: "summarizerOptions"); + expect(aso.extraArgs, bso!.extraArgs, + reason: "summarizerOptions.extraArgs"); + expect(aso.workingDirectory, bso.workingDirectory, + reason: "summarizerOptions.workingDirectory"); + expect(aso.backend, bso.backend, reason: "summarizerOptions.backend"); } else { - expect(bso, isNull); + expect(bso, isNull, reason: "summarizerOptions"); } }
diff --git a/pkgs/jnigen/test/jackson_core_test/generate.dart b/pkgs/jnigen/test/jackson_core_test/generate.dart index 7335757..7b708c4 100644 --- a/pkgs/jnigen/test/jackson_core_test/generate.dart +++ b/pkgs/jnigen/test/jackson_core_test/generate.dart
@@ -25,26 +25,25 @@ '// limitations under the License.\n'; const testName = 'jackson_core_test'; -final thirdParty = join('test', testName, 'third_party'); -const deps = ['com.fasterxml.jackson.core:jackson-core:2.13.3']; +final thirdPartyDir = join('test', testName, 'third_party'); +const deps = ['com.fasterxml.jackson.core:jackson-core:2.13.4']; Config getConfig( - {required String root, - bool generateFullVersion = false, - bool useAsm = false}) { + {String? root, bool generateFullVersion = false, bool useAsm = false}) { + final rootDir = root ?? thirdPartyDir; final config = Config( mavenDownloads: MavenDownloads( sourceDeps: deps, - sourceDir: join(thirdParty, 'java'), - jarDir: join(thirdParty, 'jar'), + sourceDir: join(thirdPartyDir, 'java'), + jarDir: join(thirdPartyDir, 'jar'), ), summarizerOptions: SummarizerOptions( backend: useAsm ? 'asm' : null, ), preamble: jacksonPreamble, libraryName: testName, - cRoot: Uri.directory(join(root, 'src')), - dartRoot: Uri.directory(join(root, 'lib')), + cRoot: Uri.directory(join(rootDir, 'src')), + dartRoot: Uri.directory(join(rootDir, 'lib')), classes: (generateFullVersion) ? ['com.fasterxml.jackson.core'] : [ @@ -52,7 +51,7 @@ 'com.fasterxml.jackson.core.JsonParser', 'com.fasterxml.jackson.core.JsonToken', ], - logLevel: Level.WARNING, + logLevel: Level.INFO, exclude: BindingExclusions( fields: excludeAll<Field>([ ['com.fasterxml.jackson.core.JsonFactory', 'DEFAULT_QUOTE_CHAR'], @@ -65,13 +64,4 @@ return config; } -Future<void> generate( - {required String root, - bool generateFullVersion = false, - bool useAsm = false}) async { - final config = getConfig( - root: root, generateFullVersion: generateFullVersion, useAsm: useAsm); - await generateJniBindings(config); -} - -void main() => generate(root: thirdParty); +void main() async => await generateJniBindings(getConfig());
diff --git a/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart b/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart index c1c9185..992c30b 100644 --- a/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart +++ b/pkgs/jnigen/test/jackson_core_test/generated_files_test.dart
@@ -2,8 +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 'dart:io'; - import 'package:path/path.dart' hide equals; import 'package:test/test.dart'; @@ -11,38 +9,21 @@ import '../test_util/test_util.dart'; import 'generate.dart'; -const packageTestsDir = 'test'; -const testName = 'jackson_core_test'; - void main() async { - final thirdParty = join(packageTestsDir, testName, 'third_party'); test("compare generated bindings for jackson_core", () async { - final tempDir = await Directory(thirdParty).createTemp("test_"); - await generate(root: tempDir.path); - - compareDirs(join(thirdParty, 'lib'), join(tempDir.path, "lib")); - compareDirs(join(thirdParty, 'src'), join(tempDir.path, "src")); - tempDir.deleteSync(recursive: true); + final lib = join(thirdPartyDir, 'lib'); + final src = join(thirdPartyDir, 'src'); + await generateAndCompareBindings(getConfig(), lib, src); }); - Future<void> analyze(String path) async { - final analyzeProc = await Process.start('dart', ['analyze', path], - mode: ProcessStartMode.inheritStdio); - final exitCode = await analyzeProc.exitCode; - expect(exitCode, 0); - } test( 'generate and analyze bindings for complete library, ' 'not just required classes', () async { - final tempDir = await Directory(thirdParty).createTemp("test_"); - await generate(root: tempDir.path, generateFullVersion: true); - await analyze(tempDir.path); - tempDir.deleteSync(recursive: true); + final config = getConfig(generateFullVersion: true); + await generateAndAnalyzeBindings(config); }, timeout: Timeout(Duration(minutes: 2))); test('generate and analyze bindings using ASM', () async { - final tempDir = await Directory(thirdParty).createTemp("test_"); - await generate(root: tempDir.path, generateFullVersion: true, useAsm: true); - await analyze(tempDir.path); - tempDir.deleteSync(recursive: true); + final config = getConfig(generateFullVersion: true, useAsm: true); + await generateAndAnalyzeBindings(config); }, timeout: Timeout(Duration(minutes: 2))); }
diff --git a/pkgs/jnigen/test/jackson_core_test/jnigen.yaml b/pkgs/jnigen/test/jackson_core_test/jnigen.yaml index d5d7d64..5a4f69c 100644 --- a/pkgs/jnigen/test/jackson_core_test/jnigen.yaml +++ b/pkgs/jnigen/test/jackson_core_test/jnigen.yaml
@@ -1,13 +1,12 @@ maven_downloads: source_deps: - - 'com.fasterxml.jackson.core:jackson-core:2.13.3' + - 'com.fasterxml.jackson.core:jackson-core:2.13.4' source_dir: test/jackson_core_test/third_party/java jar_dir: test/jackson_core_test/third_party/jar - dart_root: test/jackson_core_test/third_party/lib c_root: test/jackson_core_test/third_party/src library_name: jackson_core_test -log_level: warning +log_level: info classes: - 'com.fasterxml.jackson.core.JsonFactory' - 'com.fasterxml.jackson.core.JsonParser' @@ -16,6 +15,10 @@ exclude: fields: - 'com.fasterxml.jackson.core.JsonFactory#DEFAULT_QUOTE_CHAR' + - 'com.fasterxml.jackson.core.Base64Variant#PADDING_CHAR_NONE' + - 'com.fasterxml.jackson.core.base.ParserMinimalBase#CHAR_NULL' + - 'com.fasterxml.jackson.core.io.UTF32Reader#NC' + preamble: | // Generated from jackson-core which is licensed under the Apache License 2.0.
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/src/dartjni.h b/pkgs/jnigen/test/jackson_core_test/third_party/src/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jnigen/test/jackson_core_test/third_party/src/dartjni.h +++ b/pkgs/jnigen/test/jackson_core_test/third_party/src/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jnigen/test/package_resolver_test.dart b/pkgs/jnigen/test/package_resolver_test.dart index f8f1be8..91e32dd 100644 --- a/pkgs/jnigen/test/package_resolver_test.dart +++ b/pkgs/jnigen/test/package_resolver_test.dart
@@ -2,7 +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/bindings/symbol_resolver.dart'; +import 'package:jnigen/src/writers/files_writer.dart'; import 'package:jnigen/src/util/name_utils.dart'; import 'package:test/test.dart';
diff --git a/pkgs/jnigen/test/simple_package_test/generate.dart b/pkgs/jnigen/test/simple_package_test/generate.dart index 67a5de0..99db612 100644 --- a/pkgs/jnigen/test/simple_package_test/generate.dart +++ b/pkgs/jnigen/test/simple_package_test/generate.dart
@@ -8,8 +8,6 @@ import 'package:path/path.dart'; import 'package:jnigen/jnigen.dart'; -import '../test_util/test_util.dart'; - const testName = 'simple_package_test'; final testRoot = join('test', testName); final javaPath = join(testRoot, 'java'); @@ -28,21 +26,18 @@ join(javaPrefix, 'pkg2', 'C2.java'), ]; -Future<void> compileJavaSources(String workingDir, List<String> files) async { - await runCmd('javac', files, workingDirectory: workingDir); +void compileJavaSources(String workingDir, List<String> files) async { + final procRes = Process.runSync('javac', files, workingDirectory: workingDir); + if (procRes.exitCode != 0) { + throw "javac exited with ${procRes.exitCode}\n" + "$procRes.stderr"; + } } -Future<void> generateSources(String lib, String src) async { - await compileJavaSources(javaPath, javaFiles); - final cWrapperDir = Uri.directory(join(testRoot, src)); - final dartWrappersRoot = Uri.directory(join(testRoot, lib)); - final cDir = Directory.fromUri(cWrapperDir); - final dartDir = Directory.fromUri(dartWrappersRoot); - for (var dir in [cDir, dartDir]) { - if (await dir.exists()) { - await dir.delete(recursive: true); - } - } +Config getConfig() { + compileJavaSources(javaPath, javaFiles); + final cWrapperDir = Uri.directory(join(testRoot, "src")); + final dartWrappersRoot = Uri.directory(join(testRoot, "lib")); final config = Config( sourcePath: [Uri.directory(javaPath)], classPath: [Uri.directory(javaPath)], @@ -53,10 +48,10 @@ preamble: preamble, cRoot: cWrapperDir, dartRoot: dartWrappersRoot, - logLevel: Level.WARNING, + logLevel: Level.INFO, libraryName: 'simple_package', ); - await generateJniBindings(config); + return config; } -void main() async => await generateSources('lib', 'src'); +void main() async => await generateJniBindings(getConfig());
diff --git a/pkgs/jnigen/test/simple_package_test/generated_files_test.dart b/pkgs/jnigen/test/simple_package_test/generated_files_test.dart index ae1b4ba..c37de4a 100644 --- a/pkgs/jnigen/test/simple_package_test/generated_files_test.dart +++ b/pkgs/jnigen/test/simple_package_test/generated_files_test.dart
@@ -2,8 +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 'dart:io'; - import 'package:test/test.dart'; import 'package:path/path.dart' hide equals; @@ -11,18 +9,11 @@ import '../test_util/test_util.dart'; void main() async { - await generateSources('test_lib', 'test_src'); - // test if generated file == expected file - test('compare generated files', () { - compareDirs(join(testRoot, 'lib'), join(testRoot, 'test_lib')); - compareDirs(join(testRoot, 'src'), join(testRoot, 'test_src')); - }); - tearDownAll(() async { - for (var path in ['test_lib', 'test_src']) { - final folder = Directory(join(testRoot, path)); - if (await folder.exists()) { - await folder.delete(recursive: true); - } - } - }); + test("Generate and compare bindings for simple_package", () async { + await generateAndCompareBindings( + getConfig(), + join(testRoot, "lib"), + join(testRoot, "src"), + ); + }); // test if generated file == expected file }
diff --git a/pkgs/jnigen/test/simple_package_test/src/dartjni.h b/pkgs/jnigen/test/simple_package_test/src/dartjni.h index 0ce5069..bc72baa 100644 --- a/pkgs/jnigen/test/simple_package_test/src/dartjni.h +++ b/pkgs/jnigen/test/simple_package_test/src/dartjni.h
@@ -4,6 +4,7 @@ #pragma once +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. #include <jni.h> #include <stdint.h> #include <stdio.h> @@ -32,58 +33,102 @@ #include <android/log.h> #endif -#define JNI_LOG_TAG "Dart-JNI" - #ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv **) +#define __ENVP_CAST (JNIEnv**) #else -#define __ENVP_CAST (void **) +#define __ENVP_CAST (void**) #endif +/// Stores the global state of the JNI. typedef struct JniContext { - JavaVM *jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; } JniContext; -extern thread_local JNIEnv *jniEnv; +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; extern JniContext jni; -enum DartJniLogLevel { - JNI_VERBOSE = 2, - JNI_DEBUG, - JNI_INFO, - JNI_WARN, - JNI_ERROR -}; - +/// Types used by JNI API to distinguish between primitive types. enum JniType { - boolType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, }; -FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue result; + jthrowable exception; +} JniResult; -FFI_PLUGIN_EXPORT JavaVM *GetJavaVM(void); +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass classRef; + jthrowable exception; +} JniClassLookupResult; -FFI_PLUGIN_EXPORT int DestroyJavaVM(); +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + void* id; + jthrowable exception; +} JniPointerResult; -FFI_PLUGIN_EXPORT JNIEnv *GetJniEnv(void); +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessors { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); +} JniAccessors; -FFI_PLUGIN_EXPORT JNIEnv *SpawnJvm(JavaVMInitArgs *args); +FFI_PLUGIN_EXPORT JniAccessors* GetAccessors(); -FFI_PLUGIN_EXPORT jclass LoadClass(const char *name); +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args); + +FFI_PLUGIN_EXPORT jclass LoadClass(const char* name); FFI_PLUGIN_EXPORT jobject GetClassLoader(void); @@ -91,17 +136,8 @@ FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext (*context_getter)(void); -extern JNIEnv *(*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), - JNIEnv *(*eg)(void)); +// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c +// once migration to pure dart bindings is complete. // `static inline` because `inline` doesn't work, it may still not // inline the function in which case a linker error may be produced. @@ -109,77 +145,98 @@ // There has to be a better way to do this. Either to force inlining on target // platforms, or just leave it as normal function. -static inline void __load_class_into(jclass *cls, const char *name) { +static inline void __load_class_into(jclass* cls, const char* name) { #ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, - jni.loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader, + jni.loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); #else - *cls = (*jniEnv)->FindClass(jniEnv, name); + *cls = (*jniEnv)->FindClass(jniEnv, name); #endif } -static inline void load_class(jclass *cls, const char *name) { - if (*cls == NULL) { - __load_class_into(cls, name); - } +static inline void load_class(jclass* cls, const char* name) { + if (*cls == NULL) { + __load_class_into(cls, name); + } } -static inline void load_class_gr(jclass *cls, const char *name) { - if (*cls == NULL) { - jclass tmp; - __load_class_into(&tmp, name); - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } +static inline void load_class_gr(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp; + __load_class_into(&tmp, name); + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } } static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, - NULL); - } + if (jniEnv == NULL) { + (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL); + } } -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } } -static inline void load_method(jclass cls, jmethodID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } } -static inline void load_static_method(jclass cls, jmethodID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } } -static inline void load_field(jclass cls, jfieldID *res, const char *name, - const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } -} - -static inline void load_static_field(jclass cls, jfieldID *res, - const char *name, const char *sig) { - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } } static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; } +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext GetJniContext(); +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +}
diff --git a/pkgs/jnigen/test/test_util/test_util.dart b/pkgs/jnigen/test/test_util/test_util.dart index ca625de..1a38b16 100644 --- a/pkgs/jnigen/test/test_util/test_util.dart +++ b/pkgs/jnigen/test/test_util/test_util.dart
@@ -4,24 +4,26 @@ import 'dart:io'; +import 'package:jnigen/jnigen.dart'; import 'package:path/path.dart' hide equals; import 'package:test/test.dart'; -Future<bool> hasNoFilesInDir(String path) async { +Future<bool> isEmptyOrNotExistDir(String path) async { final dir = Directory(path); return (!await dir.exists()) || (await dir.list().length == 0); } -Future<int> runCmd(String exec, List<String> args, +/// Runs "$exec ${args...}" and returns exit code. +/// Required because only Process.start provides inheritStdio argument +Future<int> runCommand(String exec, List<String> args, {String? workingDirectory}) async { stderr.writeln('[exec] $exec ${args.join(" ")}'); final proc = await Process.start(exec, args, - workingDirectory: workingDirectory, - runInShell: true, - mode: ProcessStartMode.inheritStdio); - return proc.exitCode; + workingDirectory: workingDirectory, mode: ProcessStartMode.inheritStdio); + return await proc.exitCode; } +/// List all JAR files in [testRoot]/jar Future<List<String>> getJarPaths(String testRoot) async { final jarPath = join(testRoot, 'jar'); if (!await Directory(jarPath).exists()) { @@ -54,3 +56,38 @@ equals(b.readAsStringSync().replaceAll("\r\n", "\n"))); } } + +Future<void> _generateTempBindings(Config config, Directory tempDir) async { + final tempSrc = tempDir.uri.resolve("src/"); + final tempLib = tempDir.uri.resolve("lib/"); + config.cRoot = tempSrc; + config.dartRoot = tempLib; + await generateJniBindings(config); +} + +Future<void> generateAndCompareBindings( + Config config, String lib, String src) async { + final currentDir = Directory.current; + final tempDir = currentDir.createTempSync("jnigen_test_temp"); + final tempSrc = tempDir.uri.resolve("src/"); + final tempLib = tempDir.uri.resolve("lib/"); + try { + await _generateTempBindings(config, tempDir); + compareDirs(lib, tempLib.toFilePath()); + compareDirs(src, tempSrc.toFilePath()); + } finally { + tempDir.deleteSync(recursive: true); + } +} + +Future<void> generateAndAnalyzeBindings(Config config) async { + final tempDir = Directory.current.createTempSync("jnigen_test_temp"); + try { + await _generateTempBindings(config, tempDir); + final analyzeResult = Process.runSync("dart", ["analyze", tempDir.path]); + expect(analyzeResult.exitCode, equals(0), + reason: "Analyzer exited with non-zero status"); + } finally { + tempDir.deleteSync(recursive: true); + } +}
diff --git a/pkgs/jnigen/test/yaml_config_test.dart b/pkgs/jnigen/test/yaml_config_test.dart index 0885381..30083d1 100644 --- a/pkgs/jnigen/test/yaml_config_test.dart +++ b/pkgs/jnigen/test/yaml_config_test.dart
@@ -4,8 +4,7 @@ // End-to-end test confirming yaml config works as expected. -import 'dart:io'; - +import 'package:jnigen/jnigen.dart'; import 'package:path/path.dart' hide equals; import 'package:test/test.dart'; @@ -17,30 +16,15 @@ final testSrc = join(thirdParty, 'test_src_yaml'); final lib = join(thirdParty, 'lib'); final src = join(thirdParty, 'src'); - final config = join('test', 'jackson_core_test', 'jnigen.yaml'); + final configFile = join('test', 'jackson_core_test', 'jnigen.yaml'); test('generate and compare bindings using YAML config', () async { - final jnigenProc = await Process.start( - 'dart', - [ - 'run', - 'jnigen', - '--config', - config, - '-Dc_root=$testSrc', - '-Ddart_root=$testLib' - ], - mode: ProcessStartMode.inheritStdio); - expect(await jnigenProc.exitCode, equals(0)); - - final analyzeProc = - Process.runSync('dart', ['analyze', testLib], runInShell: true); - expect(analyzeProc.exitCode, equals(0)); - - compareDirs(lib, testLib); - compareDirs(src, testSrc); - - for (var dir in [testLib, testSrc]) { - Directory(dir).deleteSync(recursive: true); - } + final args = [ + '--config', + configFile, + '-Dc_root=$testSrc', + '-Ddart_root=$testLib', + ]; + final config = Config.parseArgs(args); + await generateAndCompareBindings(config, lib, src); }, timeout: Timeout.factor(4)); }