Skip to content

Commit

Permalink
[Pigeon] added support for void return types (flutter#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke authored Mar 23, 2020
1 parent 5cbb850 commit d410ceb
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 26 deletions.
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.0-experimental.6

* Added support for void return types.

## 0.1.0-experimental.5

* Fixed runtime exception in Android with values of ints less than 2^32.
Expand Down
15 changes: 12 additions & 3 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ void _writeHostApi(Indent indent, Api api) {
indent.dec();
indent.dec();
indent.writeln('');
final String returnStatement =
func.returnType == 'void'
? '// noop'
: 'return ${func.returnType}._fromMap(replyMap[\'${Keys.result}\']);';
indent.format('''Map replyMap = await channel.send(requestMap);
if (replyMap['error'] != null) {
\tMap error = replyMap['${Keys.error}'];
Expand All @@ -31,7 +35,7 @@ if (replyMap['error'] != null) {
\t\t\tmessage: error['${Keys.errorMessage}'],
\t\t\tdetails: error['${Keys.errorDetails}']);
} else {
\treturn ${func.returnType}._fromMap(replyMap[\'${Keys.result}\']);
\t$returnStatement
}
''');
});
Expand Down Expand Up @@ -67,8 +71,13 @@ void _writeFlutterApi(Indent indent, Api api) {
final String returnType = func.returnType;
indent.writeln('Map mapMessage = message as Map;');
indent.writeln('$argType input = $argType._fromMap(mapMessage);');
indent.writeln('$returnType output = api.${func.name}(input);');
indent.writeln('return output._toMap();');
final String call = 'api.${func.name}(input)';
if (returnType == 'void') {
indent.writeln('$call;');
} else {
indent.writeln('$returnType output = $call;');
indent.writeln('return output._toMap();');
}
});
});
}
Expand Down
8 changes: 7 additions & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

import 'dart:convert';
import 'dart:io';
import 'dart:mirrors';
import 'ast.dart';

/// The current version of pigeon.
const String pigeonVersion = '0.1.0-experimental.5';
const String pigeonVersion = '0.1.0-experimental.6';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down Expand Up @@ -154,3 +155,8 @@ class Keys {
/// The key in an error hash for the 'details' value.
static const String errorDetails = 'details';
}

/// Returns true if `type` represents 'void'.
bool isVoid(TypeMirror type) {
return MirrorSystem.getName(type.simpleName) == 'void';
}
29 changes: 20 additions & 9 deletions packages/pigeon/lib/java_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,15 @@ void _writeHostApi(Indent indent, Api api) {
'HashMap<String, HashMap> wrapped = new HashMap<String, HashMap>();');
indent.write('try ');
indent.scoped('{', '}', () {
indent
.writeln('$returnType output = api.${method.name}(input);');
indent
.writeln('wrapped.put("${Keys.result}", output.toMap());');
final String call = 'api.${method.name}(input)';
if (method.returnType == 'void') {
indent.writeln('$call;');
indent.writeln('wrapped.put("${Keys.result}", null);');
} else {
indent.writeln('$returnType output = $call;');
indent.writeln(
'wrapped.put("${Keys.result}", output.toMap());');
}
});
indent.write('catch (Exception exception) ');
indent.scoped('{', '}', () {
Expand Down Expand Up @@ -102,8 +107,10 @@ void _writeFlutterApi(Indent indent, Api api) {
});
for (Method func in api.methods) {
final String channelName = makeChannelName(api, func);
final String returnType =
func.returnType == 'void' ? 'Void' : func.returnType;
indent.write(
'public void ${func.name}(${func.argType} argInput, Reply<${func.returnType}> callback) ');
'public void ${func.name}(${func.argType} argInput, Reply<$returnType> callback) ');
indent.scoped('{', '}', () {
indent.writeln('BasicMessageChannel<Object> channel =');
indent.inc();
Expand All @@ -118,10 +125,14 @@ void _writeFlutterApi(Indent indent, Api api) {
indent.scoped('{', '});', () {
indent.write('public void reply(Object channelReply) ');
indent.scoped('{', '}', () {
indent.writeln('HashMap outputMap = (HashMap)channelReply;');
indent.writeln(
'${func.returnType} output = ${func.returnType}.fromMap(outputMap);');
indent.writeln('callback.reply(output);');
if (func.returnType == 'void') {
indent.writeln('callback.reply(null);');
} else {
indent.writeln('HashMap outputMap = (HashMap)channelReply;');
indent.writeln(
'${func.returnType} output = ${func.returnType}.fromMap(outputMap);');
indent.writeln('callback.reply(output);');
}
});
});
});
Expand Down
43 changes: 32 additions & 11 deletions packages/pigeon/lib/objc_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ String _className(String prefix, String className) {
}
}

String _callbackForType(String dartType, String objcType) {
return dartType == 'void'
? 'void(^)(NSError*)'
: 'void(^)($objcType*, NSError*)';
}

const Map<String, String> _objcTypeForDartTypeMap = <String, String>{
'bool': 'NSNumber *',
'int': 'NSNumber *',
Expand Down Expand Up @@ -101,10 +107,13 @@ void generateObjcHeader(ObjcOptions options, Root root, StringSink sink) {
if (api.location == ApiLocation.host) {
indent.writeln('@protocol $apiName');
for (Method func in api.methods) {
final String returnType = _className(options.prefix, func.returnType);
final String returnTypeName =
_className(options.prefix, func.returnType);
final String argType = _className(options.prefix, func.argType);
final String returnType =
func.returnType == 'void' ? 'void' : '$returnTypeName *';
indent.writeln(
'-($returnType *)${func.name}:($argType*)input error:(FlutterError **)error;');
'-($returnType)${func.name}:($argType*)input error:(FlutterError **)error;');
}
indent.writeln('@end');
indent.writeln('');
Expand All @@ -118,8 +127,9 @@ void generateObjcHeader(ObjcOptions options, Root root, StringSink sink) {
for (Method func in api.methods) {
final String returnType = _className(options.prefix, func.returnType);
final String argType = _className(options.prefix, func.argType);
final String callbackType = _callbackForType(func.returnType, returnType);
indent.writeln(
'- (void)${func.name}:($argType*)input completion:(void(^)($returnType*, NSError*))completion;');
'- (void)${func.name}:($argType*)input completion:($callbackType)completion;');
}
indent.writeln('@end');
}
Expand Down Expand Up @@ -176,9 +186,14 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
_className(options.prefix, func.returnType);
indent.writeln('$argType *input = [$argType fromMap:message];');
indent.writeln('FlutterError *error;');
indent.writeln(
'$returnType *output = [api ${func.name}:input error:&error];');
indent.writeln('callback(wrapResult([output toMap], error));');
final String call = '[api ${func.name}:input error:&error]';
if (func.returnType == 'void') {
indent.writeln('$call;');
indent.writeln('callback(wrapResult(nil, error));');
} else {
indent.writeln('$returnType *output = $call;');
indent.writeln('callback(wrapResult([output toMap], error));');
}
});
});
indent.write('else ');
Expand Down Expand Up @@ -213,8 +228,10 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) {
for (Method func in api.methods) {
final String returnType = _className(options.prefix, func.returnType);
final String argType = _className(options.prefix, func.argType);
final String callbackType = _callbackForType(func.returnType, returnType);

indent.write(
'- (void)${func.name}:($argType*)input completion:(void(^)($returnType*, NSError*))completion ');
'- (void)${func.name}:($argType*)input completion:($callbackType)completion ');
indent.scoped('{', '}', () {
indent.writeln('FlutterBasicMessageChannel *channel =');
indent.inc();
Expand All @@ -227,10 +244,14 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) {
indent.writeln('NSDictionary* inputMap = [input toMap];');
indent.write('[channel sendMessage:inputMap reply:^(id reply) ');
indent.scoped('{', '}];', () {
indent.writeln('NSDictionary* outputMap = reply;');
indent
.writeln('$returnType * output = [$returnType fromMap:outputMap];');
indent.writeln('completion(output, nil);');
if (func.returnType == 'void') {
indent.writeln('completion(nil);');
} else {
indent.writeln('NSDictionary* outputMap = reply;');
indent.writeln(
'$returnType * output = [$returnType fromMap:outputMap];');
indent.writeln('completion(output, nil);');
}
});
});
}
Expand Down
5 changes: 4 additions & 1 deletion packages/pigeon/lib/pigeon_lib.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:pigeon/java_generator.dart';

import 'ast.dart';
import 'dart_generator.dart';
import 'generator_tools.dart';
import 'objc_generator.dart';

const List<String> _validTypes = <String>[
Expand Down Expand Up @@ -152,7 +153,9 @@ class Pigeon {
for (ClassMirror apiMirror in apis) {
for (DeclarationMirror declaration in apiMirror.declarations.values) {
if (declaration is MethodMirror && !declaration.isConstructor) {
classes.add(declaration.returnType);
if (!isVoid(declaration.returnType)) {
classes.add(declaration.returnType);
}
classes.add(declaration.parameters[0].type);
}
}
Expand Down
14 changes: 14 additions & 0 deletions packages/pigeon/pigeons/voidflutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:pigeon/pigeon_lib.dart';

class SetRequest {
int value;
}

@FlutterApi()
abstract class Api {
void setValue(SetRequest request);
}
14 changes: 14 additions & 0 deletions packages/pigeon/pigeons/voidhost.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:pigeon/pigeon_lib.dart';

class SetRequest {
int value;
}

@HostApi()
abstract class Api {
void setValue(SetRequest request);
}
2 changes: 1 addition & 1 deletion packages/pigeon/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: pigeon
version: 0.1.0-experimental.5
version: 0.1.0-experimental.6
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
homepage: https://github.com/flutter/packages/tree/master/packages/pigeon
dependencies:
Expand Down
35 changes: 35 additions & 0 deletions packages/pigeon/test/dart_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,39 @@ void main() {
expect(code, contains('abstract class Api'));
expect(code, contains('void ApiSetup(Api'));
});

test('host void', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void')
])
], classes: <Class>[
Class(
name: 'Input',
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(root, sink);
final String code = sink.toString();
expect(code, contains('Future<void> doSomething'));
expect(code, contains('// noop'));
});

test('flutter void', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void')
])
], classes: <Class>[
Class(
name: 'Input',
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
generateDart(root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.*doSomething')));
expect(code, contains('doSomething('));
expect(code, isNot(contains('._toMap()')));
});
}
39 changes: 39 additions & 0 deletions packages/pigeon/test/java_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,43 @@ void main() {
expect(code, contains('public static class Api'));
expect(code, matches('doSomething.*Input.*Output'));
});

test('gen host void api', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void')
])
], classes: <Class>[
Class(
name: 'Input',
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
final JavaOptions javaOptions = JavaOptions();
javaOptions.className = 'Messages';
generateJava(javaOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.*doSomething')));
expect(code, contains('doSomething('));
});

test('gen flutter void api', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(name: 'doSomething', argType: 'Input', returnType: 'void')
])
], classes: <Class>[
Class(
name: 'Input',
fields: <Field>[Field(name: 'input', dataType: 'String')]),
]);
final StringBuffer sink = StringBuffer();
final JavaOptions javaOptions = JavaOptions();
javaOptions.className = 'Messages';
generateJava(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('Reply<Void>'));
expect(code, isNot(contains('.fromMap(')));
expect(code, contains('callback.reply(null)'));
});
}
Loading

0 comments on commit d410ceb

Please sign in to comment.