diff --git a/pkgs/jnigen/docs/interface_implementation.md b/pkgs/jnigen/docs/interface_implementation.md index 83e6dddd1..a465ded9c 100644 --- a/pkgs/jnigen/docs/interface_implementation.md +++ b/pkgs/jnigen/docs/interface_implementation.md @@ -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(); @@ -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) { @@ -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); @@ -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` 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 `$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); @@ -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); ``` diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart index e1e688e5a..1fe8186d2 100644 --- a/pkgs/jnigen/lib/src/bindings/dart_generator.dart +++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart @@ -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; @@ -1495,6 +1495,9 @@ class _AbstractImplMethod extends Visitor { 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;'); + } } } @@ -1511,59 +1514,49 @@ class _ConcreteImplClosureDef extends Visitor { 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 { +class _AbstractImplFactoryArg extends Visitor { 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 Function(int)`. -class _InterfaceAsyncFunctionType extends Visitor { - final Resolver resolver; - - _InterfaceAsyncFunctionType(this.resolver); - - @override - String visit(Method node) { - assert(node.returnType.name == 'void'); - const returnType = '$_core.Future'; - 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 { +/// Closure argument for concrete Impl class constructor. +/// Used for interface implementation. +class _ConcreteImplClosureCtorArg extends Visitor { 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; } } @@ -1631,27 +1624,8 @@ class _InterfaceIfAsyncMethod extends Visitor { } 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 ` is a subtype of `Never `, 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', '''); } } diff --git a/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart b/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart index 3904ce0c7..0707eb370 100644 --- a/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart +++ b/pkgs/jnigen/test/simple_package_test/bindings/simple_package.dart @@ -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 - Function(_$jni.JString) && - ($impl as _$MyInterface)._voidCallback is! _$core.Never - Function(_$jni.JString)) || - ($impl.voidCallback is _$core.Future 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; @@ -4894,10 +4886,11 @@ class MyInterface<$T extends _$jni.JObject> extends _$jni.JObject { static _$core.Map 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, @@ -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); @@ -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, @@ -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; @@ -5281,13 +5277,7 @@ class MyRunnable extends _$jni.JObject { $p, _$invokePointer, [ - if (($impl is _$MyRunnable && - ($impl as _$MyRunnable)._run is _$core.Future - Function() && - ($impl as _$MyRunnable)._run is! _$core.Never Function()) || - ($impl.run is _$core.Future Function() && - $impl.run is! _$core.Never Function())) - r'run()V', + if ($impl.run$async) r'run()V', ], ); final $a = $p.sendPort.nativePort; @@ -5306,20 +5296,24 @@ class MyRunnable extends _$jni.JObject { static _$core.Map 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(); @@ -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; @@ -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; diff --git a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart index 52d069f1d..e9afc9a0d 100644 --- a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -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)); @@ -946,7 +945,7 @@ class DartStringToIntParser implements $StringConverter { } } -class AsyncRunnable implements $MyRunnable { +class AsyncRunnable with $MyRunnable { final Completer completer; AsyncRunnable(this.completer); @@ -955,4 +954,7 @@ class AsyncRunnable implements $MyRunnable { Future run() async { completer.complete(); } + + @override + bool get run$async => true; }