Skip to content

Commit 6290151

Browse files
[jnigen] Support Kotlin operators (#1969)
1 parent b053b7b commit 6290151

File tree

11 files changed

+696
-2
lines changed

11 files changed

+696
-2
lines changed

pkgs/jnigen/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.14.1-wip
2+
- Added support for generating matching Kotlin operators as Dart operators.
3+
14
## 0.14.0
25

36
- Fixed a bug where the source parser would not have all of the type paremeters

pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/elements/KotlinFunction.java

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class KotlinFunction {
2626
public List<KotlinTypeParameter> typeParameters;
2727
public int flags;
2828
public boolean isSuspend;
29+
public boolean isOperator;
2930

3031
public static KotlinFunction fromKmFunction(KmFunction f) {
3132
var fun = new KotlinFunction();
@@ -36,6 +37,7 @@ public static KotlinFunction fromKmFunction(KmFunction f) {
3637
fun.flags = f.getFlags();
3738
// Processing the information needed from the flags.
3839
fun.isSuspend = Flag.Function.IS_SUSPEND.invoke(fun.flags);
40+
fun.isOperator = Flag.Function.IS_OPERATOR.invoke(fun.flags);
3941
fun.valueParameters =
4042
f.getValueParameters().stream()
4143
.map(KotlinValueParameter::fromKmValueParameter)

pkgs/jnigen/lib/src/bindings/dart_generator.dart

+60
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ class $name$typeParamsDef extends $superName {
432432
// Fields and Methods
433433
generateFieldsAndMethods(node, classRef);
434434

435+
// Operators
436+
for (final MapEntry(key: operator, value: method)
437+
in node.operators.entries) {
438+
method.accept(_OperatorGenerator(resolver, s, operator: operator));
439+
}
440+
node.compareTo?.accept(_ComparatorGenerator(resolver, s));
441+
435442
if (node.declKind == DeclKind.interfaceKind) {
436443
s.write('''
437444
/// Maps a specific port to the implemented interface.
@@ -1970,3 +1977,56 @@ class _CallMethodName extends Visitor<Method, String> {
19701977
return 'globalEnv_Call${node.isStatic ? 'Static' : ''}${type}Method';
19711978
}
19721979
}
1980+
1981+
class _OperatorGenerator extends Visitor<Method, void> {
1982+
final Resolver resolver;
1983+
final StringSink s;
1984+
final Operator operator;
1985+
1986+
_OperatorGenerator(this.resolver, this.s, {required this.operator});
1987+
1988+
@override
1989+
void visit(Method node) {
1990+
final returnType = operator.returnsVoid
1991+
? 'void'
1992+
: node.returnType.accept(_TypeGenerator(resolver));
1993+
final paramsDef = node.params.accept(_ParamDef(resolver)).join(', ');
1994+
final paramsCall = node.params.map((param) => param.finalName).join(', ');
1995+
s.write('''
1996+
$returnType operator ${operator.dartSymbol}($paramsDef) {
1997+
${operator.returnsVoid ? '' : 'return '}${node.finalName}($paramsCall);
1998+
}
1999+
''');
2000+
}
2001+
}
2002+
2003+
class _ComparatorGenerator extends Visitor<Method, void> {
2004+
final Resolver resolver;
2005+
final StringSink s;
2006+
2007+
_ComparatorGenerator(this.resolver, this.s);
2008+
2009+
@override
2010+
void visit(Method node) {
2011+
final paramsDef = node.params.accept(_ParamDef(resolver)).join(', ');
2012+
final paramsCall = node.params.map((param) => param.finalName).join(', ');
2013+
final name = node.finalName;
2014+
s.write('''
2015+
bool operator <($paramsDef) {
2016+
return $name($paramsCall) < 0;
2017+
}
2018+
2019+
bool operator <=($paramsDef) {
2020+
return $name($paramsCall) <= 0;
2021+
}
2022+
2023+
bool operator >($paramsDef) {
2024+
return $name($paramsCall) > 0;
2025+
}
2026+
2027+
bool operator >=($paramsDef) {
2028+
return $name($paramsCall) >= 0;
2029+
}
2030+
''');
2031+
}
2032+
}

pkgs/jnigen/lib/src/bindings/kotlin_processor.dart

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class _KotlinMethodProcessor extends Visitor<Method, void> {
136136
@override
137137
void visit(Method node) {
138138
_processParams(node.params, function.valueParameters);
139+
node.kotlinFunction = function;
139140
for (var i = 0; i < node.typeParams.length; ++i) {
140141
node.typeParams[i]
141142
.accept(_KotlinTypeParamProcessor(function.typeParameters[i]));

pkgs/jnigen/lib/src/bindings/linker.dart

+18
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,24 @@ class _MethodLinker extends Visitor<Method, void> {
221221
param.method = node;
222222
}
223223
node.asyncReturnType?.accept(typeLinker);
224+
// Fill out operator overloadings.
225+
if (node.kotlinFunction?.isOperator ?? false) {
226+
if (Operator.values.asNameMap()[node.kotlinFunction!.name]
227+
case final operatorKind? when operatorKind.isCompatibleWith(node)) {
228+
node.classDecl.operators[operatorKind] ??= node;
229+
}
230+
}
231+
// Fill out compareTo method of the class used for comparison operators.
232+
if (node.name == 'compareTo' && node.params.length == 1) {
233+
final returnType = node.returnType.type;
234+
final parameterType = node.params.single.type.type;
235+
if (parameterType is DeclaredType &&
236+
parameterType.binaryName == node.classDecl.binaryName &&
237+
returnType is PrimitiveType &&
238+
returnType.dartType == 'int') {
239+
node.classDecl.compareTo = node;
240+
}
241+
}
224242
}
225243
}
226244

pkgs/jnigen/lib/src/elements/elements.dart

+47-1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ class ClassDecl with ClassMember, Annotated implements Element<ClassDecl> {
165165
@JsonKey(includeFromJson: false)
166166
late final Map<String, int> methodNumsAfterRenaming;
167167

168+
/// Populated by [Linker].
169+
@JsonKey(includeFromJson: false)
170+
final Map<Operator, Method> operators = {};
171+
172+
/// The `compareTo` method of this class.
173+
///
174+
/// This method must take a single parameter of the same type of the enclosing
175+
/// class, and return integer.
176+
///
177+
/// Used for overloading comparison operators.
178+
///
179+
/// Populated by [Linker].
180+
@JsonKey(includeFromJson: false)
181+
Method? compareTo;
182+
168183
@override
169184
String toString() {
170185
return 'Java class declaration for $binaryName';
@@ -682,8 +697,9 @@ class Method with ClassMember, Annotated implements Element<Method> {
682697
@override
683698
late String finalName;
684699

700+
/// Populated by [KotlinProcessor].
685701
@JsonKey(includeFromJson: false)
686-
late bool isOverridden;
702+
KotlinFunction? kotlinFunction;
687703

688704
/// The actual return type when the method is a Kotlin's suspend fun.
689705
///
@@ -1040,6 +1056,7 @@ class KotlinFunction {
10401056
this.typeParameters = const [],
10411057
required this.flags,
10421058
required this.isSuspend,
1059+
required this.isOperator,
10431060
});
10441061

10451062
/// Name in the byte code.
@@ -1056,6 +1073,7 @@ class KotlinFunction {
10561073
final List<KotlinTypeParameter> typeParameters;
10571074
final int flags;
10581075
final bool isSuspend;
1076+
final bool isOperator;
10591077

10601078
factory KotlinFunction.fromJson(Map<String, dynamic> json) =>
10611079
_$KotlinFunctionFromJson(json);
@@ -1251,3 +1269,31 @@ class KotlinTypeProjection extends KotlinTypeArgument {
12511269
final KotlinType type;
12521270
final KmVariance variance;
12531271
}
1272+
1273+
enum Operator {
1274+
plus('+', parameterCount: 1),
1275+
minus('-', parameterCount: 1),
1276+
times('*', parameterCount: 1),
1277+
div('/', parameterCount: 1),
1278+
rem('%', parameterCount: 1),
1279+
get('[]', parameterCount: 1),
1280+
set('[]=', parameterCount: 2, returnsVoid: true);
1281+
1282+
final String dartSymbol;
1283+
1284+
/// The number of parameters this operator must have in Dart.
1285+
final int parameterCount;
1286+
1287+
/// Whether the return type that this operator must have in Dart is void.
1288+
final bool returnsVoid;
1289+
1290+
const Operator(
1291+
this.dartSymbol, {
1292+
required this.parameterCount,
1293+
this.returnsVoid = false,
1294+
});
1295+
1296+
bool isCompatibleWith(Method method) {
1297+
return parameterCount == method.params.length;
1298+
}
1299+
}

pkgs/jnigen/lib/src/elements/elements.g.dart

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkgs/jnigen/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

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

0 commit comments

Comments
 (0)