Skip to content

Commit 223e5f6

Browse files
[jnigen] Check for free in reference getter (#361)
1 parent 26d2a10 commit 223e5f6

File tree

24 files changed

+90
-86
lines changed

24 files changed

+90
-86
lines changed

pkgs/jni/example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,5 +519,5 @@ packages:
519519
source: hosted
520520
version: "3.1.2"
521521
sdks:
522-
dart: ">=3.1.0 <4.0.0"
522+
dart: ">=3.1.0-262.3.beta <4.0.0"
523523
flutter: ">=2.11.0"

pkgs/jni/example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
1818
version: 1.0.0+1
1919

2020
environment:
21-
sdk: '>=3.1.0 <4.0.0'
21+
sdk: '>=3.1.0-262.3.beta <4.0.0'
2222

2323
# Dependencies specify other packages that your package needs in order to work.
2424
# To automatically upgrade your package dependencies to the latest versions

pkgs/jni/lib/src/jexceptions.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,38 @@ import 'package:jni/src/third_party/generated_bindings.dart';
99
abstract class JException implements Exception {}
1010

1111
class UseAfterFreeException implements JException {
12-
dynamic object;
13-
Pointer<Void> ptr;
14-
UseAfterFreeException(this.object, this.ptr);
12+
final Pointer<Void> ptr;
13+
UseAfterFreeException(this.ptr);
1514

1615
@override
1716
String toString() {
18-
return "use after free on $ptr through $object";
17+
return 'Use after free on $ptr.';
1918
}
2019
}
2120

22-
class NullJStringException implements JException {
21+
class JNullException implements JException {
22+
const JNullException();
23+
2324
@override
24-
String toString() => 'toDartString called on null JString reference';
25+
String toString() => 'The reference was null.';
2526
}
2627

2728
class InvalidJStringException implements JException {
28-
Pointer<Void> reference;
29+
final Pointer<Void> reference;
2930
InvalidJStringException(this.reference);
31+
3032
@override
3133
String toString() => 'Not a valid Java String: '
32-
'0x${reference.address.toRadixString(16)}';
34+
'0x${reference.address.toRadixString(16)}.';
3335
}
3436

3537
class DoubleFreeException implements JException {
36-
dynamic object;
37-
Pointer<Void> ptr;
38-
DoubleFreeException(this.object, this.ptr);
38+
final Pointer<Void> ptr;
39+
DoubleFreeException(this.ptr);
3940

4041
@override
4142
String toString() {
42-
return "double free on $ptr through $object";
43+
return 'Double free on $ptr.';
4344
}
4445
}
4546

@@ -113,9 +114,10 @@ class HelperNotFoundException implements JException {
113114
final String path;
114115

115116
@override
116-
String toString() => "Lookup for helper library $path failed.\n"
117-
"Please ensure that `dartjni` shared library is built.\n"
118-
"Provided jni:setup script can be used to build the shared library."
119-
"If the library is already built, ensure that the JVM libraries can be "
120-
"loaded from Dart.";
117+
String toString() => '''
118+
Lookup for helper library $path failed.
119+
Please ensure that `dartjni` shared library is built.
120+
Provided jni:setup script can be used to build the shared library.
121+
If the library is already built, ensure that the JVM libraries can be
122+
loaded from Dart.''';
121123
}

pkgs/jni/lib/src/jni.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ extension AdditionalEnvMethods on GlobalJniEnv {
337337
/// DeleteLocalRef.
338338
String toDartString(JStringPtr jstringPtr, {bool deleteOriginal = false}) {
339339
if (jstringPtr == nullptr) {
340-
throw NullJStringException();
340+
throw const JNullException();
341341
}
342342
final chars = GetStringUTFChars(jstringPtr, nullptr);
343343
if (chars == nullptr) {

pkgs/jni/lib/src/jobject.dart

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ class JObject extends JReference {
189189
///
190190
/// This may be a subclass of compile-time class.
191191
JClass getClass() {
192-
ensureNotDeleted();
193192
final classRef = Jni.env.GetObjectClass(reference);
194193
if (classRef == nullptr) {
195194
Jni.accessors.throwException(Jni.env.ExceptionOccurred());
@@ -199,28 +198,24 @@ class JObject extends JReference {
199198

200199
/// Get [JFieldIDPtr] of instance field identified by [fieldName] & [signature].
201200
JFieldIDPtr getFieldID(String fieldName, String signature) {
202-
ensureNotDeleted();
203201
return _getID(
204202
Jni.accessors.getFieldID, _class.reference, fieldName, signature);
205203
}
206204

207205
/// Get [JFieldIDPtr] of static field identified by [fieldName] & [signature].
208206
JFieldIDPtr getStaticFieldID(String fieldName, String signature) {
209-
ensureNotDeleted();
210207
return _getID<jfieldID_>(
211208
Jni.accessors.getStaticFieldID, _class.reference, fieldName, signature);
212209
}
213210

214211
/// Get [JMethodIDPtr] of instance method [methodName] with [signature].
215212
JMethodIDPtr getMethodID(String methodName, String signature) {
216-
ensureNotDeleted();
217213
return _getID<jmethodID_>(
218214
Jni.accessors.getMethodID, _class.reference, methodName, signature);
219215
}
220216

221217
/// Get [JMethodIDPtr] of static method [methodName] with [signature].
222218
JMethodIDPtr getStaticMethodID(String methodName, String signature) {
223-
ensureNotDeleted();
224219
return _getID<jmethodID_>(Jni.accessors.getStaticMethodID, _class.reference,
225220
methodName, signature);
226221
}
@@ -235,7 +230,6 @@ class JObject extends JReference {
235230
/// If [T] is String or [JObject], required conversions are performed and
236231
/// final value is returned.
237232
T getField<T>(JFieldIDPtr fieldID, [int? callType]) {
238-
ensureNotDeleted();
239233
if (callType == JniCallType.voidType) {
240234
throw ArgumentError("void is not a valid field type.");
241235
}
@@ -258,7 +252,6 @@ class JObject extends JReference {
258252
if (callType == JniCallType.voidType) {
259253
throw ArgumentError("void is not a valid field type.");
260254
}
261-
ensureNotDeleted();
262255
return _getField<T>(callType,
263256
(ct) => Jni.accessors.getStaticField(_class.reference, fieldID, ct));
264257
}
@@ -278,7 +271,6 @@ class JObject extends JReference {
278271
///
279272
/// See [getField] for an explanation about [callType] and return type [T].
280273
T callMethod<T>(JMethodIDPtr methodID, List<dynamic> args, [int? callType]) {
281-
ensureNotDeleted();
282274
return _callMethod<T>(callType, args,
283275
(ct, jvs) => Jni.accessors.callMethod(reference, methodID, ct, jvs));
284276
}
@@ -296,7 +288,6 @@ class JObject extends JReference {
296288
/// more details about [args] and [callType].
297289
T callStaticMethod<T>(JMethodIDPtr methodID, List<dynamic> args,
298290
[int? callType]) {
299-
ensureNotDeleted();
300291
return _callMethod<T>(
301292
callType,
302293
args,
@@ -317,8 +308,9 @@ class JObject extends JReference {
317308
T castTo<T extends JObject>(JObjType<T> type, {bool deleteOriginal = false}) {
318309
if (deleteOriginal) {
319310
_jClass?.delete();
311+
final ret = type.fromRef(reference);
320312
setAsDeleted();
321-
return type.fromRef(reference);
313+
return ret;
322314
}
323315
final newRef = Jni.env.NewGlobalRef(reference);
324316
return type.fromRef(newRef);
@@ -360,28 +352,24 @@ class JClass extends JReference {
360352

361353
/// Get [JFieldIDPtr] of static field [fieldName] with [signature].
362354
JFieldIDPtr getStaticFieldID(String fieldName, String signature) {
363-
ensureNotDeleted();
364355
return _getID<jfieldID_>(
365356
Jni.accessors.getStaticFieldID, reference, fieldName, signature);
366357
}
367358

368359
/// Get [JMethodIDPtr] of static method [methodName] with [signature].
369360
JMethodIDPtr getStaticMethodID(String methodName, String signature) {
370-
ensureNotDeleted();
371361
return _getID<jmethodID_>(
372362
Jni.accessors.getStaticMethodID, reference, methodName, signature);
373363
}
374364

375365
/// Get [JFieldIDPtr] of field [fieldName] with [signature].
376366
JFieldIDPtr getFieldID(String fieldName, String signature) {
377-
ensureNotDeleted();
378367
return _getID<jfieldID_>(
379368
Jni.accessors.getFieldID, reference, fieldName, signature);
380369
}
381370

382371
/// Get [JMethodIDPtr] of method [methodName] with [signature].
383372
JMethodIDPtr getMethodID(String methodName, String signature) {
384-
ensureNotDeleted();
385373
return _getID<jmethodID_>(
386374
Jni.accessors.getMethodID, reference, methodName, signature);
387375
}
@@ -396,7 +384,6 @@ class JClass extends JReference {
396384
if (callType == JniCallType.voidType) {
397385
throw ArgumentError("void is not a valid field type.");
398386
}
399-
ensureNotDeleted();
400387
return _getField<T>(
401388
callType, (ct) => Jni.accessors.getStaticField(reference, fieldID, ct));
402389
}
@@ -415,7 +402,6 @@ class JClass extends JReference {
415402
/// about [args] and [callType].
416403
T callStaticMethod<T>(JMethodIDPtr methodID, List<dynamic> args,
417404
[int? callType]) {
418-
ensureNotDeleted();
419405
return _callMethod<T>(
420406
callType,
421407
args,
@@ -434,7 +420,6 @@ class JClass extends JReference {
434420

435421
/// Create a new instance of this class with [ctor] and [args].
436422
JObject newInstance(JMethodIDPtr ctor, List<dynamic> args) => using((arena) {
437-
ensureNotDeleted();
438423
final jArgs = JValueArgs(args, arena);
439424
final res =
440425
Jni.accessors.newObject(reference, ctor, jArgs.values).object;

pkgs/jni/lib/src/jreference.dart

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,37 @@ import 'jexceptions.dart';
1111
import 'jni.dart';
1212

1313
extension ProtectedJReference on JReference {
14-
void ensureNotDeleted() {
15-
if (_deleted) throw UseAfterFreeException(this, reference);
16-
}
17-
1814
void setAsDeleted() {
1915
if (_deleted) {
20-
throw DoubleFreeException(this, reference);
16+
throw DoubleFreeException(_reference);
2117
}
2218
_deleted = true;
2319
JReference._finalizer.detach(this);
2420
}
21+
22+
void ensureNotNull() {
23+
if (isNull) {
24+
throw const JNullException();
25+
}
26+
}
27+
28+
/// Similar to [reference].
29+
///
30+
/// Detaches the finalizer so the underlying pointer will not be deleted.
31+
JObjectPtr toPointer() {
32+
setAsDeleted();
33+
return _reference;
34+
}
2535
}
2636

2737
/// A class which holds one or more JNI references, and has a `delete` operation
2838
/// which disposes the reference(s).
2939
abstract class JReference implements Finalizable {
30-
//TODO(PR): Is it safe to cast void *f (void *) to void f (void *)?
3140
static final _finalizer =
3241
NativeFinalizer(Jni.env.ptr.ref.DeleteGlobalRef.cast());
3342

34-
JReference.fromRef(this.reference) {
35-
_finalizer.attach(this, reference, detach: this);
43+
JReference.fromRef(this._reference) {
44+
_finalizer.attach(this, _reference, detach: this);
3645
}
3746

3847
bool _deleted = false;
@@ -43,15 +52,26 @@ abstract class JReference implements Finalizable {
4352
/// Returns whether this object is deleted.
4453
bool get isDeleted => _deleted;
4554

46-
/// Deletes the underlying JNI reference. Further uses will throw
47-
/// [UseAfterFreeException].
55+
/// Deletes the underlying JNI reference.
56+
///
57+
/// Further uses will throw [UseAfterFreeException].
4858
void delete() {
4959
setAsDeleted();
50-
Jni.env.DeleteGlobalRef(reference);
60+
Jni.env.DeleteGlobalRef(_reference);
5161
}
5262

5363
/// The underlying JNI global object reference.
54-
final JObjectPtr reference;
64+
///
65+
/// Throws [UseAfterFreeException] if the object is previously deleted.
66+
///
67+
/// Be careful when storing this reference in a variable, since the underlying
68+
/// object might get deleted.
69+
JObjectPtr get reference {
70+
if (_deleted) throw UseAfterFreeException(_reference);
71+
return _reference;
72+
}
73+
74+
final JObjectPtr _reference;
5575

5676
/// Registers this object to be deleted at the end of [arena]'s lifetime.
5777
void deletedIn(Arena arena) => arena.onReleaseAll(delete);
@@ -61,7 +81,6 @@ extension JReferenceUseExtension<T extends JReference> on T {
6181
/// Applies [callback] on [this] object and then delete the underlying JNI
6282
/// reference, returning the result of [callback].
6383
R use<R>(R Function(T) callback) {
64-
ensureNotDeleted();
6584
try {
6685
final result = callback(this);
6786
delete();

pkgs/jni/lib/src/lang/jboolean.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import '../accessors.dart';
66
import '../jobject.dart';
7+
import '../jreference.dart';
78
import '../jni.dart';
89
import '../third_party/generated_bindings.dart';
910
import '../types.dart';
@@ -56,6 +57,7 @@ class JBoolean extends JObject {
5657
Jni.accessors.getMethodIDOf(_class.reference, r"booleanValue", r"()Z");
5758

5859
bool booleanValue({bool deleteOriginal = false}) {
60+
ensureNotNull();
5961
final ret = Jni.accessors.callMethodWithArgs(
6062
reference, _booleanValueId, JniCallType.booleanType, []).boolean;
6163
if (deleteOriginal) {

pkgs/jni/lib/src/lang/jcharacter.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '../accessors.dart';
22
import '../jni.dart';
33
import '../jobject.dart';
4+
import '../jreference.dart';
45
import '../jvalues.dart';
56
import '../third_party/generated_bindings.dart';
67
import '../types.dart';
@@ -54,6 +55,7 @@ class JCharacter extends JObject {
5455
Jni.accessors.getMethodIDOf(_class.reference, r"charValue", r"()C");
5556

5657
int charValue({bool deleteOriginal = false}) {
58+
ensureNotNull();
5759
final ret = Jni.accessors.callMethodWithArgs(
5860
reference, _charValueId, JniCallType.charType, []).char;
5961
if (deleteOriginal) {

pkgs/jni/lib/src/lang/jnumber.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../accessors.dart';
66
import '../jni.dart';
77
import '../jobject.dart';
8+
import '../jreference.dart';
89
import '../third_party/generated_bindings.dart';
910
import '../types.dart';
1011
import 'jboolean.dart';
@@ -64,6 +65,7 @@ class JNumber extends JObject {
6465
Jni.accessors.getMethodIDOf(_class.reference, r"intValue", r"()I");
6566

6667
int intValue({bool deleteOriginal = false}) {
68+
ensureNotNull();
6769
final ret = Jni.accessors.callMethodWithArgs(
6870
reference, _intValueId, JniCallType.intType, []).integer;
6971
if (deleteOriginal) {
@@ -76,6 +78,7 @@ class JNumber extends JObject {
7678
Jni.accessors.getMethodIDOf(_class.reference, r"longValue", r"()J");
7779

7880
int longValue({bool deleteOriginal = false}) {
81+
ensureNotNull();
7982
final ret = Jni.accessors.callMethodWithArgs(
8083
reference, _longValueId, JniCallType.longType, []).long;
8184
if (deleteOriginal) {
@@ -88,6 +91,7 @@ class JNumber extends JObject {
8891
Jni.accessors.getMethodIDOf(_class.reference, r"floatValue", r"()F");
8992

9093
double floatValue({bool deleteOriginal = false}) {
94+
ensureNotNull();
9195
final ret = Jni.accessors.callMethodWithArgs(
9296
reference, _floatValueId, JniCallType.floatType, []).float;
9397
if (deleteOriginal) {
@@ -100,6 +104,7 @@ class JNumber extends JObject {
100104
Jni.accessors.getMethodIDOf(_class.reference, r"doubleValue", r"()D");
101105

102106
double doubleValue({bool deleteOriginal = false}) {
107+
ensureNotNull();
103108
final ret = Jni.accessors.callMethodWithArgs(
104109
reference, _doubleValueId, JniCallType.doubleType, []).doubleFloat;
105110
if (deleteOriginal) {
@@ -112,6 +117,7 @@ class JNumber extends JObject {
112117
Jni.accessors.getMethodIDOf(_class.reference, r"byteValue", r"()B");
113118

114119
int byteValue({bool deleteOriginal = false}) {
120+
ensureNotNull();
115121
final ret = Jni.accessors.callMethodWithArgs(
116122
reference, _byteValueId, JniCallType.byteType, []).byte;
117123
if (deleteOriginal) {
@@ -124,6 +130,7 @@ class JNumber extends JObject {
124130
Jni.accessors.getMethodIDOf(_class.reference, r"shortValue", r"()S");
125131

126132
int shortValue({bool deleteOriginal = false}) {
133+
ensureNotNull();
127134
final ret = Jni.accessors.callMethodWithArgs(
128135
reference, _shortValueId, JniCallType.shortType, []).short;
129136
if (deleteOriginal) {

0 commit comments

Comments
 (0)