Skip to content

Commit

Permalink
Use boolean instead
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi committed Sep 26, 2024
1 parent 3edf9e1 commit 6e87fcd
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 90 deletions.
27 changes: 16 additions & 11 deletions pkgs/jnigen/docs/interface_implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,24 @@ class Runnable extends JObject {
) { /* ... */ }
}
abstract interface class $Runnable {
abstract mixin class $Runnable {
factory $Runnable({
required void Function() run,
bool run$async,
}) = _$Runnable;
bool get run$async => false;
void run();
}
class _$Runnable implements $Runnable {
_$Runnable({
required void Function() run,
this.run$async = false;
}) : _run = run;
final void Function() _run;
final bool run$async;
void run() {
return _run();
Expand Down Expand Up @@ -83,7 +87,7 @@ implementing the interface in Java instead of using the lambdas:
```java
// Java
public class Printer implements Runnable {
public class Printer with Runnable {
private final String text;
public Printer(String text) {
Expand All @@ -104,7 +108,7 @@ You can do the same in Dart by creating a subclass that implements `$Runnable`:
```dart
// Dart
class Printer implements $Runnable {
class Printer with $Runnable {
final String text;
Printer(this.text);
Expand All @@ -126,21 +130,22 @@ By default, when any of methods of the implemented interface gets called, the
caller will block until the callee returns a result.
Void-returning functions don't have to return a result, so we can choose to not
block the caller when the method is just a listener. To signal this, make the
return type of your method `Future<void>` instead of `void`.
When implementing the interface inline, this is as simple as adding `async`:
block the caller when the method is just a listener. To signal this, pass `true`
to `<method name>$async` argument when implementing the interface inline:
```dart
// Dart
final runnable = Runnable.implement($Runnable(run: () async => print('hello')));
final runnable = Runnable.implement($Runnable(
run: () => print('hello'),
run$async: true, // This makes the run method non-blocking.
));
```
Similarly, when subclassing make sure you use the correct return type:
Similarly, when subclassing
```dart
// Dart
class Printer implements $Runnable {
class Printer with $Runnable {
final String text;
Printer(this.text);
Expand Down Expand Up @@ -174,5 +179,5 @@ possible to make it a `Closable` by passing in `Closable.type` to
```dart
// Dart
final closable = object.castTo(Closable.type);
final closable = object.as(Closable.type);
```
84 changes: 29 additions & 55 deletions pkgs/jnigen/lib/src/bindings/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ class $name$typeParamsDef extends $superName {
[
...typeParams
.map((typeParam) => 'required $_jType<\$$typeParam> $typeParam,'),
...node.methods.accept(_ConcreteImplClosureCtorArg(resolver)),
...node.methods.accept(_AbstractImplFactoryArg(resolver)),
].join(_newLine(depth: 2)),
'}',
);
s.write('''
abstract interface class $implClassName$typeParamsDef {
abstract mixin class $implClassName$typeParamsDef {
factory $implClassName(
$abstractFactoryArgs
) = _$implClassName;
Expand Down Expand Up @@ -1495,6 +1495,9 @@ class _AbstractImplMethod extends Visitor<Method, void> {
final name = node.finalName;
final args = node.params.accept(_ParamDef(resolver)).join(', ');
s.writeln(' $returnType $name($args);');
if (returnType == 'void') {
s.writeln(' bool get $name\$async => false;');
}
}
}

Expand All @@ -1511,59 +1514,49 @@ class _ConcreteImplClosureDef extends Visitor<Method, void> {
final name = node.finalName;
final args = node.params.accept(_ParamDef(resolver)).join(', ');
s.writeln(' final $returnType Function($args) _$name;');
if (returnType == 'void') {
s.writeln(' final bool $name\$async;');
}
}
}

/// Closure argument for concrete Impl class constructor.
/// Closure argument for the factory of the implementation's abstract class.
/// Used for interface implementation.
class _ConcreteImplClosureCtorArg extends Visitor<Method, String> {
class _AbstractImplFactoryArg extends Visitor<Method, String> {
final Resolver resolver;

_ConcreteImplClosureCtorArg(this.resolver);
_AbstractImplFactoryArg(this.resolver);

@override
String visit(Method node) {
final returnType = node.returnType.accept(_TypeGenerator(resolver));
final name = node.finalName;
final args = node.params.accept(_ParamDef(resolver)).join(', ');
return 'required $returnType Function($args) $name,';
}
}

/// Dart async function type of an interface method.
///
/// For example: `Future<void> Function(int)`.
class _InterfaceAsyncFunctionType extends Visitor<Method, String> {
final Resolver resolver;

_InterfaceAsyncFunctionType(this.resolver);

@override
String visit(Method node) {
assert(node.returnType.name == 'void');
const returnType = '$_core.Future<void>';
final args = node.params
.map((p) => p.type)
.accept(_TypeGenerator(resolver))
.join(', ');
return '$returnType Function($args)';
final functionArg = 'required $returnType Function($args) $name,';
if (node.returnType.name == 'void') {
return '$functionArg bool $name\$async,';
}
return functionArg;
}
}

class _InterfaceNeverFunctionType extends Visitor<Method, String> {
/// Closure argument for concrete Impl class constructor.
/// Used for interface implementation.
class _ConcreteImplClosureCtorArg extends Visitor<Method, String> {
final Resolver resolver;

_InterfaceNeverFunctionType(this.resolver);
_ConcreteImplClosureCtorArg(this.resolver);

@override
String visit(Method node) {
assert(node.returnType.name == 'void');
const returnType = '$_core.Never';
final args = node.params
.map((p) => p.type)
.accept(_TypeGenerator(resolver))
.join(', ');
return '$returnType Function($args)';
final returnType = node.returnType.accept(_TypeGenerator(resolver));
final name = node.finalName;
final args = node.params.accept(_ParamDef(resolver)).join(', ');
final functionArg = 'required $returnType Function($args) $name,';
if (node.returnType.name == 'void') {
return '$functionArg this.$name\$async = false,';
}
return functionArg;
}
}

Expand Down Expand Up @@ -1631,27 +1624,8 @@ class _InterfaceIfAsyncMethod extends Visitor<Method, void> {
}
final signature = node.javaSig;
final name = node.finalName;
final asyncType = node.accept(_InterfaceAsyncFunctionType(resolver));
final neverType = node.accept(_InterfaceNeverFunctionType(resolver));
// If the implementation is using the callback passing style, look at the
// actual passed callback instead of the wrapper function. The wrapper is
// always going to return `void`.
//
// If the callback simply throws its return type will be `Never`. As any
// function `R <F>` is a subtype of `Never <F>`, we should have a special
// case for it. It's not possible to throw from a listener function, so
// functions that only throw and hence have a `Never` return type, will be
// considered to be sync.
s.write('''
if ((
\$impl is _$implClassName &&
(\$impl as _$implClassName)._$name is $asyncType &&
(\$impl as _$implClassName)._$name is! $neverType
) || (
\$impl.$name is $asyncType &&
\$impl.$name is! $neverType
))
r'$signature',
if (\$impl.$name\$async) r'$signature',
''');
}
}
Expand Down
34 changes: 14 additions & 20 deletions pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4866,15 +4866,7 @@ class MyInterface<$T extends _$jni.JObject> extends _$jni.JObject {
$p,
_$invokePointer,
[
if (($impl is _$MyInterface &&
($impl as _$MyInterface)._voidCallback is _$core.Future<void>
Function(_$jni.JString) &&
($impl as _$MyInterface)._voidCallback is! _$core.Never
Function(_$jni.JString)) ||
($impl.voidCallback is _$core.Future<void> Function(
_$jni.JString) &&
$impl.voidCallback is! _$core.Never Function(_$jni.JString)))
r'voidCallback(Ljava/lang/String;)V',
if ($impl.voidCallback$async) r'voidCallback(Ljava/lang/String;)V',
],
);
final $a = $p.sendPort.nativePort;
Expand All @@ -4894,10 +4886,11 @@ class MyInterface<$T extends _$jni.JObject> extends _$jni.JObject {
static _$core.Map<int, $MyInterface> get $impls => _$impls;
}

abstract interface class $MyInterface<$T extends _$jni.JObject> {
abstract mixin class $MyInterface<$T extends _$jni.JObject> {
factory $MyInterface({
required _$jni.JObjType<$T> T,
required void Function(_$jni.JString s) voidCallback,
bool voidCallback$async,
required _$jni.JString Function(_$jni.JString s) stringCallback,
required $T Function($T t) varCallback,
required int Function(int a, bool b, int c, double d) manyPrimitives,
Expand All @@ -4906,6 +4899,7 @@ abstract interface class $MyInterface<$T extends _$jni.JObject> {
_$jni.JObjType<$T> get T;

void voidCallback(_$jni.JString s);
bool get voidCallback$async => false;
_$jni.JString stringCallback(_$jni.JString s);
$T varCallback($T t);
int manyPrimitives(int a, bool b, int c, double d);
Expand All @@ -4915,6 +4909,7 @@ class _$MyInterface<$T extends _$jni.JObject> implements $MyInterface<$T> {
_$MyInterface({
required this.T,
required void Function(_$jni.JString s) voidCallback,
this.voidCallback$async = false,
required _$jni.JString Function(_$jni.JString s) stringCallback,
required $T Function($T t) varCallback,
required int Function(int a, bool b, int c, double d) manyPrimitives,
Expand All @@ -4927,6 +4922,7 @@ class _$MyInterface<$T extends _$jni.JObject> implements $MyInterface<$T> {
final _$jni.JObjType<$T> T;

final void Function(_$jni.JString s) _voidCallback;
final bool voidCallback$async;
final _$jni.JString Function(_$jni.JString s) _stringCallback;
final $T Function($T t) _varCallback;
final int Function(int a, bool b, int c, double d) _manyPrimitives;
Expand Down Expand Up @@ -5281,13 +5277,7 @@ class MyRunnable extends _$jni.JObject {
$p,
_$invokePointer,
[
if (($impl is _$MyRunnable &&
($impl as _$MyRunnable)._run is _$core.Future<void>
Function() &&
($impl as _$MyRunnable)._run is! _$core.Never Function()) ||
($impl.run is _$core.Future<void> Function() &&
$impl.run is! _$core.Never Function()))
r'run()V',
if ($impl.run$async) r'run()V',
],
);
final $a = $p.sendPort.nativePort;
Expand All @@ -5306,20 +5296,24 @@ class MyRunnable extends _$jni.JObject {
static _$core.Map<int, $MyRunnable> get $impls => _$impls;
}

abstract interface class $MyRunnable {
abstract mixin class $MyRunnable {
factory $MyRunnable({
required void Function() run,
bool run$async,
}) = _$MyRunnable;

void run();
bool get run$async => false;
}

class _$MyRunnable implements $MyRunnable {
_$MyRunnable({
required void Function() run,
this.run$async = false,
}) : _run = run;

final void Function() _run;
final bool run$async;

void run() {
return _run();
Expand Down Expand Up @@ -5716,7 +5710,7 @@ class StringConverter extends _$jni.JObject {
}
}

abstract interface class $StringConverter {
abstract mixin class $StringConverter {
factory $StringConverter({
required int Function(_$jni.JString s) parseToInt,
}) = _$StringConverter;
Expand Down Expand Up @@ -6402,7 +6396,7 @@ class JsonSerializable extends _$jni.JObject {
}
}

abstract interface class $JsonSerializable {
abstract mixin class $JsonSerializable {
factory $JsonSerializable({
required JsonSerializable_Case Function() value,
}) = _$JsonSerializable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,9 +709,8 @@ void registerTests(String groupName, TestRunnerCallback test) {
final MyRunnable runnable;
if (style == 'callback') {
runnable = MyRunnable.implement($MyRunnable(
run: () async /* <-- Having `async` makes this a listener. */ {
completer.complete();
},
run: completer.complete,
run$async: true,
));
} else {
runnable = MyRunnable.implement(AsyncRunnable(completer));
Expand Down Expand Up @@ -946,7 +945,7 @@ class DartStringToIntParser implements $StringConverter {
}
}

class AsyncRunnable implements $MyRunnable {
class AsyncRunnable with $MyRunnable {
final Completer<void> completer;

AsyncRunnable(this.completer);
Expand All @@ -955,4 +954,7 @@ class AsyncRunnable implements $MyRunnable {
Future<void> run() async {
completer.complete();
}

@override
bool get run$async => true;
}

0 comments on commit 6e87fcd

Please sign in to comment.