Skip to content

Commit 1f004e6

Browse files
[jnigen] Generic support (#136)
Added support for Generics – Closed #66, Closed #73 * TypeClasses are now public and have `fromRef` * Casting is possible between different types. * Nested generic classes are also supported.
1 parent b3f01ca commit 1f004e6

File tree

37 files changed

+3461
-552
lines changed

37 files changed

+3461
-552
lines changed

pkgs/jni/lib/src/jarray.dart

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,31 @@
66

77
part of 'types.dart';
88

9-
class _JArrayType<T> extends JType<JArray<T>> {
9+
class JArrayType<T> extends JObjType<JArray<T>> {
1010
final JType<T> elementType;
1111

12-
const _JArrayType(this.elementType);
12+
const JArrayType(this.elementType);
1313

1414
@override
1515
String get signature => '[${elementType.signature}';
16+
17+
@override
18+
JArray<T> fromRef(Pointer<Void> ref) => JArray.fromRef(elementType, ref);
1619
}
1720

1821
class JArray<E> extends JObject {
22+
final JType<E> elementType;
23+
24+
@override
25+
JArrayType<E> get $type => (_$type ??= type(elementType)) as JArrayType<E>;
26+
1927
/// The type which includes information such as the signature of this class.
20-
static JType<JArray<T>> type<T>(JType<T> innerType) => _JArrayType(innerType);
28+
static JObjType<JArray<T>> type<T>(JType<T> innerType) =>
29+
JArrayType(innerType);
2130

2231
/// Construct a new [JArray] with [reference] as its underlying reference.
23-
JArray.fromRef(JArrayPtr reference) : super.fromRef(reference);
32+
JArray.fromRef(this.elementType, JArrayPtr reference)
33+
: super.fromRef(reference);
2434

2535
/// Creates a [JArray] of the given length from the given [type].
2636
///
@@ -29,12 +39,14 @@ class JArray<E> extends JObject {
2939
if (type._type == JniCallType.objectType) {
3040
final clazz = type._getClass();
3141
final array = JArray<E>.fromRef(
42+
type,
3243
_accessors.newObjectArray(length, clazz.reference, nullptr).checkedRef,
3344
);
3445
clazz.delete();
3546
return array;
3647
}
3748
return JArray.fromRef(
49+
type,
3850
_accessors.newPrimitiveArray(length, type._type).checkedRef,
3951
);
4052
}
@@ -47,6 +59,7 @@ class JArray<E> extends JObject {
4759
assert(!fill.isNull, "fill must not be null.");
4860
final clazz = fill.getClass();
4961
final array = JArray<E>.fromRef(
62+
fill.$type as JObjType<E>,
5063
_accessors
5164
.newObjectArray(length, clazz.reference, fill.reference)
5265
.checkedRef,
@@ -321,7 +334,10 @@ extension ObjectArray<T extends JObject> on JArray<T> {
321334

322335
extension ArrayArray<T> on JArray<JArray<T>> {
323336
JArray<T> operator [](int index) {
324-
return JArray<T>.fromRef(elementAt(index, JniCallType.objectType).object);
337+
return JArray<T>.fromRef(
338+
(elementType as JArrayType<T>).elementType,
339+
elementAt(index, JniCallType.objectType).object,
340+
);
325341
}
326342

327343
void operator []=(int index, JArray<T> value) {

pkgs/jni/lib/src/jobject.dart

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
part of 'types.dart';
66

7-
class _JObjectType extends JType<JObject> {
8-
const _JObjectType();
7+
class JObjectType extends JObjType<JObject> {
8+
const JObjectType();
99

1010
@override
1111
String get signature => "Ljava/lang/Object;";
12+
13+
@override
14+
JObject fromRef(Pointer<Void> ref) => JObject.fromRef(ref);
1215
}
1316

1417
Pointer<T> _getID<T extends NativeType>(
@@ -123,8 +126,11 @@ T _getField<T>(int? callType, JniResult Function(int) f) {
123126
///
124127
/// This is the base class for classes generated by `jnigen`.
125128
class JObject extends JReference {
129+
JObjType<JObject>? _$type;
130+
JObjType<JObject> get $type => _$type ??= type;
131+
126132
/// The type which includes information such as the signature of this class.
127-
static const JType<JObject> type = _JObjectType();
133+
static const JObjType<JObject> type = JObjectType();
128134

129135
/// Construct a new [JObject] with [reference] as its underlying reference.
130136
JObject.fromRef(JObjectPtr reference) : super.fromRef(reference);
@@ -143,8 +149,6 @@ class JObject extends JReference {
143149
super.delete();
144150
}
145151

146-
// TODO(#55): Support casting JObject subclasses
147-
148152
/// Returns [JniClass] corresponding to concrete class of this object.
149153
///
150154
/// This may be a subclass of compile-time class.
@@ -269,6 +273,17 @@ class JObject extends JReference {
269273
final id = getStaticMethodID(name, signature);
270274
return callStaticMethod<T>(id, args, callType);
271275
}
276+
277+
/// Casts this object to another type.
278+
T castTo<T extends JObject>(JObjType<T> type, {bool deleteOriginal = false}) {
279+
if (deleteOriginal) {
280+
_jniClass?.delete();
281+
_setAsDeleted();
282+
return type.fromRef(reference);
283+
}
284+
final newRef = _env.NewGlobalRef(reference);
285+
return type.fromRef(newRef);
286+
}
272287
}
273288

274289
/// A high level wrapper over a JNI class reference.

pkgs/jni/lib/src/jprimitives.dart

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ part of 'types.dart';
77
abstract class JPrimitive {}
88

99
abstract class JByte extends JPrimitive {
10-
static const JType<JByte> type = _JByteTypeClass();
10+
static const type = JByteType();
1111
}
1212

13-
class _JByteTypeClass extends JType<JByte> {
14-
const _JByteTypeClass();
13+
class JByteType extends JType<JByte> {
14+
const JByteType();
1515

1616
@override
1717
int get _type => JniCallType.byteType;
@@ -21,11 +21,11 @@ class _JByteTypeClass extends JType<JByte> {
2121
}
2222

2323
abstract class JBoolean extends JPrimitive {
24-
static const JType<JBoolean> type = _JBooleanType();
24+
static const type = JBooleanType();
2525
}
2626

27-
class _JBooleanType extends JType<JBoolean> {
28-
const _JBooleanType();
27+
class JBooleanType extends JType<JBoolean> {
28+
const JBooleanType();
2929

3030
@override
3131
int get _type => JniCallType.booleanType;
@@ -35,11 +35,11 @@ class _JBooleanType extends JType<JBoolean> {
3535
}
3636

3737
abstract class JChar extends JPrimitive {
38-
static const JType<JChar> type = _JCharType();
38+
static const type = JCharType();
3939
}
4040

41-
class _JCharType extends JType<JChar> {
42-
const _JCharType();
41+
class JCharType extends JType<JChar> {
42+
const JCharType();
4343

4444
@override
4545
int get _type => JniCallType.charType;
@@ -49,11 +49,11 @@ class _JCharType extends JType<JChar> {
4949
}
5050

5151
abstract class JShort extends JPrimitive {
52-
static const JType<JShort> type = _JShortType();
52+
static const type = JShortType();
5353
}
5454

55-
class _JShortType extends JType<JShort> {
56-
const _JShortType();
55+
class JShortType extends JType<JShort> {
56+
const JShortType();
5757

5858
@override
5959
int get _type => JniCallType.shortType;
@@ -63,11 +63,11 @@ class _JShortType extends JType<JShort> {
6363
}
6464

6565
abstract class JInt extends JPrimitive {
66-
static const JType<JInt> type = _JIntType();
66+
static const type = JIntType();
6767
}
6868

69-
class _JIntType extends JType<JInt> {
70-
const _JIntType();
69+
class JIntType extends JType<JInt> {
70+
const JIntType();
7171

7272
@override
7373
int get _type => JniCallType.intType;
@@ -77,11 +77,11 @@ class _JIntType extends JType<JInt> {
7777
}
7878

7979
abstract class JLong extends JPrimitive {
80-
static const JType<JLong> type = _JLongType();
80+
static const type = JLongType();
8181
}
8282

83-
class _JLongType extends JType<JLong> {
84-
const _JLongType();
83+
class JLongType extends JType<JLong> {
84+
const JLongType();
8585

8686
@override
8787
int get _type => JniCallType.longType;
@@ -91,11 +91,11 @@ class _JLongType extends JType<JLong> {
9191
}
9292

9393
abstract class JFloat extends JPrimitive {
94-
static const JType<JFloat> type = _JFloatType();
94+
static const type = JFloatType();
9595
}
9696

97-
class _JFloatType extends JType<JFloat> {
98-
const _JFloatType();
97+
class JFloatType extends JType<JFloat> {
98+
const JFloatType();
9999

100100
@override
101101
int get _type => JniCallType.floatType;
@@ -105,11 +105,11 @@ class _JFloatType extends JType<JFloat> {
105105
}
106106

107107
abstract class JDouble extends JPrimitive {
108-
static const JType<JDouble> type = _JDoubleType();
108+
static const type = JDoubleType();
109109
}
110110

111-
class _JDoubleType extends JType<JDouble> {
112-
const _JDoubleType();
111+
class JDoubleType extends JType<JDouble> {
112+
const JDoubleType();
113113

114114
@override
115115
int get _type => JniCallType.doubleType;

pkgs/jni/lib/src/jreference.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ abstract class JReference implements Finalizable {
2525
/// Returns whether this object is deleted.
2626
bool get isDeleted => _deleted;
2727

28-
/// Deletes the underlying JNI reference. Further uses will throw
29-
/// [UseAfterFreeException].
30-
void delete() {
28+
void _setAsDeleted() {
3129
if (_deleted) {
3230
throw DoubleFreeException(this, reference);
3331
}
3432
_deleted = true;
3533
_finalizer.detach(this);
34+
}
35+
36+
/// Deletes the underlying JNI reference. Further uses will throw
37+
/// [UseAfterFreeException].
38+
void delete() {
39+
_setAsDeleted();
3640
_env.DeleteGlobalRef(reference);
3741
}
3842

pkgs/jni/lib/src/jstring.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@
44

55
part of 'types.dart';
66

7-
class _JStringType extends JType<JString> {
8-
const _JStringType();
7+
class JStringType extends JObjType<JString> {
8+
const JStringType();
99

1010
@override
1111
String get signature => "Ljava/lang/String;";
12+
13+
@override
14+
JString fromRef(Pointer<Void> ref) => JString.fromRef(ref);
1215
}
1316

1417
class JString extends JObject {
18+
@override
19+
JObjType<JObject> get $type => _$type ??= type;
20+
1521
/// The type which includes information such as the signature of this class.
16-
static const JType<JString> type = _JStringType();
22+
static const JObjType<JString> type = JStringType();
1723

1824
/// Construct a new [JString] with [reference] as its underlying reference.
1925
JString.fromRef(JStringPtr reference) : super.fromRef(reference);

pkgs/jni/lib/src/types.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,19 @@ typedef _VoidType = void;
2828
abstract class JType<T> {
2929
const JType();
3030

31-
int get _type => JniCallType.objectType;
31+
int get _type;
3232

3333
String get signature;
3434

3535
JniClass _getClass() => Jni.findJniClass(signature);
3636
}
37+
38+
abstract class JObjType<T extends JObject> extends JType<T> {
39+
const JObjType();
40+
41+
@override
42+
int get _type => JniCallType.objectType;
43+
44+
/// Creates an object from this type using the reference.
45+
T fromRef(Pointer<Void> ref);
46+
}

pkgs/jni/test/jobject_test.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@ void main() {
191191
.use((f) => f.callMethodByName<int>("ordinal", "()I", []));
192192
expect(ordinal, equals(1));
193193
});
194+
test("casting", () {
195+
using((arena) {
196+
final str = "hello".toJString()..deletedIn(arena);
197+
final obj = str.castTo(JObject.type)..deletedIn(arena);
198+
final backToStr = obj.castTo(JString.type);
199+
expect(backToStr.toDartString(), str.toDartString());
200+
final _ = backToStr.castTo(JObject.type, deleteOriginal: true)
201+
..deletedIn(arena);
202+
expect(backToStr.toDartString, throwsA(isA<UseAfterFreeException>()));
203+
expect(backToStr.delete, throwsA(isA<DoubleFreeException>()));
204+
});
205+
});
194206

195207
test("Isolate", () {
196208
Isolate.spawn(doSomeWorkInIsolate, null);

0 commit comments

Comments
 (0)