Skip to content

Commit

Permalink
[pigeon] implements nullable return types (flutter#849)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke authored Feb 24, 2022
1 parent 57f4169 commit 14b7d46
Show file tree
Hide file tree
Showing 23 changed files with 637 additions and 186 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 @@
## 1.0.19

* Implements nullable return types.

## 1.0.18

* [front-end] Fix error caused by parsing `copyrightHeaders` passed to options in `@ConfigurePigeon`.
Expand Down
6 changes: 3 additions & 3 deletions packages/pigeon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ denotes APIs that live in Flutter but are invoked from the host platform.
`void`.
1) Generics are supported, but can currently only be used with nullable types
(example: `List<int?>`).
1) Arguments and return values to methods must be non-nullable. Fields on
classes can be nullable or non-nullable.
1) Arguments must be non-nullable. Fields on classes and return types can be
nullable or non-nullable.

## Supported Datatypes

Expand Down Expand Up @@ -124,11 +124,11 @@ Pigeon supports generating null-safe code, but it doesn't yet support:

1) Nullable method parameters
1) Nullable generics type arguments
1) Nullable return values

It does support:

1) Nullable and Non-nullable class fields.
1) Nullable return values

The default is to generate null-safe code but in order to generate non-null-safe
code run Pigeon with the extra argument `--no-dart_null_safety`. For example:
Expand Down
2 changes: 2 additions & 0 deletions packages/pigeon/bin/run_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ Future<int> _runFlutterUnitTests() async {
'$flutterUnitTestsPath/lib/multiple_arity.gen.dart',
'pigeons/non_null_fields.dart':
'$flutterUnitTestsPath/lib/non_null_fields.gen.dart',
'pigeons/nullable_returns.dart':
'$flutterUnitTestsPath/lib/nullable_returns.gen.dart',
});
if (generateCode != 0) {
return generateCode;
Expand Down
39 changes: 27 additions & 12 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ final BinaryMessenger$nullTag _binaryMessenger;
argSignature = _getMethodArgumentsSignature(func, argNameFunc, nullTag);
}
indent.write(
'Future<${_addGenericTypes(func.returnType, nullTag)}> ${func.name}($argSignature) async ',
'Future<${_addGenericTypesNullable(func.returnType, nullTag)}> ${func.name}($argSignature) async ',
);
indent.scoped('{', '}', () {
final String channelName = makeChannelName(api, func);
Expand All @@ -200,24 +200,38 @@ final BinaryMessenger$nullTag _binaryMessenger;
final String returnType =
_makeGenericTypeArguments(func.returnType, nullTag);
final String castCall = _makeGenericCastCall(func.returnType, nullTag);
const String accessor = 'replyMap[\'${Keys.result}\']';
final String unwrapper =
func.returnType.isNullable ? '' : unwrapOperator;
final String returnStatement = func.returnType.isVoid
? 'return;'
: 'return (replyMap[\'${Keys.result}\'] as $returnType$nullTag)$unwrapOperator$castCall;';
: 'return ($accessor as $returnType$nullTag)$unwrapper$castCall;';
indent.format('''
final Map<Object$nullTag, Object$nullTag>$nullTag replyMap =\n\t\tawait channel.send($sendArgument) as Map<Object$nullTag, Object$nullTag>$nullTag;
if (replyMap == null) {
\tthrow PlatformException(
\t\tcode: 'channel-error',
\t\tmessage: 'Unable to establish connection on channel.',
\t\tdetails: null,
\t);
} else if (replyMap['error'] != null) {
\tfinal Map<Object$nullTag, Object$nullTag> error = (replyMap['${Keys.error}'] as Map<Object$nullTag, Object$nullTag>$nullTag)$unwrapOperator;
\tthrow PlatformException(
\t\tcode: (error['${Keys.errorCode}'] as String$nullTag)$unwrapOperator,
\t\tmessage: error['${Keys.errorMessage}'] as String$nullTag,
\t\tdetails: error['${Keys.errorDetails}'],
\t);
\t);''');
// On iOS we can return nil from functions to accommodate error
// handling. Returning a nil value and not returning an error is an
// exception.
if (!func.returnType.isNullable && !func.returnType.isVoid) {
indent.format('''
} else if (replyMap['${Keys.result}'] == null) {
\tthrow PlatformException(
\t\tcode: 'null-error',
\t\tmessage: 'Host platform returned null value for non-null return value.',
\t);''');
}
indent.format('''
} else {
\t$returnStatement
}''');
Expand Down Expand Up @@ -255,8 +269,8 @@ void _writeFlutterApi(
for (final Method func in api.methods) {
final bool isAsync = func.isAsynchronous;
final String returnType = isAsync
? 'Future<${_addGenericTypes(func.returnType, nullTag)}>'
: _addGenericTypes(func.returnType, nullTag);
? 'Future<${_addGenericTypesNullable(func.returnType, nullTag)}>'
: _addGenericTypesNullable(func.returnType, nullTag);
final String argSignature = _getMethodArgumentsSignature(
func,
_getArgumentName,
Expand Down Expand Up @@ -294,7 +308,7 @@ void _writeFlutterApi(
);
indent.scoped('{', '});', () {
final String returnType =
_addGenericTypes(func.returnType, nullTag);
_addGenericTypesNullable(func.returnType, nullTag);
final bool isAsync = func.isAsynchronous;
final String emptyReturnStatement = isMockHandler
? 'return <Object$nullTag, Object$nullTag>{};'
Expand Down Expand Up @@ -387,9 +401,9 @@ String _addGenericTypes(TypeDeclaration type, String nullTag) {
}
}

String _addGenericTypesNullable(NamedType field, String nullTag) {
final String genericdType = _addGenericTypes(field.type, nullTag);
return field.type.isNullable ? '$genericdType$nullTag' : genericdType;
String _addGenericTypesNullable(TypeDeclaration type, String nullTag) {
final String genericdType = _addGenericTypes(type, nullTag);
return type.isNullable ? '$genericdType$nullTag' : genericdType;
}

/// Generates Dart source code for the given AST represented by [root],
Expand Down Expand Up @@ -495,7 +509,8 @@ pigeonMap['${field.name}'] != null
'(pigeonMap[\'${field.name}\'] as $genericType$nullTag)$castCallPrefix$castCall',
);
} else {
final String genericdType = _addGenericTypesNullable(field, nullTag);
final String genericdType =
_addGenericTypesNullable(field.type, nullTag);
if (field.type.isNullable) {
indent.add(
'pigeonMap[\'${field.name}\'] as $genericdType',
Expand Down Expand Up @@ -532,7 +547,7 @@ pigeonMap['${field.name}'] != null
writeConstructor();
indent.addln('');
for (final NamedType field in klass.fields) {
final String datatype = _addGenericTypesNullable(field, nullTag);
final String datatype = _addGenericTypesNullable(field.type, nullTag);
indent.writeln('$datatype ${field.name};');
}
if (klass.fields.isNotEmpty) {
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'dart:mirrors';
import 'ast.dart';

/// The current version of pigeon. This must match the version in pubspec.yaml.
const String pigeonVersion = '1.0.18';
const String pigeonVersion = '1.0.19';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
Loading

0 comments on commit 14b7d46

Please sign in to comment.