Skip to content

Commit

Permalink
[jnigen] Fix crash on Kotlin wildcards (#1881)
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi authored Jan 13, 2025
1 parent a7fb6ee commit bd486f6
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 24 deletions.
4 changes: 4 additions & 0 deletions pkgs/jnigen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.13.1-wip

- Fixed a bug where Kotlin wildcards would crash the code generation.

## 0.13.0

- **Breaking Change**([#1516](https://github.com/dart-lang/native/issues/1516)):
Expand Down
10 changes: 7 additions & 3 deletions pkgs/jnigen/lib/src/bindings/kotlin_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,20 @@ class _KotlinTypeProcessor extends TypeVisitor<void> {
@override
void visitDeclaredType(DeclaredType node) {
for (var i = 0; i < node.params.length; ++i) {
node.params[i].accept(_KotlinTypeProcessor(kotlinType.arguments[i].type));
if (kotlinType.arguments[i] case final KotlinTypeProjection projection) {
node.params[i].accept(_KotlinTypeProcessor(projection.type));
}
}
super.visitDeclaredType(node);
}

@override
void visitArrayType(ArrayType node) {
if (kotlinType.arguments.isNotEmpty) {
node.elementType
.accept(_KotlinTypeProcessor(kotlinType.arguments.first.type));
if (kotlinType.arguments.first
case final KotlinTypeProjection projection) {
node.elementType.accept(_KotlinTypeProcessor(projection.type));
}
}
super.visitArrayType(node);
}
Expand Down
35 changes: 24 additions & 11 deletions pkgs/jnigen/lib/src/elements/elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class ClassDecl with ClassMember, Annotated implements Element<ClassDecl> {
this.kotlinPackage,
});

@JsonKey(includeFromJson: false)
bool isExcluded;

@override
Expand Down Expand Up @@ -625,6 +626,7 @@ class Method with ClassMember, Annotated implements Element<Method> {
required this.returnType,
});

@JsonKey(includeFromJson: false)
bool isExcluded;

@override
Expand Down Expand Up @@ -728,6 +730,7 @@ class Field with ClassMember, Annotated implements Element<Field> {
this.defaultValue,
});

@JsonKey(includeFromJson: false)
bool isExcluded;

@override
Expand Down Expand Up @@ -1120,7 +1123,7 @@ class KotlinType implements Element<KotlinType> {
final String kind;
final String? name;
final int id;
final List<KotlinTypeProjection> arguments;
final List<KotlinTypeArgument> arguments;
final bool isNullable;

factory KotlinType.fromJson(Map<String, dynamic> json) =>
Expand Down Expand Up @@ -1190,21 +1193,31 @@ class KotlinValueParameter implements Element<KotlinValueParameter> {
}
}

@JsonSerializable(createToJson: false)
class KotlinTypeProjection implements Element<KotlinTypeProjection> {
sealed class KotlinTypeArgument implements Element<KotlinTypeArgument> {
KotlinTypeArgument();

factory KotlinTypeArgument.fromJson(Map<String, dynamic> json) =>
json['type'] == null
? KotlinWildcard()
: KotlinTypeProjection(
type: KotlinType.fromJson(json['type'] as Map<String, dynamic>),
variance: $enumDecode(_$KmVarianceEnumMap, json['variance']),
);

@override
R accept<R>(Visitor<KotlinTypeArgument, R> v) {
return v.visit(this);
}
}

class KotlinWildcard extends KotlinTypeArgument {}

class KotlinTypeProjection extends KotlinTypeArgument {
KotlinTypeProjection({
required this.type,
required this.variance,
});

final KotlinType type;
final KmVariance variance;

factory KotlinTypeProjection.fromJson(Map<String, dynamic> json) =>
_$KotlinTypeProjectionFromJson(json);

@override
R accept<R>(Visitor<KotlinTypeProjection, R> v) {
return v.visit(this);
}
}
11 changes: 2 additions & 9 deletions pkgs/jnigen/lib/src/elements/elements.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkgs/jnigen/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

name: jnigen
description: A Dart bindings generator for Java and Kotlin that uses JNI under the hood to interop with Java virtual machine.
version: 0.13.0
version: 0.13.1-wip
repository: https://github.com/dart-lang/native/tree/main/pkgs/jnigen
issue_tracker: https://github.com/dart-lang/native/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Ajnigen

Expand Down
25 changes: 25 additions & 0 deletions pkgs/jnigen/test/kotlin_test/bindings/kotlin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,31 @@ class Nullability<$T extends jni$_.JObject?, $U extends jni$_.JObject>
.object<jni$_.JString?>(const jni$_.JStringNullableType());
}

static final _id_list = _class.instanceMethodId(
r'list',
r'()Ljava/util/List;',
);

static final _list = jni$_.ProtectedJniExtensions.lookup<
jni$_.NativeFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)>>('globalEnv_CallObjectMethod')
.asFunction<
jni$_.JniResult Function(
jni$_.Pointer<jni$_.Void>,
jni$_.JMethodIDPtr,
)>();

/// from: `public final java.util.List list()`
/// The returned object must be released after use, by calling the [release] method.
jni$_.JList<jni$_.JObject?> list() {
return _list(reference.pointer, _id_list as jni$_.JMethodIDPtr)
.object<jni$_.JList<jni$_.JObject?>>(
const jni$_.JListType<jni$_.JObject?>(jni$_.JObjectNullableType()));
}

static final _id_methodGenericEcho = _class.instanceMethodId(
r'methodGenericEcho',
r'(Ljava/lang/Object;)Ljava/lang/Object;',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public class Nullability<T, U: Any>(val t: T, val u: U, var nullableU: U?) {
return if (returnNull) null else "hello"
}

public fun list(): List<*> {
return listOf("hello", 42)
}

public fun <V: Any> methodGenericEcho(v: V): V {
return v
}
Expand Down
8 changes: 8 additions & 0 deletions pkgs/jnigen/test/kotlin_test/runtime_test_registrant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ void registerTests(String groupName, TestRunnerCallback test) {
test('Methods', () {
using((arena) {
final obj = testObject(arena);
expect(
obj
.list()
.first!
.as(JString.type, releaseOriginal: true)
.toDartString(releaseOriginal: true),
'hello',
);
expect(obj.hello().toDartString(releaseOriginal: true), 'hello');
expect(
obj.nullableHello(false)!.toDartString(releaseOriginal: true),
Expand Down

0 comments on commit bd486f6

Please sign in to comment.