Skip to content

Commit db8fd44

Browse files
authored
[jnigen] Remove FFI patch and use mutex around method/field lookups (#246)
* Add pubspec.lock from JNI example to git. This file should be checked in. * Remove ffi patch and instead depend on the internals of a pinned package:ffi version (closes #149) * Add new tests on GlobalJniEnv * Rename findJniClass -> findJClass * Locking around method/field lookups. (closes #85) * Rename asJString and asDartString to toJStringPtr and toDartString for uniform naming * Rename JniClass -> JClass * Compare bindings normally and fallback to ignoring spaces
1 parent ec04082 commit db8fd44

File tree

118 files changed

+12002
-17198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+12002
-17198
lines changed

.github/workflows/test-package.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@ jobs:
182182
parallel: true
183183
path-to-lcov: ./jni/coverage/lcov.info
184184
- name: regenerate & compare ffigen bindings
185+
## Use git to verify no source files have changed
185186
run: |
186-
cp lib/src/third_party/jni_bindings_generated.dart __old_bindings
187-
dart run ffigen --config ffigen.yaml
188-
diff lib/src/third_party/jni_bindings_generated.dart __old_bindings
187+
dart run tool/generate_ffi_bindings.dart
188+
git diff --exit-code -- lib/src/third_party src/third_party
189189
190190
## Run tests for package:jni on windows, just to confirm it works.
191191
## Do not, for example, collect coverage or check formatting.

pkgs/jni/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ migrate_working_dir/
2828
.dart_tool/
2929
.packages
3030
build/
31+
32+
.vscode/

pkgs/jni/example/integration_test/on_device_jni_test.dart

Lines changed: 18 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -2,123 +2,27 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// This file contains mostly the same tests from package:jni's
6-
// test/jni_object_test.dart. This file can be run on android device using
7-
// flutter test integration_test/ and therefore useful for checking the
8-
// working of JNI on an Android device (or emulator).
9-
10-
import 'dart:io';
11-
125
import 'package:flutter_test/flutter_test.dart';
136

14-
import 'package:jni/jni.dart';
7+
import '../../test/global_env_test.dart' as global_env_test;
8+
import '../../test/exception_test.dart' as exception_test;
9+
import '../../test/jobject_test.dart' as jobject_test;
10+
import '../../test/jarray_test.dart' as jarray_test;
11+
import '../../test/type_test.dart' as type_test;
12+
13+
void integrationTestRunner(String description, void Function() testCallback) {
14+
testWidgets(description, (widgetTester) async => testCallback());
15+
}
1516

1617
void main() {
17-
if (!Platform.isAndroid) {
18-
try {
19-
Jni.spawn(dylibDir: "build/jni_libs");
20-
} on JvmExistsException {
21-
// TODO(#51): Support destroying and restarting JVM.
22-
}
18+
final testSuites = [
19+
global_env_test.run,
20+
exception_test.run,
21+
jobject_test.run,
22+
jarray_test.run,
23+
type_test.run,
24+
];
25+
for (var testSuite in testSuites) {
26+
testSuite(testRunner: integrationTestRunner);
2327
}
24-
25-
testWidgets("Long.intValue() using JObject", (t) async {
26-
final longClass = Jni.findJniClass("java/lang/Long");
27-
28-
final longCtor = longClass.getCtorID("(J)V");
29-
30-
final long = longClass.newInstance(longCtor, [176]);
31-
32-
final intValue = long.callMethodByName<int>("intValue", "()I", []);
33-
expect(intValue, equals(176));
34-
35-
long.delete();
36-
longClass.delete();
37-
});
38-
39-
testWidgets("call a static method using JniClass APIs", (t) async {
40-
final integerClass = JniClass.fromRef(Jni.findClass("java/lang/Integer"));
41-
final result = integerClass.callStaticMethodByName<JString>(
42-
"toHexString", "(I)Ljava/lang/String;", [JValueInt(31)]);
43-
44-
final resultString = result.toDartString();
45-
46-
result.delete();
47-
expect(resultString, equals("1f"));
48-
49-
integerClass.delete();
50-
});
51-
52-
testWidgets("Example for using getMethodID", (t) async {
53-
final longClass = Jni.findJniClass("java/lang/Long");
54-
final bitCountMethod = longClass.getStaticMethodID("bitCount", "(J)I");
55-
56-
final random = Jni.newInstance("java/util/Random", "()V", []);
57-
58-
final nextIntMethod = random.getMethodID("nextInt", "(I)I");
59-
60-
for (int i = 0; i < 100; i++) {
61-
int r = random.callMethod<int>(nextIntMethod, [JValueInt(256 * 256)]);
62-
int bits = 0;
63-
final jbc = longClass.callStaticMethod<int>(bitCountMethod, [r]);
64-
while (r != 0) {
65-
bits += r % 2;
66-
r = (r / 2).floor();
67-
}
68-
expect(jbc, equals(bits));
69-
}
70-
random.delete();
71-
longClass.delete();
72-
});
73-
74-
// Actually it's not even required to get a reference to class
75-
testWidgets("invoke_", (t) async {
76-
final m = Jni.invokeStaticMethod<int>(
77-
"java/lang/Long", "min", "(JJ)J", [1234, 1324], JniCallType.longType);
78-
expect(m, equals(1234));
79-
});
80-
81-
testWidgets("retrieve_", (t) async {
82-
final maxLong = Jni.retrieveStaticField<int>(
83-
"java/lang/Short", "MAX_VALUE", "S", JniCallType.shortType);
84-
expect(maxLong, equals(32767));
85-
});
86-
87-
testWidgets("Call method with null argument, expect exception",
88-
(tester) async {
89-
final integerClass = Jni.findJniClass("java/lang/Integer");
90-
expect(
91-
() => integerClass.callStaticMethodByName<int>(
92-
"parseInt", "(Ljava/lang/String;)I", [nullptr]),
93-
throwsException);
94-
});
95-
96-
testWidgets("callStaticStringMethod", (t) async {
97-
final longClass = Jni.findJniClass("java/lang/Long");
98-
const n = 1223334444;
99-
final strFromJava = longClass.callStaticMethodByName<String>(
100-
"toOctalString", "(J)Ljava/lang/String;", [n]);
101-
expect(strFromJava, equals(n.toRadixString(8)));
102-
longClass.delete();
103-
});
104-
105-
testWidgets("Passing strings in arguments", (t) async {
106-
final twelve = Jni.invokeStaticMethod<int>("java/lang/Byte", "parseByte",
107-
"(Ljava/lang/String;)B", ["12"], JniCallType.byteType);
108-
expect(twelve, equals(12));
109-
});
110-
111-
testWidgets("use() method", (t) async {
112-
final randomInt = Jni.newInstance("java/util/Random", "()V", []).use(
113-
(random) =>
114-
random.callMethodByName<int>("nextInt", "(I)I", [JValueInt(15)]));
115-
expect(randomInt, lessThan(15));
116-
});
117-
118-
testWidgets("enums", (t) async {
119-
final ordinal = Jni.retrieveStaticField<JObject>(
120-
"java/net/Proxy\$Type", "HTTP", "Ljava/net/Proxy\$Type;")
121-
.use((f) => f.callMethodByName<int>("ordinal", "()I", []));
122-
expect(ordinal, equals(1));
123-
});
12428
}

pkgs/jni/example/lib/main.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import 'package:jni/jni.dart';
1515
// GlobalJniEnv is a thin abstraction over JNIEnv in JNI C API.
1616
//
1717
// For a more ergonomic API for common use cases of calling methods and
18-
// accessing fields, see next examples using JObject and JniClass.
18+
// accessing fields, see next examples using JObject and JClass.
1919
String toJavaStringUsingEnv(int n) => using((arena) {
2020
final env = Jni.env;
2121
final cls = env.FindClass("java/lang/String".toNativeChars(arena));
@@ -24,7 +24,7 @@ String toJavaStringUsingEnv(int n) => using((arena) {
2424
final i = arena<JValue>();
2525
i.ref.i = n;
2626
final res = env.CallStaticObjectMethodA(cls, mId, i);
27-
final str = env.asDartString(res);
27+
final str = env.toDartString(res);
2828
env.deleteAllRefs([res, cls]);
2929
return str;
3030
});
@@ -42,14 +42,14 @@ int randomUsingEnv(int n) => using((arena) {
4242
return res;
4343
});
4444
double randomDouble() {
45-
final math = Jni.findJniClass("java/lang/Math");
45+
final math = Jni.findJClass("java/lang/Math");
4646
final random = math.callStaticMethodByName<double>("random", "()D", []);
4747
math.delete();
4848
return random;
4949
}
5050

5151
int uptime() {
52-
return Jni.findJniClass("android/os/SystemClock").use(
52+
return Jni.findJClass("android/os/SystemClock").use(
5353
(systemClock) => systemClock.callStaticMethodByName<int>(
5454
"uptimeMillis", "()J", [], JniCallType.longType),
5555
);

0 commit comments

Comments
 (0)