mirror of
https://github.com/flutter/packages.git
synced 2025-07-03 09:08:54 +08:00
[Pigeon] added support for void return types (#121)
This commit is contained in:
@ -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.
|
||||
|
@ -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}'];
|
||||
@ -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
|
||||
}
|
||||
''');
|
||||
});
|
||||
@ -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();');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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() {
|
||||
@ -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';
|
||||
}
|
||||
|
@ -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('{', '}', () {
|
||||
@ -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();
|
||||
@ -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);');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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 *',
|
||||
@ -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('');
|
||||
@ -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');
|
||||
}
|
||||
@ -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 ');
|
||||
@ -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();
|
||||
@ -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);');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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>[
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
14
packages/pigeon/pigeons/voidflutter.dart
Normal file
14
packages/pigeon/pigeons/voidflutter.dart
Normal file
@ -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
packages/pigeon/pigeons/voidhost.dart
Normal file
14
packages/pigeon/pigeons/voidhost.dart
Normal file
@ -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);
|
||||
}
|
@ -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:
|
||||
|
@ -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()')));
|
||||
});
|
||||
}
|
||||
|
@ -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)'));
|
||||
});
|
||||
}
|
||||
|
@ -266,4 +266,71 @@ void main() {
|
||||
expect(code, contains('@implementation Api'));
|
||||
expect(code, matches('void.*doSomething.*Input.*Output.*{'));
|
||||
});
|
||||
|
||||
test('gen host void header', () {
|
||||
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();
|
||||
generateObjcHeader(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
|
||||
final String code = sink.toString();
|
||||
expect(code, contains('(void)doSomething'));
|
||||
});
|
||||
|
||||
test('gen host void source', () {
|
||||
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();
|
||||
generateObjcSource(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
|
||||
final String code = sink.toString();
|
||||
expect(code, isNot(matches('=.*doSomething')));
|
||||
expect(code, matches('[.*doSomething:.*]'));
|
||||
expect(code, contains('callback(wrapResult(nil, error))'));
|
||||
});
|
||||
|
||||
test('gen flutter void header', () {
|
||||
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();
|
||||
generateObjcHeader(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
|
||||
final String code = sink.toString();
|
||||
expect(code, contains('completion:(void(^)(NSError*))'));
|
||||
});
|
||||
|
||||
test('gen flutter void source', () {
|
||||
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();
|
||||
generateObjcSource(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
|
||||
final String code = sink.toString();
|
||||
expect(code, contains('completion:(void(^)(NSError*))'));
|
||||
expect(code, contains('completion(nil)'));
|
||||
});
|
||||
}
|
||||
|
@ -38,6 +38,11 @@ abstract class AFlutterApi {
|
||||
Output1 doit(Input1 input);
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class VoidApi {
|
||||
void doit(Input1 input);
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('parse args - input', () {
|
||||
final PigeonOptions opts =
|
||||
@ -145,4 +150,14 @@ void main() {
|
||||
expect(results.root.apis[0].name, equals('AFlutterApi'));
|
||||
expect(results.root.apis[0].location, equals(ApiLocation.flutter));
|
||||
});
|
||||
|
||||
test('void host api', () {
|
||||
final Pigeon pigeon = Pigeon.setup();
|
||||
final ParseResults results = pigeon.parse(<Type>[VoidApi]);
|
||||
expect(results.errors.length, equals(0));
|
||||
expect(results.root.apis.length, equals(1));
|
||||
expect(results.root.apis[0].methods.length, equals(1));
|
||||
expect(results.root.apis[0].name, equals('VoidApi'));
|
||||
expect(results.root.apis[0].methods[0].returnType, equals('void'));
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user