Skip to content

Commit fccabe3

Browse files
Generate null-safe Java code (#1692)
* As a result `JObject` can no longer have a `nullptr` reference. * Add a series of `FooNullableType` accessible through `Foo.nullableType` which is equivalent to `Foo?` or `@Nullable Foo` in Java used in generic types and arrays. * Add configuration options to add other custom annotations for nullable and non-nullable types.
1 parent 579688b commit fccabe3

File tree

84 files changed

+12779
-3225
lines changed

Some content is hidden

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

84 files changed

+12779
-3225
lines changed

.github/workflows/jnigen.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ jobs:
4646
channel: ${{ matrix.sdk }}
4747
cache: true
4848
cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:'
49-
- uses: axel-op/googlejavaformat-action@fe78db8a90171b6a836449f8d0e982d5d71e5c5a
49+
- uses: axel-op/googlejavaformat-action@0dc4ef525e7ed73d8dff50b1b062a4d441d014b5
5050
name: 'Check Java formatting with google-java-format'
5151
with:
52+
version: 'v1.24.0'
5253
args: '--set-exit-if-changed'
5354
- id: install
5455
name: Install dependencies
@@ -136,9 +137,10 @@ jobs:
136137
with:
137138
distribution: 'zulu'
138139
java-version: '17'
139-
- uses: axel-op/googlejavaformat-action@fe78db8a90171b6a836449f8d0e982d5d71e5c5a
140+
- uses: axel-op/googlejavaformat-action@0dc4ef525e7ed73d8dff50b1b062a4d441d014b5
140141
name: 'Check Java formatting with google-java-format'
141142
with:
143+
version: 'v1.24.0'
142144
args: '--set-exit-if-changed'
143145
- name: install clang tools & CMake
144146
run: |

pkgs/jni/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
## 0.13.0-wip
2+
3+
- Added nullable type classes for all Java objects.
4+
15
## 0.12.2
26

3-
- Add `JniUtils.fromReferenceAddress` which helps with sending `JObject`s
7+
- Added `JniUtils.fromReferenceAddress` which helps with sending `JObject`s
48
through method channels. You can send the address of the pointer as `long` and
59
reconstruct the class using the helper method.
610
- Fixed a bug where it would be possible for a type class inference to fail.

pkgs/jni/lib/jni_symbols.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,86 @@ files:
44
'java.lang.Object':
55
name: JObject
66
type_class: JObjectType
7+
nullable_type_class: JObjectNullableType
78
super_count: 0
89
'java.lang.String':
910
name: JString
1011
type_class: JStringType
12+
nullable_type_class: JStringNullableType
1113
super_count: 1
1214
'java.lang.Number':
1315
name: JNumber
1416
type_class: JNumberType
17+
nullable_type_class: JNumberNullableType
1518
super_count: 1
1619
'java.lang.Byte':
1720
name: JByte
1821
type_class: JByteType
22+
nullable_type_class: JByteNullableType
1923
super_count: 2
2024
'java.lang.Short':
2125
name: JShort
2226
type_class: JShortType
27+
nullable_type_class: JShortNullableType
2328
super_count: 2
2429
'java.lang.Integer':
2530
name: JInteger
2631
type_class: JIntegerType
32+
nullable_type_class: JIntegerNullableType
2733
super_count: 2
2834
'java.lang.Long':
2935
name: JLong
3036
type_class: JLongType
37+
nullable_type_class: JLongNullableType
3138
super_count: 2
3239
'java.lang.Float':
3340
name: JFloat
3441
type_class: JFloatType
42+
nullable_type_class: JFloatNullableType
3543
super_count: 2
3644
'java.lang.Double':
3745
name: JDouble
3846
type_class: JDoubleType
47+
nullable_type_class: JDoubleNullableType
3948
super_count: 2
4049
'java.lang.Boolean':
4150
name: JBoolean
4251
type_class: JBooleanType
52+
nullable_type_class: JBooleanNullableType
4353
super_count: 1
4454
'java.lang.Character':
4555
name: JCharacter
4656
type_class: JCharacterType
57+
nullable_type_class: JCharacterNullableType
4758
super_count: 1
4859
'java.util.Set':
4960
name: JSet
5061
type_class: JSetType
62+
nullable_type_class: JSetNullableType
5163
super_count: 1
5264
type_params:
5365
E:
5466
'java.lang.Object': DECLARED
5567
'java.util.List':
5668
name: JList
5769
type_class: JListType
70+
nullable_type_class: JListNullableType
5871
super_count: 1
5972
type_params:
6073
E:
6174
'java.lang.Object': DECLARED
6275
'java.util.Iterator':
6376
name: JIterator
6477
type_class: JIteratorType
78+
nullable_type_class: JIteratorNullableType
6579
super_count: 1
6680
type_params:
6781
E:
6882
'java.lang.Object': DECLARED
6983
'java.util.Map':
7084
name: JMap
7185
type_class: JMapType
86+
nullable_type_class: JMapNullableType
7287
super_count: 1
7388
type_params:
7489
K:
@@ -78,8 +93,10 @@ files:
7893
'java.nio.Buffer':
7994
name: JBuffer
8095
type_class: JBufferType
96+
nullable_type_class: JBufferNullableType
8197
super_count: 1
8298
'java.nio.ByteBuffer':
8399
name: JByteBuffer
84100
type_class: JByteBufferType
101+
nullable_type_class: JByteBufferNullableType
85102
super_count: 2

pkgs/jni/lib/src/accessors.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ extension JniResultMethods on JniResult {
6767
return pointer == nullptr ? jNullReference : JGlobalReference(pointer);
6868
}
6969

70-
T object<T extends JObject>(JObjType<T> type) {
70+
T object<T extends JObject?>(JObjType<T> type) {
7171
return type.fromReference(reference);
7272
}
7373

pkgs/jni/lib/src/jarray.dart

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

77
part of 'types.dart';
88

9+
final class JArrayNullableType<E> extends JObjType<JArray<E>?> {
10+
@internal
11+
final JArrayElementType<E> elementType;
12+
13+
@internal
14+
const JArrayNullableType(this.elementType);
15+
16+
@internal
17+
@override
18+
String get signature => '[${elementType.signature}';
19+
20+
@internal
21+
@override
22+
JArray<E>? fromReference(JReference reference) =>
23+
reference.isNull ? null : JArray.fromReference(elementType, reference);
24+
25+
@internal
26+
@override
27+
JObjType get superType => const JObjectNullableType();
28+
29+
@internal
30+
@override
31+
JObjType<JArray<E>?> get nullableType => this;
32+
33+
@internal
34+
@override
35+
final int superCount = 1;
36+
37+
@override
38+
int get hashCode => Object.hash(JArrayNullableType, elementType);
39+
40+
@override
41+
bool operator ==(Object other) {
42+
return other.runtimeType == (JArrayNullableType<E>) &&
43+
other is JArrayNullableType<E> &&
44+
elementType == other.elementType;
45+
}
46+
}
47+
948
final class JArrayType<E> extends JObjType<JArray<E>> {
1049
@internal
1150
final JArrayElementType<E> elementType;
@@ -26,6 +65,10 @@ final class JArrayType<E> extends JObjType<JArray<E>> {
2665
@override
2766
JObjType get superType => const JObjectType();
2867

68+
@internal
69+
@override
70+
JObjType<JArray<E>?> get nullableType => JArrayNullableType<E>(elementType);
71+
2972
@internal
3073
@override
3174
final int superCount = 1;
@@ -53,6 +96,11 @@ class JArray<E> extends JObject {
5396
static JArrayType<E> type<E>(JArrayElementType<E> innerType) =>
5497
JArrayType(innerType);
5598

99+
/// The type which includes information such as the signature of this class.
100+
static JArrayNullableType<E> nullableType<E>(
101+
JArrayElementType<E> innerType) =>
102+
JArrayNullableType(innerType);
103+
56104
/// Construct a new [JArray] with [reference] as its underlying reference.
57105
JArray.fromReference(this.elementType, JReference reference)
58106
: $type = type(elementType),
@@ -61,7 +109,16 @@ class JArray<E> extends JObject {
61109
/// Creates a [JArray] of the given length from the given [elementType].
62110
///
63111
/// The [length] must be a non-negative integer.
112+
/// For objects, [elementType] must be a nullable type as this constructor
113+
/// initializes all elements with `null`.
64114
factory JArray(JArrayElementType<E> elementType, int length) {
115+
RangeError.checkNotNegative(length);
116+
if (elementType is JObjType<JObject?> &&
117+
!(elementType as JObjType<JObject?>).isNullable) {
118+
throw StateError('Element type of JArray must be nullable when '
119+
'all elements with null\n\n'
120+
'Try using .nullableType instead');
121+
}
65122
return elementType._newArray(length);
66123
}
67124

@@ -366,17 +423,21 @@ extension DoubleArray on JArray<jdouble> {
366423
}
367424
}
368425

369-
extension ObjectArray<T extends JObject> on JArray<T> {
426+
extension ObjectArray<T extends JObject?> on JArray<T> {
370427
T operator [](int index) {
371428
RangeError.checkValidIndex(index, this);
372-
return (elementType as JObjType<T>).fromReference(JGlobalReference(
373-
Jni.env.GetObjectArrayElement(reference.pointer, index)));
429+
final pointer = Jni.env.GetObjectArrayElement(reference.pointer, index);
430+
if (pointer == nullptr) {
431+
return null as T;
432+
}
433+
return (elementType as JObjType<T>)
434+
.fromReference(JGlobalReference(pointer));
374435
}
375436

376437
void operator []=(int index, T value) {
377438
RangeError.checkValidIndex(index, this);
378-
Jni.env.SetObjectArrayElement(
379-
reference.pointer, index, value.reference.pointer);
439+
final valueRef = value?.reference ?? jNullReference;
440+
Jni.env.SetObjectArrayElement(reference.pointer, index, valueRef.pointer);
380441
}
381442

382443
void setRange(int start, int end, Iterable<T> iterable, [int skipCount = 0]) {

0 commit comments

Comments
 (0)