[pigeon] generics and parsing multiple parameters (#428)
* [pigeon] implemented generics
* [pigeon] implemented multiple arity parsing
* [pigeon] implemented generics support in primitives
* [pigeon] made generated dart code keep the parameter name
* [pigeon] refactored the AST to be more clear
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 3caaeba..8da8cd4 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -7,6 +7,8 @@
* Started allowing primitive data types as arguments and return types.
* Added one_language flag for allowing Pigeon to only generate code for one platform.
* Fixed NPE in java generated code for nested types.
+* Started supporting generics' type arguments for fields in classes.
+* Generics (class fields and primitives)
## 0.3.0
diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart
index bfc438f..d70f9e0 100644
--- a/packages/pigeon/lib/ast.dart
+++ b/packages/pigeon/lib/ast.dart
@@ -20,10 +20,8 @@
Method({
required this.name,
required this.returnType,
- required this.argType,
- this.isArgNullable = false,
+ required this.arguments,
this.isAsynchronous = false,
- this.isReturnNullable = false,
this.offset,
});
@@ -31,16 +29,10 @@
String name;
/// The data-type of the return value.
- String returnType;
+ TypeDeclaration returnType;
- /// True if the method can return a null value.
- bool isReturnNullable;
-
- /// The data-type of the argument.
- String argType;
-
- /// True if the argument has a null tag `?`.
- bool isArgNullable;
+ /// The arguments passed into the [Method].
+ List<NamedType> arguments;
/// Whether the receiver of this method is expected to return synchronously or not.
bool isAsynchronous;
@@ -50,7 +42,7 @@
@override
String toString() {
- return '(Api name:$name returnType:$returnType argType:$argType isAsynchronous:$isAsynchronous)';
+ return '(Method name:$name returnType:$returnType arguments:$arguments isAsynchronous:$isAsynchronous)';
}
}
@@ -82,39 +74,51 @@
}
}
-/// Represents a field on a [Class].
-class Field extends Node {
- /// Parametric constructor for [Field].
- Field({
- required this.name,
- required this.dataType,
+/// A specific instance of a type.
+class TypeDeclaration {
+ /// Constructor for [TypeDeclaration].
+ TypeDeclaration({
+ required this.baseName,
required this.isNullable,
this.typeArguments,
- this.offset,
});
- /// The name of the field.
- String name;
+ /// The base name of the [TypeDeclaration] (ex 'Foo' to 'Foo<Bar>?').
+ final String baseName;
- /// The data-type of the field (ex 'String' or 'int').
- String dataType;
+ /// The type arguments to the entity (ex 'Bar' to 'Foo<Bar>?').
+ final List<TypeDeclaration>? typeArguments;
- /// The offset in the source file where the field appears.
- int? offset;
-
- /// True if the datatype is nullable (ex `int?`).
- bool isNullable;
-
- /// Type parameters used for generics.
- List<Field>? typeArguments;
+ /// True if the type is nullable.
+ final bool isNullable;
@override
String toString() {
- return '(Field name:$name dataType:$dataType)';
+ return '(TypeDeclaration baseName:$baseName isNullable:$isNullable typeArguments:$typeArguments)';
}
}
-/// Represents a class with [Field]s.
+/// Represents a named entity that has a type.
+class NamedType extends Node {
+ /// Parametric constructor for [NamedType].
+ NamedType({required this.name, required this.type, this.offset});
+
+ /// The name of the entity.
+ String name;
+
+ /// The type.
+ TypeDeclaration type;
+
+ /// The offset in the source file where the [NamedType] appears.
+ int? offset;
+
+ @override
+ String toString() {
+ return '(NamedType name:$name type:$type)';
+ }
+}
+
+/// Represents a class with fields.
class Class extends Node {
/// Parametric constructor for [Class].
Class({
@@ -126,7 +130,7 @@
String name;
/// All the fields contained in the class.
- List<Field> fields;
+ List<NamedType> fields;
@override
String toString() {
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index c231135..5c51d51 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -92,6 +92,24 @@
});
}
+/// Creates a Dart type where all type arguments are [Objects].
+String _makeGenericTypeArguments(TypeDeclaration type, String nullTag) {
+ return type.typeArguments != null
+ ? '${type.baseName}<${type.typeArguments!.map<String>((TypeDeclaration e) => 'Object$nullTag').join(', ')}>'
+ : _addGenericTypes(type, nullTag);
+}
+
+/// Creates a `.cast<>` call for an type. Returns an empty string if the
+/// type has no type arguments.
+String _makeGenericCastCall(TypeDeclaration type, String nullTag) {
+ return type.typeArguments != null
+ ? '.cast<${_flattenTypeArguments(type.typeArguments!, nullTag)}>()'
+ : '';
+}
+
+String _getArgumentName(NamedType field) =>
+ field.name.isEmpty ? 'arg' : field.name;
+
void _writeHostApi(DartOptions opt, Indent indent, Api api) {
assert(api.location == ApiLocation.host);
final String codecName = _getCodecName(api);
@@ -121,12 +139,13 @@
}
String argSignature = '';
String sendArgument = 'null';
- if (func.argType != 'void') {
- argSignature = '${func.argType} arg';
- sendArgument = 'arg';
+ if (func.arguments.isNotEmpty) {
+ sendArgument = _getArgumentName(func.arguments[0]);
+ argSignature =
+ '${_addGenericTypes(func.arguments[0].type, nullTag)} $sendArgument';
}
indent.write(
- 'Future<${func.returnType}> ${func.name}($argSignature) async ',
+ 'Future<${_addGenericTypes(func.returnType, nullTag)}> ${func.name}($argSignature) async ',
);
indent.scoped('{', '}', () {
final String channelName = makeChannelName(api, func);
@@ -137,9 +156,12 @@
'\'$channelName\', codec, binaryMessenger: _binaryMessenger);',
);
});
- final String returnStatement = func.returnType == 'void'
+ final String returnType =
+ _makeGenericTypeArguments(func.returnType, nullTag);
+ final String castCall = _makeGenericCastCall(func.returnType, nullTag);
+ final String returnStatement = func.returnType.baseName == 'void'
? '// noop'
- : 'return (replyMap[\'${Keys.result}\'] as ${func.returnType}$nullTag)$unwrapOperator;';
+ : 'return (replyMap[\'${Keys.result}\'] as $returnType$nullTag)$unwrapOperator$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) {
@@ -181,10 +203,12 @@
indent.addln('');
for (final Method func in api.methods) {
final bool isAsync = func.isAsynchronous;
- final String returnType =
- isAsync ? 'Future<${func.returnType}>' : func.returnType;
- final String argSignature =
- func.argType == 'void' ? '' : '${func.argType} arg';
+ final String returnType = isAsync
+ ? 'Future<${_addGenericTypes(func.returnType, nullTag)}>'
+ : _addGenericTypes(func.returnType, nullTag);
+ final String argSignature = func.arguments.isEmpty
+ ? ''
+ : '${_addGenericTypes(func.arguments[0].type, nullTag)} ${_getArgumentName(func.arguments[0])}';
indent.writeln('$returnType ${func.name}($argSignature);');
}
indent.write('static void setup(${api.name}$nullTag api) ');
@@ -215,19 +239,21 @@
'channel.$messageHandlerSetter((Object$nullTag message) async ',
);
indent.scoped('{', '});', () {
- final String argType = func.argType;
- final String returnType = func.returnType;
+ final String returnType =
+ _addGenericTypes(func.returnType, nullTag);
final bool isAsync = func.isAsynchronous;
final String emptyReturnStatement = isMockHandler
? 'return <Object$nullTag, Object$nullTag>{};'
- : func.returnType == 'void'
+ : func.returnType.baseName == 'void'
? 'return;'
: 'return null;';
String call;
- if (argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.writeln('// ignore message');
call = 'api.${func.name}()';
} else {
+ final String argType =
+ _addGenericTypes(func.arguments[0].type, nullTag);
indent.writeln(
'assert(message != null, \'Argument for $channelName was null. Expected $argType.\');',
);
@@ -263,17 +289,38 @@
});
}
-String _addGenericTypes(String dataType, String nullTag) {
- switch (dataType) {
+/// Converts a [List] of [TypeDeclaration]s to a comma separated [String] to be
+/// used in Dart code.
+String _flattenTypeArguments(List<TypeDeclaration> args, String nullTag) {
+ return args
+ .map<String>((TypeDeclaration arg) => arg.typeArguments == null
+ ? '${arg.baseName}$nullTag'
+ : '${arg.baseName}<${_flattenTypeArguments(arg.typeArguments!, nullTag)}>$nullTag')
+ .join(', ');
+}
+
+/// Creates the type declaration for use in Dart code from a [NamedType] making sure
+/// that type arguments are used for primitive generic types.
+String _addGenericTypes(TypeDeclaration type, String nullTag) {
+ final List<TypeDeclaration>? typeArguments = type.typeArguments;
+ switch (type.baseName) {
case 'List':
- return 'List<Object$nullTag>$nullTag';
+ return (typeArguments == null)
+ ? 'List<Object$nullTag>'
+ : 'List<${_flattenTypeArguments(typeArguments, nullTag)}>';
case 'Map':
- return 'Map<Object$nullTag, Object$nullTag>$nullTag';
+ return (typeArguments == null)
+ ? 'Map<Object$nullTag, Object$nullTag>'
+ : 'Map<${_flattenTypeArguments(typeArguments, nullTag)}>';
default:
- return '$dataType$nullTag';
+ return type.baseName;
}
}
+String _addGenericTypesNullable(NamedType field, String nullTag) {
+ return '${_addGenericTypes(field.type, nullTag)}$nullTag';
+}
+
/// Generates Dart source code for the given AST represented by [root],
/// outputting the code to [sink].
void generateDart(DartOptions opt, Root root, StringSink sink) {
@@ -314,8 +361,8 @@
indent.writeln('');
indent.write('class ${klass.name} ');
indent.scoped('{', '}', () {
- for (final Field field in klass.fields) {
- final String datatype = _addGenericTypes(field.dataType, nullTag);
+ for (final NamedType field in klass.fields) {
+ final String datatype = _addGenericTypesNullable(field, nullTag);
indent.writeln('$datatype ${field.name};');
}
if (klass.fields.isNotEmpty) {
@@ -326,13 +373,13 @@
indent.writeln(
'final Map<Object$nullTag, Object$nullTag> pigeonMap = <Object$nullTag, Object$nullTag>{};',
);
- for (final Field field in klass.fields) {
+ for (final NamedType field in klass.fields) {
indent.write('pigeonMap[\'${field.name}\'] = ');
- if (customClassNames.contains(field.dataType)) {
+ if (customClassNames.contains(field.type.baseName)) {
indent.addln(
'${field.name} == null ? null : ${field.name}$unwrapOperator.encode();',
);
- } else if (customEnumNames.contains(field.dataType)) {
+ } else if (customEnumNames.contains(field.type.baseName)) {
indent.addln(
'${field.name} == null ? null : ${field.name}$unwrapOperator.index;',
);
@@ -353,21 +400,28 @@
indent.writeln('return ${klass.name}()');
indent.nest(1, () {
for (int index = 0; index < klass.fields.length; index += 1) {
- final Field field = klass.fields[index];
+ final NamedType field = klass.fields[index];
indent.write('..${field.name} = ');
- if (customClassNames.contains(field.dataType)) {
+ if (customClassNames.contains(field.type.baseName)) {
indent.format('''
pigeonMap['${field.name}'] != null
-\t\t? ${field.dataType}.decode(pigeonMap['${field.name}']$unwrapOperator)
+\t\t? ${field.type.baseName}.decode(pigeonMap['${field.name}']$unwrapOperator)
\t\t: null''', leadingSpace: false, trailingNewline: false);
- } else if (customEnumNames.contains(field.dataType)) {
+ } else if (customEnumNames.contains(field.type.baseName)) {
indent.format('''
pigeonMap['${field.name}'] != null
-\t\t? ${field.dataType}.values[pigeonMap['${field.name}']$unwrapOperator as int]
+\t\t? ${field.type.baseName}.values[pigeonMap['${field.name}']$unwrapOperator as int]
\t\t: null''', leadingSpace: false, trailingNewline: false);
+ } else if (field.type.typeArguments != null) {
+ final String genericType =
+ _makeGenericTypeArguments(field.type, nullTag);
+ final String castCall = _makeGenericCastCall(field.type, nullTag);
+ indent.add(
+ '(pigeonMap[\'${field.name}\'] as $genericType$nullTag)$nullTag$castCall',
+ );
} else {
indent.add(
- 'pigeonMap[\'${field.name}\'] as ${_addGenericTypes(field.dataType, nullTag)}',
+ 'pigeonMap[\'${field.name}\'] as ${_addGenericTypesNullable(field, nullTag)}',
);
}
indent.addln(index == klass.fields.length - 1 ? ';' : '');
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 560bcd1..d5bc4d1 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -154,28 +154,28 @@
final bool isBuiltin;
}
-/// Calculates the [HostDatatype] for the provided [Field]. It will check the
+/// Calculates the [HostDatatype] for the provided [NamedType]. It will check the
/// field against the `classes` to check if it is a builtin type.
/// `builtinResolver` will return the host datatype for the Dart datatype for
/// builtin types. `customResolver` can modify the datatype of custom types.
-HostDatatype getHostDatatype(Field field, List<Class> classes, List<Enum> enums,
- String? Function(String) builtinResolver,
+HostDatatype getHostDatatype(NamedType field, List<Class> classes,
+ List<Enum> enums, String? Function(NamedType) builtinResolver,
{String Function(String)? customResolver}) {
- final String? datatype = builtinResolver(field.dataType);
+ final String? datatype = builtinResolver(field);
if (datatype == null) {
- if (classes.map((Class x) => x.name).contains(field.dataType)) {
+ if (classes.map((Class x) => x.name).contains(field.type.baseName)) {
final String customName = customResolver != null
- ? customResolver(field.dataType)
- : field.dataType;
+ ? customResolver(field.type.baseName)
+ : field.type.baseName;
return HostDatatype(datatype: customName, isBuiltin: false);
- } else if (enums.map((Enum x) => x.name).contains(field.dataType)) {
+ } else if (enums.map((Enum x) => x.name).contains(field.type.baseName)) {
final String customName = customResolver != null
- ? customResolver(field.dataType)
- : field.dataType;
+ ? customResolver(field.type.baseName)
+ : field.type.baseName;
return HostDatatype(datatype: customName, isBuiltin: false);
} else {
throw Exception(
- 'unrecognized datatype for field:"${field.name}" of type:"${field.dataType}"');
+ 'unrecognized datatype for field:"${field.name}" of type:"${field.type.baseName}"');
}
} else {
return HostDatatype(datatype: datatype, isBuiltin: true);
@@ -285,8 +285,10 @@
Iterable<EnumeratedClass> getCodecClasses(Api api) sync* {
final Set<String> names = <String>{};
for (final Method method in api.methods) {
- names.add(method.returnType);
- names.add(method.argType);
+ names.add(method.returnType.baseName);
+ if (method.arguments.isNotEmpty) {
+ names.add(method.arguments[0].type.baseName);
+ }
}
final List<String> sortedNames = names
.where((String element) =>
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index f0fc6bd..7f2cd6d 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -5,19 +5,6 @@
import 'ast.dart';
import 'generator_tools.dart';
-const Map<String, String> _javaTypeForDartTypeMap = <String, String>{
- 'bool': 'Boolean',
- 'int': 'Long',
- 'String': 'String',
- 'double': 'Double',
- 'Uint8List': 'byte[]',
- 'Int32List': 'int[]',
- 'Int64List': 'long[]',
- 'Float64List': 'double[]',
- 'List': 'List<Object>',
- 'Map': 'Map<Object, Object>',
-};
-
/// Options that control how Java code will be generated.
class JavaOptions {
/// Creates a [JavaOptions] object
@@ -113,22 +100,6 @@
});
}
-/// This performs Dart to Java type conversions. If performs a passthrough of
-/// the input if it can't be converted.
-// TODO(gaaclarke): Remove this method and unify it with `_javaTypeForDartType`.
-String _javaTypeForDartTypePassthrough(String type) {
- const Map<String, String> map = <String, String>{
- 'int': 'Integer',
- 'bool': 'Boolean',
- 'double': 'Double',
- 'Int32List': 'int[]',
- 'Uint8List': 'byte[]',
- 'Int64List': 'long[]',
- 'Float64List': 'double[]',
- };
- return map[type] ?? type;
-}
-
void _writeHostApi(Indent indent, Api api) {
assert(api.location == ApiLocation.host);
@@ -137,17 +108,18 @@
indent.write('public interface ${api.name} ');
indent.scoped('{', '}', () {
for (final Method method in api.methods) {
- final String argType = _javaTypeForDartTypePassthrough(method.argType);
final String returnType = method.isAsynchronous
? 'void'
- : _javaTypeForDartTypePassthrough(method.returnType);
+ : _javaTypeForDartType(method.returnType);
final List<String> argSignature = <String>[];
- if (method.argType != 'void') {
+ if (method.arguments.isNotEmpty) {
+ final String argType = _javaTypeForDartType(method.arguments[0].type);
argSignature.add('$argType arg');
}
if (method.isAsynchronous) {
- final String returnType =
- method.returnType == 'void' ? 'Void' : method.returnType;
+ final String returnType = method.returnType.baseName == 'void'
+ ? 'Void'
+ : _javaTypeForDartType(method.returnType);
argSignature.add('Result<$returnType> result');
}
indent.writeln('$returnType ${method.name}(${argSignature.join(', ')});');
@@ -180,15 +152,14 @@
indent.scoped('{', '} else {', () {
indent.write('channel.setMessageHandler((message, reply) -> ');
indent.scoped('{', '});', () {
- final String argType =
- _javaTypeForDartTypePassthrough(method.argType);
- final String returnType =
- _javaTypeForDartTypePassthrough(method.returnType);
+ final String returnType = _javaTypeForDartType(method.returnType);
indent.writeln('Map<String, Object> wrapped = new HashMap<>();');
indent.write('try ');
indent.scoped('{', '}', () {
final List<String> methodArgument = <String>[];
- if (argType != 'void') {
+ if (method.arguments.isNotEmpty) {
+ final String argType =
+ _javaTypeForDartType(method.arguments[0].type);
indent.writeln('@SuppressWarnings("ConstantConditions")');
indent.writeln('$argType input = ($argType)message;');
indent.write('if (input == null) ');
@@ -200,7 +171,7 @@
}
if (method.isAsynchronous) {
final String resultValue =
- method.returnType == 'void' ? 'null' : 'result';
+ method.returnType.baseName == 'void' ? 'null' : 'result';
methodArgument.add(
'result -> { '
'wrapped.put("${Keys.result}", $resultValue); '
@@ -212,7 +183,7 @@
'api.${method.name}(${methodArgument.join(', ')})';
if (method.isAsynchronous) {
indent.writeln('$call;');
- } else if (method.returnType == 'void') {
+ } else if (method.returnType.baseName == 'void') {
indent.writeln('$call;');
indent.writeln('wrapped.put("${Keys.result}", null);');
} else {
@@ -265,15 +236,15 @@
''');
for (final Method func in api.methods) {
final String channelName = makeChannelName(api, func);
- final String returnType = func.returnType == 'void'
+ final String returnType = func.returnType.baseName == 'void'
? 'Void'
- : _javaTypeForDartTypePassthrough(func.returnType);
- final String argType = _javaTypeForDartTypePassthrough(func.argType);
+ : _javaTypeForDartType(func.returnType);
String sendArgument;
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.write('public void ${func.name}(Reply<$returnType> callback) ');
sendArgument = 'null';
} else {
+ final String argType = _javaTypeForDartType(func.arguments[0].type);
indent.write(
'public void ${func.name}($argType argInput, Reply<$returnType> callback) ');
sendArgument = 'argInput';
@@ -288,7 +259,7 @@
indent.dec();
indent.write('channel.send($sendArgument, channelReply -> ');
indent.scoped('{', '});', () {
- if (func.returnType == 'void') {
+ if (func.returnType.baseName == 'void') {
indent.writeln('callback.reply(null);');
} else {
indent.writeln('@SuppressWarnings("ConstantConditions")');
@@ -301,30 +272,61 @@
});
}
-String _makeGetter(Field field) {
+String _makeGetter(NamedType field) {
final String uppercased =
field.name.substring(0, 1).toUpperCase() + field.name.substring(1);
return 'get$uppercased';
}
-String _makeSetter(Field field) {
+String _makeSetter(NamedType field) {
final String uppercased =
field.name.substring(0, 1).toUpperCase() + field.name.substring(1);
return 'set$uppercased';
}
-String? _javaTypeForDartType(String datatype) {
- return _javaTypeForDartTypeMap[datatype];
+/// Converts a [List] of [TypeDeclaration]s to a comma separated [String] to be
+/// used in Java code.
+String _flattenTypeArguments(List<TypeDeclaration> args) {
+ return args.map<String>(_javaTypeForDartType).join(', ');
+}
+
+String? _javaTypeForBuiltinDartType(TypeDeclaration type) {
+ const Map<String, String> javaTypeForDartTypeMap = <String, String>{
+ 'bool': 'Boolean',
+ 'int': 'Long',
+ 'String': 'String',
+ 'double': 'Double',
+ 'Uint8List': 'byte[]',
+ 'Int32List': 'int[]',
+ 'Int64List': 'long[]',
+ 'Float64List': 'double[]',
+ 'Map': 'Map<Object, Object>',
+ };
+ if (javaTypeForDartTypeMap.containsKey(type.baseName)) {
+ return javaTypeForDartTypeMap[type.baseName];
+ } else if (type.baseName == 'List') {
+ if (type.typeArguments == null) {
+ return 'List<Object>';
+ } else {
+ return 'List<${_flattenTypeArguments(type.typeArguments!)}>';
+ }
+ } else {
+ return null;
+ }
+}
+
+String _javaTypeForDartType(TypeDeclaration type) {
+ return _javaTypeForBuiltinDartType(type) ?? type.baseName;
}
String _castObject(
- Field field, List<Class> classes, List<Enum> enums, String varName) {
- final HostDatatype hostDatatype =
- getHostDatatype(field, classes, enums, _javaTypeForDartType);
- if (field.dataType == 'int') {
+ NamedType field, List<Class> classes, List<Enum> enums, String varName) {
+ final HostDatatype hostDatatype = getHostDatatype(field, classes, enums,
+ (NamedType x) => _javaTypeForBuiltinDartType(x.type));
+ if (field.type.baseName == 'int') {
return '($varName == null) ? null : (($varName instanceof Integer) ? (Integer)$varName : (${hostDatatype.datatype})$varName)';
} else if (!hostDatatype.isBuiltin &&
- classes.map((Class x) => x.name).contains(field.dataType)) {
+ classes.map((Class x) => x.name).contains(field.type.baseName)) {
return '${hostDatatype.datatype}.fromMap((Map)$varName)';
} else {
return '(${hostDatatype.datatype})$varName';
@@ -394,9 +396,9 @@
'/** Generated class from Pigeon that represents data sent in messages. */');
indent.write('public static class ${klass.name} ');
indent.scoped('{', '}', () {
- for (final Field field in klass.fields) {
- final HostDatatype hostDatatype = getHostDatatype(
- field, root.classes, root.enums, _javaTypeForDartType);
+ for (final NamedType field in klass.fields) {
+ final HostDatatype hostDatatype = getHostDatatype(field, root.classes,
+ root.enums, (NamedType x) => _javaTypeForBuiltinDartType(x.type));
indent.writeln('private ${hostDatatype.datatype} ${field.name};');
indent.writeln(
'public ${hostDatatype.datatype} ${_makeGetter(field)}() { return ${field.name}; }');
@@ -407,16 +409,19 @@
indent.write('Map<String, Object> toMap() ');
indent.scoped('{', '}', () {
indent.writeln('Map<String, Object> toMapResult = new HashMap<>();');
- for (final Field field in klass.fields) {
+ for (final NamedType field in klass.fields) {
final HostDatatype hostDatatype = getHostDatatype(
- field, root.classes, root.enums, _javaTypeForDartType);
+ field,
+ root.classes,
+ root.enums,
+ (NamedType x) => _javaTypeForBuiltinDartType(x.type));
String toWriteValue = '';
if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.dataType)) {
+ rootClassNameSet.contains(field.type.baseName)) {
final String fieldName = field.name;
toWriteValue = '($fieldName == null) ? null : $fieldName.toMap()';
} else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.dataType)) {
+ rootEnumNameSet.contains(field.type.baseName)) {
toWriteValue = '${field.name}.index';
} else {
toWriteValue = field.name;
@@ -428,11 +433,11 @@
indent.write('static ${klass.name} fromMap(Map<String, Object> map) ');
indent.scoped('{', '}', () {
indent.writeln('${klass.name} fromMapResult = new ${klass.name}();');
- for (final Field field in klass.fields) {
+ for (final NamedType field in klass.fields) {
indent.writeln('Object ${field.name} = map.get("${field.name}");');
- if (rootEnumNameSet.contains(field.dataType)) {
+ if (rootEnumNameSet.contains(field.type.baseName)) {
indent.writeln(
- 'fromMapResult.${field.name} = ${field.dataType}.values()[(int)${field.name}];');
+ 'fromMapResult.${field.name} = ${field.type.baseName}.values()[(int)${field.name}];');
} else {
indent.writeln(
'fromMapResult.${field.name} = ${_castObject(field, root.classes, root.enums, field.name)};');
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 77612a4..3641a22 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -79,37 +79,48 @@
'Map': 'NSDictionary',
};
-const Map<String, String> _propertyTypeForDartTypeMap = <String, String>{
- 'String': 'copy',
- 'bool': 'strong',
- 'int': 'strong',
- 'double': 'strong',
- 'Uint8List': 'strong',
- 'Int32List': 'strong',
- 'Int64List': 'strong',
- 'Float64List': 'strong',
- 'List': 'strong',
- 'Map': 'strong',
-};
+String _flattenTypeArguments(String? classPrefix, List<TypeDeclaration> args) {
+ final String result = args
+ .map<String>(
+ (TypeDeclaration e) => '${_objcTypeForDartType(classPrefix, e)} *')
+ .join(', ');
+ return result;
+}
-String? _objcTypePtrForPrimitiveDartType(String type) {
- return _objcTypeForDartTypeMap.containsKey(type)
- ? '${_objcTypeForDartTypeMap[type]} *'
+String? _objcTypePtrForPrimitiveDartType(String? classPrefix, NamedType field) {
+ return _objcTypeForDartTypeMap.containsKey(field.type.baseName)
+ ? '${_objcTypeForDartType(classPrefix, field.type)} *'
: null;
}
/// Returns the objc type for a dart [type], prepending the [classPrefix] for
/// generated classes. For example:
/// _objcTypeForDartType(null, 'int') => 'NSNumber'.
-String _objcTypeForDartType(String? classPrefix, String type) {
- final String? builtinObjcType = _objcTypeForDartTypeMap[type];
- return builtinObjcType ?? _className(classPrefix, type);
+String _objcTypeForDartType(String? classPrefix, TypeDeclaration field) {
+ return _objcTypeForDartTypeMap.containsKey(field.baseName)
+ ? field.typeArguments == null
+ ? _objcTypeForDartTypeMap[field.baseName]!
+ : '${_objcTypeForDartTypeMap[field.baseName]}<${_flattenTypeArguments(classPrefix, field.typeArguments!)}>'
+ : _className(classPrefix, field.baseName);
}
-String _propertyTypeForDartType(String type) {
- final String? result = _propertyTypeForDartTypeMap[type];
+String _propertyTypeForDartType(NamedType field) {
+ const Map<String, String> propertyTypeForDartTypeMap = <String, String>{
+ 'String': 'copy',
+ 'bool': 'strong',
+ 'int': 'strong',
+ 'double': 'strong',
+ 'Uint8List': 'strong',
+ 'Int32List': 'strong',
+ 'Int64List': 'strong',
+ 'Float64List': 'strong',
+ 'List': 'strong',
+ 'Map': 'strong',
+ };
+
+ final String? result = propertyTypeForDartTypeMap[field.type.baseName];
if (result == null) {
- return 'assign';
+ return 'strong';
} else {
return result;
}
@@ -120,19 +131,17 @@
final List<String> enumNames = enums.map((Enum x) => x.name).toList();
for (final Class klass in classes) {
indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject');
- for (final Field field in klass.fields) {
- final HostDatatype hostDatatype = getHostDatatype(
- field, classes, enums, _objcTypePtrForPrimitiveDartType,
- customResolver: enumNames.contains(field.dataType)
+ for (final NamedType field in klass.fields) {
+ final HostDatatype hostDatatype = getHostDatatype(field, classes, enums,
+ (NamedType x) => _objcTypePtrForPrimitiveDartType(prefix, x),
+ customResolver: enumNames.contains(field.type.baseName)
? (String x) => _className(prefix, x)
: (String x) => '${_className(prefix, x)} *');
late final String propertyType;
- if (hostDatatype.isBuiltin) {
- propertyType = _propertyTypeForDartType(field.dataType);
- } else if (enumNames.contains(field.dataType)) {
+ if (enumNames.contains(field.type.baseName)) {
propertyType = 'assign';
} else {
- propertyType = 'strong';
+ propertyType = _propertyTypeForDartType(field);
}
final String nullability =
hostDatatype.datatype.contains('*') ? ', nullable' : '';
@@ -230,36 +239,37 @@
final String returnTypeName =
_objcTypeForDartType(options.prefix, func.returnType);
if (func.isAsynchronous) {
- if (func.returnType == 'void') {
- if (func.argType == 'void') {
+ if (func.returnType.baseName == 'void') {
+ if (func.arguments.isEmpty) {
indent.writeln(
'-(void)${func.name}:(void(^)(FlutterError *_Nullable))completion;');
} else {
final String argType =
- _objcTypeForDartType(options.prefix, func.argType);
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.writeln(
'-(void)${func.name}:(nullable $argType *)input completion:(void(^)(FlutterError *_Nullable))completion;');
}
} else {
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.writeln(
'-(void)${func.name}:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;');
} else {
final String argType =
- _objcTypeForDartType(options.prefix, func.argType);
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.writeln(
'-(void)${func.name}:(nullable $argType *)input completion:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;');
}
}
} else {
- final String returnType =
- func.returnType == 'void' ? 'void' : 'nullable $returnTypeName *';
- if (func.argType == 'void') {
+ final String returnType = func.returnType.baseName == 'void'
+ ? 'void'
+ : 'nullable $returnTypeName *';
+ if (func.arguments.isEmpty) {
indent.writeln(
'-($returnType)${func.name}:(FlutterError *_Nullable *_Nonnull)error;');
} else {
final String argType =
- _objcTypeForDartType(options.prefix, func.argType);
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.writeln(
'-($returnType)${func.name}:($argType*)input error:(FlutterError *_Nullable *_Nonnull)error;');
}
@@ -280,11 +290,13 @@
for (final Method func in api.methods) {
final String returnType =
_objcTypeForDartType(options.prefix, func.returnType);
- final String callbackType = _callbackForType(func.returnType, returnType);
- if (func.argType == 'void') {
+ final String callbackType =
+ _callbackForType(func.returnType.baseName, returnType);
+ if (func.arguments.isEmpty) {
indent.writeln('- (void)${func.name}:($callbackType)completion;');
} else {
- final String argType = _objcTypeForDartType(options.prefix, func.argType);
+ final String argType =
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.writeln(
'- (void)${func.name}:($argType*)input completion:($callbackType)completion;');
}
@@ -351,9 +363,9 @@
}
String _dictGetter(
- List<String> classNames, String dict, Field field, String? prefix) {
- if (classNames.contains(field.dataType)) {
- String className = field.dataType;
+ List<String> classNames, String dict, NamedType field, String? prefix) {
+ if (classNames.contains(field.type.baseName)) {
+ String className = field.type.baseName;
if (prefix != null) {
className = '$prefix$className';
}
@@ -364,10 +376,10 @@
}
String _dictValue(
- List<String> classNames, List<String> enumNames, Field field) {
- if (classNames.contains(field.dataType)) {
+ List<String> classNames, List<String> enumNames, NamedType field) {
+ if (classNames.contains(field.type.baseName)) {
return '(self.${field.name} ? [self.${field.name} toMap] : [NSNull null])';
- } else if (enumNames.contains(field.dataType)) {
+ } else if (enumNames.contains(field.type.baseName)) {
return '@(self.${field.name})';
} else {
return '(self.${field.name} ? self.${field.name} : [NSNull null])';
@@ -403,18 +415,18 @@
final String returnType =
_objcTypeForDartType(options.prefix, func.returnType);
String syncCall;
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
syncCall = '[api ${func.name}:&error]';
} else {
final String argType =
- _objcTypeForDartType(options.prefix, func.argType);
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.writeln('$argType *input = message;');
syncCall = '[api ${func.name}:input error:&error]';
}
if (func.isAsynchronous) {
- if (func.returnType == 'void') {
+ if (func.returnType.baseName == 'void') {
const String callback = 'callback(wrapResult(nil, error));';
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.writeScoped(
'[api ${func.name}:^(FlutterError *_Nullable error) {',
'}];', () {
@@ -429,7 +441,7 @@
}
} else {
const String callback = 'callback(wrapResult(output, error));';
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.writeScoped(
'[api ${func.name}:^($returnType *_Nullable output, FlutterError *_Nullable error) {',
'}];', () {
@@ -445,7 +457,7 @@
}
} else {
indent.writeln('FlutterError *error;');
- if (func.returnType == 'void') {
+ if (func.returnType.baseName == 'void') {
indent.writeln('$syncCall;');
indent.writeln('callback(wrapResult(nil, error));');
} else {
@@ -487,14 +499,16 @@
for (final Method func in api.methods) {
final String returnType =
_objcTypeForDartType(options.prefix, func.returnType);
- final String callbackType = _callbackForType(func.returnType, returnType);
+ final String callbackType =
+ _callbackForType(func.returnType.baseName, returnType);
String sendArgument;
- if (func.argType == 'void') {
+ if (func.arguments.isEmpty) {
indent.write('- (void)${func.name}:($callbackType)completion ');
sendArgument = 'nil';
} else {
- final String argType = _objcTypeForDartType(options.prefix, func.argType);
+ final String argType =
+ _objcTypeForDartType(options.prefix, func.arguments[0].type);
indent.write(
'- (void)${func.name}:($argType*)input completion:($callbackType)completion ');
sendArgument = 'input';
@@ -512,7 +526,7 @@
indent.dec();
indent.write('[channel sendMessage:$sendArgument reply:^(id reply) ');
indent.scoped('{', '}];', () {
- if (func.returnType == 'void') {
+ if (func.returnType.baseName == 'void') {
indent.writeln('completion(nil);');
} else {
indent.writeln('$returnType * output = reply;');
@@ -580,8 +594,8 @@
indent.scoped('{', '}', () {
const String resultName = 'result';
indent.writeln('$className* $resultName = [[$className alloc] init];');
- for (final Field field in klass.fields) {
- if (enumNames.contains(field.dataType)) {
+ for (final NamedType field in klass.fields) {
+ if (enumNames.contains(field.type.baseName)) {
indent.writeln(
'$resultName.${field.name} = [${_dictGetter(classNames, 'dict', field, options.prefix)} integerValue];');
} else {
@@ -599,7 +613,7 @@
indent.write('-(NSDictionary*)toMap ');
indent.scoped('{', '}', () {
indent.write('return [NSDictionary dictionaryWithObjectsAndKeys:');
- for (final Field field in klass.fields) {
+ for (final NamedType field in klass.fields) {
indent.add(
_dictValue(classNames, enumNames, field) + ', @"${field.name}", ');
}
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index a685a3b..9026226 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -13,7 +13,8 @@
import 'package:analyzer/dart/analysis/results.dart' show ParsedUnitResult;
import 'package:analyzer/dart/analysis/session.dart' show AnalysisSession;
import 'package:analyzer/dart/ast/ast.dart' as dart_ast;
-import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit;
+import 'package:analyzer/dart/ast/syntactic_entity.dart'
+ as dart_ast_syntactic_entity;
import 'package:analyzer/dart/ast/visitor.dart' as dart_ast_visitor;
import 'package:analyzer/error/error.dart' show AnalysisError;
import 'package:args/args.dart';
@@ -382,19 +383,24 @@
root.classes.map((Class x) => x.name).toList();
final Iterable<String> customEnums = root.enums.map((Enum x) => x.name);
for (final Class klass in root.classes) {
- for (final Field field in klass.fields) {
- if (field.typeArguments != null) {
+ for (final NamedType field in klass.fields) {
+ if (field.type.typeArguments != null) {
+ for (final TypeDeclaration typeArgument in field.type.typeArguments!) {
+ if (!typeArgument.isNullable) {
+ result.add(Error(
+ message:
+ 'Generic type arguments must be nullable in field "${field.name}" in class "${klass.name}".',
+ lineNumber: _calculateLineNumberNullable(source, field.offset),
+ ));
+ }
+ }
+ }
+ if (!(validTypes.contains(field.type.baseName) ||
+ customClasses.contains(field.type.baseName) ||
+ customEnums.contains(field.type.baseName))) {
result.add(Error(
message:
- 'Unsupported datatype:"${field.dataType}" in class "${klass.name}". Generic fields aren\'t yet supported (https://github.com/flutter/flutter/issues/63468).',
- lineNumber: _calculateLineNumberNullable(source, field.offset),
- ));
- } else if (!(validTypes.contains(field.dataType) ||
- customClasses.contains(field.dataType) ||
- customEnums.contains(field.dataType))) {
- result.add(Error(
- message:
- 'Unsupported datatype:"${field.dataType}" in class "${klass.name}".',
+ 'Unsupported datatype:"${field.type.baseName}" in class "${klass.name}".',
lineNumber: _calculateLineNumberNullable(source, field.offset),
));
}
@@ -402,31 +408,38 @@
}
for (final Api api in root.apis) {
for (final Method method in api.methods) {
- if (method.isReturnNullable) {
+ if (method.returnType.isNullable) {
result.add(Error(
message:
- 'Nullable return types types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}"',
+ 'Nullable return types types aren\'t supported for Pigeon methods: "${method.arguments[0].type.baseName}" in API: "${api.name}" method: "${method.name}"',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
- if (method.isArgNullable) {
+ if (method.arguments.length > 1) {
result.add(Error(
message:
- 'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}"',
+ 'Multiple arguments aren\'t yet supported, in API: "${api.name}" method: "${method.name} (https://github.com/flutter/flutter/issues/86971)"',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
- if (customEnums.contains(method.argType)) {
+ if (method.arguments.isNotEmpty &&
+ customEnums.contains(method.arguments[0].type.baseName)) {
result.add(Error(
message:
- 'Enums aren\'t yet supported for primitive arguments: "${method.argType}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
+ 'Enums aren\'t yet supported for primitive arguments: "${method.arguments[0]}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
- if (customEnums.contains(method.returnType)) {
+ if (customEnums.contains(method.returnType.baseName)) {
result.add(Error(
message:
'Enums aren\'t yet supported for primitive return types: "${method.returnType}" in API: "${api.name}" method: "${method.name}" (https://github.com/flutter/flutter/issues/87307)',
+ ));
+ }
+ if (method.arguments.isNotEmpty && method.arguments[0].type.isNullable) {
+ result.add(Error(
+ message:
+ 'Nullable argument types aren\'t supported for Pigeon methods: "${method.arguments[0].type.baseName}" in API: "${api.name}" method: "${method.name}"',
lineNumber: _calculateLineNumberNullable(source, method.offset),
));
}
@@ -481,8 +494,10 @@
final Set<String> referencedTypes = <String>{};
for (final Api api in _apis) {
for (final Method method in api.methods) {
- referencedTypes.add(method.argType);
- referencedTypes.add(method.returnType);
+ if (method.arguments.isNotEmpty) {
+ referencedTypes.add(method.arguments[0].type.baseName);
+ }
+ referencedTypes.add(method.returnType.baseName);
}
}
@@ -491,12 +506,12 @@
final String next = classesToCheck.last;
classesToCheck.removeLast();
final Class aClass = _classes.firstWhere((Class x) => x.name == next,
- orElse: () => Class(name: '', fields: <Field>[]));
- for (final Field field in aClass.fields) {
- if (!referencedTypes.contains(field.dataType) &&
- !validTypes.contains(field.dataType)) {
- referencedTypes.add(field.dataType);
- classesToCheck.add(field.dataType);
+ orElse: () => Class(name: '', fields: <NamedType>[]));
+ for (final NamedType field in aClass.fields) {
+ if (!referencedTypes.contains(field.type.baseName) &&
+ !validTypes.contains(field.type.baseName)) {
+ referencedTypes.add(field.type.baseName);
+ classesToCheck.add(field.type.baseName);
}
}
}
@@ -625,37 +640,59 @@
);
}
} else {
- _currentClass = Class(name: node.name.name, fields: <Field>[]);
+ _currentClass = Class(name: node.name.name, fields: <NamedType>[]);
}
node.visitChildren(this);
return null;
}
+ NamedType formalParameterToField(dart_ast.FormalParameter parameter) {
+ final dart_ast.TypeName typeName = parameter.childEntities.firstWhere(
+ (dart_ast_syntactic_entity.SyntacticEntity e) =>
+ e is dart_ast.TypeName) as dart_ast.TypeName;
+ final String argTypeBaseName = typeName.name.name;
+ final bool isNullable = typeName.question != null;
+ final List<TypeDeclaration>? argTypeArguments =
+ typeAnnotationsToTypeArguments(typeName.typeArguments);
+ return NamedType(
+ type: TypeDeclaration(
+ baseName: argTypeBaseName,
+ isNullable: isNullable,
+ typeArguments: argTypeArguments),
+ name: parameter.identifier?.name ?? '',
+ offset: null);
+ }
+
+ static T? getFirstChildOfType<T>(dart_ast.AstNode entity) {
+ for (final dart_ast_syntactic_entity.SyntacticEntity child
+ in entity.childEntities) {
+ if (child is T) {
+ return child as T;
+ }
+ }
+ return null;
+ }
+
@override
Object? visitMethodDeclaration(dart_ast.MethodDeclaration node) {
final dart_ast.FormalParameterList parameters = node.parameters!;
- late String argType;
- bool isNullable = false;
- if (parameters.parameters.isEmpty) {
- argType = 'void';
- } else {
- final dart_ast.FormalParameter firstParameter =
- parameters.parameters.first;
- final dart_ast.TypeName typeName = firstParameter.childEntities
- // ignore: always_specify_types
- .firstWhere((e) => e is dart_ast.TypeName) as dart_ast.TypeName;
- argType = typeName.name.name;
- isNullable = typeName.question != null;
- }
+ final List<NamedType> arguments =
+ parameters.parameters.map(formalParameterToField).toList();
final bool isAsynchronous = _hasMetadata(node.metadata, 'async');
if (_currentApi != null) {
+ // Methods without named return types aren't supported.
+ final dart_ast.TypeAnnotation returnType = node.returnType!;
+ final dart_ast.SimpleIdentifier returnTypeIdentifier =
+ getFirstChildOfType<dart_ast.SimpleIdentifier>(returnType)!;
_currentApi!.methods.add(Method(
name: node.name.name,
- returnType: node.returnType.toString(),
- argType: argType,
- isReturnNullable: node.returnType!.question != null,
- isArgNullable: isNullable,
+ returnType: TypeDeclaration(
+ baseName: returnTypeIdentifier.name,
+ typeArguments: typeAnnotationsToTypeArguments(
+ (returnType as dart_ast.NamedType).typeArguments),
+ isNullable: returnType.question != null),
+ arguments: arguments,
isAsynchronous: isAsynchronous,
offset: node.offset));
} else if (_currentClass != null) {
@@ -679,6 +716,23 @@
return null;
}
+ List<TypeDeclaration>? typeAnnotationsToTypeArguments(
+ dart_ast.TypeArgumentList? typeArguments) {
+ List<TypeDeclaration>? result;
+ if (typeArguments != null) {
+ for (final Object x in typeArguments.childEntities) {
+ if (x is dart_ast.TypeName) {
+ result ??= <TypeDeclaration>[];
+ result.add(TypeDeclaration(
+ baseName: x.name.name,
+ isNullable: x.question != null,
+ typeArguments: typeAnnotationsToTypeArguments(x.typeArguments)));
+ }
+ }
+ }
+ return result;
+ }
+
@override
Object? visitFieldDeclaration(dart_ast.FieldDeclaration node) {
if (_currentClass != null) {
@@ -698,26 +752,13 @@
lineNumber: _calculateLineNumber(source, node.offset)));
} else {
final dart_ast.TypeArgumentList? typeArguments = type.typeArguments;
- _currentClass!.fields.add(Field(
- name: node.fields.variables[0].name.name,
- dataType: type.name.name,
- isNullable: type.question != null,
- // TODO(aaclarke): This probably has to be recursive at some point.
- // ignore: prefer_null_aware_operators
- typeArguments: typeArguments == null
- ? null
- : typeArguments.arguments
- .map((dart_ast.TypeAnnotation e) => Field(
- name: '',
- dataType: (e.childEntities.first
- as dart_ast.SimpleIdentifier)
- .name,
- isNullable: e.question != null,
- offset: e.offset,
- ))
- .toList(),
- offset: node.offset,
- ));
+ _currentClass!.fields.add(NamedType(
+ type: TypeDeclaration(
+ baseName: type.name.name,
+ isNullable: type.question != null,
+ typeArguments: typeAnnotationsToTypeArguments(typeArguments)),
+ name: node.fields.variables[0].name.name,
+ offset: node.offset));
}
} else {
_errors.add(Error(
@@ -783,7 +824,7 @@
final ParsedUnitResult result =
session.getParsedUnit2(path) as ParsedUnitResult;
if (result.errors.isEmpty) {
- final CompilationUnit unit = result.unit;
+ final dart_ast.CompilationUnit unit = result.unit;
unit.accept(rootBuilder);
} else {
for (final AnalysisError error in result.errors) {
diff --git a/packages/pigeon/pigeons/all_datatypes.dart b/packages/pigeon/pigeons/all_datatypes.dart
index 0a8c2c2..138b4ca 100644
--- a/packages/pigeon/pigeons/all_datatypes.dart
+++ b/packages/pigeon/pigeons/all_datatypes.dart
@@ -17,6 +17,8 @@
List? aList;
// ignore: always_specify_types
Map? aMap;
+ List<List<bool?>?>? nestedList;
+ Map<String?, String?>? mapWithAnnotations;
}
@HostApi()
diff --git a/packages/pigeon/pigeons/primitive.dart b/packages/pigeon/pigeons/primitive.dart
index 2b45353..cc8eea5 100644
--- a/packages/pigeon/pigeons/primitive.dart
+++ b/packages/pigeon/pigeons/primitive.dart
@@ -15,6 +15,8 @@
// ignore: always_specify_types
List aList(List value);
Int32List anInt32List(Int32List value);
+ List<bool?> aBoolList(List<bool?> value);
+ Map<String?, int?> aStringIntMap(Map<String?, int?> value);
}
@FlutterApi()
@@ -28,4 +30,6 @@
// ignore: always_specify_types
List aList(List value);
Int32List anInt32List(Int32List value);
+ List<bool?> aBoolList(List<bool?> value);
+ Map<String?, int?> aStringIntMap(Map<String?, int?> value);
}
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java
index 36ce51c..d557d21 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java
@@ -37,10 +37,10 @@
PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger);
boolean[] didCall = {false};
api.anInt(
- 1,
- (Integer result) -> {
+ 1L,
+ (Long result) -> {
didCall[0] = true;
- assertEquals(result, (Integer) 1);
+ assertEquals(result, (Long) 1L);
});
assertTrue(didCall[0]);
}
@@ -94,7 +94,7 @@
boolean[] didCall = {false};
api.aMap(
Collections.singletonMap("hello", 1),
- (Map result) -> {
+ (Map<Object, Object> result) -> {
didCall[0] = true;
assertEquals(result, Collections.singletonMap("hello", 1));
});
@@ -108,7 +108,7 @@
boolean[] didCall = {false};
api.aList(
Collections.singletonList("hello"),
- (List result) -> {
+ (List<Object> result) -> {
didCall[0] = true;
assertEquals(result, Collections.singletonList("hello"));
});
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
index 09db457..adf2c86 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
@@ -23,6 +23,8 @@
Float64List? aFloatArray;
List<Object?>? aList;
Map<Object?, Object?>? aMap;
+ List<List<bool?>?>? nestedList;
+ Map<String?, String?>? mapWithAnnotations;
Object encode() {
final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
@@ -36,6 +38,8 @@
pigeonMap['aFloatArray'] = aFloatArray;
pigeonMap['aList'] = aList;
pigeonMap['aMap'] = aMap;
+ pigeonMap['nestedList'] = nestedList;
+ pigeonMap['mapWithAnnotations'] = mapWithAnnotations;
return pigeonMap;
}
@@ -51,7 +55,12 @@
..a8ByteArray = pigeonMap['a8ByteArray'] as Int64List?
..aFloatArray = pigeonMap['aFloatArray'] as Float64List?
..aList = pigeonMap['aList'] as List<Object?>?
- ..aMap = pigeonMap['aMap'] as Map<Object?, Object?>?;
+ ..aMap = pigeonMap['aMap'] as Map<Object?, Object?>?
+ ..nestedList =
+ (pigeonMap['nestedList'] as List<Object?>?)?.cast<List<bool?>?>()
+ ..mapWithAnnotations =
+ (pigeonMap['mapWithAnnotations'] as Map<Object?, Object?>?)
+ ?.cast<String?, String?>();
}
}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
new file mode 100644
index 0000000..ae36e10
--- /dev/null
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
@@ -0,0 +1,411 @@
+// Copyright 2013 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.
+//
+// Autogenerated from Pigeon (v0.3.0), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
+// @dart = 2.12
+import 'dart:async';
+import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
+
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
+import 'package:flutter/services.dart';
+
+class _PrimitiveHostApiCodec extends StandardMessageCodec {
+ const _PrimitiveHostApiCodec();
+}
+
+class PrimitiveHostApi {
+ /// Constructor for [PrimitiveHostApi]. The [binaryMessenger] named argument is
+ /// available for dependency injection. If it is left null, the default
+ /// BinaryMessenger will be used which routes to the host platform.
+ PrimitiveHostApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec<Object?> codec = _PrimitiveHostApiCodec();
+
+ Future<int> anInt(int arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.anInt', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as int?)!;
+ }
+ }
+
+ Future<bool> aBool(bool arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aBool', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as bool?)!;
+ }
+ }
+
+ Future<String> aString(String arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aString', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as String?)!;
+ }
+ }
+
+ Future<double> aDouble(double arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aDouble', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as double?)!;
+ }
+ }
+
+ Future<Map<Object?, Object?>> aMap(Map<Object?, Object?> arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aMap', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as Map<Object?, Object?>?)!;
+ }
+ }
+
+ Future<List<Object?>> aList(List<Object?> arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aList', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as List<Object?>?)!;
+ }
+ }
+
+ Future<Int32List> anInt32List(Int32List arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.anInt32List', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as Int32List?)!;
+ }
+ }
+
+ Future<List<bool?>> aBoolList(List<bool?> arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aBoolList', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as List<Object?>?)!.cast<bool?>();
+ }
+ }
+
+ Future<Map<String?, int?>> aStringIntMap(Map<String?, int?> arg) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aStringIntMap', codec,
+ binaryMessenger: _binaryMessenger);
+ final Map<Object?, Object?>? replyMap =
+ await channel.send(arg) as Map<Object?, Object?>?;
+ if (replyMap == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ details: null,
+ );
+ } else if (replyMap['error'] != null) {
+ final Map<Object?, Object?> error =
+ (replyMap['error'] as Map<Object?, Object?>?)!;
+ throw PlatformException(
+ code: (error['code'] as String?)!,
+ message: error['message'] as String?,
+ details: error['details'],
+ );
+ } else {
+ return (replyMap['result'] as Map<Object?, Object?>?)!
+ .cast<String?, int?>();
+ }
+ }
+}
+
+class _PrimitiveFlutterApiCodec extends StandardMessageCodec {
+ const _PrimitiveFlutterApiCodec();
+}
+
+abstract class PrimitiveFlutterApi {
+ static const MessageCodec<Object?> codec = _PrimitiveFlutterApiCodec();
+
+ int anInt(int arg);
+ bool aBool(bool arg);
+ String aString(String arg);
+ double aDouble(double arg);
+ Map<Object?, Object?> aMap(Map<Object?, Object?> arg);
+ List<Object?> aList(List<Object?> arg);
+ Int32List anInt32List(Int32List arg);
+ List<bool?> aBoolList(List<bool?> arg);
+ Map<String?, int?> aStringIntMap(Map<String?, int?> arg);
+ static void setup(PrimitiveFlutterApi? api) {
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.anInt', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.anInt was null. Expected int.');
+ final int input = (message as int?)!;
+ final int output = api.anInt(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aBool', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aBool was null. Expected bool.');
+ final bool input = (message as bool?)!;
+ final bool output = api.aBool(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aString', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aString was null. Expected String.');
+ final String input = (message as String?)!;
+ final String output = api.aString(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aDouble', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aDouble was null. Expected double.');
+ final double input = (message as double?)!;
+ final double output = api.aDouble(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aMap', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aMap was null. Expected Map<Object?, Object?>.');
+ final Map<Object?, Object?> input =
+ (message as Map<Object?, Object?>?)!;
+ final Map<Object?, Object?> output = api.aMap(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aList', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aList was null. Expected List<Object?>.');
+ final List<Object?> input = (message as List<Object?>?)!;
+ final List<Object?> output = api.aList(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.anInt32List', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.anInt32List was null. Expected Int32List.');
+ final Int32List input = (message as Int32List?)!;
+ final Int32List output = api.anInt32List(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aBoolList', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aBoolList was null. Expected List<bool?>.');
+ final List<bool?> input = (message as List<bool?>?)!;
+ final List<bool?> output = api.aBoolList(input);
+ return output;
+ });
+ }
+ }
+ {
+ const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.PrimitiveFlutterApi.aStringIntMap', codec);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.PrimitiveFlutterApi.aStringIntMap was null. Expected Map<String?, int?>.');
+ final Map<String?, int?> input = (message as Map<String?, int?>?)!;
+ final Map<String?, int?> output = api.aStringIntMap(input);
+ return output;
+ });
+ }
+ }
+ }
+}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
index 7061fb8..4cad76d 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
@@ -35,6 +35,8 @@
expect(result.aFloatArray, isNull);
expect(result.aList, isNull);
expect(result.aMap, isNull);
+ expect(result.nestedList, isNull);
+ expect(result.mapWithAnnotations, isNull);
});
test('with values', () async {
@@ -50,6 +52,10 @@
Float64List.fromList(<double>[1.0, 2.5, 3.0, 4.25]);
everything.aList = <int>[1, 2, 3, 4];
everything.aMap = <String, int>{'hello': 1234};
+ everything.aList = <List<bool?>>[
+ <bool?>[true]
+ ];
+ everything.mapWithAnnotations = <String?, String?>{'hello': 'world'};
final BinaryMessenger mockMessenger = MockBinaryMessenger();
when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any))
.thenAnswer((Invocation realInvocation) async {
@@ -70,5 +76,7 @@
expect(result.aFloatArray, everything.aFloatArray);
expect(result.aList, everything.aList);
expect(result.aMap, everything.aMap);
+ expect(result.aList, everything.aList);
+ expect(result.mapWithAnnotations, everything.mapWithAnnotations);
});
}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.dart
new file mode 100644
index 0000000..3c20172
--- /dev/null
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.dart
@@ -0,0 +1,61 @@
+// Copyright 2013 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:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter_unit_tests/primitive.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'primitive_test.mocks.dart';
+
+@GenerateMocks(<Type>[BinaryMessenger])
+void main() {
+ test('test anInt', () async {
+ final BinaryMessenger mockMessenger = MockBinaryMessenger();
+ when(mockMessenger.send('dev.flutter.pigeon.PrimitiveHostApi.anInt', any))
+ .thenAnswer((Invocation realInvocation) async {
+ const MessageCodec<Object?> codec = PrimitiveHostApi.codec;
+ final Object? input =
+ codec.decodeMessage(realInvocation.positionalArguments[1]);
+ return codec.encodeMessage(<String, Object>{'result': input!});
+ });
+ final PrimitiveHostApi api =
+ PrimitiveHostApi(binaryMessenger: mockMessenger);
+ final int result = await api.anInt(1);
+ expect(result, 1);
+ });
+
+ test('test List<bool>', () async {
+ final BinaryMessenger mockMessenger = MockBinaryMessenger();
+ when(mockMessenger.send(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aBoolList', any))
+ .thenAnswer((Invocation realInvocation) async {
+ const MessageCodec<Object?> codec = PrimitiveHostApi.codec;
+ final Object? input =
+ codec.decodeMessage(realInvocation.positionalArguments[1]);
+ return codec.encodeMessage(<String, Object>{'result': input!});
+ });
+ final PrimitiveHostApi api =
+ PrimitiveHostApi(binaryMessenger: mockMessenger);
+ final List<bool?> result = await api.aBoolList(<bool?>[true]);
+ expect(result[0], true);
+ });
+
+ test('test Map<String?, int?>', () async {
+ final BinaryMessenger mockMessenger = MockBinaryMessenger();
+ when(mockMessenger.send(
+ 'dev.flutter.pigeon.PrimitiveHostApi.aStringIntMap', any))
+ .thenAnswer((Invocation realInvocation) async {
+ const MessageCodec<Object?> codec = PrimitiveHostApi.codec;
+ final Object? input =
+ codec.decodeMessage(realInvocation.positionalArguments[1]);
+ return codec.encodeMessage(<String, Object>{'result': input!});
+ });
+ final PrimitiveHostApi api =
+ PrimitiveHostApi(binaryMessenger: mockMessenger);
+ final Map<String?, int?> result =
+ await api.aStringIntMap(<String?, int?>{'hello': 1});
+ expect(result['hello'], 1);
+ });
+}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart
new file mode 100644
index 0000000..814ea41
--- /dev/null
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/primitive_test.mocks.dart
@@ -0,0 +1,44 @@
+// Mocks generated by Mockito 5.0.7 from annotations
+// in flutter_unit_tests/test/null_safe_test.dart.
+// Do not manually edit this file.
+
+import 'dart:async' as _i3;
+import 'dart:typed_data' as _i4;
+import 'dart:ui' as _i5;
+
+import 'package:flutter/src/services/binary_messenger.dart' as _i2;
+import 'package:mockito/mockito.dart' as _i1;
+
+// ignore_for_file: comment_references
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: always_specify_types
+// Added manually; several methods have moved to
+// flutter_test/lib/src/deprecated.dart on master, but that hasn't reached
+// stable yet.
+// ignore_for_file: override_on_non_overriding_member
+
+/// A class which mocks [BinaryMessenger].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockBinaryMessenger extends _i1.Mock implements _i2.BinaryMessenger {
+ MockBinaryMessenger() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i3.Future<void> handlePlatformMessage(String? channel, _i4.ByteData? data,
+ _i5.PlatformMessageResponseCallback? callback) =>
+ (super.noSuchMethod(
+ Invocation.method(#handlePlatformMessage, [channel, data, callback]),
+ returnValue: Future<void>.value(null),
+ returnValueForMissingStub:
+ Future<dynamic>.value()) as _i3.Future<void>);
+ @override
+ _i3.Future<_i4.ByteData?>? send(String? channel, _i4.ByteData? message) =>
+ (super.noSuchMethod(Invocation.method(#send, [channel, message]))
+ as _i3.Future<_i4.ByteData?>?);
+ @override
+ void setMessageHandler(String? channel, _i2.MessageHandler? handler) => super
+ .noSuchMethod(Invocation.method(#setMessageHandler, [channel, handler]),
+ returnValueForMissingStub: null);
+}
diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh
index 2a12484..93d3a1a 100755
--- a/packages/pigeon/run_tests.sh
+++ b/packages/pigeon/run_tests.sh
@@ -204,17 +204,22 @@
}
run_flutter_unittests() {
+ local flutter_tests="platform_tests/flutter_null_safe_unit_tests"
pushd $PWD
$run_pigeon \
--input pigeons/flutter_unittests.dart \
- --dart_out platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
+ --dart_out "$flutter_tests/lib/null_safe_pigeon.dart"
$run_pigeon \
--input pigeons/all_datatypes.dart \
- --dart_out platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
- cd platform_tests/flutter_null_safe_unit_tests
+ --dart_out "$flutter_tests/lib/all_datatypes.dart"
+ $run_pigeon \
+ --input pigeons/primitive.dart \
+ --dart_out "$flutter_tests/lib/primitive.dart"
+ cd "$flutter_tests"
flutter pub get
flutter test test/null_safe_test.dart
flutter test test/all_datatypes_test.dart
+ flutter test test/primitive_test.dart
popd
}
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index 04b3488..bcbb332 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -11,12 +11,12 @@
test('gen one class', () {
final Class klass = Class(
name: 'Foobar',
- fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'dataType1',
- isNullable: true,
- ),
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'dataType1', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null),
],
);
final Root root = Root(
@@ -57,55 +57,60 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: 'input',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
generateDart(const DartOptions(isNullSafe: false), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
- expect(code, matches('Output.*doSomething.*Input'));
+ expect(code, contains('Future<Output> doSomething(Input input)'));
});
test('nested class', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
Class(
name: 'Input',
- fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
],
),
Class(
name: 'Nested',
- fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Input',
- isNullable: true,
- )
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
],
)
], enums: <Enum>[]);
@@ -131,26 +136,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: 'input',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -158,6 +168,7 @@
final String code = sink.toString();
expect(code, contains('abstract class Api'));
expect(code, contains('static void setup(Api'));
+ expect(code, contains('Output doSomething(Input input)'));
});
test('host void', () {
@@ -165,19 +176,24 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -192,19 +208,24 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -222,19 +243,18 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -249,19 +269,26 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'EnumClass',
- isArgNullable: false,
- returnType: 'EnumClass',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'EnumClass',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'EnumClass', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'EnumClass', fields: <Field>[
- Field(
- name: 'enum1',
- dataType: 'Enum',
- isNullable: true,
- )
+ Class(name: 'EnumClass', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Enum', isNullable: true, typeArguments: null),
+ name: 'enum1',
+ offset: null)
]),
], enums: <Enum>[
Enum(
@@ -286,19 +313,26 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'EnumClass',
- isArgNullable: false,
- returnType: 'EnumClass',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'EnumClass',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'EnumClass', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'EnumClass', fields: <Field>[
- Field(
- name: 'enum1',
- dataType: 'Enum',
- isNullable: true,
- )
+ Class(name: 'EnumClass', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Enum', isNullable: true, typeArguments: null),
+ name: 'enum1',
+ offset: null)
]),
], enums: <Enum>[
Enum(
@@ -325,19 +359,18 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -355,33 +388,48 @@
methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType:
+ TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
),
Method(
name: 'voidReturner',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer mainCodeSink = StringBuffer();
@@ -408,12 +456,12 @@
test('opt out of nndb', () {
final Class klass = Class(
name: 'Foobar',
- fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'dataType1',
- isNullable: true,
- ),
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'dataType1', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null),
],
);
final Root root = Root(
@@ -432,26 +480,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -468,26 +521,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -503,26 +561,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -537,19 +600,18 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -574,4 +636,182 @@
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
+
+ test('generics', () {
+ final Class klass = Class(
+ name: 'Foobar',
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'field1',
+ offset: null),
+ ],
+ );
+ final Root root = Root(
+ apis: <Api>[],
+ classes: <Class>[klass],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('class Foobar'));
+ expect(code, contains(' List<int?>? field1;'));
+ });
+
+ test('map generics', () {
+ final Class klass = Class(
+ name: 'Foobar',
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Map',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'String', isNullable: true),
+ TypeDeclaration(baseName: 'int', isNullable: true),
+ ]),
+ name: 'field1',
+ offset: null),
+ ],
+ );
+ final Root root = Root(
+ apis: <Api>[],
+ classes: <Class>[klass],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('class Foobar'));
+ expect(code, contains(' Map<String?, int?>? field1;'));
+ });
+
+ test('host generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit(List<int?> arg'));
+ });
+
+ test('flutter generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit(List<int?> arg'));
+ });
+
+ test('host generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('Future<List<int?>> doit('));
+ expect(
+ code,
+ contains(
+ 'return (replyMap[\'result\'] as List<Object?>?)!.cast<int?>();'));
+ });
+
+ test('host generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateDart(const DartOptions(isNullSafe: true), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('List<int?> doit('));
+ expect(
+ code, contains('final List<int?> input = (message as List<int?>?)!'));
+ expect(code, contains('final List<int?> output = api.doit(input)'));
+ });
}
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index 8533fe3..f0710d3 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -10,12 +10,12 @@
test('gen one class', () {
final Class klass = Class(
name: 'Foobar',
- fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'int',
- isNullable: true,
- ),
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'int', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null),
],
);
final Root root = Root(
@@ -60,12 +60,12 @@
test('package', () {
final Class klass = Class(
name: 'Foobar',
- fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'int',
- isNullable: true,
- )
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'int', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
],
);
final Root root = Root(
@@ -87,18 +87,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: true,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -112,15 +125,47 @@
test('all the simple datatypes header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(name: 'aBool', dataType: 'bool', isNullable: true),
- Field(name: 'aInt', dataType: 'int', isNullable: true),
- Field(name: 'aDouble', dataType: 'double', isNullable: true),
- Field(name: 'aString', dataType: 'String', isNullable: true),
- Field(name: 'aUint8List', dataType: 'Uint8List', isNullable: true),
- Field(name: 'aInt32List', dataType: 'Int32List', isNullable: true),
- Field(name: 'aInt64List', dataType: 'Int64List', isNullable: true),
- Field(name: 'aFloat64List', dataType: 'Float64List', isNullable: true),
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'bool', isNullable: true, typeArguments: null),
+ name: 'aBool',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'int', isNullable: true, typeArguments: null),
+ name: 'aInt',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'double', isNullable: true, typeArguments: null),
+ name: 'aDouble',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'aString',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Uint8List', isNullable: true, typeArguments: null),
+ name: 'aUint8List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Int32List', isNullable: true, typeArguments: null),
+ name: 'aInt32List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Int64List', isNullable: true, typeArguments: null),
+ name: 'aInt64List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Float64List', isNullable: true, typeArguments: null),
+ name: 'aFloat64List',
+ offset: null),
]),
], enums: <Enum>[]);
@@ -143,18 +188,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -170,15 +228,24 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -194,15 +261,24 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -218,15 +294,18 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -242,15 +321,18 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: false,
)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -263,8 +345,12 @@
test('gen list', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(name: 'field1', dataType: 'List', isNullable: true)
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -277,8 +363,12 @@
test('gen map', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(name: 'field1', dataType: 'Map', isNullable: true)
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Map', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -292,22 +382,22 @@
test('gen nested', () {
final Class klass = Class(
name: 'Outer',
- fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Nested',
- isNullable: true,
- )
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Nested', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
],
);
final Class nestedClass = Class(
name: 'Nested',
- fields: <Field>[
- Field(
- name: 'data',
- dataType: 'int',
- isNullable: true,
- )
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'int', isNullable: true, typeArguments: null),
+ name: 'data',
+ offset: null)
],
);
final Root root = Root(
@@ -333,18 +423,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: 'arg',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -367,18 +470,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: false, typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true,
)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(name: 'input', dataType: 'String', isNullable: true)
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(name: 'output', dataType: 'String', isNullable: true)
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -399,12 +515,12 @@
);
final Class klass = Class(
name: 'EnumClass',
- fields: <Field>[
- Field(
- name: 'enum1',
- dataType: 'Enum1',
- isNullable: true,
- ),
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Enum1', isNullable: true, typeArguments: null),
+ name: 'enum1',
+ offset: null),
],
);
final Root root = Root(
@@ -442,4 +558,144 @@
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
+
+ test('generics', () {
+ final Class klass = Class(
+ name: 'Foobar',
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'field1',
+ offset: null),
+ ],
+ );
+ final Root root = Root(
+ apis: <Api>[],
+ classes: <Class>[klass],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+ generateJava(javaOptions, root, sink);
+ final String code = sink.toString();
+ expect(code, contains('class Foobar'));
+ expect(code, contains('List<Long> field1;'));
+ });
+
+ test('host generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+ generateJava(javaOptions, root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit(List<Long> arg'));
+ });
+
+ test('flutter generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+ generateJava(javaOptions, root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit(List<Long> arg'));
+ });
+
+ test('host generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+ generateJava(javaOptions, root, sink);
+ final String code = sink.toString();
+ expect(code, contains('List<Long> doit('));
+ expect(code, contains('List<Long> output ='));
+ });
+
+ test('flutter generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+ generateJava(javaOptions, root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit(Reply<List<Long>> callback)'));
+ expect(code, contains('List<Long> output ='));
+ });
}
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index 8acf439..a8321d1 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -9,12 +9,12 @@
void main() {
test('gen one class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -26,12 +26,12 @@
test('gen one class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -83,17 +83,17 @@
classes: <Class>[
Class(
name: 'Foobar',
- fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'String',
- isNullable: true,
- ),
- Field(
- name: 'enum1',
- dataType: 'Enum1',
- isNullable: true,
- ),
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Enum1', isNullable: true, typeArguments: null),
+ name: 'enum1',
+ offset: null),
],
),
],
@@ -115,29 +115,72 @@
expect(code, contains('result.enum1 = [dict[@"enum1"] integerValue];'));
});
+ test('gen one class header with enum', () {
+ final Root root = Root(
+ apis: <Api>[],
+ classes: <Class>[
+ Class(
+ name: 'Foobar',
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Enum1', isNullable: true, typeArguments: null),
+ name: 'enum1',
+ offset: null),
+ ],
+ ),
+ ],
+ enums: <Enum>[
+ Enum(
+ name: 'Enum1',
+ members: <String>[
+ 'one',
+ 'two',
+ ],
+ )
+ ],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('@property(nonatomic, assign) Enum1 enum1'));
+ });
+
test('gen one api header', () {
final Root root = Root(apis: <Api>[
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -155,24 +198,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -186,47 +236,47 @@
test('all the simple datatypes header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'aBool',
- dataType: 'bool',
- isNullable: true,
- ),
- Field(
- name: 'aInt',
- dataType: 'int',
- isNullable: true,
- ),
- Field(
- name: 'aDouble',
- dataType: 'double',
- isNullable: true,
- ),
- Field(
- name: 'aString',
- dataType: 'String',
- isNullable: true,
- ),
- Field(
- name: 'aUint8List',
- dataType: 'Uint8List',
- isNullable: true,
- ),
- Field(
- name: 'aInt32List',
- dataType: 'Int32List',
- isNullable: true,
- ),
- Field(
- name: 'aInt64List',
- dataType: 'Int64List',
- isNullable: true,
- ),
- Field(
- name: 'aFloat64List',
- dataType: 'Float64List',
- isNullable: true,
- ),
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'bool', isNullable: true, typeArguments: null),
+ name: 'aBool',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'int', isNullable: true, typeArguments: null),
+ name: 'aInt',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'double', isNullable: true, typeArguments: null),
+ name: 'aDouble',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'aString',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Uint8List', isNullable: true, typeArguments: null),
+ name: 'aUint8List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Int32List', isNullable: true, typeArguments: null),
+ name: 'aInt32List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Int64List', isNullable: true, typeArguments: null),
+ name: 'aInt64List',
+ offset: null),
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Float64List', isNullable: true, typeArguments: null),
+ name: 'aFloat64List',
+ offset: null),
]),
], enums: <Enum>[]);
@@ -251,12 +301,12 @@
test('bool source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'aBool',
- dataType: 'bool',
- isNullable: true,
- ),
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'bool', isNullable: true, typeArguments: null),
+ name: 'aBool',
+ offset: null),
]),
], enums: <Enum>[]);
@@ -269,19 +319,19 @@
test('nested class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Nested', fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Input',
- isNullable: true,
- )
+ Class(name: 'Nested', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -293,19 +343,19 @@
test('nested class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Nested', fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Input',
- isNullable: true,
- )
+ Class(name: 'Nested', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -317,12 +367,12 @@
test('prefix class header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -333,12 +383,12 @@
test('prefix class source', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -352,24 +402,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Nested')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Nested', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Nested', fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Input',
- isNullable: true,
- )
+ Class(name: 'Nested', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -385,24 +442,31 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Nested')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Nested', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Nested', fields: <Field>[
- Field(
- name: 'nested',
- dataType: 'Input',
- isNullable: true,
- )
+ Class(name: 'Nested', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input', isNullable: true, typeArguments: null),
+ name: 'nested',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -418,24 +482,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -454,24 +525,31 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -486,17 +564,24 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -511,17 +596,24 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -538,17 +630,24 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -563,17 +662,24 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void')
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -589,17 +695,16 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -614,17 +719,16 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -639,17 +743,16 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -667,17 +770,16 @@
Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output')
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false))
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -693,12 +795,12 @@
test('gen list', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'List',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -710,12 +812,12 @@
test('gen map', () {
final Root root = Root(apis: <Api>[], classes: <Class>[
- Class(name: 'Foobar', fields: <Field>[
- Field(
- name: 'field1',
- dataType: 'Map',
- isNullable: true,
- )
+ Class(name: 'Foobar', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Map', isNullable: true, typeArguments: null),
+ name: 'field1',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -730,25 +832,32 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -766,25 +875,32 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -802,18 +918,17 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -831,9 +946,8 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[], enums: <Enum>[]);
@@ -852,25 +966,32 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -888,25 +1009,32 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'Input',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'Input',
+ isNullable: false,
+ typeArguments: null),
+ name: '',
+ offset: null)
+ ],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Input', fields: <Field>[
- Field(
- name: 'input',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Input', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'input',
+ offset: null)
]),
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -924,9 +1052,8 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'void',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[], enums: <Enum>[]);
@@ -943,18 +1070,17 @@
Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
Method(
name: 'doSomething',
- argType: 'void',
- isArgNullable: false,
- returnType: 'Output',
+ arguments: <NamedType>[],
+ returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
isAsynchronous: true)
])
], classes: <Class>[
- Class(name: 'Output', fields: <Field>[
- Field(
- name: 'output',
- dataType: 'String',
- isNullable: true,
- )
+ Class(name: 'Output', fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'String', isNullable: true, typeArguments: null),
+ name: 'output',
+ offset: null)
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
@@ -1000,4 +1126,215 @@
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
+
+ test('field generics', () {
+ final Class klass = Class(
+ name: 'Foobar',
+ fields: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'field1',
+ offset: null),
+ ],
+ );
+ final Root root = Root(
+ apis: <Api>[],
+ classes: <Class>[klass],
+ enums: <Enum>[],
+ );
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('NSArray<NSNumber *> * field1'));
+ });
+
+ test('host generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(NSArray<NSNumber *>*)input'));
+ }
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcSource(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('NSArray<NSNumber *> *input = message'));
+ }
+ });
+
+ test('flutter generics argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(NSArray<NSNumber *>*)input'));
+ }
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcSource(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(NSArray<NSNumber *>*)input'));
+ }
+ });
+
+ test('host nested generic argument', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(baseName: 'void', isNullable: false),
+ arguments: <NamedType>[
+ NamedType(
+ type: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(
+ baseName: 'List',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(
+ baseName: 'bool', isNullable: true)
+ ]),
+ ]),
+ name: 'arg',
+ offset: null)
+ ])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(NSArray<NSArray<NSNumber *> *>*)input'));
+ }
+ });
+
+ test('host generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('-(nullable NSArray<NSNumber *> *)doit:'));
+ }
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcSource(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('NSArray<NSNumber *> *output ='));
+ }
+ });
+
+ test('host generics return', () {
+ final Root root = Root(
+ apis: <Api>[
+ Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+ Method(
+ name: 'doit',
+ returnType: TypeDeclaration(
+ baseName: 'List',
+ isNullable: false,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'int', isNullable: true)
+ ]),
+ arguments: <NamedType>[])
+ ])
+ ],
+ classes: <Class>[],
+ enums: <Enum>[],
+ );
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcHeader(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(void(^)(NSArray<NSNumber *>*'));
+ }
+ {
+ final StringBuffer sink = StringBuffer();
+ generateObjcSource(
+ const ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink);
+ final String code = sink.toString();
+ expect(code, contains('doit:(void(^)(NSArray<NSNumber *>*'));
+ }
+ });
}
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 278e0d4..dd07556 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -98,8 +98,10 @@
expect(root.apis[0].name, equals('Api1'));
expect(root.apis[0].methods.length, equals(1));
expect(root.apis[0].methods[0].name, equals('doit'));
- expect(root.apis[0].methods[0].argType, equals('Input1'));
- expect(root.apis[0].methods[0].returnType, equals('Output1'));
+ expect(root.apis[0].methods[0].arguments[0].name, equals('input'));
+ expect(
+ root.apis[0].methods[0].arguments[0].type.baseName, equals('Input1'));
+ expect(root.apis[0].methods[0].returnType.baseName, equals('Output1'));
Class? input;
Class? output;
@@ -115,13 +117,13 @@
expect(input?.fields.length, equals(1));
expect(input?.fields[0].name, equals('input'));
- expect(input?.fields[0].dataType, equals('String'));
- expect(input?.fields[0].isNullable, isTrue);
+ expect(input?.fields[0].type.baseName, equals('String'));
+ expect(input?.fields[0].type.isNullable, isTrue);
expect(output?.fields.length, equals(1));
expect(output?.fields[0].name, equals('output'));
- expect(output?.fields[0].dataType, equals('String'));
- expect(output?.fields[0].isNullable, isTrue);
+ expect(output?.fields[0].type.baseName, equals('String'));
+ expect(output?.fields[0].type.isNullable, isTrue);
});
test('invalid datatype', () {
@@ -162,8 +164,8 @@
expect(results.root.classes.length, equals(1));
expect(results.root.classes[0].name, equals('ClassWithEnum'));
expect(results.root.classes[0].fields.length, equals(1));
- expect(results.root.classes[0].fields[0].dataType, equals('Enum1'));
- expect(results.root.classes[0].fields[0].isNullable, isTrue);
+ expect(results.root.classes[0].fields[0].type.baseName, equals('Enum1'));
+ expect(results.root.classes[0].fields[0].type.isNullable, isTrue);
expect(results.root.classes[0].fields[0].name, equals('enum1'));
});
@@ -208,8 +210,8 @@
final Class nested =
results.root.classes.firstWhere((Class x) => x.name == 'Nested');
expect(nested.fields.length, equals(1));
- expect(nested.fields[0].dataType, equals('Input1'));
- expect(nested.fields[0].isNullable, isTrue);
+ expect(nested.fields[0].type.baseName, equals('Input1'));
+ expect(nested.fields[0].type.isNullable, isTrue);
});
test('flutter api', () {
@@ -246,7 +248,7 @@
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'));
+ expect(results.root.apis[0].methods[0].returnType.baseName, equals('void'));
});
test('void arg host api', () {
@@ -265,8 +267,9 @@
expect(results.root.apis.length, equals(1));
expect(results.root.apis[0].methods.length, equals(1));
expect(results.root.apis[0].name, equals('VoidArgApi'));
- expect(results.root.apis[0].methods[0].returnType, equals('Output1'));
- expect(results.root.apis[0].methods[0].argType, equals('void'));
+ expect(
+ results.root.apis[0].methods[0].returnType.baseName, equals('Output1'));
+ expect(results.root.apis[0].methods[0].arguments.isEmpty, isTrue);
});
test('mockDartClass', () {
@@ -414,7 +417,7 @@
final Class foo =
results.root.classes.firstWhere((Class aClass) => aClass.name == 'Foo');
expect(foo.fields.length, 1);
- expect(foo.fields[0].dataType, 'Bar');
+ expect(foo.fields[0].type.baseName, 'Bar');
});
test('test compilation error', () {
@@ -542,23 +545,6 @@
expect(parseResults.errors.length, 0);
});
- test('error with generics', () {
- const String code = '''
-class WithTemplate {
- List<int>? list;
-}
-
-@HostApi()
-abstract class WithTemplateApi {
- void doit(WithTemplate withTemplate);
-}
-''';
- final ParseResults parseResult = _parseSource(code);
- expect(parseResult.errors.length, equals(1));
- expect(parseResult.errors[0].message, contains('Generic fields'));
- expect(parseResult.errors[0].lineNumber, isNotNull);
- });
-
test('error with static field', () {
const String code = '''
class WithStaticField {
@@ -577,6 +563,62 @@
expect(parseResult.errors[0].lineNumber, isNotNull);
});
+ test('parse generics', () {
+ const String code = '''
+class Foo {
+ List<int?>? list;
+}
+
+@HostApi()
+abstract class Api {
+ void doit(Foo foo);
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ expect(parseResult.errors.length, equals(0));
+ final NamedType field = parseResult.root.classes[0].fields[0];
+ expect(field.type.typeArguments!.length, 1);
+ expect(field.type.typeArguments![0].baseName, 'int');
+ });
+
+ test('parse recursive generics', () {
+ const String code = '''
+class Foo {
+ List<List<int?>?>? list;
+}
+
+@HostApi()
+abstract class Api {
+ void doit(Foo foo);
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ expect(parseResult.errors.length, equals(0));
+ final NamedType field = parseResult.root.classes[0].fields[0];
+ expect(field.type.typeArguments!.length, 1);
+ expect(field.type.typeArguments![0].baseName, 'List');
+ expect(field.type.typeArguments![0].typeArguments![0].baseName, 'int');
+ });
+
+ test('error nonnull type argument', () {
+ const String code = '''
+class Foo {
+ List<int> list;
+}
+
+@HostApi()
+abstract class Api {
+ void doit(Foo foo);
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ expect(parseResult.errors.length, equals(1));
+ expect(parseResult.errors[0].message,
+ contains('Generic type arguments must be nullable'));
+ expect(parseResult.errors[0].message, contains('"list"'));
+ expect(parseResult.errors[0].lineNumber, 2);
+ });
+
test('enums argument', () {
// TODO(gaaclarke): Make this not an error: https://github.com/flutter/flutter/issues/87307
const String code = '''
@@ -614,4 +656,83 @@
expect(parseResult.errors.length, equals(1));
expect(parseResult.errors[0].message, contains('Enums'));
});
+
+ test('return type generics', () {
+ const String code = '''
+@HostApi()
+abstract class Api {
+ List<double?> doit();
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ expect(parseResult.root.apis[0].methods[0].returnType.baseName, 'List');
+ expect(
+ parseResult
+ .root.apis[0].methods[0].returnType.typeArguments![0].baseName,
+ 'double');
+ expect(
+ parseResult
+ .root.apis[0].methods[0].returnType.typeArguments![0].isNullable,
+ isTrue);
+ });
+
+ test('argument generics', () {
+ const String code = '''
+@HostApi()
+abstract class Api {
+ void doit(List<double?> value);
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ expect(
+ parseResult.root.apis[0].methods[0].arguments[0].type.baseName, 'List');
+ expect(
+ parseResult.root.apis[0].methods[0].arguments[0].type.typeArguments![0]
+ .baseName,
+ 'double');
+ expect(
+ parseResult.root.apis[0].methods[0].arguments[0].type.typeArguments![0]
+ .isNullable,
+ isTrue);
+ });
+
+ test('map generics', () {
+ const String code = '''
+class Foo {
+ Map<String?, int?> map;
+}
+
+@HostApi()
+abstract class Api {
+ void doit(Foo foo);
+}
+''';
+ final ParseResults parseResult = _parseSource(code);
+ final NamedType field = parseResult.root.classes[0].fields[0];
+ expect(field.type.typeArguments!.length, 2);
+ expect(field.type.typeArguments![0].baseName, 'String');
+ expect(field.type.typeArguments![1].baseName, 'int');
+ });
+
+ test('two arguments', () {
+ const String code = '''
+class Input {
+ String? input;
+}
+
+@HostApi()
+abstract class Api {
+ void method(Input input1, Input input2);
+}
+''';
+ final ParseResults results = _parseSource(code);
+ expect(results.errors.length, 1);
+ expect(results.errors[0].lineNumber, 7);
+ expect(results.errors[0].message, contains('Multiple arguments'));
+ // TODO(gaaclarke): Make this not an error, https://github.com/flutter/flutter/issues/86971.
+ // expect(results.root.apis.length, 1);
+ // expect(results.root.apis[0].methods.length, equals(1));
+ // expect(results.root.apis[0].methods[0].name, equals('method'));
+ // expect(results.root.apis[0].methods[0].arguments.length, 2);
+ });
}