[pigeon] Adds StructuredGenerator class and subclasses (#3037)
* Rename generator class to Adapter
* create new generator class and dart subclass
* cpp and dart test gen
* added files
* Adds Generator class to all generators
* adds swift
* Updates tests to use new Adapter naming scheme
* Dart generate methods
* convert all generate functions to use new method
* chagngelog
* remove Generator class fields
* move paths to options
* remove dartTestOptions
* Moves write header to generator class method
* Updates tests to use new generator class
* source -> header
* correct options
* header -> source
* header -> prefix, source -> header
* remove headers from generateTestDart
* changelog
* Nits and combines source and header generators
* renames Adapter to GeneratorAdapter
* Update version number for breaking changes
* nits
* more personal nits
* update tests to match new merged generators
* cleaner header methods
* Fixes dart header bug
* add gen files for clarity
* better field naming
* better field naming
* removed unneeded dart test generator
* Add filetype to generator
* Adds filetype as field to generatorAdapters
* merge
* analyze
* add import method
* re-remove DartTestGenerator
* Moves imports to new method
* adds writeEnum method to generator class
* nits
* assert
* objc enum
* fix code order issues
* add writeDataClass method
* remove writeMainClass from java
* java + kotlin
* remove dead code
* swift
* fix dart test error
* cpp + objc
* objc + cpp
* Move all migrated methods into class
* Creates writeHeader method on Generator classes
* private unique methods and reorder
* changelog
* changelog
* changelog
* changelog
* prologue
* gen
* dart
* java
* kotlin
* swift
* cpp
* objc
* remove unneeded java method
* analyze
* single file gens
* objc
* Cpp
* analyze
* vocab
* vocab
* typo
* less comma
* wrap write methods
* move code from writeGeneralUtilities
* update changelog
* adds open and close namespace and fixes nits
* makes writeEnum optional
* remove unneeded sink
* remove unneeded namespace from guardname
* [indent]
* clean up
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 1c47219..2bf8a72 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 6.0.0
+
+* Creates StructuredGenerator class and implements it on all platforms.
+
## 5.0.1
* [c++] Fixes undefined behavior in `@async` methods.
diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart
index ae5f912..105f6bd 100644
--- a/packages/pigeon/lib/cpp_generator.dart
+++ b/packages/pigeon/lib/cpp_generator.dart
@@ -72,105 +72,348 @@
/// Class that manages all Cpp code generation.
class CppGenerator extends Generator<OutputFileOptions<CppOptions>> {
- /// Instantiates a Cpp Generator.
- CppGenerator();
+ /// Constructor.
+ const CppGenerator();
- /// Generates Cpp files with specified [OutputFileOptions<CppOptions>]
+ /// Generates C++ file of type specified in [generatorOptions]
@override
- void generate(OutputFileOptions<CppOptions> languageOptions, Root root,
+ void generate(OutputFileOptions<CppOptions> generatorOptions, Root root,
StringSink sink) {
- final FileType fileType = languageOptions.fileType;
- assert(fileType == FileType.header || fileType == FileType.source);
- if (fileType == FileType.header) {
- generateCppHeader(languageOptions.languageOptions, root, sink);
- } else {
- generateCppSource(languageOptions.languageOptions, root, sink);
+ assert(generatorOptions.fileType == FileType.header ||
+ generatorOptions.fileType == FileType.source);
+ if (generatorOptions.fileType == FileType.header) {
+ const CppHeaderGenerator()
+ .generate(generatorOptions.languageOptions, root, sink);
+ } else if (generatorOptions.fileType == FileType.source) {
+ const CppSourceGenerator()
+ .generate(generatorOptions.languageOptions, root, sink);
}
}
}
-String _getCodecSerializerName(Api api) => '${api.name}CodecSerializer';
+/// Writes C++ header (.h) file to sink.
+class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
+ /// Constructor.
+ const CppHeaderGenerator();
-const String _pointerPrefix = 'pointer';
-const String _encodablePrefix = 'encodable';
+ @override
+ void writeFilePrologue(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('$_commentPrefix $generatedCodeWarning');
+ indent.writeln('$_commentPrefix $seeAlsoWarning');
+ indent.addln('');
+ }
-void _writeCodecHeader(Indent indent, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final String codeSerializerName = _getCodecSerializerName(api);
- indent.write('class $codeSerializerName : public $_defaultCodecSerializer ');
- indent.scoped('{', '};', () {
- indent.scoped(' public:', '', () {
- indent.writeln('');
- indent.format('''
+ @override
+ void writeFileImports(CppOptions generatorOptions, Root root, Indent indent) {
+ final String guardName = _getGuardName(generatorOptions.headerIncludePath);
+ indent.writeln('#ifndef $guardName');
+ indent.writeln('#define $guardName');
+
+ _writeSystemHeaderIncludeBlock(indent, <String>[
+ 'flutter/basic_message_channel.h',
+ 'flutter/binary_messenger.h',
+ 'flutter/encodable_value.h',
+ 'flutter/standard_message_codec.h',
+ ]);
+ indent.addln('');
+ _writeSystemHeaderIncludeBlock(indent, <String>[
+ 'map',
+ 'string',
+ 'optional',
+ ]);
+ indent.addln('');
+ if (generatorOptions.namespace != null) {
+ indent.writeln('namespace ${generatorOptions.namespace} {');
+ }
+ indent.addln('');
+ if (generatorOptions.namespace?.endsWith('_pigeontest') ?? false) {
+ final String testFixtureClass =
+ '${_pascalCaseFromSnakeCase(generatorOptions.namespace!.replaceAll('_pigeontest', ''))}Test';
+ indent.writeln('class $testFixtureClass;');
+ }
+ indent.addln('');
+ indent.writeln('$_commentPrefix Generated class from Pigeon.');
+ }
+
+ @override
+ void writeEnum(
+ CppOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+ indent.write('enum class ${anEnum.name} ');
+ indent.scoped('{', '};', () {
+ enumerate(anEnum.members, (int index, final EnumMember member) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ indent.writeln(
+ '${member.name} = $index${index == anEnum.members.length - 1 ? '' : ','}');
+ });
+ });
+ }
+
+ @override
+ void writeGeneralUtilities(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ _writeErrorOr(indent, friends: root.apis.map((Api api) => api.name));
+ }
+
+ @override
+ void writeDataClass(
+ CppOptions generatorOptions, Root root, Indent indent, Class klass) {
+ // When generating for a Pigeon unit test, add a test fixture friend class to
+ // allow unit testing private methods, since testing serialization via public
+ // methods is essentially an end-to-end test.
+ String? testFixtureClass;
+ if (generatorOptions.namespace?.endsWith('_pigeontest') ?? false) {
+ testFixtureClass =
+ '${_pascalCaseFromSnakeCase(generatorOptions.namespace!.replaceAll('_pigeontest', ''))}Test';
+ }
+ indent.addln('');
+
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents data sent in messages.'
+ ];
+
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('class ${klass.name} ');
+ indent.scoped('{', '};', () {
+ indent.scoped(' public:', '', () {
+ indent.writeln('${klass.name}();');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+ final HostDatatype baseDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ indent.writeln(
+ '${_getterReturnType(baseDatatype)} ${_makeGetterName(field)}() const;');
+ indent.writeln(
+ 'void ${_makeSetterName(field)}(${_unownedArgumentType(baseDatatype)} value_arg);');
+ if (field.type.isNullable) {
+ // Add a second setter that takes the non-nullable version of the
+ // argument for convenience, since setting literal values with the
+ // pointer version is non-trivial.
+ final HostDatatype nonNullType = _nonNullableType(baseDatatype);
+ indent.writeln(
+ 'void ${_makeSetterName(field)}(${_unownedArgumentType(nonNullType)} value_arg);');
+ }
+ indent.addln('');
+ }
+ });
+
+ indent.scoped(' private:', '', () {
+ indent.writeln('${klass.name}(const flutter::EncodableList& list);');
+ indent.writeln('flutter::EncodableList ToEncodableList() const;');
+ for (final Class friend in root.classes) {
+ if (friend != klass &&
+ friend.fields.any(
+ (NamedType element) => element.type.baseName == klass.name)) {
+ indent.writeln('friend class ${friend.name};');
+ }
+ }
+ for (final Api api in root.apis) {
+ // TODO(gaaclarke): Find a way to be more precise with our
+ // friendships.
+ indent.writeln('friend class ${api.name};');
+ indent.writeln('friend class ${_getCodecSerializerName(api)};');
+ }
+ if (testFixtureClass != null) {
+ indent.writeln('friend class $testFixtureClass;');
+ }
+
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ indent.writeln(
+ '${_valueType(hostDatatype)} ${_makeInstanceVariableName(field)};');
+ }
+ });
+ }, nestCount: 0);
+ indent.writeln('');
+ }
+
+ @override
+ void writeFlutterApi(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(generatorOptions, root, indent, api);
+ }
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents Flutter messages that can be called from C++.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+ indent.write('class ${api.name} ');
+ indent.scoped('{', '};', () {
+ indent.scoped(' private:', '', () {
+ indent.writeln('flutter::BinaryMessenger* binary_messenger_;');
+ });
+ indent.scoped(' public:', '', () {
+ indent
+ .write('${api.name}(flutter::BinaryMessenger* binary_messenger);');
+ indent.writeln('');
+ indent
+ .writeln('static const flutter::StandardMessageCodec& GetCodec();');
+ for (final Method func in api.methods) {
+ final String returnType = func.returnType.isVoid
+ ? 'void'
+ : _nullSafeCppTypeForDartType(func.returnType);
+ final String callback = 'std::function<void($returnType)>&& callback';
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+ if (func.arguments.isEmpty) {
+ indent.writeln('void ${func.name}($callback);');
+ } else {
+ final Iterable<String> argTypes = func.arguments
+ .map((NamedType e) => _nullSafeCppTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgumentName);
+ final String argsSignature =
+ map2(argTypes, argNames, (String x, String y) => '$x $y')
+ .join(', ');
+ indent.writeln('void ${func.name}($argsSignature, $callback);');
+ }
+ }
+ });
+ }, nestCount: 0);
+ indent.writeln('');
+ }
+
+ @override
+ void writeHostApi(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.host);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(generatorOptions, root, indent, api);
+ }
+ const List<String> generatedMessages = <String>[
+ ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+ indent.write('class ${api.name} ');
+ indent.scoped('{', '};', () {
+ indent.scoped(' public:', '', () {
+ indent.writeln('${api.name}(const ${api.name}&) = delete;');
+ indent.writeln('${api.name}& operator=(const ${api.name}&) = delete;');
+ indent.writeln('virtual ~${api.name}() { };');
+ for (final Method method in api.methods) {
+ final HostDatatype returnType = getHostDatatype(
+ method.returnType,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ final String returnTypeName = _apiReturnType(returnType);
+
+ final List<String> argSignature = <String>[];
+ if (method.arguments.isNotEmpty) {
+ final Iterable<String> argTypes =
+ method.arguments.map((NamedType arg) {
+ final HostDatatype hostType = getFieldHostDatatype(
+ arg,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ return _hostApiArgumentType(hostType);
+ });
+ final Iterable<String> argNames =
+ method.arguments.map((NamedType e) => _makeVariableName(e));
+ argSignature.addAll(
+ map2(argTypes, argNames, (String argType, String argName) {
+ return '$argType $argName';
+ }));
+ }
+
+ addDocumentationComments(
+ indent, method.documentationComments, _docCommentSpec);
+
+ if (method.isAsynchronous) {
+ argSignature
+ .add('std::function<void($returnTypeName reply)> result');
+ indent.writeln(
+ 'virtual void ${_makeMethodName(method)}(${argSignature.join(', ')}) = 0;');
+ } else {
+ indent.writeln(
+ 'virtual $returnTypeName ${_makeMethodName(method)}(${argSignature.join(', ')}) = 0;');
+ }
+ }
+ indent.addln('');
+ indent.writeln('$_commentPrefix The codec used by ${api.name}.');
+ indent
+ .writeln('static const flutter::StandardMessageCodec& GetCodec();');
+ indent.writeln(
+ '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
+ indent.writeln(
+ 'static void SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api);');
+ indent.writeln(
+ 'static flutter::EncodableValue WrapError(std::string_view error_message);');
+ indent.writeln(
+ 'static flutter::EncodableValue WrapError(const FlutterError& error);');
+ });
+ indent.scoped(' protected:', '', () {
+ indent.writeln('${api.name}() = default;');
+ });
+ }, nestCount: 0);
+ }
+
+ void _writeCodec(
+ CppOptions generatorOptions, Root root, Indent indent, Api api) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final String codeSerializerName = _getCodecSerializerName(api);
+ indent
+ .write('class $codeSerializerName : public $_defaultCodecSerializer ');
+ indent.scoped('{', '};', () {
+ indent.scoped(' public:', '', () {
+ indent.writeln('');
+ indent.format('''
inline static $codeSerializerName& GetInstance() {
\tstatic $codeSerializerName sInstance;
\treturn sInstance;
}
''');
- indent.writeln('$codeSerializerName();');
- });
- indent.writeScoped(' public:', '', () {
- indent.writeln(
- 'void WriteValue(const flutter::EncodableValue& value, flutter::ByteStreamWriter* stream) const override;');
- });
- indent.writeScoped(' protected:', '', () {
- indent.writeln(
- 'flutter::EncodableValue ReadValueOfType(uint8_t type, flutter::ByteStreamReader* stream) const override;');
- });
- }, nestCount: 0);
-}
-
-void _writeCodecSource(Indent indent, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final String codeSerializerName = _getCodecSerializerName(api);
- indent.writeln('$codeSerializerName::$codeSerializerName() {}');
- indent.write(
- 'flutter::EncodableValue $codeSerializerName::ReadValueOfType(uint8_t type, flutter::ByteStreamReader* stream) const ');
- indent.scoped('{', '}', () {
- indent.write('switch (type) ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
- indent.write('case ${customClass.enumeration}:');
- indent.writeScoped('', '', () {
- indent.writeln(
- 'return flutter::CustomEncodableValue(${customClass.name}(std::get<flutter::EncodableList>(ReadValue(stream))));');
- });
- }
- indent.write('default:');
- indent.writeScoped('', '', () {
+ indent.writeln('$codeSerializerName();');
+ });
+ indent.writeScoped(' public:', '', () {
indent.writeln(
- 'return $_defaultCodecSerializer::ReadValueOfType(type, stream);');
- }, addTrailingNewline: false);
- });
- });
- indent.writeln('');
- indent.write(
- 'void $codeSerializerName::WriteValue(const flutter::EncodableValue& value, flutter::ByteStreamWriter* stream) const ');
- indent.writeScoped('{', '}', () {
- indent.write(
- 'if (const flutter::CustomEncodableValue* custom_value = std::get_if<flutter::CustomEncodableValue>(&value)) ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
- indent
- .write('if (custom_value->type() == typeid(${customClass.name})) ');
- indent.scoped('{', '}', () {
- indent.writeln('stream->WriteByte(${customClass.enumeration});');
- indent.writeln(
- 'WriteValue(flutter::EncodableValue(std::any_cast<${customClass.name}>(*custom_value).ToEncodableList()), stream);');
- indent.writeln('return;');
- });
- }
- });
- indent.writeln('$_defaultCodecSerializer::WriteValue(value, stream);');
- });
-}
+ 'void WriteValue(const flutter::EncodableValue& value, flutter::ByteStreamWriter* stream) const override;');
+ });
+ indent.writeScoped(' protected:', '', () {
+ indent.writeln(
+ 'flutter::EncodableValue ReadValueOfType(uint8_t type, flutter::ByteStreamReader* stream) const override;');
+ });
+ }, nestCount: 0);
+ indent.addln('');
+ }
-void _writeErrorOr(Indent indent,
- {Iterable<String> friends = const <String>[]}) {
- final String friendLines = friends
- .map((String className) => '\tfriend class $className;')
- .join('\n');
- indent.format('''
+ void _writeErrorOr(Indent indent,
+ {Iterable<String> friends = const <String>[]}) {
+ final String friendLines = friends
+ .map((String className) => '\tfriend class $className;')
+ .join('\n');
+ indent.format('''
+
class FlutterError {
public:
\texplicit FlutterError(const std::string& code)
@@ -211,101 +454,563 @@
\tstd::variant<T, FlutterError> v_;
};
''');
+ }
+
+ @override
+ void writeCloseNamespace(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.namespace != null) {
+ indent.writeln('} // namespace ${generatorOptions.namespace}');
+ }
+ final String guardName = _getGuardName(generatorOptions.headerIncludePath);
+ indent.writeln('#endif // $guardName');
+ }
}
-/// Writes the declaration for the custom class [klass].
-///
-/// See [_writeDataClassImplementation] for the corresponding declaration.
-/// This is intended to be added to the header.
-void _writeDataClassDeclaration(Indent indent, Class klass, Root root,
- {String? testFriend}) {
- indent.addln('');
+/// Writes C++ source (.cpp) file to sink.
+class CppSourceGenerator extends StructuredGenerator<CppOptions> {
+ /// Constructor.
+ const CppSourceGenerator();
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents data sent in messages.'
- ];
+ @override
+ void writeFilePrologue(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('$_commentPrefix $generatedCodeWarning');
+ indent.writeln('$_commentPrefix $seeAlsoWarning');
+ indent.addln('');
+ indent.addln('#undef _HAS_EXCEPTIONS');
+ indent.addln('');
+ }
- addDocumentationComments(indent, klass.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
+ @override
+ void writeFileImports(CppOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln('#include "${generatorOptions.headerIncludePath}"');
+ indent.addln('');
+ _writeSystemHeaderIncludeBlock(indent, <String>[
+ 'flutter/basic_message_channel.h',
+ 'flutter/binary_messenger.h',
+ 'flutter/encodable_value.h',
+ 'flutter/standard_message_codec.h',
+ ]);
+ indent.addln('');
+ _writeSystemHeaderIncludeBlock(indent, <String>[
+ 'map',
+ 'string',
+ 'optional',
+ ]);
+ indent.addln('');
+ }
- indent.write('class ${klass.name} ');
- indent.scoped('{', '};', () {
- indent.scoped(' public:', '', () {
- indent.writeln('${klass.name}();');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
- final HostDatatype baseDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- indent.writeln(
- '${_getterReturnType(baseDatatype)} ${_makeGetterName(field)}() const;');
- indent.writeln(
- 'void ${_makeSetterName(field)}(${_unownedArgumentType(baseDatatype)} value_arg);');
- if (field.type.isNullable) {
- // Add a second setter that takes the non-nullable version of the
- // argument for convenience, since setting literal values with the
- // pointer version is non-trivial.
- final HostDatatype nonNullType = _nonNullableType(baseDatatype);
+ @override
+ void writeOpenNamespace(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.namespace != null) {
+ indent.writeln('namespace ${generatorOptions.namespace} {');
+ }
+ }
+
+ @override
+ void writeDataClass(
+ CppOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+
+ indent.addln('');
+ indent.writeln('$_commentPrefix ${klass.name}');
+ indent.addln('');
+
+ // Getters and setters.
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ _writeCppSourceClassField(generatorOptions, root, indent, klass, field);
+ }
+
+ // Serialization.
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+
+ // Default constructor.
+ indent.writeln('${klass.name}::${klass.name}() {}');
+ indent.addln('');
+
+ // Deserialization.
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ }
+
+ @override
+ void writeClassEncode(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write(
+ 'flutter::EncodableList ${klass.name}::ToEncodableList() const ');
+ indent.scoped('{', '}', () {
+ indent.scoped('return flutter::EncodableList{', '};', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+
+ final String instanceVariable = _makeInstanceVariableName(field);
+
+ String encodableValue = '';
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ final String operator = field.type.isNullable ? '->' : '.';
+ encodableValue =
+ 'flutter::EncodableValue($instanceVariable${operator}ToEncodableList())';
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ final String nonNullValue = field.type.isNullable
+ ? '(*$instanceVariable)'
+ : instanceVariable;
+ encodableValue = 'flutter::EncodableValue((int)$nonNullValue)';
+ } else {
+ final String operator = field.type.isNullable ? '*' : '';
+ encodableValue =
+ 'flutter::EncodableValue($operator$instanceVariable)';
+ }
+
+ if (field.type.isNullable) {
+ encodableValue =
+ '$instanceVariable ? $encodableValue : flutter::EncodableValue()';
+ }
+
+ indent.writeln('$encodableValue,');
+ }
+ });
+ });
+ indent.addln('');
+ }
+
+ @override
+ void writeClassDecode(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write(
+ '${klass.name}::${klass.name}(const flutter::EncodableList& list) ');
+ indent.scoped('{', '}', () {
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ final String instanceVariableName = _makeInstanceVariableName(field);
+ final String pointerFieldName =
+ '${_pointerPrefix}_${_makeVariableName(field)}';
+ final String encodableFieldName =
+ '${_encodablePrefix}_${_makeVariableName(field)}';
+ indent.writeln('auto& $encodableFieldName = list[$index];');
+ if (customEnumNames.contains(field.type.baseName)) {
indent.writeln(
- 'void ${_makeSetterName(field)}(${_unownedArgumentType(nonNullType)} value_arg);');
+ 'if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName))\t$instanceVariableName = (${field.type.baseName})*$pointerFieldName;');
+ } else {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ if (field.type.baseName == 'int') {
+ indent.format('''
+if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName))
+\t$instanceVariableName = *$pointerFieldName;
+else if (const int64_t* ${pointerFieldName}_64 = std::get_if<int64_t>(&$encodableFieldName))
+\t$instanceVariableName = *${pointerFieldName}_64;''');
+ } else if (!hostDatatype.isBuiltin &&
+ root.classes
+ .map((Class x) => x.name)
+ .contains(field.type.baseName)) {
+ indent.write(
+ 'if (const flutter::EncodableList* $pointerFieldName = std::get_if<flutter::EncodableList>(&$encodableFieldName)) ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ '$instanceVariableName = ${hostDatatype.datatype}(*$pointerFieldName);');
+ });
+ } else {
+ indent.write(
+ 'if (const ${hostDatatype.datatype}* $pointerFieldName = std::get_if<${hostDatatype.datatype}>(&$encodableFieldName)) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('$instanceVariableName = *$pointerFieldName;');
+ });
+ }
}
- indent.addln('');
- }
+ });
});
+ indent.addln('');
+ }
- indent.scoped(' private:', '', () {
- indent.writeln('${klass.name}(const flutter::EncodableList& list);');
- indent.writeln('flutter::EncodableList ToEncodableList() const;');
- for (final Class friend in root.classes) {
- if (friend != klass &&
- friend.fields.any(
- (NamedType element) => element.type.baseName == klass.name)) {
- indent.writeln('friend class ${friend.name};');
- }
- }
- for (final Api api in root.apis) {
- // TODO(gaaclarke): Find a way to be more precise with our
- // friendships.
- indent.writeln('friend class ${api.name};');
- indent.writeln('friend class ${_getCodecSerializerName(api)};');
- }
- if (testFriend != null) {
- indent.writeln('friend class $testFriend;');
- }
-
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- indent.writeln(
- '${_valueType(hostDatatype)} ${_makeInstanceVariableName(field)};');
- }
+ @override
+ void writeFlutterApi(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(generatorOptions, root, indent, api);
+ }
+ indent.writeln(
+ '$_commentPrefix Generated class from Pigeon that represents Flutter messages that can be called from C++.');
+ indent.write(
+ '${api.name}::${api.name}(flutter::BinaryMessenger* binary_messenger) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('this->binary_messenger_ = binary_messenger;');
});
- }, nestCount: 0);
- indent.writeln('');
+ indent.writeln('');
+ final String codeSerializerName = getCodecClasses(api, root).isNotEmpty
+ ? _getCodecSerializerName(api)
+ : _defaultCodecSerializer;
+ indent.format('''
+const flutter::StandardMessageCodec& ${api.name}::GetCodec() {
+\treturn flutter::StandardMessageCodec::GetInstance(&$codeSerializerName::GetInstance());
}
+''');
+ for (final Method func in api.methods) {
+ final String channelName = makeChannelName(api, func);
+ final String returnType = func.returnType.isVoid
+ ? 'void'
+ : _nullSafeCppTypeForDartType(func.returnType);
+ String sendArgument;
+ final String callback = 'std::function<void($returnType)>&& callback';
+ if (func.arguments.isEmpty) {
+ indent.write('void ${api.name}::${func.name}($callback) ');
+ sendArgument = 'flutter::EncodableValue()';
+ } else {
+ final Iterable<String> argTypes = func.arguments
+ .map((NamedType e) => _nullSafeCppTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgumentName);
+ sendArgument =
+ 'flutter::EncodableList { ${argNames.map((String arg) => 'flutter::CustomEncodableValue($arg)').join(', ')} }';
+ final String argsSignature =
+ map2(argTypes, argNames, (String x, String y) => '$x $y')
+ .join(', ');
+ indent.write(
+ 'void ${api.name}::${func.name}($argsSignature, $callback) ');
+ }
+ indent.scoped('{', '}', () {
+ const String channel = 'channel';
+ indent.writeln(
+ 'auto channel = std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(');
+ indent.inc();
+ indent.inc();
+ indent.writeln('binary_messenger_, "$channelName", &GetCodec());');
+ indent.dec();
+ indent.dec();
+ indent.write(
+ '$channel->Send($sendArgument, [callback](const uint8_t* reply, size_t reply_size) ');
+ indent.scoped('{', '});', () {
+ if (func.returnType.isVoid) {
+ indent.writeln('callback();');
+ } else {
+ indent.writeln(
+ 'std::unique_ptr<flutter::EncodableValue> decoded_reply = GetCodec().DecodeMessage(reply, reply_size);');
+ indent.writeln(
+ 'flutter::EncodableValue args = *(flutter::EncodableValue*)(decoded_reply.release());');
+ const String output = 'output';
-/// Writes the implementation for the custom class [klass].
-///
-/// See [_writeDataClassDeclaration] for the corresponding declaration.
-/// This is intended to be added to the implementation file.
-void _writeDataClassImplementation(Indent indent, Class klass, Root root) {
- final Set<String> rootClassNameSet =
- root.classes.map((Class x) => x.name).toSet();
- final Set<String> rootEnumNameSet =
- root.enums.map((Enum x) => x.name).toSet();
+ final bool isBuiltin =
+ _baseCppTypeForBuiltinDartType(func.returnType) != null;
+ final String returnTypeName =
+ _baseCppTypeForDartType(func.returnType);
+ if (func.returnType.isNullable) {
+ indent.writeln('$returnType $output{};');
+ } else {
+ indent.writeln('$returnTypeName $output{};');
+ }
+ const String pointerVariable = '${_pointerPrefix}_$output';
+ if (func.returnType.baseName == 'int') {
+ indent.format('''
+if (const int32_t* $pointerVariable = std::get_if<int32_t>(&args))
+\t$output = *$pointerVariable;
+else if (const int64_t* ${pointerVariable}_64 = std::get_if<int64_t>(&args))
+\t$output = *${pointerVariable}_64;''');
+ } else if (!isBuiltin) {
+ indent.write(
+ 'if (const flutter::EncodableList* $pointerVariable = std::get_if<flutter::EncodableList>(&args)) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('$output = $returnTypeName(*$pointerVariable);');
+ });
+ } else {
+ indent.write(
+ 'if (const $returnTypeName* $pointerVariable = std::get_if<$returnTypeName>(&args)) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('$output = *$pointerVariable;');
+ });
+ }
- indent.addln('');
- indent.writeln('$_commentPrefix ${klass.name}');
- indent.addln('');
+ indent.writeln('callback($output);');
+ }
+ });
+ });
+ }
+ }
- // Getters and setters.
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ @override
+ void writeHostApi(
+ CppOptions generatorOptions, Root root, Indent indent, Api api) {
+ assert(api.location == ApiLocation.host);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(generatorOptions, root, indent, api);
+ }
+
+ final String codeSerializerName = getCodecClasses(api, root).isNotEmpty
+ ? _getCodecSerializerName(api)
+ : _defaultCodecSerializer;
+ indent.format('''
+/// The codec used by ${api.name}.
+const flutter::StandardMessageCodec& ${api.name}::GetCodec() {
+\treturn flutter::StandardMessageCodec::GetInstance(&$codeSerializerName::GetInstance());
+}
+''');
+ indent.writeln(
+ '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
+ indent.write(
+ 'void ${api.name}::SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api) ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ final String channelName = makeChannelName(api, method);
+ indent.write('');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ 'auto channel = std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(');
+ indent.inc();
+ indent.inc();
+ indent.writeln('binary_messenger, "$channelName", &GetCodec());');
+ indent.dec();
+ indent.dec();
+ indent.write('if (api != nullptr) ');
+ indent.scoped('{', '} else {', () {
+ indent.write(
+ 'channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply<flutter::EncodableValue>& reply) ');
+ indent.scoped('{', '});', () {
+ indent.write('try ');
+ indent.scoped('{', '}', () {
+ final List<String> methodArgument = <String>[];
+ if (method.arguments.isNotEmpty) {
+ indent.writeln(
+ 'const auto& args = std::get<flutter::EncodableList>(message);');
+
+ // Writes the code to declare and populate a variable called
+ // [argName] to use as a parameter to an API method call from
+ // an existing EncodablValue variable called [encodableArgName]
+ // which corresponds to [arg] in the API definition.
+ void extractEncodedArgument(
+ String argName,
+ String encodableArgName,
+ NamedType arg,
+ HostDatatype hostType) {
+ if (arg.type.isNullable) {
+ // Nullable arguments are always pointers, with nullptr
+ // corresponding to null.
+ if (hostType.datatype == 'int64_t') {
+ // The EncodableValue will either be an int32_t or an
+ // int64_t depending on the value, but the generated API
+ // requires an int64_t so that it can handle any case.
+ // Create a local variable for the 64-bit value...
+ final String valueVarName = '${argName}_value';
+ indent.writeln(
+ 'const int64_t $valueVarName = $encodableArgName.IsNull() ? 0 : $encodableArgName.LongValue();');
+ // ... then declare the arg as a reference to that local.
+ indent.writeln(
+ 'const auto* $argName = $encodableArgName.IsNull() ? nullptr : &$valueVarName;');
+ } else if (hostType.datatype ==
+ 'flutter::EncodableValue') {
+ // Generic objects just pass the EncodableValue through
+ // directly.
+ indent.writeln(
+ 'const auto* $argName = &$encodableArgName;');
+ } else if (hostType.isBuiltin) {
+ indent.writeln(
+ 'const auto* $argName = std::get_if<${hostType.datatype}>(&$encodableArgName);');
+ } else {
+ indent.writeln(
+ 'const auto* $argName = &(std::any_cast<const ${hostType.datatype}&>(std::get<flutter::CustomEncodableValue>($encodableArgName)));');
+ }
+ } else {
+ // Non-nullable arguments are either passed by value or
+ // reference, but the extraction doesn't need to distinguish
+ // since those are the same at the call site.
+ if (hostType.datatype == 'int64_t') {
+ // The EncodableValue will either be an int32_t or an
+ // int64_t depending on the value, but the generated API
+ // requires an int64_t so that it can handle any case.
+ indent.writeln(
+ 'const int64_t $argName = $encodableArgName.LongValue();');
+ } else if (hostType.datatype ==
+ 'flutter::EncodableValue') {
+ // Generic objects just pass the EncodableValue through
+ // directly. This creates an alias just to avoid having to
+ // special-case the argName/encodableArgName distinction
+ // at a higher level.
+ indent.writeln(
+ 'const auto& $argName = $encodableArgName;');
+ } else if (hostType.isBuiltin) {
+ indent.writeln(
+ 'const auto& $argName = std::get<${hostType.datatype}>($encodableArgName);');
+ } else {
+ indent.writeln(
+ 'const auto& $argName = std::any_cast<const ${hostType.datatype}&>(std::get<flutter::CustomEncodableValue>($encodableArgName));');
+ }
+ }
+ }
+
+ enumerate(method.arguments, (int index, NamedType arg) {
+ final HostDatatype hostType = getHostDatatype(
+ arg.type,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) =>
+ _baseCppTypeForBuiltinDartType(x));
+ final String argName = _getSafeArgumentName(index, arg);
+
+ final String encodableArgName =
+ '${_encodablePrefix}_$argName';
+ indent.writeln(
+ 'const auto& $encodableArgName = args.at($index);');
+ if (!arg.type.isNullable) {
+ indent.write('if ($encodableArgName.IsNull()) ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ 'reply(WrapError("$argName unexpectedly null."));');
+ indent.writeln('return;');
+ });
+ }
+ extractEncodedArgument(
+ argName, encodableArgName, arg, hostType);
+ methodArgument.add(argName);
+ });
+ }
+
+ final HostDatatype returnType = getHostDatatype(
+ method.returnType,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ final String returnTypeName = _apiReturnType(returnType);
+ if (method.isAsynchronous) {
+ methodArgument.add(
+ '[reply]($returnTypeName&& output) {${indent.newline}'
+ '${_wrapResponse(indent, root, method.returnType, prefix: '\t')}${indent.newline}'
+ '}',
+ );
+ }
+ final String call =
+ 'api->${_makeMethodName(method)}(${methodArgument.join(', ')})';
+ if (method.isAsynchronous) {
+ indent.format('$call;');
+ } else {
+ indent.writeln('$returnTypeName output = $call;');
+ indent.format(_wrapResponse(indent, root, method.returnType));
+ }
+ });
+ indent.write('catch (const std::exception& exception) ');
+ indent.scoped('{', '}', () {
+ // There is a potential here for `reply` to be called twice, which
+ // is a violation of the API contract, because there's no way of
+ // knowing whether or not the plugin code called `reply` before
+ // throwing. Since use of `@async` suggests that the reply is
+ // probably not sent within the scope of the stack, err on the
+ // side of potential double-call rather than no call (which is
+ // also an API violation) so that unexpected errors have a better
+ // chance of being caught and handled in a useful way.
+ indent.writeln('reply(WrapError(exception.what()));');
+ });
+ });
+ });
+ indent.scoped(null, '}', () {
+ indent.writeln('channel->SetMessageHandler(nullptr);');
+ });
+ });
+ }
+ });
+
+ indent.addln('');
+ indent.format('''
+flutter::EncodableValue ${api.name}::WrapError(std::string_view error_message) {
+\treturn flutter::EncodableValue(flutter::EncodableList{
+\t\tflutter::EncodableValue(std::string(error_message)),
+\t\tflutter::EncodableValue("Error"),
+\t\tflutter::EncodableValue()
+\t});
+}
+flutter::EncodableValue ${api.name}::WrapError(const FlutterError& error) {
+\treturn flutter::EncodableValue(flutter::EncodableList{
+\t\tflutter::EncodableValue(error.message()),
+\t\tflutter::EncodableValue(error.code()),
+\t\terror.details()
+\t});
+}''');
+ indent.addln('');
+ }
+
+ void _writeCodec(
+ CppOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final String codeSerializerName = _getCodecSerializerName(api);
+ indent.writeln('$codeSerializerName::$codeSerializerName() {}');
+ indent.write(
+ 'flutter::EncodableValue $codeSerializerName::ReadValueOfType(uint8_t type, flutter::ByteStreamReader* stream) const ');
+ indent.scoped('{', '}', () {
+ indent.write('switch (type) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
+ indent.write('case ${customClass.enumeration}:');
+ indent.writeScoped('', '', () {
+ indent.writeln(
+ 'return flutter::CustomEncodableValue(${customClass.name}(std::get<flutter::EncodableList>(ReadValue(stream))));');
+ });
+ }
+ indent.write('default:');
+ indent.writeScoped('', '', () {
+ indent.writeln(
+ 'return $_defaultCodecSerializer::ReadValueOfType(type, stream);');
+ }, addTrailingNewline: false);
+ });
+ });
+ indent.writeln('');
+ indent.write(
+ 'void $codeSerializerName::WriteValue(const flutter::EncodableValue& value, flutter::ByteStreamWriter* stream) const ');
+ indent.writeScoped('{', '}', () {
+ indent.write(
+ 'if (const flutter::CustomEncodableValue* custom_value = std::get_if<flutter::CustomEncodableValue>(&value)) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
+ indent.write(
+ 'if (custom_value->type() == typeid(${customClass.name})) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('stream->WriteByte(${customClass.enumeration});');
+ indent.writeln(
+ 'WriteValue(flutter::EncodableValue(std::any_cast<${customClass.name}>(*custom_value).ToEncodableList()), stream);');
+ indent.writeln('return;');
+ });
+ }
+ });
+ indent.writeln('$_defaultCodecSerializer::WriteValue(value, stream);');
+ });
+ indent.writeln('');
+ }
+
+ void _writeCppSourceClassField(CppOptions generatorOptions, Root root,
+ Indent indent, Class klass, NamedType field) {
final HostDatatype hostDatatype = getFieldHostDatatype(field, root.classes,
root.enums, (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
final String instanceVariableName = _makeInstanceVariableName(field);
@@ -333,7 +1038,7 @@
'{ return $returnExpression; }');
indent.writeln(makeSetter(hostDatatype));
if (hostDatatype.isNullable) {
- // Write the non-nullable variant; see _writeDataClassDeclaration.
+ // Write the non-nullable variant; see _writeCppHeaderDataClass.
final HostDatatype nonNullType = _nonNullableType(hostDatatype);
indent.writeln(makeSetter(nonNullType));
}
@@ -341,344 +1046,46 @@
indent.addln('');
}
- // Serialization.
- indent
- .write('flutter::EncodableList ${klass.name}::ToEncodableList() const ');
- indent.scoped('{', '}', () {
- indent.scoped('return flutter::EncodableList{', '};', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ String _wrapResponse(Indent indent, Root root, TypeDeclaration returnType,
+ {String prefix = ''}) {
+ final String nonErrorPath;
+ final String errorCondition;
+ final String errorGetter;
- final String instanceVariable = _makeInstanceVariableName(field);
-
- String encodableValue = '';
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- final String operator = field.type.isNullable ? '->' : '.';
- encodableValue =
- 'flutter::EncodableValue($instanceVariable${operator}ToEncodableList())';
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- final String nonNullValue =
- field.type.isNullable ? '(*$instanceVariable)' : instanceVariable;
- encodableValue = 'flutter::EncodableValue((int)$nonNullValue)';
- } else {
- final String operator = field.type.isNullable ? '*' : '';
- encodableValue =
- 'flutter::EncodableValue($operator$instanceVariable)';
- }
-
- if (field.type.isNullable) {
- encodableValue =
- '$instanceVariable ? $encodableValue : flutter::EncodableValue()';
- }
-
- indent.writeln('$encodableValue,');
- }
- });
- });
- indent.addln('');
-
- // Default constructor.
- indent.writeln('${klass.name}::${klass.name}() {}');
- indent.addln('');
-
- // Deserialization.
- indent.write(
- '${klass.name}::${klass.name}(const flutter::EncodableList& list) ');
- indent.scoped('{', '}', () {
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- final String instanceVariableName = _makeInstanceVariableName(field);
- final String pointerFieldName =
- '${_pointerPrefix}_${_makeVariableName(field)}';
- final String encodableFieldName =
- '${_encodablePrefix}_${_makeVariableName(field)}';
- indent.writeln('auto& $encodableFieldName = list[$index];');
- if (rootEnumNameSet.contains(field.type.baseName)) {
- indent.writeln(
- 'if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName))\t$instanceVariableName = (${field.type.baseName})*$pointerFieldName;');
- } else {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- if (field.type.baseName == 'int') {
- indent.format('''
-if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName))
-\t$instanceVariableName = *$pointerFieldName;
-else if (const int64_t* ${pointerFieldName}_64 = std::get_if<int64_t>(&$encodableFieldName))
-\t$instanceVariableName = *${pointerFieldName}_64;''');
- } else if (!hostDatatype.isBuiltin &&
- root.classes
- .map((Class x) => x.name)
- .contains(field.type.baseName)) {
- indent.write(
- 'if (const flutter::EncodableList* $pointerFieldName = std::get_if<flutter::EncodableList>(&$encodableFieldName)) ');
- indent.scoped('{', '}', () {
- indent.writeln(
- '$instanceVariableName = ${hostDatatype.datatype}(*$pointerFieldName);');
- });
- } else {
- indent.write(
- 'if (const ${hostDatatype.datatype}* $pointerFieldName = std::get_if<${hostDatatype.datatype}>(&$encodableFieldName)) ');
- indent.scoped('{', '}', () {
- indent.writeln('$instanceVariableName = *$pointerFieldName;');
- });
- }
- }
- });
- });
- indent.addln('');
-}
-
-void _writeHostApiHeader(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
-
- const List<String> generatedMessages = <String>[
- ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
- indent.write('class ${api.name} ');
- indent.scoped('{', '};', () {
- indent.scoped(' public:', '', () {
- indent.writeln('${api.name}(const ${api.name}&) = delete;');
- indent.writeln('${api.name}& operator=(const ${api.name}&) = delete;');
- indent.writeln('virtual ~${api.name}() { };');
- for (final Method method in api.methods) {
- final HostDatatype returnType = getHostDatatype(
- method.returnType,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- final String returnTypeName = _apiReturnType(returnType);
-
- final List<String> argSignature = <String>[];
- if (method.arguments.isNotEmpty) {
- final Iterable<String> argTypes =
- method.arguments.map((NamedType arg) {
- final HostDatatype hostType = getFieldHostDatatype(
- arg,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- return _hostApiArgumentType(hostType);
- });
- final Iterable<String> argNames =
- method.arguments.map((NamedType e) => _makeVariableName(e));
- argSignature.addAll(
- map2(argTypes, argNames, (String argType, String argName) {
- return '$argType $argName';
- }));
- }
-
- addDocumentationComments(
- indent, method.documentationComments, _docCommentSpec);
-
- if (method.isAsynchronous) {
- argSignature.add('std::function<void($returnTypeName reply)> result');
- indent.writeln(
- 'virtual void ${_makeMethodName(method)}(${argSignature.join(', ')}) = 0;');
- } else {
- indent.writeln(
- 'virtual $returnTypeName ${_makeMethodName(method)}(${argSignature.join(', ')}) = 0;');
- }
- }
- indent.addln('');
- indent.writeln('$_commentPrefix The codec used by ${api.name}.');
- indent.writeln('static const flutter::StandardMessageCodec& GetCodec();');
- indent.writeln(
- '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
- indent.writeln(
- 'static void SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api);');
- indent.writeln(
- 'static flutter::EncodableValue WrapError(std::string_view error_message);');
- indent.writeln(
- 'static flutter::EncodableValue WrapError(const FlutterError& error);');
- });
- indent.scoped(' protected:', '', () {
- indent.writeln('${api.name}() = default;');
- });
- }, nestCount: 0);
-}
-
-void _writeHostApiSource(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
-
- final String codeSerializerName = getCodecClasses(api, root).isNotEmpty
- ? _getCodecSerializerName(api)
- : _defaultCodecSerializer;
- indent.format('''
-/// The codec used by ${api.name}.
-const flutter::StandardMessageCodec& ${api.name}::GetCodec() {
-\treturn flutter::StandardMessageCodec::GetInstance(&$codeSerializerName::GetInstance());
-}
-''');
- indent.writeln(
- '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
- indent.write(
- 'void ${api.name}::SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api) ');
- indent.scoped('{', '}', () {
- for (final Method method in api.methods) {
- final String channelName = makeChannelName(api, method);
- indent.write('');
- indent.scoped('{', '}', () {
- indent.writeln(
- 'auto channel = std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(');
- indent.inc();
- indent.inc();
- indent.writeln('binary_messenger, "$channelName", &GetCodec());');
- indent.dec();
- indent.dec();
- indent.write('if (api != nullptr) ');
- indent.scoped('{', '} else {', () {
- indent.write(
- 'channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply<flutter::EncodableValue>& reply) ');
- indent.scoped('{', '});', () {
- indent.write('try ');
- indent.scoped('{', '}', () {
- final List<String> methodArgument = <String>[];
- if (method.arguments.isNotEmpty) {
- indent.writeln(
- 'const auto& args = std::get<flutter::EncodableList>(message);');
-
- // Writes the code to declare and populate a variable called
- // [argName] to use as a parameter to an API method call from
- // an existing EncodablValue variable called [encodableArgName]
- // which corresponds to [arg] in the API definition.
- void extractEncodedArgument(
- String argName,
- String encodableArgName,
- NamedType arg,
- HostDatatype hostType) {
- if (arg.type.isNullable) {
- // Nullable arguments are always pointers, with nullptr
- // corresponding to null.
- if (hostType.datatype == 'int64_t') {
- // The EncodableValue will either be an int32_t or an
- // int64_t depending on the value, but the generated API
- // requires an int64_t so that it can handle any case.
- // Create a local variable for the 64-bit value...
- final String valueVarName = '${argName}_value';
- indent.writeln(
- 'const int64_t $valueVarName = $encodableArgName.IsNull() ? 0 : $encodableArgName.LongValue();');
- // ... then declare the arg as a reference to that local.
- indent.writeln(
- 'const auto* $argName = $encodableArgName.IsNull() ? nullptr : &$valueVarName;');
- } else if (hostType.datatype == 'flutter::EncodableValue') {
- // Generic objects just pass the EncodableValue through
- // directly.
- indent.writeln(
- 'const auto* $argName = &$encodableArgName;');
- } else if (hostType.isBuiltin) {
- indent.writeln(
- 'const auto* $argName = std::get_if<${hostType.datatype}>(&$encodableArgName);');
- } else {
- indent.writeln(
- 'const auto* $argName = &(std::any_cast<const ${hostType.datatype}&>(std::get<flutter::CustomEncodableValue>($encodableArgName)));');
- }
- } else {
- // Non-nullable arguments are either passed by value or
- // reference, but the extraction doesn't need to distinguish
- // since those are the same at the call site.
- if (hostType.datatype == 'int64_t') {
- // The EncodableValue will either be an int32_t or an
- // int64_t depending on the value, but the generated API
- // requires an int64_t so that it can handle any case.
- indent.writeln(
- 'const int64_t $argName = $encodableArgName.LongValue();');
- } else if (hostType.datatype == 'flutter::EncodableValue') {
- // Generic objects just pass the EncodableValue through
- // directly. This creates an alias just to avoid having to
- // special-case the argName/encodableArgName distinction
- // at a higher level.
- indent
- .writeln('const auto& $argName = $encodableArgName;');
- } else if (hostType.isBuiltin) {
- indent.writeln(
- 'const auto& $argName = std::get<${hostType.datatype}>($encodableArgName);');
- } else {
- indent.writeln(
- 'const auto& $argName = std::any_cast<const ${hostType.datatype}&>(std::get<flutter::CustomEncodableValue>($encodableArgName));');
- }
- }
- }
-
- enumerate(method.arguments, (int index, NamedType arg) {
- final HostDatatype hostType = getHostDatatype(
- arg.type,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- final String argName = _getSafeArgumentName(index, arg);
-
- final String encodableArgName =
- '${_encodablePrefix}_$argName';
- indent.writeln(
- 'const auto& $encodableArgName = args.at($index);');
- if (!arg.type.isNullable) {
- indent.write('if ($encodableArgName.IsNull()) ');
- indent.scoped('{', '}', () {
- indent.writeln(
- 'reply(WrapError("$argName unexpectedly null."));');
- indent.writeln('return;');
- });
- }
- extractEncodedArgument(
- argName, encodableArgName, arg, hostType);
- methodArgument.add(argName);
- });
- }
-
- String wrapResponse(TypeDeclaration returnType,
- {String prefix = ''}) {
- final String nonErrorPath;
- final String errorCondition;
- final String errorGetter;
- const String nullValue = 'flutter::EncodableValue()';
-
- if (returnType.isVoid) {
- nonErrorPath = '${prefix}wrapped.push_back($nullValue);';
- errorCondition = 'output.has_value()';
- errorGetter = 'value';
- } else {
- final HostDatatype hostType = getHostDatatype(
- returnType,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- const String extractedValue = 'std::move(output).TakeValue()';
- final String wrapperType = hostType.isBuiltin
- ? 'flutter::EncodableValue'
- : 'flutter::CustomEncodableValue';
- if (returnType.isNullable) {
- // The value is a std::optional, so needs an extra layer of
- // handling.
- nonErrorPath = '''
+ const String nullValue = 'flutter::EncodableValue()';
+ if (returnType.isVoid) {
+ nonErrorPath = '${prefix}wrapped.push_back($nullValue);';
+ errorCondition = 'output.has_value()';
+ errorGetter = 'value';
+ } else {
+ final HostDatatype hostType = getHostDatatype(returnType, root.classes,
+ root.enums, (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
+ const String extractedValue = 'std::move(output).TakeValue()';
+ final String wrapperType = hostType.isBuiltin
+ ? 'flutter::EncodableValue'
+ : 'flutter::CustomEncodableValue';
+ if (returnType.isNullable) {
+ // The value is a std::optional, so needs an extra layer of
+ // handling.
+ nonErrorPath = '''
${prefix}auto output_optional = $extractedValue;
${prefix}if (output_optional) {
$prefix\twrapped.push_back($wrapperType(std::move(output_optional).value()));
$prefix} else {
$prefix\twrapped.push_back($nullValue);
$prefix}''';
- } else {
- nonErrorPath =
- '${prefix}wrapped.push_back($wrapperType($extractedValue));';
- }
- errorCondition = 'output.has_error()';
- errorGetter = 'error';
- }
- // Ideally this code would use an initializer list to create
- // an EncodableList inline, which would be less code. However,
- // that would always copy the element, so the slightly more
- // verbose create-and-push approach is used instead.
- return '''
+ } else {
+ nonErrorPath =
+ '${prefix}wrapped.push_back($wrapperType($extractedValue));';
+ }
+ errorCondition = 'output.has_error()';
+ errorGetter = 'error';
+ }
+ // Ideally this code would use an initializer list to create
+ // an EncodableList inline, which would be less code. However,
+ // that would always copy the element, so the slightly more
+ // verbose create-and-push approach is used instead.
+ return '''
${prefix}if ($errorCondition) {
$prefix\treply(WrapError(output.$errorGetter()));
$prefix\treturn;
@@ -686,52 +1093,22 @@
${prefix}flutter::EncodableList wrapped;
$nonErrorPath
${prefix}reply(flutter::EncodableValue(std::move(wrapped)));''';
- }
+ }
- final HostDatatype returnType = getHostDatatype(
- method.returnType,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _baseCppTypeForBuiltinDartType(x));
- final String returnTypeName = _apiReturnType(returnType);
- if (method.isAsynchronous) {
- methodArgument.add(
- '[reply]($returnTypeName&& output) {${indent.newline}'
- '${wrapResponse(method.returnType, prefix: '\t')}${indent.newline}'
- '}',
- );
- }
- final String call =
- 'api->${_makeMethodName(method)}(${methodArgument.join(', ')})';
- if (method.isAsynchronous) {
- indent.format('$call;');
- } else {
- indent.writeln('$returnTypeName output = $call;');
- indent.format(wrapResponse(method.returnType));
- }
- });
- indent.write('catch (const std::exception& exception) ');
- indent.scoped('{', '}', () {
- // There is a potential here for `reply` to be called twice, which
- // is a violation of the API contract, because there's no way of
- // knowing whether or not the plugin code called `reply` before
- // throwing. Since use of `@async` suggests that the reply is
- // probably not sent within the scope of the stack, err on the
- // side of potential double-call rather than no call (which is
- // also an API violation) so that unexpected errors have a better
- // chance of being caught and handled in a useful way.
- indent.writeln('reply(WrapError(exception.what()));');
- });
- });
- });
- indent.scoped(null, '}', () {
- indent.writeln('channel->SetMessageHandler(nullptr);');
- });
- });
+ @override
+ void writeCloseNamespace(
+ CppOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.namespace != null) {
+ indent.writeln('} // namespace ${generatorOptions.namespace}');
}
- });
+ }
}
+String _getCodecSerializerName(Api api) => '${api.name}CodecSerializer';
+
+const String _pointerPrefix = 'pointer';
+const String _encodablePrefix = 'encodable';
+
String _getArgumentName(int count, NamedType argument) =>
argument.name.isEmpty ? 'arg$count' : _makeVariableName(argument);
@@ -739,145 +1116,6 @@
String _getSafeArgumentName(int count, NamedType argument) =>
'${_getArgumentName(count, argument)}_arg';
-void _writeFlutterApiHeader(Indent indent, Api api) {
- assert(api.location == ApiLocation.flutter);
-
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents Flutter messages that can be called from C++.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
- indent.write('class ${api.name} ');
- indent.scoped('{', '};', () {
- indent.scoped(' private:', '', () {
- indent.writeln('flutter::BinaryMessenger* binary_messenger_;');
- });
- indent.scoped(' public:', '', () {
- indent.write('${api.name}(flutter::BinaryMessenger* binary_messenger);');
- indent.writeln('');
- indent.writeln('static const flutter::StandardMessageCodec& GetCodec();');
- for (final Method func in api.methods) {
- final String returnType = func.returnType.isVoid
- ? 'void'
- : _nullSafeCppTypeForDartType(func.returnType);
- final String callback = 'std::function<void($returnType)>&& callback';
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
- if (func.arguments.isEmpty) {
- indent.writeln('void ${func.name}($callback);');
- } else {
- final Iterable<String> argTypes = func.arguments
- .map((NamedType e) => _nullSafeCppTypeForDartType(e.type));
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgumentName);
- final String argsSignature =
- map2(argTypes, argNames, (String x, String y) => '$x $y')
- .join(', ');
- indent.writeln('void ${func.name}($argsSignature, $callback);');
- }
- }
- });
- }, nestCount: 0);
-}
-
-void _writeFlutterApiSource(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.flutter);
- indent.writeln(
- '$_commentPrefix Generated class from Pigeon that represents Flutter messages that can be called from C++.');
- indent.write(
- '${api.name}::${api.name}(flutter::BinaryMessenger* binary_messenger) ');
- indent.scoped('{', '}', () {
- indent.writeln('this->binary_messenger_ = binary_messenger;');
- });
- indent.writeln('');
- final String codeSerializerName = getCodecClasses(api, root).isNotEmpty
- ? _getCodecSerializerName(api)
- : _defaultCodecSerializer;
- indent.format('''
-const flutter::StandardMessageCodec& ${api.name}::GetCodec() {
-\treturn flutter::StandardMessageCodec::GetInstance(&$codeSerializerName::GetInstance());
-}
-''');
- for (final Method func in api.methods) {
- final String channelName = makeChannelName(api, func);
- final String returnType = func.returnType.isVoid
- ? 'void'
- : _nullSafeCppTypeForDartType(func.returnType);
- String sendArgument;
- final String callback = 'std::function<void($returnType)>&& callback';
- if (func.arguments.isEmpty) {
- indent.write('void ${api.name}::${func.name}($callback) ');
- sendArgument = 'flutter::EncodableValue()';
- } else {
- final Iterable<String> argTypes = func.arguments
- .map((NamedType e) => _nullSafeCppTypeForDartType(e.type));
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgumentName);
- sendArgument =
- 'flutter::EncodableList { ${argNames.map((String arg) => 'flutter::CustomEncodableValue($arg)').join(', ')} }';
- final String argsSignature =
- map2(argTypes, argNames, (String x, String y) => '$x $y').join(', ');
- indent
- .write('void ${api.name}::${func.name}($argsSignature, $callback) ');
- }
- indent.scoped('{', '}', () {
- const String channel = 'channel';
- indent.writeln(
- 'auto channel = std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(');
- indent.inc();
- indent.inc();
- indent.writeln('binary_messenger_, "$channelName", &GetCodec());');
- indent.dec();
- indent.dec();
- indent.write(
- '$channel->Send($sendArgument, [callback](const uint8_t* reply, size_t reply_size) ');
- indent.scoped('{', '});', () {
- if (func.returnType.isVoid) {
- indent.writeln('callback();');
- } else {
- indent.writeln(
- 'std::unique_ptr<flutter::EncodableValue> decoded_reply = GetCodec().DecodeMessage(reply, reply_size);');
- indent.writeln(
- 'flutter::EncodableValue args = *(flutter::EncodableValue*)(decoded_reply.release());');
- const String output = 'output';
-
- final bool isBuiltin =
- _baseCppTypeForBuiltinDartType(func.returnType) != null;
- final String returnTypeName =
- _baseCppTypeForDartType(func.returnType);
- if (func.returnType.isNullable) {
- indent.writeln('$returnType $output{};');
- } else {
- indent.writeln('$returnTypeName $output{};');
- }
- const String pointerVariable = '${_pointerPrefix}_$output';
- if (func.returnType.baseName == 'int') {
- indent.format('''
-if (const int32_t* $pointerVariable = std::get_if<int32_t>(&args))
-\t$output = *$pointerVariable;
-else if (const int64_t* ${pointerVariable}_64 = std::get_if<int64_t>(&args))
-\t$output = *${pointerVariable}_64;''');
- } else if (!isBuiltin) {
- indent.write(
- 'if (const flutter::EncodableList* $pointerVariable = std::get_if<flutter::EncodableList>(&args)) ');
- indent.scoped('{', '}', () {
- indent.writeln('$output = $returnTypeName(*$pointerVariable);');
- });
- } else {
- indent.write(
- 'if (const $returnTypeName* $pointerVariable = std::get_if<$returnTypeName>(&args)) ');
- indent.scoped('{', '}', () {
- indent.writeln('$output = *$pointerVariable;');
- });
- }
-
- indent.writeln('callback($output);');
- }
- });
- });
- }
-}
-
/// Returns a non-nullable variant of [type].
HostDatatype _nonNullableType(HostDatatype type) {
return HostDatatype(
@@ -1035,14 +1273,11 @@
}
}
-String _getGuardName(String? headerFileName, String? namespace) {
+String _getGuardName(String? headerFileName) {
String guardName = 'PIGEON_';
if (headerFileName != null) {
guardName += '${headerFileName.replaceAll('.', '_').toUpperCase()}_';
}
- if (namespace != null) {
- guardName += '${namespace.toUpperCase()}_';
- }
return '${guardName}H_';
}
@@ -1053,169 +1288,6 @@
}
}
-/// Generates the ".h" file for the AST represented by [root] to [sink] with the
-/// provided [options] and [headerFileName].
-void generateCppHeader(CppOptions options, Root root, StringSink sink) {
- final String? headerFileName = options.headerOutPath;
- final Indent indent = Indent(sink);
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('$_commentPrefix $generatedCodeWarning');
- indent.writeln('$_commentPrefix $seeAlsoWarning');
- indent.addln('');
- final String guardName = _getGuardName(headerFileName, options.namespace);
- indent.writeln('#ifndef $guardName');
- indent.writeln('#define $guardName');
-
- _writeSystemHeaderIncludeBlock(indent, <String>[
- 'flutter/basic_message_channel.h',
- 'flutter/binary_messenger.h',
- 'flutter/encodable_value.h',
- 'flutter/standard_message_codec.h',
- ]);
- indent.addln('');
- _writeSystemHeaderIncludeBlock(indent, <String>[
- 'map',
- 'string',
- 'optional',
- ]);
- indent.addln('');
-
- if (options.namespace != null) {
- indent.writeln('namespace ${options.namespace} {');
- }
-
- // When generating for a Pigeon unit test, add a test fixture friend class to
- // allow unit testing private methods, since testing serialization via public
- // methods is essentially an end-to-end test.
- String? testFixtureClass;
- if (options.namespace?.endsWith('_pigeontest') ?? false) {
- testFixtureClass =
- '${_pascalCaseFromSnakeCase(options.namespace!.replaceAll('_pigeontest', ''))}Test';
- indent.writeln('class $testFixtureClass;');
- }
-
- indent.addln('');
- indent.writeln('$_commentPrefix Generated class from Pigeon.');
-
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
- indent.write('enum class ${anEnum.name} ');
- indent.scoped('{', '};', () {
- enumerate(anEnum.members, (int index, final EnumMember member) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- indent.writeln(
- '${member.name} = $index${index == anEnum.members.length - 1 ? '' : ','}');
- });
- });
- }
-
- indent.addln('');
-
- _writeErrorOr(indent, friends: root.apis.map((Api api) => api.name));
-
- for (final Class klass in root.classes) {
- _writeDataClassDeclaration(indent, klass, root,
- // Add a hook for unit testing data classes when using the namespace
- // used by pigeon tests.
- testFriend: testFixtureClass);
- }
-
- for (final Api api in root.apis) {
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodecHeader(indent, api, root);
- }
- indent.addln('');
- if (api.location == ApiLocation.host) {
- _writeHostApiHeader(indent, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApiHeader(indent, api);
- }
- }
-
- if (options.namespace != null) {
- indent.writeln('} // namespace ${options.namespace}');
- }
-
- indent.writeln('#endif // $guardName');
-}
-
-/// Generates the ".cpp" file for the AST represented by [root] to [sink] with the
-/// provided [options].
-void generateCppSource(CppOptions options, Root root, StringSink sink) {
- final Indent indent = Indent(sink);
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('$_commentPrefix $generatedCodeWarning');
- indent.writeln('$_commentPrefix $seeAlsoWarning');
- indent.addln('');
- indent.addln('#undef _HAS_EXCEPTIONS');
- indent.addln('');
-
- indent.writeln('#include "${options.headerIncludePath}"');
- indent.addln('');
- _writeSystemHeaderIncludeBlock(indent, <String>[
- 'flutter/basic_message_channel.h',
- 'flutter/binary_messenger.h',
- 'flutter/encodable_value.h',
- 'flutter/standard_message_codec.h',
- ]);
- indent.addln('');
- _writeSystemHeaderIncludeBlock(indent, <String>[
- 'map',
- 'string',
- 'optional',
- ]);
- indent.addln('');
-
- if (options.namespace != null) {
- indent.writeln('namespace ${options.namespace} {');
- }
-
- for (final Class klass in root.classes) {
- _writeDataClassImplementation(indent, klass, root);
- }
-
- for (final Api api in root.apis) {
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodecSource(indent, api, root);
- indent.addln('');
- }
- if (api.location == ApiLocation.host) {
- _writeHostApiSource(indent, api, root);
-
- indent.addln('');
- indent.format('''
-flutter::EncodableValue ${api.name}::WrapError(std::string_view error_message) {
-\treturn flutter::EncodableValue(flutter::EncodableList{
-\t\tflutter::EncodableValue(std::string(error_message)),
-\t\tflutter::EncodableValue("Error"),
-\t\tflutter::EncodableValue()
-\t});
-}
-flutter::EncodableValue ${api.name}::WrapError(const FlutterError& error) {
-\treturn flutter::EncodableValue(flutter::EncodableList{
-\t\tflutter::EncodableValue(error.message()),
-\t\tflutter::EncodableValue(error.code()),
-\t\terror.details()
-\t});
-}''');
- indent.addln('');
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApiSource(indent, api, root);
- }
- }
-
- if (options.namespace != null) {
- indent.writeln('} // namespace ${options.namespace}');
- }
-}
-
/// Validates an AST to make sure the cpp generator supports everything.
List<Error> validateCpp(CppOptions options, Root root) {
final List<Error> result = <Error>[];
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index e6f6850..3b62aff 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -71,29 +71,557 @@
}
/// Class that manages all Dart code generation.
-class DartGenerator extends Generator<DartOptions> {
+class DartGenerator extends StructuredGenerator<DartOptions> {
/// Instantiates a Dart Generator.
- DartGenerator();
+ const DartGenerator();
- /// Generates Dart files with specified [DartOptions]
@override
- void generate(DartOptions languageOptions, Root root, StringSink sink,
- {FileType fileType = FileType.na}) {
- assert(fileType == FileType.na);
- generateDart(languageOptions, root, sink);
+ void writeFilePrologue(
+ DartOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.writeln(
+ '// 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, unnecessary_import',
+ );
+ indent.addln('');
}
- /// Generates Dart files for testing with specified [DartOptions]
- void generateTest(DartOptions languageOptions, Root root, StringSink sink) {
- final String sourceOutPath = languageOptions.sourceOutPath ?? '';
- final String testOutPath = languageOptions.testOutPath ?? '';
- generateTestDart(
- languageOptions,
- root,
- sink,
- sourceOutPath: sourceOutPath,
- testOutPath: testOutPath,
+ @override
+ void writeFileImports(
+ DartOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln("import 'dart:async';");
+ indent.writeln(
+ "import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;",
);
+ indent.addln('');
+ indent.writeln(
+ "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;");
+ indent.writeln("import 'package:flutter/services.dart';");
+ }
+
+ @override
+ void writeEnum(
+ DartOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+ indent.write('enum ${anEnum.name} ');
+ indent.scoped('{', '}', () {
+ for (final EnumMember member in anEnum.members) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ indent.writeln('${member.name},');
+ }
+ });
+ }
+
+ @override
+ void writeDataClass(
+ DartOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+
+ indent.writeln('');
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec);
+
+ indent.write('class ${klass.name} ');
+ indent.scoped('{', '}', () {
+ _writeConstructor(indent, klass);
+ indent.addln('');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+
+ final String datatype = _addGenericTypesNullable(field.type);
+ indent.writeln('$datatype ${field.name};');
+ indent.writeln('');
+ }
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ indent.writeln('');
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ });
+ }
+
+ void _writeConstructor(Indent indent, Class klass) {
+ indent.write(klass.name);
+ indent.scoped('({', '});', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final String required = field.type.isNullable ? '' : 'required ';
+ indent.writeln('${required}this.${field.name},');
+ }
+ });
+ }
+
+ @override
+ void writeClassEncode(
+ DartOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write('Object encode() ');
+ indent.scoped('{', '}', () {
+ indent.write(
+ 'return <Object?>',
+ );
+ indent.scoped('[', '];', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final String conditional = field.type.isNullable ? '?' : '';
+ if (customClassNames.contains(field.type.baseName)) {
+ indent.writeln(
+ '${field.name}$conditional.encode(),',
+ );
+ } else if (customEnumNames.contains(field.type.baseName)) {
+ indent.writeln(
+ '${field.name}$conditional.index,',
+ );
+ } else {
+ indent.writeln('${field.name},');
+ }
+ }
+ });
+ });
+ }
+
+ @override
+ void writeClassDecode(
+ DartOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ void writeValueDecode(NamedType field, int index) {
+ final String resultAt = 'result[$index]';
+ if (customClassNames.contains(field.type.baseName)) {
+ final String nonNullValue =
+ '${field.type.baseName}.decode($resultAt! as List<Object?>)';
+ indent.format(
+ field.type.isNullable
+ ? '''
+$resultAt != null
+\t\t? $nonNullValue
+\t\t: null'''
+ : nonNullValue,
+ leadingSpace: false,
+ trailingNewline: false);
+ } else if (customEnumNames.contains(field.type.baseName)) {
+ final String nonNullValue =
+ '${field.type.baseName}.values[$resultAt! as int]';
+ indent.format(
+ field.type.isNullable
+ ? '''
+$resultAt != null
+\t\t? $nonNullValue
+\t\t: null'''
+ : nonNullValue,
+ leadingSpace: false,
+ trailingNewline: false);
+ } else if (field.type.typeArguments.isNotEmpty) {
+ final String genericType = _makeGenericTypeArguments(field.type);
+ final String castCall = _makeGenericCastCall(field.type);
+ final String castCallPrefix = field.type.isNullable ? '?' : '!';
+ indent.add(
+ '($resultAt as $genericType?)$castCallPrefix$castCall',
+ );
+ } else {
+ final String genericdType = _addGenericTypesNullable(field.type);
+ if (field.type.isNullable) {
+ indent.add(
+ '$resultAt as $genericdType',
+ );
+ } else {
+ indent.add(
+ '$resultAt! as $genericdType',
+ );
+ }
+ }
+ }
+
+ indent.write(
+ 'static ${klass.name} decode(Object result) ',
+ );
+ indent.scoped('{', '}', () {
+ indent.writeln('result as List<Object?>;');
+ indent.write('return ${klass.name}');
+ indent.scoped('(', ');', () {
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ indent.write('${field.name}: ');
+ writeValueDecode(field, index);
+ indent.addln(',');
+ });
+ });
+ });
+ }
+
+ /// Writes the code for host [Api], [api].
+ /// Example:
+ /// class FooCodec extends StandardMessageCodec {...}
+ ///
+ /// abstract class Foo {
+ /// static const MessageCodec<Object?> codec = FooCodec();
+ /// int add(int x, int y);
+ /// static void setup(Foo api, {BinaryMessenger? binaryMessenger}) {...}
+ /// }
+ @override
+ void writeFlutterApi(
+ DartOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api, {
+ String Function(Method)? channelNameFunc,
+ bool isMockHandler = false,
+ }) {
+ assert(api.location == ApiLocation.flutter);
+ final List<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toList();
+ String codecName = _standardMessageCodec;
+ if (getCodecClasses(api, root).isNotEmpty) {
+ codecName = _getCodecName(api);
+ _writeCodec(indent, codecName, api, root);
+ }
+ indent.addln('');
+ addDocumentationComments(
+ indent, api.documentationComments, _docCommentSpec);
+
+ indent.write('abstract class ${api.name} ');
+ indent.scoped('{', '}', () {
+ indent
+ .writeln('static const MessageCodec<Object?> codec = $codecName();');
+ indent.addln('');
+ for (final Method func in api.methods) {
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
+ final bool isAsync = func.isAsynchronous;
+ final String returnType = isAsync
+ ? 'Future<${_addGenericTypesNullable(func.returnType)}>'
+ : _addGenericTypesNullable(func.returnType);
+ final String argSignature = _getMethodArgumentsSignature(
+ func,
+ _getArgumentName,
+ );
+ indent.writeln('$returnType ${func.name}($argSignature);');
+ indent.writeln('');
+ }
+ indent.write(
+ 'static void setup(${api.name}? api, {BinaryMessenger? binaryMessenger}) ');
+ indent.scoped('{', '}', () {
+ for (final Method func in api.methods) {
+ indent.write('');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ 'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(',
+ );
+ final String channelName = channelNameFunc == null
+ ? makeChannelName(api, func)
+ : channelNameFunc(func);
+ indent.nest(2, () {
+ indent.writeln("'$channelName', codec,");
+ indent.writeln(
+ 'binaryMessenger: binaryMessenger);',
+ );
+ });
+ final String messageHandlerSetter =
+ isMockHandler ? 'setMockMessageHandler' : 'setMessageHandler';
+ indent.write('if (api == null) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('channel.$messageHandlerSetter(null);');
+ }, addTrailingNewline: false);
+ indent.add(' else ');
+ indent.scoped('{', '}', () {
+ indent.write(
+ 'channel.$messageHandlerSetter((Object? message) async ',
+ );
+ indent.scoped('{', '});', () {
+ final String returnType =
+ _addGenericTypesNullable(func.returnType);
+ final bool isAsync = func.isAsynchronous;
+ final String emptyReturnStatement = isMockHandler
+ ? 'return <Object?>[];'
+ : func.returnType.isVoid
+ ? 'return;'
+ : 'return null;';
+ String call;
+ if (func.arguments.isEmpty) {
+ indent.writeln('// ignore message');
+ call = 'api.${func.name}()';
+ } else {
+ indent.writeln('assert(message != null,');
+ indent.writeln("'Argument for $channelName was null.');");
+ const String argsArray = 'args';
+ indent.writeln(
+ 'final List<Object?> $argsArray = (message as List<Object?>?)!;');
+ String argNameFunc(int index, NamedType type) =>
+ _getSafeArgumentName(index, type);
+ enumerate(func.arguments, (int count, NamedType arg) {
+ final String argType = _addGenericTypes(arg.type);
+ final String argName = argNameFunc(count, arg);
+ final String genericArgType =
+ _makeGenericTypeArguments(arg.type);
+ final String castCall = _makeGenericCastCall(arg.type);
+
+ final String leftHandSide = 'final $argType? $argName';
+ if (customEnumNames.contains(arg.type.baseName)) {
+ indent.writeln(
+ '$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count] as int];');
+ } else {
+ indent.writeln(
+ '$leftHandSide = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
+ }
+ if (!arg.type.isNullable) {
+ indent.writeln(
+ "assert($argName != null, 'Argument for $channelName was null, expected non-null $argType.');");
+ }
+ });
+ final Iterable<String> argNames =
+ indexMap(func.arguments, (int index, NamedType field) {
+ final String name = _getSafeArgumentName(index, field);
+ return '$name${field.type.isNullable ? '' : '!'}';
+ });
+ call = 'api.${func.name}(${argNames.join(', ')})';
+ }
+ if (func.returnType.isVoid) {
+ if (isAsync) {
+ indent.writeln('await $call;');
+ } else {
+ indent.writeln('$call;');
+ }
+ indent.writeln(emptyReturnStatement);
+ } else {
+ if (isAsync) {
+ indent.writeln('final $returnType output = await $call;');
+ } else {
+ indent.writeln('final $returnType output = $call;');
+ }
+ const String returnExpression = 'output';
+ final String returnStatement = isMockHandler
+ ? 'return <Object?>[$returnExpression];'
+ : 'return $returnExpression;';
+ indent.writeln(returnStatement);
+ }
+ });
+ });
+ });
+ }
+ });
+ });
+ }
+
+ /// Writes the code for host [Api], [api].
+ /// Example:
+ /// class FooCodec extends StandardMessageCodec {...}
+ ///
+ /// class Foo {
+ /// Foo(BinaryMessenger? binaryMessenger) {}
+ /// static const MessageCodec<Object?> codec = FooCodec();
+ /// Future<int> add(int x, int y) async {...}
+ /// }
+ ///
+ /// Messages will be sent and received in a list.
+ ///
+ /// If the message recieved was succesful,
+ /// the result will be contained at the 0'th index.
+ ///
+ /// If the message was a failure, the list will contain 3 items:
+ /// a code, a message, and details in that order.
+ @override
+ void writeHostApi(
+ DartOptions generatorOptions, Root root, Indent indent, Api api) {
+ assert(api.location == ApiLocation.host);
+ String codecName = _standardMessageCodec;
+ if (getCodecClasses(api, root).isNotEmpty) {
+ codecName = _getCodecName(api);
+ _writeCodec(indent, codecName, api, root);
+ }
+ indent.addln('');
+ bool first = true;
+ addDocumentationComments(
+ indent, api.documentationComments, _docCommentSpec);
+ indent.write('class ${api.name} ');
+ indent.scoped('{', '}', () {
+ indent.format('''
+/// Constructor for [${api.name}]. 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.
+${api.name}({BinaryMessenger? binaryMessenger})
+\t\t: _binaryMessenger = binaryMessenger;
+final BinaryMessenger? _binaryMessenger;
+''');
+
+ indent
+ .writeln('static const MessageCodec<Object?> codec = $codecName();');
+ indent.addln('');
+ for (final Method func in api.methods) {
+ if (!first) {
+ indent.writeln('');
+ } else {
+ first = false;
+ }
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+ String argSignature = '';
+ String sendArgument = 'null';
+ if (func.arguments.isNotEmpty) {
+ String argNameFunc(int index, NamedType type) =>
+ _getSafeArgumentName(index, type);
+ final Iterable<String> argExpressions =
+ indexMap(func.arguments, (int index, NamedType type) {
+ final String name = argNameFunc(index, type);
+ if (root.enums
+ .map((Enum e) => e.name)
+ .contains(type.type.baseName)) {
+ return '$name${type.type.isNullable ? '?' : ''}.index';
+ } else {
+ return name;
+ }
+ });
+ sendArgument = '<Object?>[${argExpressions.join(', ')}]';
+ argSignature = _getMethodArgumentsSignature(func, argNameFunc);
+ }
+ indent.write(
+ 'Future<${_addGenericTypesNullable(func.returnType)}> ${func.name}($argSignature) async ',
+ );
+ indent.scoped('{', '}', () {
+ final String channelName = makeChannelName(api, func);
+ indent.writeln(
+ 'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(');
+ indent.nest(2, () {
+ indent.writeln("'$channelName', codec,");
+ indent.writeln('binaryMessenger: _binaryMessenger);');
+ });
+ final String returnType = _makeGenericTypeArguments(func.returnType);
+ final String genericCastCall = _makeGenericCastCall(func.returnType);
+ const String accessor = 'replyList[0]';
+ // Avoid warnings from pointlessly casting to `Object?`.
+ final String nullablyTypedAccessor =
+ returnType == 'Object' ? accessor : '($accessor as $returnType?)';
+ final String nullHandler = func.returnType.isNullable
+ ? (genericCastCall.isEmpty ? '' : '?')
+ : '!';
+ final String returnStatement = func.returnType.isVoid
+ ? 'return;'
+ : 'return $nullablyTypedAccessor$nullHandler$genericCastCall;';
+ indent.format('''
+final List<Object?>? replyList =
+\t\tawait channel.send($sendArgument) as List<Object?>?;
+if (replyList == null) {
+\tthrow PlatformException(
+\t\tcode: 'channel-error',
+\t\tmessage: 'Unable to establish connection on channel.',
+\t);
+} else if (replyList.length > 1) {
+\tthrow PlatformException(
+\t\tcode: replyList[0]! as String,
+\t\tmessage: replyList[1] as String?,
+\t\tdetails: replyList[2],
+\t);''');
+ // On iOS we can return nil from functions to accommodate error
+ // handling. Returning a nil value and not returning an error is an
+ // exception.
+ if (!func.returnType.isNullable && !func.returnType.isVoid) {
+ indent.format('''
+} else if (replyList[0] == null) {
+\tthrow PlatformException(
+\t\tcode: 'null-error',
+\t\tmessage: 'Host platform returned null value for non-null return value.',
+\t);''');
+ }
+ indent.format('''
+} else {
+\t$returnStatement
+}''');
+ });
+ }
+ });
+ }
+
+ /// Generates Dart source code for test support libraries based on the given AST
+ /// represented by [root], outputting the code to [sink]. [sourceOutPath] is the
+ /// path of the generated dart code to be tested. [testOutPath] is where the
+ /// test code will be generated.
+ void generateTest(DartOptions generatorOptions, Root root, StringSink sink) {
+ final Indent indent = Indent(sink);
+ final String sourceOutPath = generatorOptions.sourceOutPath ?? '';
+ final String testOutPath = generatorOptions.testOutPath ?? '';
+ _writeTestPrologue(generatorOptions, root, indent);
+ _writeTestImports(generatorOptions, root, indent);
+ final String relativeDartPath =
+ path.Context(style: path.Style.posix).relative(
+ _posixify(sourceOutPath),
+ from: _posixify(path.dirname(testOutPath)),
+ );
+ late final String? packageName = _deducePackageName(sourceOutPath);
+ if (!relativeDartPath.contains('/lib/') || packageName == null) {
+ // If we can't figure out the package name or the relative path doesn't
+ // include a 'lib' directory, try relative path import which only works in
+ // certain (older) versions of Dart.
+ // TODO(gaaclarke): We should add a command-line parameter to override this import.
+ indent.writeln(
+ "import '${_escapeForDartSingleQuotedString(relativeDartPath)}';");
+ } else {
+ final String path =
+ relativeDartPath.replaceFirst(RegExp(r'^.*/lib/'), '');
+ indent.writeln("import 'package:$packageName/$path';");
+ }
+ for (final Api api in root.apis) {
+ if (api.location == ApiLocation.host && api.dartHostTestHandler != null) {
+ final Api mockApi = Api(
+ name: api.dartHostTestHandler!,
+ methods: api.methods,
+ location: ApiLocation.flutter,
+ dartHostTestHandler: api.dartHostTestHandler,
+ documentationComments: api.documentationComments,
+ );
+ indent.writeln('');
+ writeFlutterApi(
+ generatorOptions,
+ root,
+ indent,
+ mockApi,
+ channelNameFunc: (Method func) => makeChannelName(api, func),
+ isMockHandler: true,
+ );
+ }
+ }
+ }
+
+ /// Writes file header to sink.
+ void _writeTestPrologue(DartOptions opt, Root root, Indent indent) {
+ if (opt.copyrightHeader != null) {
+ addLines(indent, opt.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.writeln(
+ '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import',
+ );
+ indent.writeln('// ignore_for_file: avoid_relative_lib_imports');
+ }
+
+ /// Writes file imports to sink.
+ void _writeTestImports(DartOptions opt, Root root, Indent indent) {
+ indent.writeln("import 'dart:async';");
+ indent.writeln(
+ "import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;",
+ );
+ indent.writeln(
+ "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;");
+ indent.writeln("import 'package:flutter/services.dart';");
+ indent.writeln("import 'package:flutter_test/flutter_test.dart';");
+ indent.writeln('');
}
}
@@ -195,279 +723,6 @@
}).join(', ');
}
-/// Writes the code for host [Api], [api].
-/// Example:
-/// class FooCodec extends StandardMessageCodec {...}
-///
-/// class Foo {
-/// Foo(BinaryMessenger? binaryMessenger) {}
-/// static const MessageCodec<Object?> codec = FooCodec();
-/// Future<int> add(int x, int y) async {...}
-/// }
-///
-/// Messages will be sent and received in a list.
-///
-/// If the message recieved was succesful,
-/// the result will be contained at the 0'th index.
-///
-/// If the message was a failure, the list will contain 3 items:
-/// a code, a message, and details in that order.
-void _writeHostApi(DartOptions opt, Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
- String codecName = _standardMessageCodec;
- if (getCodecClasses(api, root).isNotEmpty) {
- codecName = _getCodecName(api);
- _writeCodec(indent, codecName, api, root);
- }
- indent.addln('');
- bool first = true;
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
- indent.write('class ${api.name} ');
- indent.scoped('{', '}', () {
- indent.format('''
-/// Constructor for [${api.name}]. 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.
-${api.name}({BinaryMessenger? binaryMessenger})
-\t\t: _binaryMessenger = binaryMessenger;
-final BinaryMessenger? _binaryMessenger;
-''');
-
- indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
- indent.addln('');
- for (final Method func in api.methods) {
- if (!first) {
- indent.writeln('');
- } else {
- first = false;
- }
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
- String argSignature = '';
- String sendArgument = 'null';
- if (func.arguments.isNotEmpty) {
- String argNameFunc(int index, NamedType type) =>
- _getSafeArgumentName(index, type);
- final Iterable<String> argExpressions =
- indexMap(func.arguments, (int index, NamedType type) {
- final String name = argNameFunc(index, type);
- if (root.enums.map((Enum e) => e.name).contains(type.type.baseName)) {
- return '$name${type.type.isNullable ? '?' : ''}.index';
- } else {
- return name;
- }
- });
- sendArgument = '<Object?>[${argExpressions.join(', ')}]';
- argSignature = _getMethodArgumentsSignature(func, argNameFunc);
- }
- indent.write(
- 'Future<${_addGenericTypesNullable(func.returnType)}> ${func.name}($argSignature) async ',
- );
- indent.scoped('{', '}', () {
- final String channelName = makeChannelName(api, func);
- indent.writeln(
- 'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(');
- indent.nest(2, () {
- indent.writeln("'$channelName', codec,");
- indent.writeln('binaryMessenger: _binaryMessenger);');
- });
- final String returnType = _makeGenericTypeArguments(func.returnType);
- final String genericCastCall = _makeGenericCastCall(func.returnType);
- const String accessor = 'replyList[0]';
- // Avoid warnings from pointlessly casting to `Object?`.
- final String nullablyTypedAccessor =
- returnType == 'Object' ? accessor : '($accessor as $returnType?)';
- final String nullHandler = func.returnType.isNullable
- ? (genericCastCall.isEmpty ? '' : '?')
- : '!';
- final String returnStatement = func.returnType.isVoid
- ? 'return;'
- : 'return $nullablyTypedAccessor$nullHandler$genericCastCall;';
- indent.format('''
-final List<Object?>? replyList =
-\t\tawait channel.send($sendArgument) as List<Object?>?;
-if (replyList == null) {
-\tthrow PlatformException(
-\t\tcode: 'channel-error',
-\t\tmessage: 'Unable to establish connection on channel.',
-\t);
-} else if (replyList.length > 1) {
-\tthrow PlatformException(
-\t\tcode: replyList[0]! as String,
-\t\tmessage: replyList[1] as String?,
-\t\tdetails: replyList[2],
-\t);''');
- // On iOS we can return nil from functions to accommodate error
- // handling. Returning a nil value and not returning an error is an
- // exception.
- if (!func.returnType.isNullable && !func.returnType.isVoid) {
- indent.format('''
-} else if (replyList[0] == null) {
-\tthrow PlatformException(
-\t\tcode: 'null-error',
-\t\tmessage: 'Host platform returned null value for non-null return value.',
-\t);''');
- }
- indent.format('''
-} else {
-\t$returnStatement
-}''');
- });
- }
- });
-}
-
-/// Writes the code for host [Api], [api].
-/// Example:
-/// class FooCodec extends StandardMessageCodec {...}
-///
-/// abstract class Foo {
-/// static const MessageCodec<Object?> codec = FooCodec();
-/// int add(int x, int y);
-/// static void setup(Foo api, {BinaryMessenger? binaryMessenger}) {...}
-/// }
-void _writeFlutterApi(
- DartOptions opt,
- Indent indent,
- Api api,
- Root root, {
- String Function(Method)? channelNameFunc,
- bool isMockHandler = false,
-}) {
- assert(api.location == ApiLocation.flutter);
- final List<String> customEnumNames =
- root.enums.map((Enum x) => x.name).toList();
- String codecName = _standardMessageCodec;
- if (getCodecClasses(api, root).isNotEmpty) {
- codecName = _getCodecName(api);
- _writeCodec(indent, codecName, api, root);
- }
- indent.addln('');
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
-
- indent.write('abstract class ${api.name} ');
- indent.scoped('{', '}', () {
- indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
- indent.addln('');
- for (final Method func in api.methods) {
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
-
- final bool isAsync = func.isAsynchronous;
- final String returnType = isAsync
- ? 'Future<${_addGenericTypesNullable(func.returnType)}>'
- : _addGenericTypesNullable(func.returnType);
- final String argSignature = _getMethodArgumentsSignature(
- func,
- _getArgumentName,
- );
- indent.writeln('$returnType ${func.name}($argSignature);');
- indent.writeln('');
- }
- indent.write(
- 'static void setup(${api.name}? api, {BinaryMessenger? binaryMessenger}) ');
- indent.scoped('{', '}', () {
- for (final Method func in api.methods) {
- indent.write('');
- indent.scoped('{', '}', () {
- indent.writeln(
- 'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(',
- );
- final String channelName = channelNameFunc == null
- ? makeChannelName(api, func)
- : channelNameFunc(func);
- indent.nest(2, () {
- indent.writeln("'$channelName', codec,");
- indent.writeln(
- 'binaryMessenger: binaryMessenger);',
- );
- });
- final String messageHandlerSetter =
- isMockHandler ? 'setMockMessageHandler' : 'setMessageHandler';
- indent.write('if (api == null) ');
- indent.scoped('{', '}', () {
- indent.writeln('channel.$messageHandlerSetter(null);');
- }, addTrailingNewline: false);
- indent.add(' else ');
- indent.scoped('{', '}', () {
- indent.write(
- 'channel.$messageHandlerSetter((Object? message) async ',
- );
- indent.scoped('{', '});', () {
- final String returnType =
- _addGenericTypesNullable(func.returnType);
- final bool isAsync = func.isAsynchronous;
- final String emptyReturnStatement = isMockHandler
- ? 'return <Object?>[];'
- : func.returnType.isVoid
- ? 'return;'
- : 'return null;';
- String call;
- if (func.arguments.isEmpty) {
- indent.writeln('// ignore message');
- call = 'api.${func.name}()';
- } else {
- indent.writeln('assert(message != null,');
- indent.writeln("'Argument for $channelName was null.');");
- const String argsArray = 'args';
- indent.writeln(
- 'final List<Object?> $argsArray = (message as List<Object?>?)!;');
- String argNameFunc(int index, NamedType type) =>
- _getSafeArgumentName(index, type);
- enumerate(func.arguments, (int count, NamedType arg) {
- final String argType = _addGenericTypes(arg.type);
- final String argName = argNameFunc(count, arg);
- final String genericArgType =
- _makeGenericTypeArguments(arg.type);
- final String castCall = _makeGenericCastCall(arg.type);
-
- final String leftHandSide = 'final $argType? $argName';
- if (customEnumNames.contains(arg.type.baseName)) {
- indent.writeln(
- '$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count] as int];');
- } else {
- indent.writeln(
- '$leftHandSide = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
- }
- if (!arg.type.isNullable) {
- indent.writeln(
- "assert($argName != null, 'Argument for $channelName was null, expected non-null $argType.');");
- }
- });
- final Iterable<String> argNames =
- indexMap(func.arguments, (int index, NamedType field) {
- final String name = _getSafeArgumentName(index, field);
- return '$name${field.type.isNullable ? '' : '!'}';
- });
- call = 'api.${func.name}(${argNames.join(', ')})';
- }
- if (func.returnType.isVoid) {
- if (isAsync) {
- indent.writeln('await $call;');
- } else {
- indent.writeln('$call;');
- }
- indent.writeln(emptyReturnStatement);
- } else {
- if (isAsync) {
- indent.writeln('final $returnType output = await $call;');
- } else {
- indent.writeln('final $returnType output = $call;');
- }
- const String returnExpression = 'output';
- final String returnStatement = isMockHandler
- ? 'return <Object?>[$returnExpression];'
- : 'return $returnExpression;';
- indent.writeln(returnStatement);
- }
- });
- });
- });
- }
- });
- });
-}
-
/// Converts a [List] of [TypeDeclaration]s to a comma separated [String] to be
/// used in Dart code.
String _flattenTypeArguments(List<TypeDeclaration> args) {
@@ -501,196 +756,6 @@
return type.isNullable ? '$genericdType?' : genericdType;
}
-/// Generates Dart source code for the given AST represented by [root],
-/// outputting the code to [sink].
-void generateDart(DartOptions opt, Root root, StringSink sink) {
- final List<String> customClassNames =
- root.classes.map((Class x) => x.name).toList();
- final List<String> customEnumNames =
- root.enums.map((Enum x) => x.name).toList();
- final Indent indent = Indent(sink);
-
- void writeHeader() {
- if (opt.copyrightHeader != null) {
- addLines(indent, opt.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- indent.writeln(
- '// 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, unnecessary_import',
- );
- }
-
- void writeEnums() {
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
- indent.write('enum ${anEnum.name} ');
- indent.scoped('{', '}', () {
- for (final EnumMember member in anEnum.members) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- indent.writeln('${member.name},');
- }
- });
- }
- }
-
- void writeImports() {
- indent.writeln("import 'dart:async';");
- indent.writeln(
- "import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;",
- );
- indent.addln('');
- indent.writeln(
- "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;");
- indent.writeln("import 'package:flutter/services.dart';");
- }
-
- void writeDataClass(Class klass) {
- void writeConstructor() {
- indent.write(klass.name);
- indent.scoped('({', '});', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final String required = field.type.isNullable ? '' : 'required ';
- indent.writeln('${required}this.${field.name},');
- }
- });
- }
-
- void writeEncode() {
- indent.write('Object encode() ');
- indent.scoped('{', '}', () {
- indent.write(
- 'return <Object?>',
- );
- indent.scoped('[', '];', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final String conditional = field.type.isNullable ? '?' : '';
- if (customClassNames.contains(field.type.baseName)) {
- indent.writeln(
- '${field.name}$conditional.encode(),',
- );
- } else if (customEnumNames.contains(field.type.baseName)) {
- indent.writeln(
- '${field.name}$conditional.index,',
- );
- } else {
- indent.writeln('${field.name},');
- }
- }
- });
- });
- }
-
- void writeDecode() {
- void writeValueDecode(NamedType field, int index) {
- final String resultAt = 'result[$index]';
- if (customClassNames.contains(field.type.baseName)) {
- final String nonNullValue =
- '${field.type.baseName}.decode($resultAt! as List<Object?>)';
- indent.format(
- field.type.isNullable
- ? '''
-$resultAt != null
-\t\t? $nonNullValue
-\t\t: null'''
- : nonNullValue,
- leadingSpace: false,
- trailingNewline: false);
- } else if (customEnumNames.contains(field.type.baseName)) {
- final String nonNullValue =
- '${field.type.baseName}.values[$resultAt! as int]';
- indent.format(
- field.type.isNullable
- ? '''
-$resultAt != null
-\t\t? $nonNullValue
-\t\t: null'''
- : nonNullValue,
- leadingSpace: false,
- trailingNewline: false);
- } else if (field.type.typeArguments.isNotEmpty) {
- final String genericType = _makeGenericTypeArguments(field.type);
- final String castCall = _makeGenericCastCall(field.type);
- final String castCallPrefix = field.type.isNullable ? '?' : '!';
- indent.add(
- '($resultAt as $genericType?)$castCallPrefix$castCall',
- );
- } else {
- final String genericdType = _addGenericTypesNullable(field.type);
- if (field.type.isNullable) {
- indent.add(
- '$resultAt as $genericdType',
- );
- } else {
- indent.add(
- '$resultAt! as $genericdType',
- );
- }
- }
- }
-
- indent.write(
- 'static ${klass.name} decode(Object result) ',
- );
- indent.scoped('{', '}', () {
- indent.writeln('result as List<Object?>;');
- indent.write('return ${klass.name}');
- indent.scoped('(', ');', () {
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- indent.write('${field.name}: ');
- writeValueDecode(field, index);
- indent.addln(',');
- });
- });
- });
- }
-
- addDocumentationComments(
- indent, klass.documentationComments, _docCommentSpec);
-
- indent.write('class ${klass.name} ');
- indent.scoped('{', '}', () {
- writeConstructor();
- indent.addln('');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
-
- final String datatype = _addGenericTypesNullable(field.type);
- indent.writeln('$datatype ${field.name};');
- indent.writeln('');
- }
- writeEncode();
- indent.writeln('');
- writeDecode();
- });
- }
-
- void writeApi(Api api) {
- if (api.location == ApiLocation.host) {
- _writeHostApi(opt, indent, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApi(opt, indent, api, root);
- }
- }
-
- writeHeader();
- writeImports();
- writeEnums();
- for (final Class klass in root.classes) {
- indent.writeln('');
- writeDataClass(klass);
- }
- for (final Api api in root.apis) {
- indent.writeln('');
- writeApi(api);
- }
-}
-
/// Crawls up the path of [dartFilePath] until it finds a pubspec.yaml in a
/// parent directory and returns its path.
String? _findPubspecPath(String dartFilePath) {
@@ -739,72 +804,3 @@
final path.Context context = path.Context(style: path.Style.posix);
return context.fromUri(path.toUri(path.absolute(inputPath)));
}
-
-/// Generates Dart source code for test support libraries based on the given AST
-/// represented by [root], outputting the code to [sink]. [sourceOutPath] is the
-/// path of the generated dart code to be tested. [testOutPath] is where the
-/// test code will be generated.
-void generateTestDart(
- DartOptions opt,
- Root root,
- StringSink sink, {
- required String sourceOutPath,
- required String testOutPath,
-}) {
- final Indent indent = Indent(sink);
- if (opt.copyrightHeader != null) {
- addLines(indent, opt.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- indent.writeln(
- '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import',
- );
- indent.writeln('// ignore_for_file: avoid_relative_lib_imports');
- indent.writeln("import 'dart:async';");
- indent.writeln(
- "import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;",
- );
- indent.writeln(
- "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;");
- indent.writeln("import 'package:flutter/services.dart';");
- indent.writeln("import 'package:flutter_test/flutter_test.dart';");
- indent.writeln('');
- final String relativeDartPath =
- path.Context(style: path.Style.posix).relative(
- _posixify(sourceOutPath),
- from: _posixify(path.dirname(testOutPath)),
- );
- late final String? packageName = _deducePackageName(sourceOutPath);
- if (!relativeDartPath.contains('/lib/') || packageName == null) {
- // If we can't figure out the package name or the relative path doesn't
- // include a 'lib' directory, try relative path import which only works in
- // certain (older) versions of Dart.
- // TODO(gaaclarke): We should add a command-line parameter to override this import.
- indent.writeln(
- "import '${_escapeForDartSingleQuotedString(relativeDartPath)}';");
- } else {
- final String path = relativeDartPath.replaceFirst(RegExp(r'^.*/lib/'), '');
- indent.writeln("import 'package:$packageName/$path';");
- }
- for (final Api api in root.apis) {
- if (api.location == ApiLocation.host && api.dartHostTestHandler != null) {
- final Api mockApi = Api(
- name: api.dartHostTestHandler!,
- methods: api.methods,
- location: ApiLocation.flutter,
- dartHostTestHandler: api.dartHostTestHandler,
- documentationComments: api.documentationComments,
- );
- indent.writeln('');
- _writeFlutterApi(
- opt,
- indent,
- mockApi,
- root,
- channelNameFunc: (Method func) => makeChannelName(api, func),
- isMockHandler: true,
- );
- }
- }
-}
diff --git a/packages/pigeon/lib/generator.dart b/packages/pigeon/lib/generator.dart
index 02667e4..c730f92 100644
--- a/packages/pigeon/lib/generator.dart
+++ b/packages/pigeon/lib/generator.dart
@@ -3,11 +3,119 @@
// found in the LICENSE file.
import 'ast.dart';
+import 'generator_tools.dart';
-/// A superclass of generator classes.
+/// An abstract base class of generators.
///
/// This provides the structure that is common across generators for different languages.
abstract class Generator<T> {
- /// Generates files for specified language with specified [languageOptions]
- void generate(T languageOptions, Root root, StringSink sink);
+ /// Constructor.
+ const Generator();
+
+ /// Generates files for specified language with specified [generatorOptions]
+ void generate(T generatorOptions, Root root, StringSink sink);
+}
+
+/// An abstract base class that enforces code generation across platforms.
+abstract class StructuredGenerator<T> extends Generator<T> {
+ /// Constructor.
+ const StructuredGenerator();
+
+ @override
+ void generate(
+ T generatorOptions,
+ Root root,
+ StringSink sink,
+ ) {
+ final Indent indent = Indent(sink);
+
+ writeFilePrologue(generatorOptions, root, indent);
+
+ writeFileImports(generatorOptions, root, indent);
+
+ writeOpenNamespace(generatorOptions, root, indent);
+
+ writeGeneralUtilities(generatorOptions, root, indent);
+
+ writeEnums(generatorOptions, root, indent);
+
+ writeDataClasses(generatorOptions, root, indent);
+
+ writeApis(generatorOptions, root, indent);
+
+ writeCloseNamespace(generatorOptions, root, indent);
+ }
+
+ /// Adds specified headers to [indent].
+ void writeFilePrologue(T generatorOptions, Root root, Indent indent);
+
+ /// Writes specified imports to [indent].
+ void writeFileImports(T generatorOptions, Root root, Indent indent);
+
+ /// Writes code to [indent] that opens file namespace if needed.
+ ///
+ /// This method is not required, and does not need to be overridden.
+ void writeOpenNamespace(T generatorOptions, Root root, Indent indent) {}
+
+ /// Writes code to [indent] that closes file namespace if needed.
+ ///
+ /// This method is not required, and does not need to be overridden.
+ void writeCloseNamespace(T generatorOptions, Root root, Indent indent) {}
+
+ /// Writes any necessary helper utilities to [indent] if needed.
+ ///
+ /// This method is not required, and does not need to be overridden.
+ void writeGeneralUtilities(T generatorOptions, Root root, Indent indent) {}
+
+ /// Writes all enums to [indent].
+ ///
+ /// Can be overridden to add extra code before/after enums.
+ void writeEnums(T generatorOptions, Root root, Indent indent) {
+ for (final Enum anEnum in root.enums) {
+ writeEnum(generatorOptions, root, indent, anEnum);
+ }
+ }
+
+ /// Writes a single Enum to [indent]. This is needed in most generators.
+ void writeEnum(T generatorOptions, Root root, Indent indent, Enum anEnum) {}
+
+ /// Writes all data classes to [indent].
+ ///
+ /// Can be overridden to add extra code before/after apis.
+ void writeDataClasses(T generatorOptions, Root root, Indent indent) {
+ for (final Class klass in root.classes) {
+ writeDataClass(generatorOptions, root, indent, klass);
+ }
+ }
+
+ /// Writes a single data class to [indent].
+ void writeDataClass(
+ T generatorOptions, Root root, Indent indent, Class klass);
+
+ /// Writes a single class encode method to [indent].
+ void writeClassEncode(T generatorOptions, Root root, Indent indent,
+ Class klass, Set<String> customClassNames, Set<String> customEnumNames) {}
+
+ /// Writes a single class decode method to [indent].
+ void writeClassDecode(T generatorOptions, Root root, Indent indent,
+ Class klass, Set<String> customClassNames, Set<String> customEnumNames) {}
+
+ /// Writes all apis to [indent].
+ ///
+ /// Can be overridden to add extra code before/after classes.
+ void writeApis(T generatorOptions, Root root, Indent indent) {
+ for (final Api api in root.apis) {
+ if (api.location == ApiLocation.host) {
+ writeHostApi(generatorOptions, root, indent, api);
+ } else if (api.location == ApiLocation.flutter) {
+ writeFlutterApi(generatorOptions, root, indent, api);
+ }
+ }
+ }
+
+ /// Writes a single Flutter Api to [indent].
+ void writeFlutterApi(T generatorOptions, Root root, Indent indent, Api api);
+
+ /// Writes a single Host Api to [indent].
+ void writeHostApi(T generatorOptions, Root root, Indent indent, Api api);
}
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 4355096..3dc1df4 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -9,7 +9,7 @@
import 'ast.dart';
/// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '5.0.1';
+const String pigeonVersion = '6.0.0';
/// Read all the content from [stdin] to a String.
String readStdin() {
@@ -510,7 +510,7 @@
na,
}
-/// Options for [Generator]'s that have multiple output file types.
+/// Options for [Generator]s that have multiple output file types.
///
/// Specifies which file to write as well as wraps all language options.
class OutputFileOptions<T> {
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index daca638..e6e7f8c 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -86,94 +86,433 @@
}
/// Class that manages all Java code generation.
-class JavaGenerator extends Generator<JavaOptions> {
+class JavaGenerator extends StructuredGenerator<JavaOptions> {
/// Instantiates a Java Generator.
- JavaGenerator();
+ const JavaGenerator();
- /// Generates Java files with specified [JavaOptions]
@override
- void generate(JavaOptions languageOptions, Root root, StringSink sink,
- {FileType fileType = FileType.na}) {
- assert(fileType == FileType.na);
- generateJava(languageOptions, root, sink);
+ void writeFilePrologue(
+ JavaOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.addln('');
}
-}
-/// Calculates the name of the codec that will be generated for [api].
-String _getCodecName(Api api) => '${api.name}Codec';
+ @override
+ void writeFileImports(
+ JavaOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.package != null) {
+ indent.writeln('package ${generatorOptions.package};');
+ }
+ indent.writeln('import android.util.Log;');
+ indent.writeln('import androidx.annotation.NonNull;');
+ indent.writeln('import androidx.annotation.Nullable;');
+ indent.writeln('import io.flutter.plugin.common.BasicMessageChannel;');
+ indent.writeln('import io.flutter.plugin.common.BinaryMessenger;');
+ indent.writeln('import io.flutter.plugin.common.MessageCodec;');
+ indent.writeln('import io.flutter.plugin.common.StandardMessageCodec;');
+ indent.writeln('import java.io.ByteArrayOutputStream;');
+ indent.writeln('import java.nio.ByteBuffer;');
+ indent.writeln('import java.util.Arrays;');
+ indent.writeln('import java.util.ArrayList;');
+ indent.writeln('import java.util.Collections;');
+ indent.writeln('import java.util.List;');
+ indent.writeln('import java.util.Map;');
+ indent.writeln('import java.util.HashMap;');
+ indent.addln('');
+ }
-/// Converts an expression that evaluates to an nullable int to an expression
-/// that evaluates to a nullable enum.
-String _intToEnum(String expression, String enumName) =>
- '$expression == null ? null : $enumName.values()[(int)$expression]';
+ @override
+ void writeOpenNamespace(
+ JavaOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln(
+ '$_docCommentPrefix Generated class from Pigeon.$_docCommentSuffix');
+ indent.writeln(
+ '@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})');
+ if (generatorOptions.useGeneratedAnnotation ?? false) {
+ indent.writeln('@javax.annotation.Generated("dev.flutter.pigeon")');
+ }
+ indent.writeln('public class ${generatorOptions.className!} {');
+ indent.inc();
+ }
-/// Writes the codec class that will be used by [api].
-/// Example:
-/// private static class FooCodec extends StandardMessageCodec {...}
-void _writeCodec(Indent indent, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
- final String codecName = _getCodecName(api);
- indent
- .write('private static class $codecName extends $_standardMessageCodec ');
- indent.scoped('{', '}', () {
- indent
- .writeln('public static final $codecName INSTANCE = new $codecName();');
- indent.writeln('private $codecName() {}');
- indent.writeln('@Override');
- indent.write(
- 'protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) ');
+ @override
+ void writeEnum(
+ JavaOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ String camelToSnake(String camelCase) {
+ final RegExp regex = RegExp('([a-z])([A-Z]+)');
+ return camelCase
+ .replaceAllMapped(regex, (Match m) => '${m[1]}_${m[2]}')
+ .toUpperCase();
+ }
+
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+
+ indent.write('public enum ${anEnum.name} ');
indent.scoped('{', '}', () {
- indent.write('switch (type) ');
+ enumerate(anEnum.members, (int index, final EnumMember member) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ indent.writeln(
+ '${camelToSnake(member.name)}($index)${index == anEnum.members.length - 1 ? ';' : ','}');
+ });
+ indent.writeln('');
+ indent.writeln('private final int index;');
+ indent.write('private ${anEnum.name}(final int index) ');
indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write('case (byte)${customClass.enumeration}: ');
- indent.writeScoped('', '', () {
- indent.writeln(
- 'return ${customClass.name}.fromList((ArrayList<Object>) readValue(buffer));');
- });
- }
- indent.write('default:');
- indent.writeScoped('', '', () {
- indent.writeln('return super.readValueOfType(type, buffer);');
- });
+ indent.writeln('this.index = index;');
});
});
- indent.writeln('@Override');
- indent.write(
- 'protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) ');
- indent.writeScoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write('if (value instanceof ${customClass.name}) ');
- indent.scoped('{', '} else ', () {
- indent.writeln('stream.write(${customClass.enumeration});');
+ }
+
+ @override
+ void writeDataClass(
+ JavaOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents data sent in messages.'
+ ];
+ indent.addln('');
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('public static class ${klass.name} ');
+ indent.scoped('{', '}', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ _writeClassField(generatorOptions, root, indent, field);
+ indent.addln('');
+ }
+
+ if (getFieldsInSerializationOrder(klass)
+ .map((NamedType e) => !e.type.isNullable)
+ .any((bool e) => e)) {
+ indent.writeln(
+ '${_docCommentPrefix}Constructor is private to enforce null safety; use Builder.$_docCommentSuffix');
+ indent.writeln('private ${klass.name}() {}');
+ }
+
+ _writeClassBuilder(generatorOptions, root, indent, klass);
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ });
+ }
+
+ void _writeClassField(
+ JavaOptions generatorOptions, Root root, Indent indent, NamedType field) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(field, root.classes,
+ root.enums, (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
+ final String nullability = field.type.isNullable ? '@Nullable' : '@NonNull';
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+
+ indent.writeln(
+ 'private $nullability ${hostDatatype.datatype} ${field.name};');
+ indent.writeln(
+ 'public $nullability ${hostDatatype.datatype} ${_makeGetter(field)}() { return ${field.name}; }');
+ indent.writeScoped(
+ 'public void ${_makeSetter(field)}($nullability ${hostDatatype.datatype} setterArg) {',
+ '}', () {
+ if (!field.type.isNullable) {
+ indent.writeScoped('if (setterArg == null) {', '}', () {
indent.writeln(
- 'writeValue(stream, ((${customClass.name}) value).toList());');
+ 'throw new IllegalStateException("Nonnull field \\"${field.name}\\" is null.");');
});
}
+ indent.writeln('this.${field.name} = setterArg;');
+ });
+ }
+
+ void _writeClassBuilder(
+ JavaOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ ) {
+ indent.write('public static final class Builder ');
+ indent.scoped('{', '}', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
+ final String nullability =
+ field.type.isNullable ? '@Nullable' : '@NonNull';
+ indent.writeln(
+ 'private @Nullable ${hostDatatype.datatype} ${field.name};');
+ indent.writeScoped(
+ 'public @NonNull Builder ${_makeSetter(field)}($nullability ${hostDatatype.datatype} setterArg) {',
+ '}', () {
+ indent.writeln('this.${field.name} = setterArg;');
+ indent.writeln('return this;');
+ });
+ }
+ indent.write('public @NonNull ${klass.name} build() ');
indent.scoped('{', '}', () {
- indent.writeln('super.writeValue(stream, value);');
+ const String returnVal = 'pigeonReturn';
+ indent.writeln('${klass.name} $returnVal = new ${klass.name}();');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ indent.writeln('$returnVal.${_makeSetter(field)}(${field.name});');
+ }
+ indent.writeln('return $returnVal;');
});
});
- });
-}
+ }
-/// Write the java code that represents a host [Api], [api].
-/// Example:
-/// public interface Foo {
-/// int add(int x, int y);
-/// static void setup(BinaryMessenger binaryMessenger, Foo api) {...}
-/// }
-void _writeHostApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
+ @override
+ void writeClassEncode(
+ JavaOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write('@NonNull ArrayList<Object> toList() ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ 'ArrayList<Object> toListResult = new ArrayList<Object>(${klass.fields.length});');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ root.classes,
+ root.enums,
+ (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
+ String toWriteValue = '';
+ final String fieldName = field.name;
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ toWriteValue = '($fieldName == null) ? null : $fieldName.toList()';
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ toWriteValue = '$fieldName == null ? null : $fieldName.index';
+ } else {
+ toWriteValue = field.name;
+ }
+ indent.writeln('toListResult.add($toWriteValue);');
+ }
+ indent.writeln('return toListResult;');
+ });
+ }
- bool isEnum(TypeDeclaration type) =>
- root.enums.map((Enum e) => e.name).contains(type.baseName);
+ @override
+ void writeClassDecode(
+ JavaOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write(
+ 'static @NonNull ${klass.name} fromList(@NonNull ArrayList<Object> list) ');
+ indent.scoped('{', '}', () {
+ const String result = 'pigeonResult';
+ indent.writeln('${klass.name} $result = new ${klass.name}();');
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ final String fieldVariable = field.name;
+ final String setter = _makeSetter(field);
+ indent.writeln('Object $fieldVariable = list.get($index);');
+ if (customEnumNames.contains(field.type.baseName)) {
+ indent.writeln(
+ '$result.$setter(${_intToEnum(fieldVariable, field.type.baseName)});');
+ } else {
+ indent.writeln(
+ '$result.$setter(${_castObject(field, root.classes, root.enums, fieldVariable)});');
+ }
+ });
+ indent.writeln('return $result;');
+ });
+ }
+
+ /// Writes the code for a flutter [Api], [api].
+ /// Example:
+ /// public static class Foo {
+ /// public Foo(BinaryMessenger argBinaryMessenger) {...}
+ /// public interface Reply<T> {
+ /// void reply(T reply);
+ /// }
+ /// public int add(int x, int y, Reply<int> callback) {...}
+ /// }
+ @override
+ void writeFlutterApi(
+ JavaOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(indent, api, root);
+ }
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents Flutter messages that can be called from Java.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('public static class ${api.name} ');
+ indent.scoped('{', '}', () {
+ indent.writeln('private final BinaryMessenger binaryMessenger;');
+ indent.write('public ${api.name}(BinaryMessenger argBinaryMessenger)');
+ indent.scoped('{', '}', () {
+ indent.writeln('this.binaryMessenger = argBinaryMessenger;');
+ });
+ indent.write('public interface Reply<T> ');
+ indent.scoped('{', '}', () {
+ indent.writeln('void reply(T reply);');
+ });
+ final String codecName = _getCodecName(api);
+ indent.writeln('/** The codec used by ${api.name}. */');
+ indent.write('static MessageCodec<Object> getCodec() ');
+ indent.scoped('{', '}', () {
+ indent.write('return ');
+ if (getCodecClasses(api, root).isNotEmpty) {
+ indent.writeln('$codecName.INSTANCE;');
+ } else {
+ indent.writeln('new $_standardMessageCodec();');
+ }
+ });
+
+ for (final Method func in api.methods) {
+ final String channelName = makeChannelName(api, func);
+ final String returnType = func.returnType.isVoid
+ ? 'Void'
+ : _javaTypeForDartType(func.returnType);
+ String sendArgument;
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+ if (func.arguments.isEmpty) {
+ indent
+ .write('public void ${func.name}(Reply<$returnType> callback) ');
+ sendArgument = 'null';
+ } else {
+ final Iterable<String> argTypes = func.arguments
+ .map((NamedType e) => _nullsafeJavaTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgumentName);
+ if (func.arguments.length == 1) {
+ sendArgument =
+ 'new ArrayList<Object>(Collections.singletonList(${argNames.first}))';
+ } else {
+ sendArgument =
+ 'new ArrayList<Object>(Arrays.asList(${argNames.join(', ')}))';
+ }
+ final String argsSignature =
+ map2(argTypes, argNames, (String x, String y) => '$x $y')
+ .join(', ');
+ indent.write(
+ 'public void ${func.name}($argsSignature, Reply<$returnType> callback) ');
+ }
+ indent.scoped('{', '}', () {
+ const String channel = 'channel';
+ indent.writeln('BasicMessageChannel<Object> $channel =');
+ indent.inc();
+ indent.inc();
+ indent.writeln(
+ 'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
+ indent.dec();
+ indent.dec();
+ indent.write('$channel.send($sendArgument, channelReply -> ');
+ indent.scoped('{', '});', () {
+ if (func.returnType.isVoid) {
+ indent.writeln('callback.reply(null);');
+ } else {
+ const String output = 'output';
+ indent.writeln('@SuppressWarnings("ConstantConditions")');
+ if (func.returnType.baseName == 'int') {
+ indent.writeln(
+ '$returnType $output = channelReply == null ? null : ((Number)channelReply).longValue();');
+ } else {
+ indent.writeln(
+ '$returnType $output = ($returnType)channelReply;');
+ }
+ indent.writeln('callback.reply($output);');
+ }
+ });
+ });
+ }
+ });
+ }
+
+ @override
+ void writeApis(JavaOptions generatorOptions, Root root, Indent indent) {
+ if (root.apis.any((Api api) =>
+ api.location == ApiLocation.host &&
+ api.methods.any((Method it) => it.isAsynchronous))) {
+ indent.addln('');
+ _writeResultInterface(indent);
+ }
+ super.writeApis(generatorOptions, root, indent);
+ }
+
+ /// Write the java code that represents a host [Api], [api].
+ /// Example:
+ /// public interface Foo {
+ /// int add(int x, int y);
+ /// static void setup(BinaryMessenger binaryMessenger, Foo api) {...}
+ /// }
+ @override
+ void writeHostApi(
+ JavaOptions generatorOptions, Root root, Indent indent, Api api) {
+ assert(api.location == ApiLocation.host);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(indent, api, root);
+ }
+ const List<String> generatedMessages = <String>[
+ ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('public interface ${api.name} ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ _writeInterfaceMethod(generatorOptions, root, indent, api, method);
+ }
+ indent.addln('');
+ final String codecName = _getCodecName(api);
+ indent.writeln('/** The codec used by ${api.name}. */');
+ indent.write('static MessageCodec<Object> getCodec() ');
+ indent.scoped('{', '}', () {
+ indent.write('return ');
+ if (getCodecClasses(api, root).isNotEmpty) {
+ indent.write('$codecName.INSTANCE;');
+ } else {
+ indent.write('new $_standardMessageCodec();');
+ }
+ });
+
+ indent.writeln(
+ '${_docCommentPrefix}Sets up an instance of `${api.name}` to handle messages through the `binaryMessenger`.$_docCommentSuffix');
+ indent.write(
+ 'static void setup(BinaryMessenger binaryMessenger, ${api.name} api) ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ _writeMethodSetup(generatorOptions, root, indent, api, method);
+ }
+ });
+ });
+ }
/// Write a method in the interface.
/// Example:
/// int add(int x, int y);
- void writeInterfaceMethod(final Method method) {
+ void _writeInterfaceMethod(JavaOptions generatorOptions, Root root,
+ Indent indent, Api api, final Method method) {
final String returnType = method.isAsynchronous
? 'void'
: _nullsafeJavaTypeForDartType(method.returnType);
@@ -203,7 +542,8 @@
/// Write a static setup function in the interface.
/// Example:
/// static void setup(BinaryMessenger binaryMessenger, Foo api) {...}
- void writeMethodSetup(final Method method) {
+ void _writeMethodSetup(JavaOptions generatorOptions, Root root, Indent indent,
+ Api api, final Method method) {
final String channelName = makeChannelName(api, method);
indent.write('');
indent.scoped('{', '}', () {
@@ -252,7 +592,7 @@
? '($argName == null) ? null : $argName.longValue()'
: argName;
String accessor = 'args.get($index)';
- if (isEnum(arg.type)) {
+ if (isEnum(root, arg.type)) {
accessor = _intToEnum(accessor, arg.type.baseName);
} else if (argType != 'Object') {
accessor = '($argType)$accessor';
@@ -319,38 +659,99 @@
});
}
- const List<String> generatedMessages = <String>[
- ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- indent.write('public interface ${api.name} ');
- indent.scoped('{', '}', () {
- api.methods.forEach(writeInterfaceMethod);
- indent.addln('');
+ /// Writes the codec class that will be used by [api].
+ /// Example:
+ /// private static class FooCodec extends StandardMessageCodec {...}
+ void _writeCodec(Indent indent, Api api, Root root) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
final String codecName = _getCodecName(api);
- indent.writeln('/** The codec used by ${api.name}. */');
- indent.write('static MessageCodec<Object> getCodec() ');
- indent.scoped('{', '}', () {
- indent.write('return ');
- if (getCodecClasses(api, root).isNotEmpty) {
- indent.write('$codecName.INSTANCE;');
- } else {
- indent.write('new $_standardMessageCodec();');
- }
- });
-
- indent.writeln(
- '${_docCommentPrefix}Sets up an instance of `${api.name}` to handle messages through the `binaryMessenger`.$_docCommentSuffix');
indent.write(
- 'static void setup(BinaryMessenger binaryMessenger, ${api.name} api) ');
+ 'private static class $codecName extends $_standardMessageCodec ');
indent.scoped('{', '}', () {
- api.methods.forEach(writeMethodSetup);
+ indent.writeln(
+ 'public static final $codecName INSTANCE = new $codecName();');
+ indent.writeln('private $codecName() {}');
+ indent.writeln('@Override');
+ indent.write(
+ 'protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) ');
+ indent.scoped('{', '}', () {
+ indent.write('switch (type) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write('case (byte)${customClass.enumeration}: ');
+ indent.writeScoped('', '', () {
+ indent.writeln(
+ 'return ${customClass.name}.fromList((ArrayList<Object>) readValue(buffer));');
+ });
+ }
+ indent.write('default:');
+ indent.writeScoped('', '', () {
+ indent.writeln('return super.readValueOfType(type, buffer);');
+ });
+ });
+ });
+ indent.writeln('@Override');
+ indent.write(
+ 'protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) ');
+ indent.writeScoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write('if (value instanceof ${customClass.name}) ');
+ indent.scoped('{', '} else ', () {
+ indent.writeln('stream.write(${customClass.enumeration});');
+ indent.writeln(
+ 'writeValue(stream, ((${customClass.name}) value).toList());');
+ });
+ }
+ indent.scoped('{', '}', () {
+ indent.writeln('super.writeValue(stream, value);');
+ });
+ });
});
- });
+ indent.addln('');
+ }
+
+ void _writeResultInterface(Indent indent) {
+ indent.write('public interface Result<T> ');
+ indent.scoped('{', '}', () {
+ indent.writeln('void success(T result);');
+ indent.writeln('void error(Throwable error);');
+ });
+ }
+
+ void _writeWrapError(Indent indent) {
+ indent.format('''
+@NonNull private static ArrayList<Object> wrapError(@NonNull Throwable exception) {
+\tArrayList<Object> errorList = new ArrayList<>(3);
+\terrorList.add(exception.toString());
+\terrorList.add(exception.getClass().getSimpleName());
+\terrorList.add("Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
+\treturn errorList;
+}''');
+ }
+
+ @override
+ void writeGeneralUtilities(
+ JavaOptions generatorOptions, Root root, Indent indent) {
+ _writeWrapError(indent);
+ }
+
+ @override
+ void writeCloseNamespace(
+ JavaOptions generatorOptions, Root root, Indent indent) {
+ indent.dec();
+ indent.addln('}');
+ }
}
+/// Calculates the name of the codec that will be generated for [api].
+String _getCodecName(Api api) => '${api.name}Codec';
+
+/// Converts an expression that evaluates to an nullable int to an expression
+/// that evaluates to a nullable enum.
+String _intToEnum(String expression, String enumName) =>
+ '$expression == null ? null : $enumName.values()[(int)$expression]';
+
String _getArgumentName(int count, NamedType argument) =>
argument.name.isEmpty ? 'arg$count' : argument.name;
@@ -358,106 +759,6 @@
String _getSafeArgumentName(int count, NamedType argument) =>
'${_getArgumentName(count, argument)}Arg';
-/// Writes the code for a flutter [Api], [api].
-/// Example:
-/// public static class Foo {
-/// public Foo(BinaryMessenger argBinaryMessenger) {...}
-/// public interface Reply<T> {
-/// void reply(T reply);
-/// }
-/// public int add(int x, int y, Reply<int> callback) {...}
-/// }
-void _writeFlutterApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.flutter);
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents Flutter messages that can be called from Java.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- indent.write('public static class ${api.name} ');
- indent.scoped('{', '}', () {
- indent.writeln('private final BinaryMessenger binaryMessenger;');
- indent.write('public ${api.name}(BinaryMessenger argBinaryMessenger)');
- indent.scoped('{', '}', () {
- indent.writeln('this.binaryMessenger = argBinaryMessenger;');
- });
- indent.write('public interface Reply<T> ');
- indent.scoped('{', '}', () {
- indent.writeln('void reply(T reply);');
- });
- final String codecName = _getCodecName(api);
- indent.writeln('/** The codec used by ${api.name}. */');
- indent.write('static MessageCodec<Object> getCodec() ');
- indent.scoped('{', '}', () {
- indent.write('return ');
- if (getCodecClasses(api, root).isNotEmpty) {
- indent.writeln('$codecName.INSTANCE;');
- } else {
- indent.writeln('new $_standardMessageCodec();');
- }
- });
-
- for (final Method func in api.methods) {
- final String channelName = makeChannelName(api, func);
- final String returnType = func.returnType.isVoid
- ? 'Void'
- : _javaTypeForDartType(func.returnType);
- String sendArgument;
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
- if (func.arguments.isEmpty) {
- indent.write('public void ${func.name}(Reply<$returnType> callback) ');
- sendArgument = 'null';
- } else {
- final Iterable<String> argTypes = func.arguments
- .map((NamedType e) => _nullsafeJavaTypeForDartType(e.type));
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgumentName);
- if (func.arguments.length == 1) {
- sendArgument =
- 'new ArrayList<Object>(Collections.singletonList(${argNames.first}))';
- } else {
- sendArgument =
- 'new ArrayList<Object>(Arrays.asList(${argNames.join(', ')}))';
- }
- final String argsSignature =
- map2(argTypes, argNames, (String x, String y) => '$x $y')
- .join(', ');
- indent.write(
- 'public void ${func.name}($argsSignature, Reply<$returnType> callback) ');
- }
- indent.scoped('{', '}', () {
- const String channel = 'channel';
- indent.writeln('BasicMessageChannel<Object> $channel =');
- indent.inc();
- indent.inc();
- indent.writeln(
- 'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
- indent.dec();
- indent.dec();
- indent.write('$channel.send($sendArgument, channelReply -> ');
- indent.scoped('{', '});', () {
- if (func.returnType.isVoid) {
- indent.writeln('callback.reply(null);');
- } else {
- const String output = 'output';
- indent.writeln('@SuppressWarnings("ConstantConditions")');
- if (func.returnType.baseName == 'int') {
- indent.writeln(
- '$returnType $output = channelReply == null ? null : ((Number)channelReply).longValue();');
- } else {
- indent
- .writeln('$returnType $output = ($returnType)channelReply;');
- }
- indent.writeln('callback.reply($output);');
- }
- });
- });
- }
- });
-}
-
String _makeGetter(NamedType field) {
final String uppercased =
field.name.substring(0, 1).toUpperCase() + field.name.substring(1);
@@ -535,279 +836,3 @@
return '(${hostDatatype.datatype})$varName';
}
}
-
-/// Generates the ".java" file for the AST represented by [root] to [sink] with the
-/// provided [options].
-void generateJava(JavaOptions options, Root root, StringSink sink) {
- final Set<String> rootClassNameSet =
- root.classes.map((Class x) => x.name).toSet();
- final Set<String> rootEnumNameSet =
- root.enums.map((Enum x) => x.name).toSet();
- final Indent indent = Indent(sink);
-
- void writeHeader() {
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- }
-
- void writeImports() {
- indent.writeln('import android.util.Log;');
- indent.writeln('import androidx.annotation.NonNull;');
- indent.writeln('import androidx.annotation.Nullable;');
- indent.writeln('import io.flutter.plugin.common.BasicMessageChannel;');
- indent.writeln('import io.flutter.plugin.common.BinaryMessenger;');
- indent.writeln('import io.flutter.plugin.common.MessageCodec;');
- indent.writeln('import io.flutter.plugin.common.StandardMessageCodec;');
- indent.writeln('import java.io.ByteArrayOutputStream;');
- indent.writeln('import java.nio.ByteBuffer;');
- indent.writeln('import java.util.Arrays;');
- indent.writeln('import java.util.ArrayList;');
- indent.writeln('import java.util.Collections;');
- indent.writeln('import java.util.List;');
- indent.writeln('import java.util.Map;');
- indent.writeln('import java.util.HashMap;');
- }
-
- String camelToSnake(String camelCase) {
- final RegExp regex = RegExp('([a-z])([A-Z]+)');
- return camelCase
- .replaceAllMapped(regex, (Match m) => '${m[1]}_${m[2]}')
- .toUpperCase();
- }
-
- void writeEnum(Enum anEnum) {
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
-
- indent.write('public enum ${anEnum.name} ');
- indent.scoped('{', '}', () {
- enumerate(anEnum.members, (int index, final EnumMember member) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- indent.writeln(
- '${camelToSnake(member.name)}($index)${index == anEnum.members.length - 1 ? ';' : ','}');
- });
- indent.writeln('');
- indent.writeln('private final int index;');
- indent.write('private ${anEnum.name}(final int index) ');
- indent.scoped('{', '}', () {
- indent.writeln('this.index = index;');
- });
- });
- }
-
- void writeDataClass(Class klass) {
- void writeField(NamedType field) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
- final String nullability =
- field.type.isNullable ? '@Nullable' : '@NonNull';
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
-
- indent.writeln(
- 'private $nullability ${hostDatatype.datatype} ${field.name};');
- indent.writeln(
- 'public $nullability ${hostDatatype.datatype} ${_makeGetter(field)}() { return ${field.name}; }');
- indent.writeScoped(
- 'public void ${_makeSetter(field)}($nullability ${hostDatatype.datatype} setterArg) {',
- '}', () {
- if (!field.type.isNullable) {
- indent.writeScoped('if (setterArg == null) {', '}', () {
- indent.writeln(
- 'throw new IllegalStateException("Nonnull field \\"${field.name}\\" is null.");');
- });
- }
- indent.writeln('this.${field.name} = setterArg;');
- });
- }
-
- void writeToList() {
- indent.write('@NonNull ArrayList<Object> toList() ');
- indent.scoped('{', '}', () {
- indent.writeln(
- 'ArrayList<Object> toListResult = new ArrayList<Object>(${klass.fields.length});');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
- String toWriteValue = '';
- final String fieldName = field.name;
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- toWriteValue = '($fieldName == null) ? null : $fieldName.toList()';
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- toWriteValue = '$fieldName == null ? null : $fieldName.index';
- } else {
- toWriteValue = field.name;
- }
- indent.writeln('toListResult.add($toWriteValue);');
- }
- indent.writeln('return toListResult;');
- });
- }
-
- void writeFromList() {
- indent.write(
- 'static @NonNull ${klass.name} fromList(@NonNull ArrayList<Object> list) ');
- indent.scoped('{', '}', () {
- const String result = 'pigeonResult';
- indent.writeln('${klass.name} $result = new ${klass.name}();');
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- final String fieldVariable = field.name;
- final String setter = _makeSetter(field);
- indent.writeln('Object $fieldVariable = list.get($index);');
- if (rootEnumNameSet.contains(field.type.baseName)) {
- indent.writeln(
- '$result.$setter(${_intToEnum(fieldVariable, field.type.baseName)});');
- } else {
- indent.writeln(
- '$result.$setter(${_castObject(field, root.classes, root.enums, fieldVariable)});');
- }
- });
- indent.writeln('return $result;');
- });
- }
-
- void writeBuilder() {
- indent.write('public static final class Builder ');
- indent.scoped('{', '}', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- root.classes,
- root.enums,
- (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
- final String nullability =
- field.type.isNullable ? '@Nullable' : '@NonNull';
- indent.writeln(
- 'private @Nullable ${hostDatatype.datatype} ${field.name};');
- indent.writeScoped(
- 'public @NonNull Builder ${_makeSetter(field)}($nullability ${hostDatatype.datatype} setterArg) {',
- '}', () {
- indent.writeln('this.${field.name} = setterArg;');
- indent.writeln('return this;');
- });
- }
- indent.write('public @NonNull ${klass.name} build() ');
- indent.scoped('{', '}', () {
- const String returnVal = 'pigeonReturn';
- indent.writeln('${klass.name} $returnVal = new ${klass.name}();');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- indent.writeln('$returnVal.${_makeSetter(field)}(${field.name});');
- }
- indent.writeln('return $returnVal;');
- });
- });
- }
-
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents data sent in messages.'
- ];
- addDocumentationComments(
- indent, klass.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- indent.write('public static class ${klass.name} ');
- indent.scoped('{', '}', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- writeField(field);
- indent.addln('');
- }
-
- if (getFieldsInSerializationOrder(klass)
- .map((NamedType e) => !e.type.isNullable)
- .any((bool e) => e)) {
- indent.writeln(
- '${_docCommentPrefix}Constructor is private to enforce null safety; use Builder.$_docCommentSuffix');
- indent.writeln('private ${klass.name}() {}');
- }
-
- writeBuilder();
- writeToList();
- writeFromList();
- });
- }
-
- void writeResultInterface() {
- indent.write('public interface Result<T> ');
- indent.scoped('{', '}', () {
- indent.writeln('void success(T result);');
- indent.writeln('void error(Throwable error);');
- });
- }
-
- void writeApi(Api api) {
- if (api.location == ApiLocation.host) {
- _writeHostApi(indent, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApi(indent, api, root);
- }
- }
-
- void writeWrapError() {
- indent.format('''
-@NonNull private static ArrayList<Object> wrapError(@NonNull Throwable exception) {
-\tArrayList<Object> errorList = new ArrayList<>(3);
-\terrorList.add(exception.toString());
-\terrorList.add(exception.getClass().getSimpleName());
-\terrorList.add("Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
-\treturn errorList;
-}''');
- }
-
- writeHeader();
- indent.addln('');
- if (options.package != null) {
- indent.writeln('package ${options.package};');
- }
- indent.addln('');
- writeImports();
- indent.addln('');
- indent.writeln(
- '$_docCommentPrefix Generated class from Pigeon.$_docCommentSuffix');
- indent.writeln(
- '@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})');
- if (options.useGeneratedAnnotation ?? false) {
- indent.writeln('@javax.annotation.Generated("dev.flutter.pigeon")');
- }
- indent.write('public class ${options.className!} ');
- indent.scoped('{', '}', () {
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- writeEnum(anEnum);
- }
-
- for (final Class klass in root.classes) {
- indent.addln('');
- writeDataClass(klass);
- }
-
- if (root.apis.any((Api api) =>
- api.location == ApiLocation.host &&
- api.methods.any((Method it) => it.isAsynchronous))) {
- indent.addln('');
- writeResultInterface();
- }
-
- for (final Api api in root.apis) {
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodec(indent, api, root);
- indent.addln('');
- }
- writeApi(api);
- }
-
- writeWrapError();
- });
-}
diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart
index 18e08c7..76b270b 100644
--- a/packages/pigeon/lib/kotlin_generator.dart
+++ b/packages/pigeon/lib/kotlin_generator.dart
@@ -66,220 +66,574 @@
}
/// Class that manages all Kotlin code generation.
-class KotlinGenerator extends Generator<KotlinOptions> {
+class KotlinGenerator extends StructuredGenerator<KotlinOptions> {
/// Instantiates a Kotlin Generator.
- KotlinGenerator();
+ const KotlinGenerator();
- /// Generates Kotlin files with specified [KotlinOptions]
@override
- void generate(KotlinOptions languageOptions, Root root, StringSink sink,
- {FileType fileType = FileType.na}) {
- assert(fileType == FileType.na);
- generateKotlin(languageOptions, root, sink);
+ void writeFilePrologue(
+ KotlinOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
}
+
+ @override
+ void writeFileImports(
+ KotlinOptions generatorOptions, Root root, Indent indent) {
+ indent.addln('');
+ if (generatorOptions.package != null) {
+ indent.writeln('package ${generatorOptions.package}');
+ }
+ indent.addln('');
+ indent.writeln('import android.util.Log');
+ indent.writeln('import io.flutter.plugin.common.BasicMessageChannel');
+ indent.writeln('import io.flutter.plugin.common.BinaryMessenger');
+ indent.writeln('import io.flutter.plugin.common.MessageCodec');
+ indent.writeln('import io.flutter.plugin.common.StandardMessageCodec');
+ indent.writeln('import java.io.ByteArrayOutputStream');
+ indent.writeln('import java.nio.ByteBuffer');
+ }
+
+ @override
+ void writeEnum(
+ KotlinOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+ indent.write('enum class ${anEnum.name}(val raw: Int) ');
+ indent.scoped('{', '}', () {
+ enumerate(anEnum.members, (int index, final EnumMember member) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ indent.write('${member.name.toUpperCase()}($index)');
+ if (index != anEnum.members.length - 1) {
+ indent.addln(',');
+ } else {
+ indent.addln(';');
+ }
+ });
+
+ indent.writeln('');
+ indent.write('companion object ');
+ indent.scoped('{', '}', () {
+ indent.write('fun ofRaw(raw: Int): ${anEnum.name}? ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return values().firstOrNull { it.raw == raw }');
+ });
+ });
+ });
+ }
+
+ @override
+ void writeDataClass(
+ KotlinOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents data sent in messages.'
+ ];
+ indent.addln('');
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('data class ${klass.name} ');
+ indent.scoped('(', '', () {
+ for (final NamedType element in getFieldsInSerializationOrder(klass)) {
+ _writeClassField(indent, element);
+ if (getFieldsInSerializationOrder(klass).last != element) {
+ indent.addln(',');
+ } else {
+ indent.addln('');
+ }
+ }
+ });
+
+ indent.scoped(') {', '}', () {
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ });
+ }
+
+ @override
+ void writeClassEncode(
+ KotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write('fun toList(): List<Any?> ');
+ indent.scoped('{', '}', () {
+ indent.write('return listOf<Any?>');
+ indent.scoped('(', ')', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = _getHostDatatype(root, field);
+ String toWriteValue = '';
+ final String fieldName = field.name;
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ toWriteValue = '$fieldName?.toList()';
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ toWriteValue = '$fieldName?.raw';
+ } else {
+ toWriteValue = fieldName;
+ }
+ indent.writeln('$toWriteValue,');
+ }
+ });
+ });
+ }
+
+ @override
+ void writeClassDecode(
+ KotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ final String className = klass.name;
+
+ indent.write('companion object ');
+ indent.scoped('{', '}', () {
+ indent.writeln('@Suppress("UNCHECKED_CAST")');
+ indent.write('fun fromList(list: List<Any?>): $className ');
+
+ indent.scoped('{', '}', () {
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ final HostDatatype hostDatatype = _getHostDatatype(root, field);
+
+ // The StandardMessageCodec can give us [Integer, Long] for
+ // a Dart 'int'. To keep things simple we just use 64bit
+ // longs in Pigeon with Kotlin.
+ final bool isInt = field.type.baseName == 'int';
+
+ final String listValue = 'list[$index]';
+ final String fieldType = _kotlinTypeForDartType(field.type);
+
+ if (field.type.isNullable) {
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ indent.write('val ${field.name}: $fieldType? = ');
+ indent.add('($listValue as? List<Any?>)?.let ');
+ indent.scoped('{', '}', () {
+ indent.writeln('$fieldType.fromList(it)');
+ });
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ indent.write('val ${field.name}: $fieldType? = ');
+ indent.add('($listValue as? Int)?.let ');
+ indent.scoped('{', '}', () {
+ indent.writeln('$fieldType.ofRaw(it)');
+ });
+ } else if (isInt) {
+ indent.write('val ${field.name} = $listValue');
+ indent.addln(
+ '.let { if (it is Int) it.toLong() else it as? Long }');
+ } else {
+ indent.writeln('val ${field.name} = $listValue as? $fieldType');
+ }
+ } else {
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ indent.writeln(
+ 'val ${field.name} = $fieldType.fromList($listValue as List<Any?>)');
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ indent.write(
+ 'val ${field.name} = $fieldType.ofRaw($listValue as Int)!!');
+ } else {
+ indent.writeln('val ${field.name} = $listValue as $fieldType');
+ }
+ }
+ });
+
+ indent.writeln('');
+ indent.write('return $className(');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final String comma =
+ getFieldsInSerializationOrder(klass).last == field ? '' : ', ';
+ indent.add('${field.name}$comma');
+ }
+ indent.addln(')');
+ });
+ });
+ }
+
+ void _writeClassField(Indent indent, NamedType field) {
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+ indent.write(
+ 'val ${field.name}: ${_nullsafeKotlinTypeForDartType(field.type)}');
+ final String defaultNil = field.type.isNullable ? ' = null' : '';
+ indent.add(defaultNil);
+ }
+
+ @override
+ void writeApis(
+ KotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ ) {
+ if (root.apis.any((Api api) =>
+ api.location == ApiLocation.host &&
+ api.methods.any((Method it) => it.isAsynchronous))) {
+ indent.addln('');
+ }
+ super.writeApis(generatorOptions, root, indent);
+ }
+
+ /// Writes the code for a flutter [Api], [api].
+ /// Example:
+ /// class Foo(private val binaryMessenger: BinaryMessenger) {
+ /// fun add(x: Int, y: Int, callback: (Int?) -> Unit) {...}
+ /// }
+ @override
+ void writeFlutterApi(
+ KotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
+ if (isCustomCodec) {
+ _writeCodec(indent, api, root);
+ }
+
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents Flutter messages that can be called from Kotlin.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ final String apiName = api.name;
+ indent.writeln('@Suppress("UNCHECKED_CAST")');
+ indent
+ .write('class $apiName(private val binaryMessenger: BinaryMessenger) ');
+ indent.scoped('{', '}', () {
+ indent.write('companion object ');
+ indent.scoped('{', '}', () {
+ indent.writeln('/** The codec used by $apiName. */');
+ indent.write('val codec: MessageCodec<Any?> by lazy ');
+ indent.scoped('{', '}', () {
+ if (isCustomCodec) {
+ indent.writeln(_getCodecName(api));
+ } else {
+ indent.writeln('StandardMessageCodec()');
+ }
+ });
+ });
+
+ for (final Method func in api.methods) {
+ final String channelName = makeChannelName(api, func);
+ final String returnType = func.returnType.isVoid
+ ? ''
+ : _nullsafeKotlinTypeForDartType(func.returnType);
+ String sendArgument;
+
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
+ if (func.arguments.isEmpty) {
+ indent.write('fun ${func.name}(callback: ($returnType) -> Unit) ');
+ sendArgument = 'null';
+ } else {
+ final Iterable<String> argTypes = func.arguments
+ .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgumentName);
+ sendArgument = 'listOf(${argNames.join(', ')})';
+ final String argsSignature = map2(argTypes, argNames,
+ (String type, String name) => '$name: $type').join(', ');
+ if (func.returnType.isVoid) {
+ indent.write(
+ 'fun ${func.name}($argsSignature, callback: () -> Unit) ');
+ } else {
+ indent.write(
+ 'fun ${func.name}($argsSignature, callback: ($returnType) -> Unit) ');
+ }
+ }
+ indent.scoped('{', '}', () {
+ const String channel = 'channel';
+ indent.writeln(
+ 'val $channel = BasicMessageChannel<Any?>(binaryMessenger, "$channelName", codec)');
+ indent.write('$channel.send($sendArgument) ');
+ if (func.returnType.isVoid) {
+ indent.scoped('{', '}', () {
+ indent.writeln('callback()');
+ });
+ } else {
+ final String forceUnwrap = func.returnType.isNullable ? '?' : '';
+ indent.scoped('{', '}', () {
+ indent.writeln('val result = it as$forceUnwrap $returnType');
+ indent.writeln('callback(result)');
+ });
+ }
+ });
+ }
+ });
+ }
+
+ /// Write the kotlin code that represents a host [Api], [api].
+ /// Example:
+ /// interface Foo {
+ /// Int add(x: Int, y: Int);
+ /// companion object {
+ /// fun setUp(binaryMessenger: BinaryMessenger, api: Api) {...}
+ /// }
+ /// }
+ @override
+ void writeHostApi(
+ KotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.host);
+
+ final String apiName = api.name;
+
+ final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
+ if (isCustomCodec) {
+ _writeCodec(indent, api, root);
+ }
+
+ const List<String> generatedMessages = <String>[
+ ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
+ indent.write('interface $apiName ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ final List<String> argSignature = <String>[];
+ if (method.arguments.isNotEmpty) {
+ final Iterable<String> argTypes = method.arguments
+ .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ method.arguments.map((NamedType e) => e.name);
+ argSignature.addAll(
+ map2(argTypes, argNames, (String argType, String argName) {
+ return '$argName: $argType';
+ }));
+ }
+
+ final String returnType = method.returnType.isVoid
+ ? ''
+ : _nullsafeKotlinTypeForDartType(method.returnType);
+
+ addDocumentationComments(
+ indent, method.documentationComments, _docCommentSpec);
+
+ if (method.isAsynchronous) {
+ argSignature.add('callback: ($returnType) -> Unit');
+ indent.writeln('fun ${method.name}(${argSignature.join(', ')})');
+ } else if (method.returnType.isVoid) {
+ indent.writeln('fun ${method.name}(${argSignature.join(', ')})');
+ } else {
+ indent.writeln(
+ 'fun ${method.name}(${argSignature.join(', ')}): $returnType');
+ }
+ }
+
+ indent.addln('');
+ indent.write('companion object ');
+ indent.scoped('{', '}', () {
+ indent.writeln('/** The codec used by $apiName. */');
+ indent.write('val codec: MessageCodec<Any?> by lazy ');
+ indent.scoped('{', '}', () {
+ if (isCustomCodec) {
+ indent.writeln(_getCodecName(api));
+ } else {
+ indent.writeln('StandardMessageCodec()');
+ }
+ });
+ indent.writeln(
+ '/** Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`. */');
+ indent.writeln('@Suppress("UNCHECKED_CAST")');
+ indent.write(
+ 'fun setUp(binaryMessenger: BinaryMessenger, api: $apiName?) ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ indent.write('run ');
+ indent.scoped('{', '}', () {
+ String? taskQueue;
+ if (method.taskQueueType != TaskQueueType.serial) {
+ taskQueue = 'taskQueue';
+ indent.writeln(
+ 'val $taskQueue = binaryMessenger.makeBackgroundTaskQueue()');
+ }
+
+ final String channelName = makeChannelName(api, method);
+
+ indent.write(
+ 'val channel = BasicMessageChannel<Any?>(binaryMessenger, "$channelName", codec');
+
+ if (taskQueue != null) {
+ indent.addln(', $taskQueue)');
+ } else {
+ indent.addln(')');
+ }
+
+ indent.write('if (api != null) ');
+ indent.scoped('{', '}', () {
+ final String messageVarName =
+ method.arguments.isNotEmpty ? 'message' : '_';
+
+ indent.write('channel.setMessageHandler ');
+ indent.scoped('{ $messageVarName, reply ->', '}', () {
+ indent.writeln('var wrapped = listOf<Any?>()');
+ indent.write('try ');
+ indent.scoped('{', '}', () {
+ final List<String> methodArgument = <String>[];
+ if (method.arguments.isNotEmpty) {
+ indent.writeln('val args = message as List<Any?>');
+ enumerate(method.arguments, (int index, NamedType arg) {
+ final String argName = _getSafeArgumentName(index, arg);
+ final String argIndex = 'args[$index]';
+ indent.writeln(
+ 'val $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
+ methodArgument.add(argName);
+ });
+ }
+ final String call =
+ 'api.${method.name}(${methodArgument.join(', ')})';
+ if (method.isAsynchronous) {
+ indent.write('$call ');
+ final String resultValue =
+ method.returnType.isVoid ? 'null' : 'it';
+ indent.scoped('{', '}', () {
+ indent.writeln('reply.reply(wrapResult($resultValue))');
+ });
+ } else if (method.returnType.isVoid) {
+ indent.writeln(call);
+ indent.writeln('wrapped = listOf<Any?>(null)');
+ } else {
+ indent.writeln('wrapped = listOf<Any?>($call)');
+ }
+ }, addTrailingNewline: false);
+ indent.add(' catch (exception: Error) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('wrapped = wrapError(exception)');
+ if (method.isAsynchronous) {
+ indent.writeln('reply.reply(wrapped)');
+ }
+ });
+ if (!method.isAsynchronous) {
+ indent.writeln('reply.reply(wrapped)');
+ }
+ });
+ }, addTrailingNewline: false);
+ indent.scoped(' else {', '}', () {
+ indent.writeln('channel.setMessageHandler(null)');
+ });
+ });
+ }
+ });
+ });
+ });
+ }
+
+ /// Writes the codec class that will be used by [api].
+ /// Example:
+ /// private static class FooCodec extends StandardMessageCodec {...}
+ void _writeCodec(Indent indent, Api api, Root root) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
+ final String codecName = _getCodecName(api);
+ indent.writeln('@Suppress("UNCHECKED_CAST")');
+ indent.write('private object $codecName : StandardMessageCodec() ');
+ indent.scoped('{', '}', () {
+ indent.write(
+ 'override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? ');
+ indent.scoped('{', '}', () {
+ indent.write('return when (type) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write('${customClass.enumeration}.toByte() -> ');
+ indent.scoped('{', '}', () {
+ indent.write('return (readValue(buffer) as? List<Any?>)?.let ');
+ indent.scoped('{', '}', () {
+ indent.writeln('${customClass.name}.fromList(it)');
+ });
+ });
+ }
+ indent.writeln('else -> super.readValueOfType(type, buffer)');
+ });
+ });
+
+ indent.write(
+ 'override fun writeValue(stream: ByteArrayOutputStream, value: Any?) ');
+ indent.writeScoped('{', '}', () {
+ indent.write('when (value) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write('is ${customClass.name} -> ');
+ indent.scoped('{', '}', () {
+ indent.writeln('stream.write(${customClass.enumeration})');
+ indent.writeln('writeValue(stream, value.toList())');
+ });
+ }
+ indent.writeln('else -> super.writeValue(stream, value)');
+ });
+ });
+ });
+ indent.addln('');
+ }
+
+ void _writeWrapResult(Indent indent) {
+ indent.addln('');
+ indent.write('private fun wrapResult(result: Any?): List<Any?> ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return listOf(result)');
+ });
+ }
+
+ void _writeWrapError(Indent indent) {
+ indent.addln('');
+ indent.write('private fun wrapError(exception: Throwable): List<Any> ');
+ indent.scoped('{', '}', () {
+ indent.write('return ');
+ indent.scoped('listOf<Any>(', ')', () {
+ indent.writeln('exception.javaClass.simpleName,');
+ indent.writeln('exception.toString(),');
+ indent.writeln(
+ '"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)');
+ });
+ });
+ }
+
+ @override
+ void writeGeneralUtilities(
+ KotlinOptions generatorOptions, Root root, Indent indent) {
+ _writeWrapResult(indent);
+ _writeWrapError(indent);
+ }
+}
+
+HostDatatype _getHostDatatype(Root root, NamedType field) {
+ return getFieldHostDatatype(field, root.classes, root.enums,
+ (TypeDeclaration x) => _kotlinTypeForBuiltinDartType(x));
}
/// Calculates the name of the codec that will be generated for [api].
String _getCodecName(Api api) => '${api.name}Codec';
-/// Writes the codec class that will be used by [api].
-/// Example:
-/// private static class FooCodec extends StandardMessageCodec {...}
-void _writeCodec(Indent indent, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
- final String codecName = _getCodecName(api);
- indent.writeln('@Suppress("UNCHECKED_CAST")');
- indent.write('private object $codecName : StandardMessageCodec() ');
- indent.scoped('{', '}', () {
- indent.write(
- 'override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? ');
- indent.scoped('{', '}', () {
- indent.write('return when (type) ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write('${customClass.enumeration}.toByte() -> ');
- indent.scoped('{', '}', () {
- indent.write('return (readValue(buffer) as? List<Any?>)?.let ');
- indent.scoped('{', '}', () {
- indent.writeln('${customClass.name}.fromList(it)');
- });
- });
- }
- indent.writeln('else -> super.readValueOfType(type, buffer)');
- });
- });
-
- indent.write(
- 'override fun writeValue(stream: ByteArrayOutputStream, value: Any?) ');
- indent.writeScoped('{', '}', () {
- indent.write('when (value) ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write('is ${customClass.name} -> ');
- indent.scoped('{', '}', () {
- indent.writeln('stream.write(${customClass.enumeration})');
- indent.writeln('writeValue(stream, value.toList())');
- });
- }
- indent.writeln('else -> super.writeValue(stream, value)');
- });
- });
- });
-}
-
-/// Write the kotlin code that represents a host [Api], [api].
-/// Example:
-/// interface Foo {
-/// Int add(x: Int, y: Int);
-/// companion object {
-/// fun setUp(binaryMessenger: BinaryMessenger, api: Api) {...}
-/// }
-/// }
-void _writeHostApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
-
- final String apiName = api.name;
-
- final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
-
- const List<String> generatedMessages = <String>[
- ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- indent.write('interface $apiName ');
- indent.scoped('{', '}', () {
- for (final Method method in api.methods) {
- final List<String> argSignature = <String>[];
- if (method.arguments.isNotEmpty) {
- final Iterable<String> argTypes = method.arguments
- .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type));
- final Iterable<String> argNames =
- method.arguments.map((NamedType e) => e.name);
- argSignature
- .addAll(map2(argTypes, argNames, (String argType, String argName) {
- return '$argName: $argType';
- }));
- }
-
- final String returnType = method.returnType.isVoid
- ? ''
- : _nullsafeKotlinTypeForDartType(method.returnType);
-
- addDocumentationComments(
- indent, method.documentationComments, _docCommentSpec);
-
- if (method.isAsynchronous) {
- argSignature.add('callback: ($returnType) -> Unit');
- indent.writeln('fun ${method.name}(${argSignature.join(', ')})');
- } else if (method.returnType.isVoid) {
- indent.writeln('fun ${method.name}(${argSignature.join(', ')})');
- } else {
- indent.writeln(
- 'fun ${method.name}(${argSignature.join(', ')}): $returnType');
- }
- }
-
- indent.addln('');
- indent.write('companion object ');
- indent.scoped('{', '}', () {
- indent.writeln('/** The codec used by $apiName. */');
- indent.write('val codec: MessageCodec<Any?> by lazy ');
- indent.scoped('{', '}', () {
- if (isCustomCodec) {
- indent.writeln(_getCodecName(api));
- } else {
- indent.writeln('StandardMessageCodec()');
- }
- });
- indent.writeln(
- '/** Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`. */');
- indent.writeln('@Suppress("UNCHECKED_CAST")');
- indent.write(
- 'fun setUp(binaryMessenger: BinaryMessenger, api: $apiName?) ');
- indent.scoped('{', '}', () {
- for (final Method method in api.methods) {
- indent.write('run ');
- indent.scoped('{', '}', () {
- String? taskQueue;
- if (method.taskQueueType != TaskQueueType.serial) {
- taskQueue = 'taskQueue';
- indent.writeln(
- 'val $taskQueue = binaryMessenger.makeBackgroundTaskQueue()');
- }
-
- final String channelName = makeChannelName(api, method);
-
- indent.write(
- 'val channel = BasicMessageChannel<Any?>(binaryMessenger, "$channelName", codec');
-
- if (taskQueue != null) {
- indent.addln(', $taskQueue)');
- } else {
- indent.addln(')');
- }
-
- indent.write('if (api != null) ');
- indent.scoped('{', '}', () {
- final String messageVarName =
- method.arguments.isNotEmpty ? 'message' : '_';
-
- indent.write('channel.setMessageHandler ');
- indent.scoped('{ $messageVarName, reply ->', '}', () {
- indent.writeln('var wrapped = listOf<Any?>()');
- indent.write('try ');
- indent.scoped('{', '}', () {
- final List<String> methodArgument = <String>[];
- if (method.arguments.isNotEmpty) {
- indent.writeln('val args = message as List<Any?>');
- enumerate(method.arguments, (int index, NamedType arg) {
- final String argName = _getSafeArgumentName(index, arg);
- final String argIndex = 'args[$index]';
- indent.writeln(
- 'val $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
- methodArgument.add(argName);
- });
- }
- final String call =
- 'api.${method.name}(${methodArgument.join(', ')})';
- if (method.isAsynchronous) {
- indent.write('$call ');
- final String resultValue =
- method.returnType.isVoid ? 'null' : 'it';
- indent.scoped('{', '}', () {
- indent.writeln('reply.reply(wrapResult($resultValue))');
- });
- } else if (method.returnType.isVoid) {
- indent.writeln(call);
- indent.writeln('wrapped = listOf<Any?>(null)');
- } else {
- indent.writeln('wrapped = listOf<Any?>($call)');
- }
- }, addTrailingNewline: false);
- indent.add(' catch (exception: Error) ');
- indent.scoped('{', '}', () {
- indent.writeln('wrapped = wrapError(exception)');
- if (method.isAsynchronous) {
- indent.writeln('reply.reply(wrapped)');
- }
- });
- if (!method.isAsynchronous) {
- indent.writeln('reply.reply(wrapped)');
- }
- });
- }, addTrailingNewline: false);
- indent.scoped(' else {', '}', () {
- indent.writeln('channel.setMessageHandler(null)');
- });
- });
- }
- });
- });
- });
-}
-
String _getArgumentName(int count, NamedType argument) =>
argument.name.isEmpty ? 'arg$count' : argument.name;
@@ -287,88 +641,6 @@
String _getSafeArgumentName(int count, NamedType argument) =>
'${_getArgumentName(count, argument)}Arg';
-/// Writes the code for a flutter [Api], [api].
-/// Example:
-/// class Foo(private val binaryMessenger: BinaryMessenger) {
-/// fun add(x: Int, y: Int, callback: (Int?) -> Unit) {...}
-/// }
-void _writeFlutterApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.flutter);
- final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
-
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents Flutter messages that can be called from Kotlin.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- final String apiName = api.name;
- indent.writeln('@Suppress("UNCHECKED_CAST")');
- indent.write('class $apiName(private val binaryMessenger: BinaryMessenger) ');
- indent.scoped('{', '}', () {
- indent.write('companion object ');
- indent.scoped('{', '}', () {
- indent.writeln('/** The codec used by $apiName. */');
- indent.write('val codec: MessageCodec<Any?> by lazy ');
- indent.scoped('{', '}', () {
- if (isCustomCodec) {
- indent.writeln(_getCodecName(api));
- } else {
- indent.writeln('StandardMessageCodec()');
- }
- });
- });
-
- for (final Method func in api.methods) {
- final String channelName = makeChannelName(api, func);
- final String returnType = func.returnType.isVoid
- ? ''
- : _nullsafeKotlinTypeForDartType(func.returnType);
- String sendArgument;
-
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
-
- if (func.arguments.isEmpty) {
- indent.write('fun ${func.name}(callback: ($returnType) -> Unit) ');
- sendArgument = 'null';
- } else {
- final Iterable<String> argTypes = func.arguments
- .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type));
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgumentName);
- sendArgument = 'listOf(${argNames.join(', ')})';
- final String argsSignature = map2(argTypes, argNames,
- (String type, String name) => '$name: $type').join(', ');
- if (func.returnType.isVoid) {
- indent
- .write('fun ${func.name}($argsSignature, callback: () -> Unit) ');
- } else {
- indent.write(
- 'fun ${func.name}($argsSignature, callback: ($returnType) -> Unit) ');
- }
- }
- indent.scoped('{', '}', () {
- const String channel = 'channel';
- indent.writeln(
- 'val $channel = BasicMessageChannel<Any?>(binaryMessenger, "$channelName", codec)');
- indent.write('$channel.send($sendArgument) ');
- if (func.returnType.isVoid) {
- indent.scoped('{', '}', () {
- indent.writeln('callback()');
- });
- } else {
- final String forceUnwrap = func.returnType.isNullable ? '?' : '';
- indent.scoped('{', '}', () {
- indent.writeln('val result = it as$forceUnwrap $returnType');
- indent.writeln('callback(result)');
- });
- }
- });
- }
- });
-}
-
String _castForceUnwrap(String value, TypeDeclaration type, Root root) {
if (isEnum(root, type)) {
final String forceUnwrap = type.isNullable ? '' : '!!';
@@ -448,258 +720,3 @@
final String nullSafe = type.isNullable ? '?' : '';
return '${_kotlinTypeForDartType(type)}$nullSafe';
}
-
-/// Generates the ".kotlin" file for the AST represented by [root] to [sink] with the
-/// provided [options].
-void generateKotlin(KotlinOptions options, Root root, StringSink sink) {
- final Set<String> rootClassNameSet =
- root.classes.map((Class x) => x.name).toSet();
- final Set<String> rootEnumNameSet =
- root.enums.map((Enum x) => x.name).toSet();
- final Indent indent = Indent(sink);
-
- HostDatatype getHostDatatype(NamedType field) {
- return getFieldHostDatatype(field, root.classes, root.enums,
- (TypeDeclaration x) => _kotlinTypeForBuiltinDartType(x));
- }
-
- void writeHeader() {
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- }
-
- void writeImports() {
- indent.writeln('import android.util.Log');
- indent.writeln('import io.flutter.plugin.common.BasicMessageChannel');
- indent.writeln('import io.flutter.plugin.common.BinaryMessenger');
- indent.writeln('import io.flutter.plugin.common.MessageCodec');
- indent.writeln('import io.flutter.plugin.common.StandardMessageCodec');
- indent.writeln('import java.io.ByteArrayOutputStream');
- indent.writeln('import java.nio.ByteBuffer');
- }
-
- void writeEnum(Enum anEnum) {
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
- indent.write('enum class ${anEnum.name}(val raw: Int) ');
- indent.scoped('{', '}', () {
- enumerate(anEnum.members, (int index, final EnumMember member) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- indent.write('${member.name.toUpperCase()}($index)');
- if (index != anEnum.members.length - 1) {
- indent.addln(',');
- } else {
- indent.addln(';');
- }
- });
-
- indent.writeln('');
- indent.write('companion object ');
- indent.scoped('{', '}', () {
- indent.write('fun ofRaw(raw: Int): ${anEnum.name}? ');
- indent.scoped('{', '}', () {
- indent.writeln('return values().firstOrNull { it.raw == raw }');
- });
- });
- });
- }
-
- void writeDataClass(Class klass) {
- void writeField(NamedType field) {
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
- indent.write(
- 'val ${field.name}: ${_nullsafeKotlinTypeForDartType(field.type)}');
- final String defaultNil = field.type.isNullable ? ' = null' : '';
- indent.add(defaultNil);
- }
-
- void writeToList() {
- indent.write('fun toList(): List<Any?> ');
- indent.scoped('{', '}', () {
- indent.write('return listOf<Any?>');
- indent.scoped('(', ')', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getHostDatatype(field);
- String toWriteValue = '';
- final String fieldName = field.name;
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- toWriteValue = '$fieldName?.toList()';
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- toWriteValue = '$fieldName?.raw';
- } else {
- toWriteValue = fieldName;
- }
- indent.writeln('$toWriteValue,');
- }
- });
- });
- }
-
- void writeFromList() {
- final String className = klass.name;
-
- indent.write('companion object ');
- indent.scoped('{', '}', () {
- indent.writeln('@Suppress("UNCHECKED_CAST")');
- indent.write('fun fromList(list: List<Any?>): $className ');
-
- indent.scoped('{', '}', () {
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- final HostDatatype hostDatatype = getHostDatatype(field);
-
- // The StandardMessageCodec can give us [Integer, Long] for
- // a Dart 'int'. To keep things simple we just use 64bit
- // longs in Pigeon with Kotlin.
- final bool isInt = field.type.baseName == 'int';
-
- final String listValue = 'list[$index]';
- final String fieldType = _kotlinTypeForDartType(field.type);
-
- if (field.type.isNullable) {
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- indent.write('val ${field.name}: $fieldType? = ');
- indent.add('($listValue as? List<Any?>)?.let ');
- indent.scoped('{', '}', () {
- indent.writeln('$fieldType.fromList(it)');
- });
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- indent.write('val ${field.name}: $fieldType? = ');
- indent.add('($listValue as? Int)?.let ');
- indent.scoped('{', '}', () {
- indent.writeln('$fieldType.ofRaw(it)');
- });
- } else if (isInt) {
- indent.write('val ${field.name} = $listValue');
- indent.addln(
- '.let { if (it is Int) it.toLong() else it as? Long }');
- } else {
- indent.writeln('val ${field.name} = $listValue as? $fieldType');
- }
- } else {
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- indent.writeln(
- 'val ${field.name} = $fieldType.fromList($listValue as List<Any?>)');
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- indent.write(
- 'val ${field.name} = $fieldType.ofRaw($listValue as Int)!!');
- } else {
- indent.writeln('val ${field.name} = $listValue as $fieldType');
- }
- }
- });
-
- indent.writeln('');
- indent.write('return $className(');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final String comma =
- getFieldsInSerializationOrder(klass).last == field ? '' : ', ';
- indent.add('${field.name}$comma');
- }
- indent.addln(')');
- });
- });
- }
-
- const List<String> generatedMessages = <String>[
- ' Generated class from Pigeon that represents data sent in messages.'
- ];
- addDocumentationComments(
- indent, klass.documentationComments, _docCommentSpec,
- generatorComments: generatedMessages);
-
- indent.write('data class ${klass.name} ');
- indent.scoped('(', '', () {
- for (final NamedType element in getFieldsInSerializationOrder(klass)) {
- writeField(element);
- if (getFieldsInSerializationOrder(klass).last != element) {
- indent.addln(',');
- } else {
- indent.addln('');
- }
- }
- });
-
- indent.scoped(') {', '}', () {
- writeFromList();
- writeToList();
- });
- }
-
- void writeApi(Api api) {
- if (api.location == ApiLocation.host) {
- _writeHostApi(indent, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApi(indent, api, root);
- }
- }
-
- void writeWrapResult() {
- indent.write('private fun wrapResult(result: Any?): List<Any?> ');
- indent.scoped('{', '}', () {
- indent.writeln('return listOf(result)');
- });
- }
-
- void writeWrapError() {
- indent.write('private fun wrapError(exception: Throwable): List<Any> ');
- indent.scoped('{', '}', () {
- indent.write('return ');
- indent.scoped('listOf<Any>(', ')', () {
- indent.writeln('exception.javaClass.simpleName,');
- indent.writeln('exception.toString(),');
- indent.writeln(
- '"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)');
- });
- });
- }
-
- writeHeader();
- indent.addln('');
- if (options.package != null) {
- indent.writeln('package ${options.package}');
- }
- indent.addln('');
- writeImports();
- indent.addln('');
- indent.writeln('/** Generated class from Pigeon. */');
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- writeEnum(anEnum);
- }
-
- for (final Class klass in root.classes) {
- indent.addln('');
- writeDataClass(klass);
- }
-
- if (root.apis.any((Api api) =>
- api.location == ApiLocation.host &&
- api.methods.any((Method it) => it.isAsynchronous))) {
- indent.addln('');
- }
-
- for (final Api api in root.apis) {
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodec(indent, api, root);
- indent.addln('');
- }
- writeApi(api);
- }
-
- indent.addln('');
- writeWrapResult();
- indent.addln('');
- writeWrapError();
-}
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 3d7e3a6..2b165c9 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -64,26 +64,770 @@
}
}
-/// Class that manages all Objc header code generation.
+/// Class that manages all Objc code generation.
class ObjcGenerator extends Generator<OutputFileOptions<ObjcOptions>> {
/// Instantiates a Objc Generator.
- ObjcGenerator();
+ const ObjcGenerator();
- /// Generates Objc files with specified [OutputFileOptions<ObjcOptions>]
+ /// Generates Objc file of type specified in [generatorOptions]
@override
- void generate(OutputFileOptions<ObjcOptions> languageOptions, Root root,
+ void generate(OutputFileOptions<ObjcOptions> generatorOptions, Root root,
StringSink sink) {
- final FileType fileType = languageOptions.fileType;
- assert(fileType == FileType.header || fileType == FileType.source);
-
- if (fileType == FileType.header) {
- generateObjcHeader(languageOptions.languageOptions, root, sink);
- } else {
- generateObjcSource(languageOptions.languageOptions, root, sink);
+ if (generatorOptions.fileType == FileType.header) {
+ const ObjcHeaderGenerator()
+ .generate(generatorOptions.languageOptions, root, sink);
+ } else if (generatorOptions.fileType == FileType.source) {
+ const ObjcSourceGenerator()
+ .generate(generatorOptions.languageOptions, root, sink);
}
}
}
+/// Generates Objc .h file.
+class ObjcHeaderGenerator extends StructuredGenerator<ObjcOptions> {
+ /// Constructor.
+ const ObjcHeaderGenerator();
+
+ @override
+ void writeFilePrologue(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.addln('');
+ }
+
+ @override
+ void writeFileImports(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln('#import <Foundation/Foundation.h>');
+ indent.addln('');
+
+ indent.writeln('@protocol FlutterBinaryMessenger;');
+ indent.writeln('@protocol FlutterMessageCodec;');
+ indent.writeln('@class FlutterError;');
+ indent.writeln('@class FlutterStandardTypedData;');
+ indent.addln('');
+ indent.writeln('NS_ASSUME_NONNULL_BEGIN');
+ }
+
+ @override
+ void writeEnum(
+ ObjcOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ final String enumName = _className(generatorOptions.prefix, anEnum.name);
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+
+ indent.write('typedef NS_ENUM(NSUInteger, $enumName) ');
+ indent.scoped('{', '};', () {
+ enumerate(anEnum.members, (int index, final EnumMember member) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ // Capitalized first letter to ensure Swift compatibility
+ indent.writeln(
+ '$enumName${member.name[0].toUpperCase()}${member.name.substring(1)} = $index,');
+ });
+ });
+ }
+
+ @override
+ void writeDataClasses(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln('');
+ for (final Class klass in root.classes) {
+ indent.writeln(
+ '@class ${_className(generatorOptions.prefix, klass.name)};');
+ }
+ indent.writeln('');
+ super.writeDataClasses(generatorOptions, root, indent);
+ }
+
+ @override
+ void writeDataClass(
+ ObjcOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final List<Class> classes = root.classes;
+ final List<Enum> enums = root.enums;
+ final String? prefix = generatorOptions.prefix;
+ final List<String> customEnumNames = enums.map((Enum x) => x.name).toList();
+
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec);
+
+ indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject');
+ if (getFieldsInSerializationOrder(klass).isNotEmpty) {
+ if (getFieldsInSerializationOrder(klass)
+ .map((NamedType e) => !e.type.isNullable)
+ .any((bool e) => e)) {
+ indent.writeln(
+ '$_docCommentPrefix `init` unavailable to enforce nonnull fields, see the `make` class method.');
+ indent.writeln('- (instancetype)init NS_UNAVAILABLE;');
+ }
+ _writeObjcSourceClassInitializerDeclaration(
+ indent, klass, classes, enums, prefix);
+ indent.addln(';');
+ }
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ classes,
+ enums,
+ (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
+ customResolver: customEnumNames.contains(field.type.baseName)
+ ? (String x) => _className(prefix, x)
+ : (String x) => '${_className(prefix, x)} *');
+ late final String propertyType;
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+ if (customEnumNames.contains(field.type.baseName)) {
+ propertyType = 'assign';
+ } else {
+ propertyType = _propertyTypeForDartType(field);
+ }
+ final String nullability =
+ _isNullable(hostDatatype, field.type) ? ', nullable' : '';
+ indent.writeln(
+ '@property(nonatomic, $propertyType$nullability) ${hostDatatype.datatype} ${field.name};');
+ }
+ indent.writeln('@end');
+ indent.writeln('');
+ }
+
+ @override
+ void writeClassEncode(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {}
+
+ @override
+ void writeClassDecode(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {}
+
+ @override
+ void writeApis(ObjcOptions generatorOptions, Root root, Indent indent) {
+ super.writeApis(generatorOptions, root, indent);
+ indent.writeln('NS_ASSUME_NONNULL_END');
+ }
+
+ @override
+ void writeFlutterApi(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ indent.writeln(
+ '$_docCommentPrefix The codec used by ${_className(generatorOptions.prefix, api.name)}.');
+ indent.writeln(
+ 'NSObject<FlutterMessageCodec> *${_getCodecGetterName(generatorOptions.prefix, api.name)}(void);');
+ indent.addln('');
+ final String apiName = _className(generatorOptions.prefix, api.name);
+ addDocumentationComments(
+ indent, api.documentationComments, _docCommentSpec);
+
+ indent.writeln('@interface $apiName : NSObject');
+ indent.writeln(
+ '- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;');
+ for (final Method func in api.methods) {
+ final _ObjcPtr returnType =
+ _objcTypeForDartType(generatorOptions.prefix, func.returnType);
+ final String callbackType = _callbackForType(func.returnType, returnType);
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
+ indent.writeln('${_makeObjcSignature(
+ func: func,
+ options: generatorOptions,
+ returnType: 'void',
+ lastArgName: 'completion',
+ lastArgType: callbackType,
+ isEnum: (TypeDeclaration t) => isEnum(root, t),
+ )};');
+ }
+ indent.writeln('@end');
+ indent.writeln('');
+ }
+
+ @override
+ void writeHostApi(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ indent.writeln(
+ '$_docCommentPrefix The codec used by ${_className(generatorOptions.prefix, api.name)}.');
+ indent.writeln(
+ 'NSObject<FlutterMessageCodec> *${_getCodecGetterName(generatorOptions.prefix, api.name)}(void);');
+ indent.addln('');
+ final String apiName = _className(generatorOptions.prefix, api.name);
+ addDocumentationComments(
+ indent, api.documentationComments, _docCommentSpec);
+
+ indent.writeln('@protocol $apiName');
+ for (final Method func in api.methods) {
+ final _ObjcPtr returnTypeName =
+ _objcTypeForDartType(generatorOptions.prefix, func.returnType);
+
+ String? lastArgName;
+ String? lastArgType;
+ String? returnType;
+ if (func.isAsynchronous) {
+ returnType = 'void';
+ if (func.returnType.isVoid) {
+ lastArgType = 'void(^)(FlutterError *_Nullable)';
+ lastArgName = 'completion';
+ } else {
+ lastArgType =
+ 'void(^)(${returnTypeName.ptr}_Nullable, FlutterError *_Nullable)';
+ lastArgName = 'completion';
+ }
+ } else {
+ returnType = func.returnType.isVoid
+ ? 'void'
+ : 'nullable ${returnTypeName.ptr.trim()}';
+ lastArgType = 'FlutterError *_Nullable *_Nonnull';
+ lastArgName = 'error';
+ }
+ final List<String> generatorComments = <String>[];
+ if (!func.returnType.isNullable &&
+ !func.returnType.isVoid &&
+ !func.isAsynchronous) {
+ generatorComments.add(' @return `nil` only when `error != nil`.');
+ }
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec,
+ generatorComments: generatorComments);
+
+ final String signature = _makeObjcSignature(
+ func: func,
+ options: generatorOptions,
+ returnType: returnType,
+ lastArgName: lastArgName,
+ lastArgType: lastArgType,
+ isEnum: (TypeDeclaration t) => isEnum(root, t),
+ );
+ indent.writeln('$signature;');
+ }
+ indent.writeln('@end');
+ indent.writeln('');
+ indent.writeln(
+ 'extern void ${apiName}Setup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<$apiName> *_Nullable api);');
+ indent.writeln('');
+ }
+}
+
+/// Generates Objc .m file.
+class ObjcSourceGenerator extends StructuredGenerator<ObjcOptions> {
+ /// Constructor.
+ const ObjcSourceGenerator();
+
+ @override
+ void writeFilePrologue(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.addln('');
+ }
+
+ @override
+ void writeFileImports(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln('#import "${generatorOptions.headerIncludePath}"');
+ indent.writeln('#import <Flutter/Flutter.h>');
+ indent.addln('');
+
+ indent.writeln('#if !__has_feature(objc_arc)');
+ indent.writeln('#error File requires ARC to be enabled.');
+ indent.writeln('#endif');
+ indent.addln('');
+ }
+
+ @override
+ void writeDataClasses(
+ ObjcOptions generatorOptions, Root root, Indent indent) {
+ _writeObjcSourceHelperFunctions(indent);
+ indent.addln('');
+
+ for (final Class klass in root.classes) {
+ _writeObjcSourceDataClassExtension(generatorOptions, indent, klass);
+ }
+ indent.writeln('');
+ super.writeDataClasses(generatorOptions, root, indent);
+ }
+
+ @override
+ void writeDataClass(
+ ObjcOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+ final String className = _className(generatorOptions.prefix, klass.name);
+
+ indent.writeln('@implementation $className');
+ _writeObjcSourceClassInitializer(generatorOptions, root, indent, klass,
+ customClassNames, customEnumNames, className);
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ indent.writeln('@end');
+ indent.writeln('');
+ }
+
+ @override
+ void writeClassEncode(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write('- (NSArray *)toList ');
+ indent.scoped('{', '}', () {
+ indent.write('return');
+ indent.scoped(' @[', '];', () {
+ for (final NamedType field in klass.fields) {
+ indent.writeln(
+ '${_arrayValue(customClassNames, customEnumNames, field)},');
+ }
+ });
+ });
+ }
+
+ @override
+ void writeClassDecode(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ final String className = _className(generatorOptions.prefix, klass.name);
+ indent.write('+ ($className *)fromList:(NSArray *)list ');
+ indent.scoped('{', '}', () {
+ const String resultName = 'pigeonResult';
+ indent.writeln('$className *$resultName = [[$className alloc] init];');
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ if (customEnumNames.contains(field.type.baseName)) {
+ indent.writeln(
+ '$resultName.${field.name} = [${_listGetter(customClassNames, 'list', field, index, generatorOptions.prefix)} integerValue];');
+ } else {
+ indent.writeln(
+ '$resultName.${field.name} = ${_listGetter(customClassNames, 'list', field, index, generatorOptions.prefix)};');
+ if (!field.type.isNullable) {
+ indent.writeln('NSAssert($resultName.${field.name} != nil, @"");');
+ }
+ }
+ });
+ indent.writeln('return $resultName;');
+ });
+
+ indent.writeln(
+ '+ (nullable $className *)nullableFromList:(NSArray *)list { return (list) ? [$className fromList:list] : nil; }');
+ }
+
+ void _writeCodecAndGetter(
+ ObjcOptions generatorOptions, Root root, Indent indent, Api api) {
+ final String codecName = _getCodecName(generatorOptions.prefix, api.name);
+ if (getCodecClasses(api, root).isNotEmpty) {
+ _writeCodec(indent, codecName, generatorOptions, api, root);
+ indent.addln('');
+ }
+ _writeCodecGetter(indent, codecName, generatorOptions, api, root);
+ indent.addln('');
+ }
+
+ @override
+ void writeFlutterApi(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ final String apiName = _className(generatorOptions.prefix, api.name);
+
+ _writeCodecAndGetter(generatorOptions, root, indent, api);
+
+ _writeExtension(indent, apiName);
+ indent.addln('');
+ indent.writeln('@implementation $apiName');
+ indent.addln('');
+ _writeInitializer(indent);
+ for (final Method func in api.methods) {
+ _writeMethod(generatorOptions, root, indent, api, func);
+ }
+ indent.writeln('@end');
+ indent.writeln('');
+ }
+
+ @override
+ void writeHostApi(
+ ObjcOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.host);
+ final String apiName = _className(generatorOptions.prefix, api.name);
+
+ _writeCodecAndGetter(generatorOptions, root, indent, api);
+
+ const String channelName = 'channel';
+ indent.write(
+ 'void ${apiName}Setup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<$apiName> *api) ');
+ indent.scoped('{', '}', () {
+ for (final Method func in api.methods) {
+ indent.write('');
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
+ indent.scoped('{', '}', () {
+ String? taskQueue;
+ if (func.taskQueueType != TaskQueueType.serial) {
+ taskQueue = 'taskQueue';
+ indent.writeln(
+ 'NSObject<FlutterTaskQueue> *$taskQueue = [binaryMessenger makeBackgroundTaskQueue];');
+ }
+ _writeChannelAllocation(
+ generatorOptions, indent, api, func, channelName, taskQueue);
+ indent.write('if (api) ');
+ indent.scoped('{', '}', () {
+ _writeChannelApiBinding(
+ generatorOptions, root, indent, apiName, func, channelName);
+ });
+ indent.write('else ');
+ indent.scoped('{', '}', () {
+ indent.writeln('[$channelName setMessageHandler:nil];');
+ });
+ });
+ }
+ });
+ }
+
+ void _writeChannelApiBinding(ObjcOptions generatorOptions, Root root,
+ Indent indent, String apiName, Method func, String channel) {
+ void unpackArgs(String variable, Iterable<String> argNames) {
+ indent.writeln('NSArray *args = $variable;');
+ map3(wholeNumbers.take(func.arguments.length), argNames, func.arguments,
+ (int count, String argName, NamedType arg) {
+ if (isEnum(root, arg.type)) {
+ return '${_className(generatorOptions.prefix, arg.type.baseName)} $argName = [GetNullableObjectAtIndex(args, $count) integerValue];';
+ } else {
+ final _ObjcPtr argType =
+ _objcTypeForDartType(generatorOptions.prefix, arg.type);
+ return '${argType.ptr}$argName = GetNullableObjectAtIndex(args, $count);';
+ }
+ }).forEach(indent.writeln);
+ }
+
+ void writeAsyncBindings(Iterable<String> selectorComponents,
+ String callSignature, _ObjcPtr returnType) {
+ if (func.returnType.isVoid) {
+ const String callback = 'callback(wrapResult(nil, error));';
+ if (func.arguments.isEmpty) {
+ indent.writeScoped(
+ '[api ${selectorComponents.first}:^(FlutterError *_Nullable error) {',
+ '}];', () {
+ indent.writeln(callback);
+ });
+ } else {
+ indent.writeScoped(
+ '[api $callSignature ${selectorComponents.last}:^(FlutterError *_Nullable error) {',
+ '}];', () {
+ indent.writeln(callback);
+ });
+ }
+ } else {
+ const String callback = 'callback(wrapResult(output, error));';
+ if (func.arguments.isEmpty) {
+ indent.writeScoped(
+ '[api ${selectorComponents.first}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
+ '}];', () {
+ indent.writeln(callback);
+ });
+ } else {
+ indent.writeScoped(
+ '[api $callSignature ${selectorComponents.last}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
+ '}];', () {
+ indent.writeln(callback);
+ });
+ }
+ }
+ }
+
+ void writeSyncBindings(String call, _ObjcPtr returnType) {
+ indent.writeln('FlutterError *error;');
+ if (func.returnType.isVoid) {
+ indent.writeln('$call;');
+ indent.writeln('callback(wrapResult(nil, error));');
+ } else {
+ indent.writeln('${returnType.ptr}output = $call;');
+ indent.writeln('callback(wrapResult(output, error));');
+ }
+ }
+
+ // TODO(gaaclarke): Incorporate this into _getSelectorComponents.
+ final String lastSelectorComponent =
+ func.isAsynchronous ? 'completion' : 'error';
+ final String selector = _getSelector(func, lastSelectorComponent);
+ indent.writeln(
+ 'NSCAssert([api respondsToSelector:@selector($selector)], @"$apiName api (%@) doesn\'t respond to @selector($selector)", api);');
+ indent.write(
+ '[$channel setMessageHandler:^(id _Nullable message, FlutterReply callback) ');
+ indent.scoped('{', '}];', () {
+ final _ObjcPtr returnType =
+ _objcTypeForDartType(generatorOptions.prefix, func.returnType);
+ final Iterable<String> selectorComponents =
+ _getSelectorComponents(func, lastSelectorComponent);
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgName);
+ final String callSignature =
+ map2(selectorComponents.take(argNames.length), argNames,
+ (String selectorComponent, String argName) {
+ return '$selectorComponent:$argName';
+ }).join(' ');
+ if (func.arguments.isNotEmpty) {
+ unpackArgs('message', argNames);
+ }
+ if (func.isAsynchronous) {
+ writeAsyncBindings(selectorComponents, callSignature, returnType);
+ } else {
+ final String syncCall = func.arguments.isEmpty
+ ? '[api ${selectorComponents.first}:&error]'
+ : '[api $callSignature error:&error]';
+ writeSyncBindings(syncCall, returnType);
+ }
+ });
+ }
+
+ void _writeChannelAllocation(ObjcOptions generatorOptions, Indent indent,
+ Api api, Method func, String varName, String? taskQueue) {
+ indent.writeln('FlutterBasicMessageChannel *$varName =');
+ indent.inc();
+ indent.writeln('[[FlutterBasicMessageChannel alloc]');
+ indent.inc();
+ indent.writeln('initWithName:@"${makeChannelName(api, func)}"');
+ indent.writeln('binaryMessenger:binaryMessenger');
+ indent.write('codec:');
+ indent.add('${_getCodecGetterName(generatorOptions.prefix, api.name)}()');
+
+ if (taskQueue != null) {
+ indent.addln('');
+ indent.addln('taskQueue:$taskQueue];');
+ } else {
+ indent.addln('];');
+ }
+ indent.dec();
+ indent.dec();
+ }
+
+ void _writeObjcSourceHelperFunctions(Indent indent) {
+ indent.format('''
+static NSArray *wrapResult(id result, FlutterError *error) {
+\tif (error) {
+\t\treturn @[ error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] ];
+\t}
+\treturn @[ result ?: [NSNull null] ];
+}''');
+ indent.format('''
+static id GetNullableObject(NSDictionary* dict, id key) {
+\tid result = dict[key];
+\treturn (result == [NSNull null]) ? nil : result;
+}
+static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) {
+\tid result = array[key];
+\treturn (result == [NSNull null]) ? nil : result;
+}
+''');
+ }
+
+ void _writeObjcSourceDataClassExtension(
+ ObjcOptions languageOptions, Indent indent, Class klass) {
+ final String className = _className(languageOptions.prefix, klass.name);
+ indent.writeln('@interface $className ()');
+ indent.writeln('+ ($className *)fromList:(NSArray *)list;');
+ indent
+ .writeln('+ (nullable $className *)nullableFromList:(NSArray *)list;');
+ indent.writeln('- (NSArray *)toList;');
+ indent.writeln('@end');
+ indent.writeln('');
+ }
+
+ void _writeObjcSourceClassInitializer(
+ ObjcOptions languageOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ String className,
+ ) {
+ _writeObjcSourceClassInitializerDeclaration(
+ indent, klass, root.classes, root.enums, languageOptions.prefix);
+ indent.writeScoped(' {', '}', () {
+ const String result = 'pigeonResult';
+ indent.writeln('$className* $result = [[$className alloc] init];');
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ indent.writeln('$result.${field.name} = ${field.name};');
+ }
+ indent.writeln('return $result;');
+ });
+ }
+
+ /// Writes the codec that will be used for encoding messages for the [api].
+ ///
+ /// Example:
+ /// @interface FooHostApiCodecReader : FlutterStandardReader
+ /// ...
+ /// @interface FooHostApiCodecWriter : FlutterStandardWriter
+ /// ...
+ /// @interface FooHostApiCodecReaderWriter : FlutterStandardReaderWriter
+ /// ...
+ /// NSObject<FlutterMessageCodec> *FooHostApiCodecGetCodec() {...}
+ void _writeCodec(
+ Indent indent, String name, ObjcOptions options, Api api, Root root) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
+ final String readerWriterName = '${name}ReaderWriter';
+ final String readerName = '${name}Reader';
+ final String writerName = '${name}Writer';
+ indent.writeln('@interface $readerName : FlutterStandardReader');
+ indent.writeln('@end');
+ indent.writeln('@implementation $readerName');
+ indent.writeln('- (nullable id)readValueOfType:(UInt8)type ');
+ indent.scoped('{', '}', () {
+ indent.write('switch (type) ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write('case ${customClass.enumeration}: ');
+ indent.writeScoped('', '', () {
+ indent.writeln(
+ 'return [${_className(options.prefix, customClass.name)} fromList:[self readValue]];');
+ });
+ }
+ indent.write('default:');
+ indent.writeScoped('', '', () {
+ indent.writeln('return [super readValueOfType:type];');
+ });
+ });
+ });
+ indent.writeln('@end');
+ indent.addln('');
+ indent.writeln('@interface $writerName : FlutterStandardWriter');
+ indent.writeln('@end');
+ indent.writeln('@implementation $writerName');
+ indent.writeln('- (void)writeValue:(id)value ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass in codecClasses) {
+ indent.write(
+ 'if ([value isKindOfClass:[${_className(options.prefix, customClass.name)} class]]) ');
+ indent.scoped('{', '} else ', () {
+ indent.writeln('[self writeByte:${customClass.enumeration}];');
+ indent.writeln('[self writeValue:[value toList]];');
+ });
+ }
+ indent.scoped('{', '}', () {
+ indent.writeln('[super writeValue:value];');
+ });
+ });
+ indent.writeln('@end');
+ indent.addln('');
+ indent.format('''
+@interface $readerWriterName : FlutterStandardReaderWriter
+@end
+@implementation $readerWriterName
+- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
+\treturn [[$writerName alloc] initWithData:data];
+}
+- (FlutterStandardReader *)readerWithData:(NSData *)data {
+\treturn [[$readerName alloc] initWithData:data];
+}
+@end
+''');
+ }
+
+ void _writeCodecGetter(
+ Indent indent, String name, ObjcOptions options, Api api, Root root) {
+ final String readerWriterName = '${name}ReaderWriter';
+
+ indent.write(
+ 'NSObject<FlutterMessageCodec> *${_getCodecGetterName(options.prefix, api.name)}() ');
+ indent.scoped('{', '}', () {
+ indent
+ .writeln('static FlutterStandardMessageCodec *sSharedObject = nil;');
+ if (getCodecClasses(api, root).isNotEmpty) {
+ indent.writeln('static dispatch_once_t sPred = 0;');
+ indent.write('dispatch_once(&sPred, ^');
+ indent.scoped('{', '});', () {
+ indent.writeln(
+ '$readerWriterName *readerWriter = [[$readerWriterName alloc] init];');
+ indent.writeln(
+ 'sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];');
+ });
+ } else {
+ indent.writeln(
+ 'sSharedObject = [FlutterStandardMessageCodec sharedInstance];');
+ }
+
+ indent.writeln('return sSharedObject;');
+ });
+ }
+}
+
+/// Writes the method declaration for the initializer.
+///
+/// Example '+ (instancetype)makeWithFoo:(NSString *)foo'
+void _writeObjcSourceClassInitializerDeclaration(Indent indent, Class klass,
+ List<Class> classes, List<Enum> enums, String? prefix) {
+ final List<String> customEnumNames = enums.map((Enum x) => x.name).toList();
+ indent.write('+ (instancetype)makeWith');
+ bool isFirst = true;
+ indent.nest(2, () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final String label = isFirst ? _capitalize(field.name) : field.name;
+ final void Function(String) printer = isFirst
+ ? indent.add
+ : (String x) {
+ indent.addln('');
+ indent.write(x);
+ };
+ isFirst = false;
+ final HostDatatype hostDatatype = getFieldHostDatatype(
+ field,
+ classes,
+ enums,
+ (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
+ customResolver: customEnumNames.contains(field.type.baseName)
+ ? (String x) => _className(prefix, x)
+ : (String x) => '${_className(prefix, x)} *');
+ final String nullable =
+ _isNullable(hostDatatype, field.type) ? 'nullable ' : '';
+ printer('$label:($nullable${hostDatatype.datatype})${field.name}');
+ }
+ });
+}
+
/// Calculates the ObjC class name, possibly prefixed.
String _className(String? prefix, String className) {
if (prefix != null) {
@@ -180,91 +924,6 @@
bool _isNullable(HostDatatype hostDatatype, TypeDeclaration type) =>
hostDatatype.datatype.contains('*') && type.isNullable;
-/// Writes the method declaration for the initializer.
-///
-/// Example '+ (instancetype)makeWithFoo:(NSString *)foo'
-void _writeInitializerDeclaration(Indent indent, Class klass,
- List<Class> classes, List<Enum> enums, String? prefix) {
- final List<String> enumNames = enums.map((Enum x) => x.name).toList();
- indent.write('+ (instancetype)makeWith');
- bool isFirst = true;
- indent.nest(2, () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final String label = isFirst ? _capitalize(field.name) : field.name;
- final void Function(String) printer = isFirst
- ? indent.add
- : (String x) {
- indent.addln('');
- indent.write(x);
- };
- isFirst = false;
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- classes,
- enums,
- (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
- customResolver: enumNames.contains(field.type.baseName)
- ? (String x) => _className(prefix, x)
- : (String x) => '${_className(prefix, x)} *');
- final String nullable =
- _isNullable(hostDatatype, field.type) ? 'nullable ' : '';
- printer('$label:($nullable${hostDatatype.datatype})${field.name}');
- }
- });
-}
-
-/// Writes the class declaration for a data class.
-///
-/// Example:
-/// @interface Foo : NSObject
-/// @property (nonatomic, copy) NSString *bar;
-/// @end
-void _writeClassDeclarations(
- Indent indent, List<Class> classes, List<Enum> enums, String? prefix) {
- final List<String> enumNames = enums.map((Enum x) => x.name).toList();
- for (final Class klass in classes) {
- addDocumentationComments(
- indent, klass.documentationComments, _docCommentSpec);
-
- indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject');
- if (getFieldsInSerializationOrder(klass).isNotEmpty) {
- if (getFieldsInSerializationOrder(klass)
- .map((NamedType e) => !e.type.isNullable)
- .any((bool e) => e)) {
- indent.writeln(
- '$_docCommentPrefix `init` unavailable to enforce nonnull fields, see the `make` class method.');
- indent.writeln('- (instancetype)init NS_UNAVAILABLE;');
- }
- _writeInitializerDeclaration(indent, klass, classes, enums, prefix);
- indent.addln(';');
- }
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getFieldHostDatatype(
- field,
- classes,
- enums,
- (TypeDeclaration x) => _objcTypePtrForPrimitiveDartType(prefix, x),
- customResolver: enumNames.contains(field.type.baseName)
- ? (String x) => _className(prefix, x)
- : (String x) => '${_className(prefix, x)} *');
- late final String propertyType;
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
- if (enumNames.contains(field.type.baseName)) {
- propertyType = 'assign';
- } else {
- propertyType = _propertyTypeForDartType(field);
- }
- final String nullability =
- _isNullable(hostDatatype, field.type) ? ', nullable' : '';
- indent.writeln(
- '@property(nonatomic, $propertyType$nullability) ${hostDatatype.datatype} ${field.name};');
- }
- indent.writeln('@end');
- indent.writeln('');
- }
-}
-
/// Generates the name of the codec that will be generated.
String _getCodecName(String? prefix, String className) =>
'${_className(prefix, className)}Codec';
@@ -274,104 +933,6 @@
String _getCodecGetterName(String? prefix, String className) =>
'${_className(prefix, className)}GetCodec';
-/// Writes the codec that will be used for encoding messages for the [api].
-///
-/// Example:
-/// @interface FooHostApiCodecReader : FlutterStandardReader
-/// ...
-/// @interface FooHostApiCodecWriter : FlutterStandardWriter
-/// ...
-/// @interface FooHostApiCodecReaderWriter : FlutterStandardReaderWriter
-/// ...
-/// NSObject<FlutterMessageCodec> *FooHostApiCodecGetCodec() {...}
-void _writeCodec(
- Indent indent, String name, ObjcOptions options, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final Iterable<EnumeratedClass> codecClasses = getCodecClasses(api, root);
- final String readerWriterName = '${name}ReaderWriter';
- final String readerName = '${name}Reader';
- final String writerName = '${name}Writer';
- indent.writeln('@interface $readerName : FlutterStandardReader');
- indent.writeln('@end');
- indent.writeln('@implementation $readerName');
- indent.writeln('- (nullable id)readValueOfType:(UInt8)type ');
- indent.scoped('{', '}', () {
- indent.write('switch (type) ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write('case ${customClass.enumeration}: ');
- indent.writeScoped('', '', () {
- indent.writeln(
- 'return [${_className(options.prefix, customClass.name)} fromList:[self readValue]];');
- });
- }
- indent.write('default:');
- indent.writeScoped('', '', () {
- indent.writeln('return [super readValueOfType:type];');
- });
- });
- });
- indent.writeln('@end');
- indent.addln('');
- indent.writeln('@interface $writerName : FlutterStandardWriter');
- indent.writeln('@end');
- indent.writeln('@implementation $writerName');
- indent.writeln('- (void)writeValue:(id)value ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass in codecClasses) {
- indent.write(
- 'if ([value isKindOfClass:[${_className(options.prefix, customClass.name)} class]]) ');
- indent.scoped('{', '} else ', () {
- indent.writeln('[self writeByte:${customClass.enumeration}];');
- indent.writeln('[self writeValue:[value toList]];');
- });
- }
- indent.scoped('{', '}', () {
- indent.writeln('[super writeValue:value];');
- });
- });
- indent.writeln('@end');
- indent.addln('');
- indent.format('''
-@interface $readerWriterName : FlutterStandardReaderWriter
-@end
-@implementation $readerWriterName
-- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
-\treturn [[$writerName alloc] initWithData:data];
-}
-- (FlutterStandardReader *)readerWithData:(NSData *)data {
-\treturn [[$readerName alloc] initWithData:data];
-}
-@end
-''');
-}
-
-void _writeCodecGetter(
- Indent indent, String name, ObjcOptions options, Api api, Root root) {
- final String readerWriterName = '${name}ReaderWriter';
-
- indent.write(
- 'NSObject<FlutterMessageCodec> *${_getCodecGetterName(options.prefix, api.name)}() ');
- indent.scoped('{', '}', () {
- indent.writeln('static FlutterStandardMessageCodec *sSharedObject = nil;');
- if (getCodecClasses(api, root).isNotEmpty) {
- indent.writeln('static dispatch_once_t sPred = 0;');
- indent.write('dispatch_once(&sPred, ^');
- indent.scoped('{', '});', () {
- indent.writeln(
- '$readerWriterName *readerWriter = [[$readerWriterName alloc] init];');
- indent.writeln(
- 'sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];');
- });
- } else {
- indent.writeln(
- 'sSharedObject = [FlutterStandardMessageCodec sharedInstance];');
- }
-
- indent.writeln('return sSharedObject;');
- });
-}
-
String _capitalize(String str) =>
(str.isEmpty) ? '' : str[0].toUpperCase() + str.substring(1);
@@ -451,187 +1012,13 @@
return '- ($returnType)$argSignature';
}
-/// Writes the declaration for an host [Api].
-///
-/// Example:
-/// @protocol Foo
-/// - (NSInteger)add:(NSInteger)x to:(NSInteger)y error:(NSError**)error;
-/// @end
-///
-/// extern void FooSetup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<Foo> *_Nullable api);
-void _writeHostApiDeclaration(
- Indent indent, Api api, ObjcOptions options, Root root) {
- final String apiName = _className(options.prefix, api.name);
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
-
- indent.writeln('@protocol $apiName');
- for (final Method func in api.methods) {
- final _ObjcPtr returnTypeName =
- _objcTypeForDartType(options.prefix, func.returnType);
-
- String? lastArgName;
- String? lastArgType;
- String? returnType;
- if (func.isAsynchronous) {
- returnType = 'void';
- if (func.returnType.isVoid) {
- lastArgType = 'void(^)(FlutterError *_Nullable)';
- lastArgName = 'completion';
- } else {
- lastArgType =
- 'void(^)(${returnTypeName.ptr}_Nullable, FlutterError *_Nullable)';
- lastArgName = 'completion';
- }
- } else {
- returnType = func.returnType.isVoid
- ? 'void'
- : 'nullable ${returnTypeName.ptr.trim()}';
- lastArgType = 'FlutterError *_Nullable *_Nonnull';
- lastArgName = 'error';
- }
- final List<String> generatorComments = <String>[];
- if (!func.returnType.isNullable &&
- !func.returnType.isVoid &&
- !func.isAsynchronous) {
- generatorComments.add(' @return `nil` only when `error != nil`.');
- }
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec,
- generatorComments: generatorComments);
-
- final String signature = _makeObjcSignature(
- func: func,
- options: options,
- returnType: returnType,
- lastArgName: lastArgName,
- lastArgType: lastArgType,
- isEnum: (TypeDeclaration t) => isEnum(root, t),
- );
- indent.writeln('$signature;');
- }
- indent.writeln('@end');
- indent.writeln('');
- indent.writeln(
- 'extern void ${apiName}Setup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<$apiName> *_Nullable api);');
- indent.writeln('');
-}
-
-/// Writes the declaration for an flutter [Api].
-///
-/// Example:
-///
-/// @interface Foo : NSObject
-/// - (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
-/// - (void)add:(NSInteger)x to:(NSInteger)y completion:(void(^)(NSError *, NSInteger result)completion;
-/// @end
-void _writeFlutterApiDeclaration(
- Indent indent, Api api, ObjcOptions options, Root root) {
- final String apiName = _className(options.prefix, api.name);
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
-
- indent.writeln('@interface $apiName : NSObject');
- indent.writeln(
- '- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;');
- for (final Method func in api.methods) {
- final _ObjcPtr returnType =
- _objcTypeForDartType(options.prefix, func.returnType);
- final String callbackType = _callbackForType(func.returnType, returnType);
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
-
- indent.writeln('${_makeObjcSignature(
- func: func,
- options: options,
- returnType: 'void',
- lastArgName: 'completion',
- lastArgType: callbackType,
- isEnum: (TypeDeclaration t) => isEnum(root, t),
- )};');
- }
- indent.writeln('@end');
-}
-
/// Generates the ".h" file for the AST represented by [root] to [sink] with the
/// provided [options].
-void generateObjcHeader(ObjcOptions options, Root root, StringSink sink) {
- final Indent indent = Indent(sink);
+void generateObjcHeader(ObjcOptions options, Root root, Indent indent) {}
- void writeHeader() {
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- }
-
- void writeImports() {
- indent.writeln('#import <Foundation/Foundation.h>');
- }
-
- void writeForwardDeclarations() {
- indent.writeln('@protocol FlutterBinaryMessenger;');
- indent.writeln('@protocol FlutterMessageCodec;');
- indent.writeln('@class FlutterError;');
- indent.writeln('@class FlutterStandardTypedData;');
- }
-
- void writeEnum(Enum anEnum) {
- final String enumName = _className(options.prefix, anEnum.name);
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
-
- indent.write('typedef NS_ENUM(NSUInteger, $enumName) ');
- indent.scoped('{', '};', () {
- enumerate(anEnum.members, (int index, final EnumMember member) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- // Capitalized first letter to ensure Swift compatibility
- indent.writeln(
- '$enumName${member.name[0].toUpperCase()}${member.name.substring(1)} = $index,');
- });
- });
- }
-
- writeHeader();
- writeImports();
- writeForwardDeclarations();
- indent.writeln('');
-
- indent.writeln('NS_ASSUME_NONNULL_BEGIN');
-
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- writeEnum(anEnum);
- }
- indent.writeln('');
-
- for (final Class klass in root.classes) {
- indent.writeln('@class ${_className(options.prefix, klass.name)};');
- }
-
- indent.writeln('');
-
- _writeClassDeclarations(indent, root.classes, root.enums, options.prefix);
-
- for (final Api api in root.apis) {
- indent.writeln(
- '$_docCommentPrefix The codec used by ${_className(options.prefix, api.name)}.');
- indent.writeln(
- 'NSObject<FlutterMessageCodec> *${_getCodecGetterName(options.prefix, api.name)}(void);');
- indent.addln('');
- if (api.location == ApiLocation.host) {
- _writeHostApiDeclaration(indent, api, options, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApiDeclaration(indent, api, options, root);
- }
- }
-
- indent.writeln('NS_ASSUME_NONNULL_END');
-}
-
-String _listGetter(List<String> classNames, String list, NamedType field,
+String _listGetter(Set<String> customClassNames, String list, NamedType field,
int index, String? prefix) {
- if (classNames.contains(field.type.baseName)) {
+ if (customClassNames.contains(field.type.baseName)) {
String className = field.type.baseName;
if (prefix != null) {
className = '$prefix$className';
@@ -642,11 +1029,11 @@
}
}
-String _arrayValue(
- List<String> classNames, List<String> enumNames, NamedType field) {
- if (classNames.contains(field.type.baseName)) {
+String _arrayValue(Set<String> customClassNames, Set<String> customEnumNames,
+ NamedType field) {
+ if (customClassNames.contains(field.type.baseName)) {
return '(self.${field.name} ? [self.${field.name} toList] : [NSNull null])';
- } else if (enumNames.contains(field.type.baseName)) {
+ } else if (customEnumNames.contains(field.type.baseName)) {
return '@(self.${field.name})';
} else {
return '(self.${field.name} ?: [NSNull null])';
@@ -660,386 +1047,72 @@
String _getSafeArgName(int count, NamedType arg) =>
arg.name.isEmpty ? 'arg$count' : 'arg_${arg.name}';
-/// Writes the definition code for a host [Api].
-/// See also: [_writeHostApiDeclaration]
-void _writeHostApiSource(
- Indent indent, ObjcOptions options, Api api, Root root) {
- assert(api.location == ApiLocation.host);
- final String apiName = _className(options.prefix, api.name);
-
- void writeChannelAllocation(Method func, String varName, String? taskQueue) {
- indent.writeln('FlutterBasicMessageChannel *$varName =');
- indent.inc();
- indent.writeln('[[FlutterBasicMessageChannel alloc]');
- indent.inc();
- indent.writeln('initWithName:@"${makeChannelName(api, func)}"');
- indent.writeln('binaryMessenger:binaryMessenger');
- indent.write('codec:');
- indent.add('${_getCodecGetterName(options.prefix, api.name)}()');
-
- if (taskQueue != null) {
- indent.addln('');
- indent.addln('taskQueue:$taskQueue];');
- } else {
- indent.addln('];');
- }
- indent.dec();
- indent.dec();
- }
-
- void writeChannelApiBinding(Method func, String channel) {
- void unpackArgs(String variable, Iterable<String> argNames) {
- indent.writeln('NSArray *args = $variable;');
- map3(wholeNumbers.take(func.arguments.length), argNames, func.arguments,
- (int count, String argName, NamedType arg) {
- if (isEnum(root, arg.type)) {
- return '${_className(options.prefix, arg.type.baseName)} $argName = [GetNullableObjectAtIndex(args, $count) integerValue];';
- } else {
- final _ObjcPtr argType =
- _objcTypeForDartType(options.prefix, arg.type);
- return '${argType.ptr}$argName = GetNullableObjectAtIndex(args, $count);';
- }
- }).forEach(indent.writeln);
- }
-
- void writeAsyncBindings(Iterable<String> selectorComponents,
- String callSignature, _ObjcPtr returnType) {
- if (func.returnType.isVoid) {
- const String callback = 'callback(wrapResult(nil, error));';
- if (func.arguments.isEmpty) {
- indent.writeScoped(
- '[api ${selectorComponents.first}:^(FlutterError *_Nullable error) {',
- '}];', () {
- indent.writeln(callback);
- });
- } else {
- indent.writeScoped(
- '[api $callSignature ${selectorComponents.last}:^(FlutterError *_Nullable error) {',
- '}];', () {
- indent.writeln(callback);
- });
- }
- } else {
- const String callback = 'callback(wrapResult(output, error));';
- if (func.arguments.isEmpty) {
- indent.writeScoped(
- '[api ${selectorComponents.first}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
- '}];', () {
- indent.writeln(callback);
- });
- } else {
- indent.writeScoped(
- '[api $callSignature ${selectorComponents.last}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
- '}];', () {
- indent.writeln(callback);
- });
- }
- }
- }
-
- void writeSyncBindings(String call, _ObjcPtr returnType) {
- indent.writeln('FlutterError *error;');
- if (func.returnType.isVoid) {
- indent.writeln('$call;');
- indent.writeln('callback(wrapResult(nil, error));');
- } else {
- indent.writeln('${returnType.ptr}output = $call;');
- indent.writeln('callback(wrapResult(output, error));');
- }
- }
-
- // TODO(gaaclarke): Incorporate this into _getSelectorComponents.
- final String lastSelectorComponent =
- func.isAsynchronous ? 'completion' : 'error';
- final String selector = _getSelector(func, lastSelectorComponent);
- indent.writeln(
- 'NSCAssert([api respondsToSelector:@selector($selector)], @"$apiName api (%@) doesn\'t respond to @selector($selector)", api);');
- indent.write(
- '[$channel setMessageHandler:^(id _Nullable message, FlutterReply callback) ');
- indent.scoped('{', '}];', () {
- final _ObjcPtr returnType =
- _objcTypeForDartType(options.prefix, func.returnType);
- final Iterable<String> selectorComponents =
- _getSelectorComponents(func, lastSelectorComponent);
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgName);
- final String callSignature =
- map2(selectorComponents.take(argNames.length), argNames,
- (String selectorComponent, String argName) {
- return '$selectorComponent:$argName';
- }).join(' ');
- if (func.arguments.isNotEmpty) {
- unpackArgs('message', argNames);
- }
- if (func.isAsynchronous) {
- writeAsyncBindings(selectorComponents, callSignature, returnType);
- } else {
- final String syncCall = func.arguments.isEmpty
- ? '[api ${selectorComponents.first}:&error]'
- : '[api $callSignature error:&error]';
- writeSyncBindings(syncCall, returnType);
- }
- });
- }
-
- const String channelName = 'channel';
- indent.write(
- 'void ${apiName}Setup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<$apiName> *api) ');
- indent.scoped('{', '}', () {
- for (final Method func in api.methods) {
- indent.write('');
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
-
- indent.scoped('{', '}', () {
- String? taskQueue;
- if (func.taskQueueType != TaskQueueType.serial) {
- taskQueue = 'taskQueue';
- indent.writeln(
- 'NSObject<FlutterTaskQueue> *$taskQueue = [binaryMessenger makeBackgroundTaskQueue];');
- }
- writeChannelAllocation(func, channelName, taskQueue);
- indent.write('if (api) ');
- indent.scoped('{', '}', () {
- writeChannelApiBinding(func, channelName);
- });
- indent.write('else ');
- indent.scoped('{', '}', () {
- indent.writeln('[$channelName setMessageHandler:nil];');
- });
- });
- }
- });
-}
-
-/// Writes the definition code for a flutter [Api].
-/// See also: [_writeFlutterApiDeclaration]
-void _writeFlutterApiSource(
- Indent indent, ObjcOptions options, Api api, Root root) {
- assert(api.location == ApiLocation.flutter);
- final String apiName = _className(options.prefix, api.name);
-
- void writeExtension() {
- indent.writeln('@interface $apiName ()');
- indent.writeln(
- '@property (nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;');
- indent.writeln('@end');
- }
-
- void writeInitializer() {
- indent.write(
- '- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger ');
- indent.scoped('{', '}', () {
- indent.writeln('self = [super init];');
- indent.write('if (self) ');
- indent.scoped('{', '}', () {
- indent.writeln('_binaryMessenger = binaryMessenger;');
- });
- indent.writeln('return self;');
- });
- }
-
- void writeMethod(Method func) {
- final _ObjcPtr returnType =
- _objcTypeForDartType(options.prefix, func.returnType);
- final String callbackType = _callbackForType(func.returnType, returnType);
-
- String argNameFunc(int count, NamedType arg) => _getSafeArgName(count, arg);
- final Iterable<String> argNames = indexMap(func.arguments, argNameFunc);
- String sendArgument;
- if (func.arguments.isEmpty) {
- sendArgument = 'nil';
- } else {
- String makeVarOrNSNullExpression(String x) => '$x ?: [NSNull null]';
- sendArgument = '@[${argNames.map(makeVarOrNSNullExpression).join(', ')}]';
- }
- indent.write(_makeObjcSignature(
- func: func,
- options: options,
- returnType: 'void',
- lastArgName: 'completion',
- lastArgType: callbackType,
- argNameFunc: argNameFunc,
- isEnum: (TypeDeclaration t) => isEnum(root, t),
- ));
- indent.scoped(' {', '}', () {
- indent.writeln('FlutterBasicMessageChannel *channel =');
- indent.inc();
- indent.writeln('[FlutterBasicMessageChannel');
- indent.inc();
- indent.writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
- indent.writeln('binaryMessenger:self.binaryMessenger');
- indent.write('codec:${_getCodecGetterName(options.prefix, api.name)}()');
- indent.addln('];');
- indent.dec();
- indent.dec();
- indent.write('[channel sendMessage:$sendArgument reply:^(id reply) ');
- indent.scoped('{', '}];', () {
- if (func.returnType.isVoid) {
- indent.writeln('completion(nil);');
- } else {
- indent.writeln('${returnType.ptr}output = reply;');
- indent.writeln('completion(output, nil);');
- }
- });
- });
- }
-
- writeExtension();
- indent.addln('');
- indent.writeln('@implementation $apiName');
- indent.addln('');
- writeInitializer();
- api.methods.forEach(writeMethod);
+void _writeExtension(Indent indent, String apiName) {
+ indent.writeln('@interface $apiName ()');
+ indent.writeln(
+ '@property (nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;');
indent.writeln('@end');
}
-/// Generates the ".m" file for the AST represented by [root] to [sink] with the
-/// provided [options].
-void generateObjcSource(ObjcOptions options, Root root, StringSink sink) {
- final Indent indent = Indent(sink);
- final List<String> classNames =
- root.classes.map((Class x) => x.name).toList();
- final List<String> enumNames = root.enums.map((Enum x) => x.name).toList();
-
- void writeHeader() {
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- }
-
- void writeImports() {
- indent.writeln('#import "${options.headerIncludePath}"');
- indent.writeln('#import <Flutter/Flutter.h>');
- }
-
- void writeArcEnforcer() {
- indent.writeln('#if !__has_feature(objc_arc)');
- indent.writeln('#error File requires ARC to be enabled.');
- indent.writeln('#endif');
- }
-
- void writeHelperFunctions() {
- indent.format('''
-static NSArray *wrapResult(id result, FlutterError *error) {
-\tif (error) {
-\t\treturn @[ error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] ];
-\t}
-\treturn @[ result ?: [NSNull null] ];
-}''');
- indent.format('''
-static id GetNullableObject(NSDictionary* dict, id key) {
-\tid result = dict[key];
-\treturn (result == [NSNull null]) ? nil : result;
+void _writeInitializer(Indent indent) {
+ indent.write(
+ '- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger ');
+ indent.scoped('{', '}', () {
+ indent.writeln('self = [super init];');
+ indent.write('if (self) ');
+ indent.scoped('{', '}', () {
+ indent.writeln('_binaryMessenger = binaryMessenger;');
+ });
+ indent.writeln('return self;');
+ });
}
-static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) {
-\tid result = array[key];
-\treturn (result == [NSNull null]) ? nil : result;
-}
-''');
+
+void _writeMethod(ObjcOptions languageOptions, Root root, Indent indent,
+ Api api, Method func) {
+ final _ObjcPtr returnType =
+ _objcTypeForDartType(languageOptions.prefix, func.returnType);
+ final String callbackType = _callbackForType(func.returnType, returnType);
+
+ String argNameFunc(int count, NamedType arg) => _getSafeArgName(count, arg);
+ final Iterable<String> argNames = indexMap(func.arguments, argNameFunc);
+ String sendArgument;
+ if (func.arguments.isEmpty) {
+ sendArgument = 'nil';
+ } else {
+ String makeVarOrNSNullExpression(String x) => '$x ?: [NSNull null]';
+ sendArgument = '@[${argNames.map(makeVarOrNSNullExpression).join(', ')}]';
}
-
- void writeDataClassExtension(Class klass) {
- final String className = _className(options.prefix, klass.name);
- indent.writeln('@interface $className ()');
- indent.writeln('+ ($className *)fromList:(NSArray *)list;');
- indent
- .writeln('+ (nullable $className *)nullableFromList:(NSArray *)list;');
- indent.writeln('- (NSArray *)toList;');
- indent.writeln('@end');
- }
-
- void writeDataClassImplementation(Class klass) {
- final String className = _className(options.prefix, klass.name);
- void writeInitializer() {
- _writeInitializerDeclaration(
- indent, klass, root.classes, root.enums, options.prefix);
- indent.writeScoped(' {', '}', () {
- const String result = 'pigeonResult';
- indent.writeln('$className* $result = [[$className alloc] init];');
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- indent.writeln('$result.${field.name} = ${field.name};');
- }
- indent.writeln('return $result;');
- });
- }
-
- void writeFromList() {
- indent.write('+ ($className *)fromList:(NSArray *)list ');
- indent.scoped('{', '}', () {
- const String resultName = 'pigeonResult';
- indent.writeln('$className *$resultName = [[$className alloc] init];');
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- if (enumNames.contains(field.type.baseName)) {
- indent.writeln(
- '$resultName.${field.name} = [${_listGetter(classNames, 'list', field, index, options.prefix)} integerValue];');
- } else {
- indent.writeln(
- '$resultName.${field.name} = ${_listGetter(classNames, 'list', field, index, options.prefix)};');
- if (!field.type.isNullable) {
- indent
- .writeln('NSAssert($resultName.${field.name} != nil, @"");');
- }
- }
- });
- indent.writeln('return $resultName;');
- });
-
- indent.writeln(
- '+ (nullable $className *)nullableFromList:(NSArray *)list { return (list) ? [$className fromList:list] : nil; }');
- }
-
- void writeToList() {
- indent.write('- (NSArray *)toList ');
- indent.scoped('{', '}', () {
- indent.write('return');
- indent.scoped(' @[', '];', () {
- for (final NamedType field in klass.fields) {
- indent.writeln('${_arrayValue(classNames, enumNames, field)},');
- }
- });
- });
- }
-
- indent.writeln('@implementation $className');
- writeInitializer();
- writeFromList();
- writeToList();
- indent.writeln('@end');
- }
-
- void writeApi(Api api) {
- final String codecName = _getCodecName(options.prefix, api.name);
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodec(indent, codecName, options, api, root);
- indent.addln('');
- }
- _writeCodecGetter(indent, codecName, options, api, root);
- indent.addln('');
- if (api.location == ApiLocation.host) {
- _writeHostApiSource(indent, options, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApiSource(indent, options, api, root);
- }
- }
-
- writeHeader();
- writeImports();
- indent.writeln('');
- writeArcEnforcer();
- indent.addln('');
- writeHelperFunctions();
- indent.addln('');
- root.classes.forEach(writeDataClassExtension);
- indent.writeln('');
- for (final Class klass in root.classes) {
- writeDataClassImplementation(klass);
- indent.writeln('');
- }
- root.apis.forEach(writeApi);
+ indent.write(_makeObjcSignature(
+ func: func,
+ options: languageOptions,
+ returnType: 'void',
+ lastArgName: 'completion',
+ lastArgType: callbackType,
+ argNameFunc: argNameFunc,
+ isEnum: (TypeDeclaration t) => isEnum(root, t),
+ ));
+ indent.scoped(' {', '}', () {
+ indent.writeln('FlutterBasicMessageChannel *channel =');
+ indent.inc();
+ indent.writeln('[FlutterBasicMessageChannel');
+ indent.inc();
+ indent.writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
+ indent.writeln('binaryMessenger:self.binaryMessenger');
+ indent.write(
+ 'codec:${_getCodecGetterName(languageOptions.prefix, api.name)}()');
+ indent.addln('];');
+ indent.dec();
+ indent.dec();
+ indent.write('[channel sendMessage:$sendArgument reply:^(id reply) ');
+ indent.scoped('{', '}];', () {
+ if (func.returnType.isVoid) {
+ indent.writeln('completion(nil);');
+ } else {
+ indent.writeln('${returnType.ptr}output = reply;');
+ indent.writeln('completion(output, nil);');
+ }
+ });
+ });
}
/// Looks through the AST for features that aren't supported by the ObjC
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index 5082dd1..04c0b95 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -426,7 +426,7 @@
StringSink sink, PigeonOptions options, Root root, FileType fileType) {
final DartOptions dartOptionsWithHeader = _dartOptionsWithCopyrightHeader(
options.dartOptions, options.copyrightHeader);
- final DartGenerator generator = DartGenerator();
+ const DartGenerator generator = DartGenerator();
generator.generate(dartOptionsWithHeader, root, sink);
}
@@ -455,12 +455,8 @@
dartOutPath: options.dartOut,
testOutPath: options.dartTestOut,
);
- final DartGenerator testGenerator = DartGenerator();
- testGenerator.generateTest(
- dartOptionsWithHeader,
- root,
- sink,
- );
+ const DartGenerator testGenerator = DartGenerator();
+ testGenerator.generateTest(dartOptionsWithHeader, root, sink);
}
@override
@@ -497,7 +493,7 @@
final OutputFileOptions<ObjcOptions> outputFileOptions =
OutputFileOptions<ObjcOptions>(
fileType: fileType, languageOptions: objcOptionsWithHeader);
- final ObjcGenerator generator = ObjcGenerator();
+ const ObjcGenerator generator = ObjcGenerator();
generator.generate(outputFileOptions, root, sink);
}
@@ -532,7 +528,7 @@
copyrightHeader: options.copyrightHeader != null
? _lineReader(options.copyrightHeader!)
: null));
- final JavaGenerator generator = JavaGenerator();
+ const JavaGenerator generator = JavaGenerator();
generator.generate(javaOptions, root, sink);
}
@@ -560,7 +556,7 @@
copyrightHeader: options.copyrightHeader != null
? _lineReader(options.copyrightHeader!)
: null));
- final SwiftGenerator generator = SwiftGenerator();
+ const SwiftGenerator generator = SwiftGenerator();
generator.generate(swiftOptions, root, sink);
}
@@ -593,7 +589,7 @@
final OutputFileOptions<CppOptions> outputFileOptions =
OutputFileOptions<CppOptions>(
fileType: fileType, languageOptions: cppOptionsWithHeader);
- final CppGenerator generator = CppGenerator();
+ const CppGenerator generator = CppGenerator();
generator.generate(outputFileOptions, root, sink);
}
@@ -627,7 +623,7 @@
copyrightHeader: options.copyrightHeader != null
? _lineReader(options.copyrightHeader!)
: null));
- final KotlinGenerator generator = KotlinGenerator();
+ const KotlinGenerator generator = KotlinGenerator();
generator.generate(kotlinOptions, root, sink);
}
diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart
index 77fff7e..46e1400 100644
--- a/packages/pigeon/lib/swift_generator.dart
+++ b/packages/pigeon/lib/swift_generator.dart
@@ -49,230 +49,565 @@
}
/// Class that manages all Swift code generation.
-class SwiftGenerator extends Generator<SwiftOptions> {
+class SwiftGenerator extends StructuredGenerator<SwiftOptions> {
/// Instantiates a Swift Generator.
- SwiftGenerator();
+ const SwiftGenerator();
- /// Generates Swift files with specified [SwiftOptions]
@override
- void generate(SwiftOptions languageOptions, Root root, StringSink sink,
- {FileType fileType = FileType.na}) {
- assert(fileType == FileType.na);
- generateSwift(languageOptions, root, sink);
+ void writeFilePrologue(
+ SwiftOptions generatorOptions, Root root, Indent indent) {
+ if (generatorOptions.copyrightHeader != null) {
+ addLines(indent, generatorOptions.copyrightHeader!, linePrefix: '// ');
+ }
+ indent.writeln('// $generatedCodeWarning');
+ indent.writeln('// $seeAlsoWarning');
+ indent.addln('');
}
+
+ @override
+ void writeFileImports(
+ SwiftOptions generatorOptions, Root root, Indent indent) {
+ indent.writeln('import Foundation');
+ indent.format('''
+#if os(iOS)
+import Flutter
+#elseif os(macOS)
+import FlutterMacOS
+#else
+#error("Unsupported platform.")
+#endif
+''');
+ indent.writeln('');
+ }
+
+ @override
+ void writeEnum(
+ SwiftOptions generatorOptions, Root root, Indent indent, Enum anEnum) {
+ indent.writeln('');
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
+
+ indent.write('enum ${anEnum.name}: Int ');
+ indent.scoped('{', '}', () {
+ enumerate(anEnum.members, (int index, final EnumMember member) {
+ addDocumentationComments(
+ indent, member.documentationComments, _docCommentSpec);
+ indent.writeln('case ${_camelCase(member.name)} = $index');
+ });
+ });
+ }
+
+ @override
+ void writeDataClass(
+ SwiftOptions generatorOptions, Root root, Indent indent, Class klass) {
+ final Set<String> customClassNames =
+ root.classes.map((Class x) => x.name).toSet();
+ final Set<String> customEnumNames =
+ root.enums.map((Enum x) => x.name).toSet();
+
+ const List<String> generatedComments = <String>[
+ ' Generated class from Pigeon that represents data sent in messages.'
+ ];
+ indent.addln('');
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec,
+ generatorComments: generatedComments);
+
+ indent.write('struct ${klass.name} ');
+ indent.scoped('{', '}', () {
+ getFieldsInSerializationOrder(klass).forEach((NamedType field) {
+ _writeClassField(indent, field);
+ });
+
+ indent.writeln('');
+ writeClassDecode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ writeClassEncode(generatorOptions, root, indent, klass, customClassNames,
+ customEnumNames);
+ });
+ }
+
+ @override
+ void writeClassEncode(
+ SwiftOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ indent.write('func toList() -> [Any?] ');
+ indent.scoped('{', '}', () {
+ indent.write('return ');
+ indent.scoped('[', ']', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final HostDatatype hostDatatype = _getHostDatatype(root, field);
+ String toWriteValue = '';
+ final String fieldName = field.name;
+ final String nullsafe = field.type.isNullable ? '?' : '';
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ toWriteValue = '$fieldName$nullsafe.toList()';
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ toWriteValue = '$fieldName$nullsafe.rawValue';
+ } else {
+ toWriteValue = field.name;
+ }
+
+ indent.writeln('$toWriteValue,');
+ }
+ });
+ });
+ }
+
+ @override
+ void writeClassDecode(
+ SwiftOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class klass,
+ Set<String> customClassNames,
+ Set<String> customEnumNames,
+ ) {
+ final String className = klass.name;
+ indent.write('static func fromList(_ list: [Any?]) -> $className? ');
+
+ indent.scoped('{', '}', () {
+ enumerate(getFieldsInSerializationOrder(klass),
+ (int index, final NamedType field) {
+ final HostDatatype hostDatatype = _getHostDatatype(root, field);
+
+ final String listValue = 'list[$index]';
+ final String fieldType = _swiftTypeForDartType(field.type);
+
+ if (field.type.isNullable) {
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ indent.writeln('var ${field.name}: $fieldType? = nil');
+ indent.write('if let ${field.name}List = $listValue as? [Any?] ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ '${field.name} = $fieldType.fromList(${field.name}List)');
+ });
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ indent.writeln('var ${field.name}: $fieldType? = nil');
+ indent.write('if let ${field.name}RawValue = $listValue as? Int ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ '${field.name} = $fieldType(rawValue: ${field.name}RawValue)');
+ });
+ } else {
+ indent.writeln('let ${field.name} = $listValue as? $fieldType ');
+ }
+ } else {
+ if (!hostDatatype.isBuiltin &&
+ customClassNames.contains(field.type.baseName)) {
+ indent.writeln(
+ 'let ${field.name} = $fieldType.fromList($listValue as! [Any?])!');
+ } else if (!hostDatatype.isBuiltin &&
+ customEnumNames.contains(field.type.baseName)) {
+ indent.writeln(
+ 'let ${field.name} = $fieldType(rawValue: $listValue as! Int)!');
+ } else {
+ indent.writeln('let ${field.name} = $listValue as! $fieldType');
+ }
+ }
+ });
+
+ indent.writeln('');
+ indent.write('return ');
+ indent.scoped('$className(', ')', () {
+ for (final NamedType field in getFieldsInSerializationOrder(klass)) {
+ final String comma =
+ getFieldsInSerializationOrder(klass).last == field ? '' : ',';
+ indent.writeln('${field.name}: ${field.name}$comma');
+ }
+ });
+ });
+ }
+
+ void _writeClassField(Indent indent, NamedType field) {
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
+
+ indent.write(
+ 'var ${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}');
+ final String defaultNil = field.type.isNullable ? ' = nil' : '';
+ indent.addln(defaultNil);
+ }
+
+ @override
+ void writeApis(
+ SwiftOptions generatorOptions,
+ Root root,
+ Indent indent,
+ ) {
+ if (root.apis.any((Api api) =>
+ api.location == ApiLocation.host &&
+ api.methods.any((Method it) => it.isAsynchronous))) {
+ indent.addln('');
+ }
+ super.writeApis(generatorOptions, root, indent);
+ }
+
+ /// Writes the code for a flutter [Api], [api].
+ /// Example:
+ /// class Foo {
+ /// private let binaryMessenger: FlutterBinaryMessenger
+ /// init(binaryMessenger: FlutterBinaryMessenger) {...}
+ /// func add(x: Int32, y: Int32, completion: @escaping (Int32?) -> Void) {...}
+ /// }
+ @override
+ void writeFlutterApi(
+ SwiftOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.flutter);
+ final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
+ if (isCustomCodec) {
+ _writeCodec(indent, api, root);
+ }
+ const List<String> generatedComments = <String>[
+ ' Generated class from Pigeon that represents Flutter messages that can be called from Swift.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedComments);
+
+ indent.write('class ${api.name} ');
+ indent.scoped('{', '}', () {
+ indent.writeln('private let binaryMessenger: FlutterBinaryMessenger');
+ indent.write('init(binaryMessenger: FlutterBinaryMessenger)');
+ indent.scoped('{', '}', () {
+ indent.writeln('self.binaryMessenger = binaryMessenger');
+ });
+ final String codecName = _getCodecName(api);
+ String codecArgumentString = '';
+ if (getCodecClasses(api, root).isNotEmpty) {
+ codecArgumentString = ', codec: codec';
+ indent.write('var codec: FlutterStandardMessageCodec ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return $codecName.shared');
+ });
+ }
+ for (final Method func in api.methods) {
+ final String channelName = makeChannelName(api, func);
+ final String returnType = func.returnType.isVoid
+ ? ''
+ : _nullsafeSwiftTypeForDartType(func.returnType);
+ String sendArgument;
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
+ if (func.arguments.isEmpty) {
+ indent.write(
+ 'func ${func.name}(completion: @escaping ($returnType) -> Void) ');
+ sendArgument = 'nil';
+ } else {
+ final Iterable<String> argTypes = func.arguments
+ .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
+ final Iterable<String> argLabels =
+ indexMap(func.arguments, _getArgumentName);
+ final Iterable<String> argNames =
+ indexMap(func.arguments, _getSafeArgumentName);
+ sendArgument = '[${argNames.join(', ')}]';
+ final String argsSignature = map3(
+ argTypes,
+ argLabels,
+ argNames,
+ (String type, String label, String name) =>
+ '$label $name: $type').join(', ');
+ if (func.returnType.isVoid) {
+ indent.write(
+ 'func ${func.name}($argsSignature, completion: @escaping () -> Void) ');
+ } else {
+ indent.write(
+ 'func ${func.name}($argsSignature, completion: @escaping ($returnType) -> Void) ');
+ }
+ }
+ indent.scoped('{', '}', () {
+ const String channel = 'channel';
+ indent.writeln(
+ 'let $channel = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
+ indent.write('$channel.sendMessage($sendArgument) ');
+ if (func.returnType.isVoid) {
+ indent.scoped('{ _ in', '}', () {
+ indent.writeln('completion()');
+ });
+ } else {
+ indent.scoped('{ response in', '}', () {
+ indent.writeln(
+ 'let result = ${_castForceUnwrap("response", func.returnType, root)}');
+ indent.writeln('completion(result)');
+ });
+ }
+ });
+ }
+ });
+ }
+
+ /// Write the swift code that represents a host [Api], [api].
+ /// Example:
+ /// protocol Foo {
+ /// Int32 add(x: Int32, y: Int32)
+ /// }
+ @override
+ void writeHostApi(
+ SwiftOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Api api,
+ ) {
+ assert(api.location == ApiLocation.host);
+
+ final String apiName = api.name;
+
+ final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
+ if (isCustomCodec) {
+ _writeCodec(indent, api, root);
+ }
+
+ const List<String> generatedComments = <String>[
+ ' Generated protocol from Pigeon that represents a handler of messages from Flutter.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedComments);
+
+ indent.write('protocol $apiName ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ final List<String> argSignature = <String>[];
+ if (method.arguments.isNotEmpty) {
+ final Iterable<String> argTypes = method.arguments
+ .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
+ final Iterable<String> argNames =
+ method.arguments.map((NamedType e) => e.name);
+ argSignature.addAll(
+ map2(argTypes, argNames, (String argType, String argName) {
+ return '$argName: $argType';
+ }));
+ }
+
+ final String returnType = method.returnType.isVoid
+ ? ''
+ : _nullsafeSwiftTypeForDartType(method.returnType);
+ addDocumentationComments(
+ indent, method.documentationComments, _docCommentSpec);
+
+ if (method.isAsynchronous) {
+ argSignature.add('completion: @escaping ($returnType) -> Void');
+ indent.writeln('func ${method.name}(${argSignature.join(', ')})');
+ } else if (method.returnType.isVoid) {
+ indent.writeln('func ${method.name}(${argSignature.join(', ')})');
+ } else {
+ indent.writeln(
+ 'func ${method.name}(${argSignature.join(', ')}) -> $returnType');
+ }
+ }
+ });
+
+ indent.addln('');
+ indent.writeln(
+ '$_docCommentPrefix Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
+ indent.write('class ${apiName}Setup ');
+ indent.scoped('{', '}', () {
+ final String codecName = _getCodecName(api);
+ indent.writeln('$_docCommentPrefix The codec used by $apiName.');
+ String codecArgumentString = '';
+ if (getCodecClasses(api, root).isNotEmpty) {
+ codecArgumentString = ', codec: codec';
+ indent.writeln(
+ 'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
+ }
+ indent.writeln(
+ '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
+ indent.write(
+ 'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
+ indent.scoped('{', '}', () {
+ for (final Method method in api.methods) {
+ final String channelName = makeChannelName(api, method);
+ final String varChannelName = '${method.name}Channel';
+ addDocumentationComments(
+ indent, method.documentationComments, _docCommentSpec);
+
+ indent.writeln(
+ 'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
+ indent.write('if let api = api ');
+ indent.scoped('{', '}', () {
+ indent.write('$varChannelName.setMessageHandler ');
+ final String messageVarName =
+ method.arguments.isNotEmpty ? 'message' : '_';
+ indent.scoped('{ $messageVarName, reply in', '}', () {
+ final List<String> methodArgument = <String>[];
+ if (method.arguments.isNotEmpty) {
+ indent.writeln('let args = message as! [Any?]');
+ enumerate(method.arguments, (int index, NamedType arg) {
+ final String argName = _getSafeArgumentName(index, arg);
+ final String argIndex = 'args[$index]';
+ indent.writeln(
+ 'let $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
+ methodArgument.add('${arg.name}: $argName');
+ });
+ }
+ final String call =
+ 'api.${method.name}(${methodArgument.join(', ')})';
+ if (method.isAsynchronous) {
+ indent.write('$call ');
+ if (method.returnType.isVoid) {
+ indent.scoped('{', '}', () {
+ indent.writeln('reply(wrapResult(nil))');
+ });
+ } else {
+ indent.scoped('{ result in', '}', () {
+ indent.writeln('reply(wrapResult(result))');
+ });
+ }
+ } else {
+ if (method.returnType.isVoid) {
+ indent.writeln(call);
+ indent.writeln('reply(wrapResult(nil))');
+ } else {
+ indent.writeln('let result = $call');
+ indent.writeln('reply(wrapResult(result))');
+ }
+ }
+ });
+ }, addTrailingNewline: false);
+ indent.scoped(' else {', '}', () {
+ indent.writeln('$varChannelName.setMessageHandler(nil)');
+ });
+ }
+ });
+ });
+ }
+
+ /// Writes the codec classwill be used for encoding messages for the [api].
+ /// Example:
+ /// private class FooHostApiCodecReader: FlutterStandardReader {...}
+ /// private class FooHostApiCodecWriter: FlutterStandardWriter {...}
+ /// private class FooHostApiCodecReaderWriter: FlutterStandardReaderWriter {...}
+ void _writeCodec(Indent indent, Api api, Root root) {
+ assert(getCodecClasses(api, root).isNotEmpty);
+ final String codecName = _getCodecName(api);
+ final String readerWriterName = '${codecName}ReaderWriter';
+ final String readerName = '${codecName}Reader';
+ final String writerName = '${codecName}Writer';
+
+ // Generate Reader
+ indent.write('private class $readerName: FlutterStandardReader ');
+ indent.scoped('{', '}', () {
+ if (getCodecClasses(api, root).isNotEmpty) {
+ indent.write('override func readValue(ofType type: UInt8) -> Any? ');
+ indent.scoped('{', '}', () {
+ indent.write('switch type ');
+ indent.scoped('{', '}', () {
+ for (final EnumeratedClass customClass
+ in getCodecClasses(api, root)) {
+ indent.write('case ${customClass.enumeration}:');
+ indent.scoped('', '', () {
+ indent.write(
+ 'return ${customClass.name}.fromList(self.readValue() as! [Any])');
+ });
+ }
+ indent.write('default:');
+ indent.scoped('', '', () {
+ indent.writeln('return super.readValue(ofType: type)');
+ });
+ });
+ });
+ }
+ });
+
+ // Generate Writer
+ indent.write('private class $writerName: FlutterStandardWriter ');
+ indent.scoped('{', '}', () {
+ if (getCodecClasses(api, root).isNotEmpty) {
+ indent.write('override func writeValue(_ value: Any) ');
+ indent.scoped('{', '}', () {
+ indent.write('');
+ for (final EnumeratedClass customClass
+ in getCodecClasses(api, root)) {
+ indent.add('if let value = value as? ${customClass.name} ');
+ indent.scoped('{', '} else ', () {
+ indent.writeln('super.writeByte(${customClass.enumeration})');
+ indent.writeln('super.writeValue(value.toList())');
+ }, addTrailingNewline: false);
+ }
+ indent.scoped('{', '}', () {
+ indent.writeln('super.writeValue(value)');
+ });
+ });
+ }
+ });
+ indent.writeln('');
+
+ // Generate ReaderWriter
+ indent
+ .write('private class $readerWriterName: FlutterStandardReaderWriter ');
+ indent.scoped('{', '}', () {
+ indent.write(
+ 'override func reader(with data: Data) -> FlutterStandardReader ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return $readerName(data: data)');
+ });
+ indent.writeln('');
+ indent.write(
+ 'override func writer(with data: NSMutableData) -> FlutterStandardWriter ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return $writerName(data: data)');
+ });
+ });
+ indent.writeln('');
+
+ // Generate Codec
+ indent.write('class $codecName: FlutterStandardMessageCodec ');
+ indent.scoped('{', '}', () {
+ indent.writeln(
+ 'static let shared = $codecName(readerWriter: $readerWriterName())');
+ });
+ indent.addln('');
+ }
+
+ void _writeWrapResult(Indent indent) {
+ indent.addln('');
+ indent.write('private func wrapResult(_ result: Any?) -> [Any?] ');
+ indent.scoped('{', '}', () {
+ indent.writeln('return [result]');
+ });
+ }
+
+ void _writeWrapError(Indent indent) {
+ indent.addln('');
+ indent.write('private func wrapError(_ error: FlutterError) -> [Any?] ');
+ indent.scoped('{', '}', () {
+ indent.write('return ');
+ indent.scoped('[', ']', () {
+ indent.writeln('error.code,');
+ indent.writeln('error.message,');
+ indent.writeln('error.details');
+ });
+ });
+ }
+
+ @override
+ void writeGeneralUtilities(
+ SwiftOptions generatorOptions, Root root, Indent indent) {
+ _writeWrapResult(indent);
+ _writeWrapError(indent);
+ }
+}
+
+HostDatatype _getHostDatatype(Root root, NamedType field) {
+ return getFieldHostDatatype(field, root.classes, root.enums,
+ (TypeDeclaration x) => _swiftTypeForBuiltinDartType(x));
}
/// Calculates the name of the codec that will be generated for [api].
String _getCodecName(Api api) => '${api.name}Codec';
-/// Writes the codec classwill be used for encoding messages for the [api].
-/// Example:
-/// private class FooHostApiCodecReader: FlutterStandardReader {...}
-/// private class FooHostApiCodecWriter: FlutterStandardWriter {...}
-/// private class FooHostApiCodecReaderWriter: FlutterStandardReaderWriter {...}
-void _writeCodec(Indent indent, Api api, Root root) {
- assert(getCodecClasses(api, root).isNotEmpty);
- final String codecName = _getCodecName(api);
- final String readerWriterName = '${codecName}ReaderWriter';
- final String readerName = '${codecName}Reader';
- final String writerName = '${codecName}Writer';
-
- // Generate Reader
- indent.write('private class $readerName: FlutterStandardReader ');
- indent.scoped('{', '}', () {
- if (getCodecClasses(api, root).isNotEmpty) {
- indent.write('override func readValue(ofType type: UInt8) -> Any? ');
- indent.scoped('{', '}', () {
- indent.write('switch type ');
- indent.scoped('{', '}', () {
- for (final EnumeratedClass customClass
- in getCodecClasses(api, root)) {
- indent.write('case ${customClass.enumeration}:');
- indent.scoped('', '', () {
- indent.write(
- 'return ${customClass.name}.fromList(self.readValue() as! [Any])');
- });
- }
- indent.write('default:');
- indent.scoped('', '', () {
- indent.writeln('return super.readValue(ofType: type)');
- });
- });
- });
- }
- });
-
- // Generate Writer
- indent.write('private class $writerName: FlutterStandardWriter ');
- indent.scoped('{', '}', () {
- if (getCodecClasses(api, root).isNotEmpty) {
- indent.write('override func writeValue(_ value: Any) ');
- indent.scoped('{', '}', () {
- indent.write('');
- for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
- indent.add('if let value = value as? ${customClass.name} ');
- indent.scoped('{', '} else ', () {
- indent.writeln('super.writeByte(${customClass.enumeration})');
- indent.writeln('super.writeValue(value.toList())');
- }, addTrailingNewline: false);
- }
- indent.scoped('{', '}', () {
- indent.writeln('super.writeValue(value)');
- });
- });
- }
- });
- indent.writeln('');
-
- // Generate ReaderWriter
- indent.write('private class $readerWriterName: FlutterStandardReaderWriter ');
- indent.scoped('{', '}', () {
- indent.write(
- 'override func reader(with data: Data) -> FlutterStandardReader ');
- indent.scoped('{', '}', () {
- indent.writeln('return $readerName(data: data)');
- });
- indent.writeln('');
- indent.write(
- 'override func writer(with data: NSMutableData) -> FlutterStandardWriter ');
- indent.scoped('{', '}', () {
- indent.writeln('return $writerName(data: data)');
- });
- });
- indent.writeln('');
-
- // Generate Codec
- indent.write('class $codecName: FlutterStandardMessageCodec ');
- indent.scoped('{', '}', () {
- indent.writeln(
- 'static let shared = $codecName(readerWriter: $readerWriterName())');
- });
-}
-
-/// Write the swift code that represents a host [Api], [api].
-/// Example:
-/// protocol Foo {
-/// Int32 add(x: Int32, y: Int32)
-/// }
-void _writeHostApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.host);
-
- final String apiName = api.name;
-
- const List<String> generatedComments = <String>[
- ' Generated protocol from Pigeon that represents a handler of messages from Flutter.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedComments);
-
- indent.write('protocol $apiName ');
- indent.scoped('{', '}', () {
- for (final Method method in api.methods) {
- final List<String> argSignature = <String>[];
- if (method.arguments.isNotEmpty) {
- final Iterable<String> argTypes = method.arguments
- .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
- final Iterable<String> argNames =
- method.arguments.map((NamedType e) => e.name);
- argSignature
- .addAll(map2(argTypes, argNames, (String argType, String argName) {
- return '$argName: $argType';
- }));
- }
-
- final String returnType = method.returnType.isVoid
- ? ''
- : _nullsafeSwiftTypeForDartType(method.returnType);
- addDocumentationComments(
- indent, method.documentationComments, _docCommentSpec);
-
- if (method.isAsynchronous) {
- argSignature.add('completion: @escaping ($returnType) -> Void');
- indent.writeln('func ${method.name}(${argSignature.join(', ')})');
- } else if (method.returnType.isVoid) {
- indent.writeln('func ${method.name}(${argSignature.join(', ')})');
- } else {
- indent.writeln(
- 'func ${method.name}(${argSignature.join(', ')}) -> $returnType');
- }
- }
- });
-
- indent.addln('');
- indent.writeln(
- '$_docCommentPrefix Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
- indent.write('class ${apiName}Setup ');
- indent.scoped('{', '}', () {
- final String codecName = _getCodecName(api);
- indent.writeln('$_docCommentPrefix The codec used by $apiName.');
- String codecArgumentString = '';
- if (getCodecClasses(api, root).isNotEmpty) {
- codecArgumentString = ', codec: codec';
- indent.writeln(
- 'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
- }
- indent.writeln(
- '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
- indent.write(
- 'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
- indent.scoped('{', '}', () {
- for (final Method method in api.methods) {
- final String channelName = makeChannelName(api, method);
- final String varChannelName = '${method.name}Channel';
- addDocumentationComments(
- indent, method.documentationComments, _docCommentSpec);
-
- indent.writeln(
- 'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
- indent.write('if let api = api ');
- indent.scoped('{', '}', () {
- indent.write('$varChannelName.setMessageHandler ');
- final String messageVarName =
- method.arguments.isNotEmpty ? 'message' : '_';
- indent.scoped('{ $messageVarName, reply in', '}', () {
- final List<String> methodArgument = <String>[];
- if (method.arguments.isNotEmpty) {
- indent.writeln('let args = message as! [Any?]');
- enumerate(method.arguments, (int index, NamedType arg) {
- final String argName = _getSafeArgumentName(index, arg);
- final String argIndex = 'args[$index]';
- indent.writeln(
- 'let $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
- methodArgument.add('${arg.name}: $argName');
- });
- }
- final String call =
- 'api.${method.name}(${methodArgument.join(', ')})';
- if (method.isAsynchronous) {
- indent.write('$call ');
- if (method.returnType.isVoid) {
- indent.scoped('{', '}', () {
- indent.writeln('reply(wrapResult(nil))');
- });
- } else {
- indent.scoped('{ result in', '}', () {
- indent.writeln('reply(wrapResult(result))');
- });
- }
- } else {
- if (method.returnType.isVoid) {
- indent.writeln(call);
- indent.writeln('reply(wrapResult(nil))');
- } else {
- indent.writeln('let result = $call');
- indent.writeln('reply(wrapResult(result))');
- }
- }
- });
- }, addTrailingNewline: false);
- indent.scoped(' else {', '}', () {
- indent.writeln('$varChannelName.setMessageHandler(nil)');
- });
- }
- });
- });
-}
-
String _getArgumentName(int count, NamedType argument) =>
argument.name.isEmpty ? 'arg$count' : argument.name;
@@ -287,93 +622,6 @@
return pascal[0].toLowerCase() + pascal.substring(1);
}
-/// Writes the code for a flutter [Api], [api].
-/// Example:
-/// class Foo {
-/// private let binaryMessenger: FlutterBinaryMessenger
-/// init(binaryMessenger: FlutterBinaryMessenger) {...}
-/// func add(x: Int32, y: Int32, completion: @escaping (Int32?) -> Void) {...}
-/// }
-void _writeFlutterApi(Indent indent, Api api, Root root) {
- assert(api.location == ApiLocation.flutter);
- const List<String> generatedComments = <String>[
- ' Generated class from Pigeon that represents Flutter messages that can be called from Swift.'
- ];
- addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
- generatorComments: generatedComments);
-
- indent.write('class ${api.name} ');
- indent.scoped('{', '}', () {
- indent.writeln('private let binaryMessenger: FlutterBinaryMessenger');
- indent.write('init(binaryMessenger: FlutterBinaryMessenger)');
- indent.scoped('{', '}', () {
- indent.writeln('self.binaryMessenger = binaryMessenger');
- });
- final String codecName = _getCodecName(api);
- String codecArgumentString = '';
- if (getCodecClasses(api, root).isNotEmpty) {
- codecArgumentString = ', codec: codec';
- indent.write('var codec: FlutterStandardMessageCodec ');
- indent.scoped('{', '}', () {
- indent.writeln('return $codecName.shared');
- });
- }
- for (final Method func in api.methods) {
- final String channelName = makeChannelName(api, func);
- final String returnType = func.returnType.isVoid
- ? ''
- : _nullsafeSwiftTypeForDartType(func.returnType);
- String sendArgument;
- addDocumentationComments(
- indent, func.documentationComments, _docCommentSpec);
-
- if (func.arguments.isEmpty) {
- indent.write(
- 'func ${func.name}(completion: @escaping ($returnType) -> Void) ');
- sendArgument = 'nil';
- } else {
- final Iterable<String> argTypes = func.arguments
- .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
- final Iterable<String> argLabels =
- indexMap(func.arguments, _getArgumentName);
- final Iterable<String> argNames =
- indexMap(func.arguments, _getSafeArgumentName);
- sendArgument = '[${argNames.join(', ')}]';
- final String argsSignature = map3(
- argTypes,
- argLabels,
- argNames,
- (String type, String label, String name) =>
- '$label $name: $type').join(', ');
- if (func.returnType.isVoid) {
- indent.write(
- 'func ${func.name}($argsSignature, completion: @escaping () -> Void) ');
- } else {
- indent.write(
- 'func ${func.name}($argsSignature, completion: @escaping ($returnType) -> Void) ');
- }
- }
- indent.scoped('{', '}', () {
- const String channel = 'channel';
- indent.writeln(
- 'let $channel = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
- indent.write('$channel.sendMessage($sendArgument) ');
- if (func.returnType.isVoid) {
- indent.scoped('{ _ in', '}', () {
- indent.writeln('completion()');
- });
- } else {
- indent.scoped('{ response in', '}', () {
- indent.writeln(
- 'let result = ${_castForceUnwrap("response", func.returnType, root)}');
- indent.writeln('completion(result)');
- });
- }
- });
- }
- });
-}
-
String _castForceUnwrap(String value, TypeDeclaration type, Root root) {
if (isEnum(root, type)) {
final String forceUnwrap = type.isNullable ? '' : '!';
@@ -446,228 +694,3 @@
final String nullSafe = type.isNullable ? '?' : '';
return '${_swiftTypeForDartType(type)}$nullSafe';
}
-
-/// Generates the ".swift" file for the AST represented by [root] to [sink] with the
-/// provided [options].
-void generateSwift(SwiftOptions options, Root root, StringSink sink) {
- final Set<String> rootClassNameSet =
- root.classes.map((Class x) => x.name).toSet();
- final Set<String> rootEnumNameSet =
- root.enums.map((Enum x) => x.name).toSet();
- final Indent indent = Indent(sink);
-
- HostDatatype getHostDatatype(NamedType field) {
- return getFieldHostDatatype(field, root.classes, root.enums,
- (TypeDeclaration x) => _swiftTypeForBuiltinDartType(x));
- }
-
- void writeHeader() {
- if (options.copyrightHeader != null) {
- addLines(indent, options.copyrightHeader!, linePrefix: '// ');
- }
- indent.writeln('// $generatedCodeWarning');
- indent.writeln('// $seeAlsoWarning');
- }
-
- void writeImports() {
- indent.writeln('import Foundation');
- indent.format('''
-#if os(iOS)
-import Flutter
-#elseif os(macOS)
-import FlutterMacOS
-#else
-#error("Unsupported platform.")
-#endif
-''');
- }
-
- void writeEnum(Enum anEnum) {
- addDocumentationComments(
- indent, anEnum.documentationComments, _docCommentSpec);
-
- indent.write('enum ${anEnum.name}: Int ');
- indent.scoped('{', '}', () {
- enumerate(anEnum.members, (int index, final EnumMember member) {
- addDocumentationComments(
- indent, member.documentationComments, _docCommentSpec);
- indent.writeln('case ${_camelCase(member.name)} = $index');
- });
- });
- }
-
- void writeDataClass(Class klass) {
- void writeField(NamedType field) {
- addDocumentationComments(
- indent, field.documentationComments, _docCommentSpec);
-
- indent.write(
- 'var ${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}');
- final String defaultNil = field.type.isNullable ? ' = nil' : '';
- indent.addln(defaultNil);
- }
-
- void writeToList() {
- indent.write('func toList() -> [Any?] ');
- indent.scoped('{', '}', () {
- indent.write('return ');
- indent.scoped('[', ']', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final HostDatatype hostDatatype = getHostDatatype(field);
- String toWriteValue = '';
- final String fieldName = field.name;
- final String nullsafe = field.type.isNullable ? '?' : '';
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- toWriteValue = '$fieldName$nullsafe.toList()';
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- toWriteValue = '$fieldName$nullsafe.rawValue';
- } else {
- toWriteValue = field.name;
- }
-
- indent.writeln('$toWriteValue,');
- }
- });
- });
- }
-
- void writeFromList() {
- final String className = klass.name;
- indent.write('static func fromList(_ list: [Any?]) -> $className? ');
-
- indent.scoped('{', '}', () {
- enumerate(getFieldsInSerializationOrder(klass),
- (int index, final NamedType field) {
- final HostDatatype hostDatatype = getHostDatatype(field);
-
- final String listValue = 'list[$index]';
- final String fieldType = _swiftTypeForDartType(field.type);
-
- if (field.type.isNullable) {
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- indent.writeln('var ${field.name}: $fieldType? = nil');
- indent.write('if let ${field.name}List = $listValue as? [Any?] ');
- indent.scoped('{', '}', () {
- indent.writeln(
- '${field.name} = $fieldType.fromList(${field.name}List)');
- });
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- indent.writeln('var ${field.name}: $fieldType? = nil');
- indent
- .write('if let ${field.name}RawValue = $listValue as? Int ');
- indent.scoped('{', '}', () {
- indent.writeln(
- '${field.name} = $fieldType(rawValue: ${field.name}RawValue)');
- });
- } else {
- indent.writeln('let ${field.name} = $listValue as? $fieldType ');
- }
- } else {
- if (!hostDatatype.isBuiltin &&
- rootClassNameSet.contains(field.type.baseName)) {
- indent.writeln(
- 'let ${field.name} = $fieldType.fromList($listValue as! [Any?])!');
- } else if (!hostDatatype.isBuiltin &&
- rootEnumNameSet.contains(field.type.baseName)) {
- indent.writeln(
- 'let ${field.name} = $fieldType(rawValue: $listValue as! Int)!');
- } else {
- indent.writeln('let ${field.name} = $listValue as! $fieldType');
- }
- }
- });
-
- indent.writeln('');
- indent.write('return ');
- indent.scoped('$className(', ')', () {
- for (final NamedType field in getFieldsInSerializationOrder(klass)) {
- final String comma =
- getFieldsInSerializationOrder(klass).last == field ? '' : ',';
- indent.writeln('${field.name}: ${field.name}$comma');
- }
- });
- });
- }
-
- const List<String> generatedComments = <String>[
- ' Generated class from Pigeon that represents data sent in messages.'
- ];
- addDocumentationComments(
- indent, klass.documentationComments, _docCommentSpec,
- generatorComments: generatedComments);
-
- indent.write('struct ${klass.name} ');
- indent.scoped('{', '}', () {
- getFieldsInSerializationOrder(klass).forEach(writeField);
-
- indent.writeln('');
- writeFromList();
- writeToList();
- });
- }
-
- void writeApi(Api api, Root root) {
- if (api.location == ApiLocation.host) {
- _writeHostApi(indent, api, root);
- } else if (api.location == ApiLocation.flutter) {
- _writeFlutterApi(indent, api, root);
- }
- }
-
- void writeWrapResult() {
- indent.write('private func wrapResult(_ result: Any?) -> [Any?] ');
- indent.scoped('{', '}', () {
- indent.writeln('return [result]');
- });
- }
-
- void writeWrapError() {
- indent.write('private func wrapError(_ error: FlutterError) -> [Any?] ');
- indent.scoped('{', '}', () {
- indent.write('return ');
- indent.scoped('[', ']', () {
- indent.writeln('error.code,');
- indent.writeln('error.message,');
- indent.writeln('error.details');
- });
- });
- }
-
- writeHeader();
- indent.addln('');
- writeImports();
- indent.addln('');
- indent.writeln('$_docCommentPrefix Generated class from Pigeon.');
- for (final Enum anEnum in root.enums) {
- indent.writeln('');
- writeEnum(anEnum);
- }
-
- for (final Class klass in root.classes) {
- indent.addln('');
- writeDataClass(klass);
- }
-
- if (root.apis.any((Api api) =>
- api.location == ApiLocation.host &&
- api.methods.any((Method it) => it.isAsynchronous))) {
- indent.addln('');
- }
-
- for (final Api api in root.apis) {
- if (getCodecClasses(api, root).isNotEmpty) {
- _writeCodec(indent, api, root);
- indent.addln('');
- }
- writeApi(api, root);
- }
-
- indent.addln('');
- writeWrapResult();
- indent.addln('');
- writeWrapError();
-}
diff --git a/packages/pigeon/mock_handler_tester/test/message.dart b/packages/pigeon/mock_handler_tester/test/message.dart
index 4f6c9d1..bf02b40 100644
--- a/packages/pigeon/mock_handler_tester/test/message.dart
+++ b/packages/pigeon/mock_handler_tester/test/message.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/mock_handler_tester/test/test.dart b/packages/pigeon/mock_handler_tester/test/test.dart
index 9b97826..5cc8473 100644
--- a/packages/pigeon/mock_handler_tester/test/test.dart
+++ b/packages/pigeon/mock_handler_tester/test/test.dart
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
// ignore_for_file: avoid_relative_lib_imports
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
index ea8e343..d97d0cc 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package com.example.alternate_language_test_plugin;
@@ -24,6 +24,15 @@
/** Generated class from Pigeon. */
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
public class CoreTests {
+ @NonNull
+ private static ArrayList<Object> wrapError(@NonNull Throwable exception) {
+ ArrayList<Object> errorList = new ArrayList<>(3);
+ errorList.add(exception.toString());
+ errorList.add(exception.getClass().getSimpleName());
+ errorList.add(
+ "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
+ return errorList;
+ }
public enum AnEnum {
ONE(0),
@@ -1647,14 +1656,4 @@
}
}
}
-
- @NonNull
- private static ArrayList<Object> wrapError(@NonNull Throwable exception) {
- ArrayList<Object> errorList = new ArrayList<>(3);
- errorList.add(exception.toString());
- errorList.add(exception.getClass().getSimpleName());
- errorList.add(
- "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
- return errorList;
- }
}
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
index 2db8f71..bfab566 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
+
#import <Foundation/Foundation.h>
+
@protocol FlutterBinaryMessenger;
@protocol FlutterMessageCodec;
@class FlutterError;
@@ -207,6 +209,7 @@
- (void)echoString:(NSString *)aString
completion:(void (^)(NSString *_Nullable, NSError *_Nullable))completion;
@end
+
/// The codec used by HostTrivialApi.
NSObject<FlutterMessageCodec> *HostTrivialApiGetCodec(void);
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
index 078bf32..7f73cf6 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
+
#import "CoreTests.gen.h"
#import <Flutter/Flutter.h>
@@ -33,11 +34,13 @@
+ (nullable AllTypes *)nullableFromList:(NSArray *)list;
- (NSArray *)toList;
@end
+
@interface AllNullableTypes ()
+ (AllNullableTypes *)fromList:(NSArray *)list;
+ (nullable AllNullableTypes *)nullableFromList:(NSArray *)list;
- (NSArray *)toList;
@end
+
@interface AllNullableTypesWrapper ()
+ (AllNullableTypesWrapper *)fromList:(NSArray *)list;
+ (nullable AllNullableTypesWrapper *)nullableFromList:(NSArray *)list;
@@ -894,6 +897,7 @@
}];
}
@end
+
NSObject<FlutterMessageCodec> *HostTrivialApiGetCodec() {
static FlutterStandardMessageCodec *sSharedObject = nil;
sSharedObject = [FlutterStandardMessageCodec sharedInstance];
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/core_tests.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/core_tests.gen.dart
index fe0fe33..1040f61 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/core_tests.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/core_tests.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
index e92ab29..c3b44b0 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
index 1ff71bf..3bf1fa5 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
index 88f4611..82bfce2 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
index 13fe926..a4a52e6 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
index be1979c..51af74c 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
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
index 36a569e..f9855f6 100644
--- 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
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.0), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
index 3560cc2..1040f61 100644
--- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
+++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.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, unnecessary_import
+
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
index 17d7e9f..c2c0390 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package com.example.test_plugin
@@ -15,7 +15,17 @@
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
-/** Generated class from Pigeon. */
+private fun wrapResult(result: Any?): List<Any?> {
+ return listOf(result)
+}
+
+private fun wrapError(exception: Throwable): List<Any> {
+ return listOf<Any>(
+ exception.javaClass.simpleName,
+ exception.toString(),
+ "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
+ )
+}
enum class AnEnum(val raw: Int) {
ONE(0),
@@ -810,15 +820,3 @@
}
}
}
-
-private fun wrapResult(result: Any?): List<Any?> {
- return listOf(result)
-}
-
-private fun wrapError(exception: Throwable): List<Any> {
- return listOf<Any>(
- exception.javaClass.simpleName,
- exception.toString(),
- "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
- )
-}
diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
index bcf93bc..407ee69 100644
--- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
+++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -15,7 +15,18 @@
#endif
-/// Generated class from Pigeon.
+
+private func wrapResult(_ result: Any?) -> [Any?] {
+ return [result]
+}
+
+private func wrapError(_ error: FlutterError) -> [Any?] {
+ return [
+ error.code,
+ error.message,
+ error.details
+ ]
+}
enum AnEnum: Int {
case one = 0
@@ -666,15 +677,3 @@
}
}
}
-
-private func wrapResult(_ result: Any?) -> [Any?] {
- return [result]
-}
-
-private func wrapError(_ error: FlutterError) -> [Any?] {
- return [
- error.code,
- error.message,
- error.details
- ]
-}
diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
index bcf93bc..407ee69 100644
--- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
+++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -15,7 +15,18 @@
#endif
-/// Generated class from Pigeon.
+
+private func wrapResult(_ result: Any?) -> [Any?] {
+ return [result]
+}
+
+private func wrapError(_ error: FlutterError) -> [Any?] {
+ return [
+ error.code,
+ error.message,
+ error.details
+ ]
+}
enum AnEnum: Int {
case one = 0
@@ -666,15 +677,3 @@
}
}
}
-
-private func wrapResult(_ result: Any?) -> [Any?] {
- return [result]
-}
-
-private func wrapError(_ error: FlutterError) -> [Any?] {
- return [
- error.code,
- error.message,
- error.details
- ]
-}
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
index 98b9395..6c39bce 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
+++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#undef _HAS_EXCEPTIONS
diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
index aac405e..c752a5a 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
+++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Autogenerated from Pigeon (v5.0.1), do not edit directly.
+// Autogenerated from Pigeon (v6.0.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
-#ifndef PIGEON_CORE_TESTS_PIGEONTEST_H_
-#define PIGEON_CORE_TESTS_PIGEONTEST_H_
+#ifndef PIGEON_CORE_TESTS_GEN_H_H_
+#define PIGEON_CORE_TESTS_GEN_H_H_
#include <flutter/basic_message_channel.h>
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
@@ -17,12 +17,11 @@
#include <string>
namespace core_tests_pigeontest {
+
class CoreTestsTest;
// Generated class from Pigeon.
-enum class AnEnum { one = 0, two = 1, three = 2 };
-
class FlutterError {
public:
explicit FlutterError(const std::string& code) : code_(code) {}
@@ -64,6 +63,8 @@
std::variant<T, FlutterError> v_;
};
+enum class AnEnum { one = 0, two = 1, three = 2 };
+
// Generated class from Pigeon that represents data sent in messages.
class AllTypes {
public:
@@ -412,4 +413,4 @@
HostTrivialApi() = default;
};
} // namespace core_tests_pigeontest
-#endif // PIGEON_CORE_TESTS_PIGEONTEST_H_
+#endif // PIGEON_CORE_TESTS_GEN_H_H_
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index f2a5a9a..caa82a3 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
repository: https://github.com/flutter/packages/tree/main/packages/pigeon
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 5.0.1 # This must match the version in lib/generator_tools.dart
+version: 6.0.0 # This must match the version in lib/generator_tools.dart
environment:
sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart
index d8e9917..97dc129 100644
--- a/packages/pigeon/test/cpp_generator_test.dart
+++ b/packages/pigeon/test/cpp_generator_test.dart
@@ -4,6 +4,7 @@
import 'package:pigeon/ast.dart';
import 'package:pigeon/cpp_generator.dart';
+import 'package:pigeon/generator_tools.dart';
import 'package:pigeon/pigeon.dart' show Error;
import 'package:test/test.dart';
@@ -45,7 +46,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Input'));
expect(code, contains('class Output'));
@@ -53,7 +60,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('Input::Input()'));
expect(code, contains('Output::Output'));
@@ -101,7 +114,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Method name and argument names should be adjusted.
expect(code, contains(' DoSomething(const Input& some_input)'));
@@ -116,7 +135,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('pointer_input_field'));
expect(code, contains('Output::output_field()'));
@@ -144,7 +169,17 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(
+ generatorOptions,
+ root,
+ sink,
+ );
final String code = sink.toString();
expect(
@@ -184,7 +219,13 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
@@ -238,14 +279,26 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains('){')));
expect(code, isNot(contains('const{')));
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains('){')));
expect(code, isNot(contains('const{')));
@@ -271,7 +324,13 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('''
#include <flutter/basic_message_channel.h>
@@ -286,8 +345,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(
- const CppOptions(headerIncludePath: 'a_header.h'), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(headerIncludePath: 'a_header.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('''
#include "a_header.h"
@@ -323,14 +387,26 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(namespace: 'foo'), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(namespace: 'foo'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('namespace foo {'));
expect(code, contains('} // namespace foo'));
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(namespace: 'foo'), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(namespace: 'foo'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('namespace foo {'));
expect(code, contains('} // namespace foo'));
@@ -391,7 +467,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Getters should return const pointers.
expect(code, contains('const bool* nullable_bool()'));
@@ -423,7 +505,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Getters extract optionals.
expect(code,
@@ -522,7 +610,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// POD getters should return copies references.
expect(code, contains('bool non_nullable_bool()'));
@@ -547,7 +641,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Getters just return the value.
expect(code, contains('return non_nullable_bool_;'));
@@ -645,7 +745,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code, contains('ErrorOr<std::optional<bool>> ReturnNullableBool()'));
@@ -750,7 +856,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('ErrorOr<bool> ReturnBool()'));
expect(code, contains('ErrorOr<int64_t> ReturnInt()'));
@@ -832,7 +944,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -846,7 +964,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Most types should just use get_if, since the parameter is a pointer,
// and get_if will automatically handle null values (since a null
@@ -963,7 +1087,13 @@
], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -977,7 +1107,13 @@
}
{
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Most types should extract references. Since the type is non-nullable,
// there's only one possible type.
@@ -1037,7 +1173,13 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// A bare 'auto' here would create a copy, not a reference, which is
// ineffecient.
@@ -1149,7 +1291,13 @@
],
);
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(headerIncludePath: 'foo'), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(headerIncludePath: 'foo'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
for (final String comment in comments) {
expect(code, contains('//$comment'));
@@ -1184,7 +1332,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains(' : public flutter::StandardCodecSerializer')));
});
@@ -1226,7 +1380,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateCppHeader(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.header,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains(' : public flutter::StandardCodecSerializer'));
});
@@ -1295,7 +1455,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains('reply(wrap')));
expect(code, contains('reply(flutter::EncodableValue('));
@@ -1349,7 +1515,13 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateCppSource(const CppOptions(), root, sink);
+ const CppGenerator generator = CppGenerator();
+ final OutputFileOptions<CppOptions> generatorOptions =
+ OutputFileOptions<CppOptions>(
+ fileType: FileType.source,
+ languageOptions: const CppOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
// Nothing should be captured by reference for async handlers, since their
// lifetime is unknown (and expected to be longer than the stack's).
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index c77e5d8..d99dde7 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -29,7 +29,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains(' dataType1? field1;'));
@@ -49,7 +50,8 @@
enums: <Enum>[anEnum],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('enum Foobar'));
expect(code, contains(' one,'));
@@ -92,7 +94,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, contains('Future<Output> doSomething(Input arg_input)'));
@@ -118,7 +121,8 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, contains('Future<int> add(int arg_x, int arg_y)'));
@@ -145,7 +149,8 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, contains('int add(int x, int y)'));
@@ -182,7 +187,8 @@
)
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(
code,
@@ -224,7 +230,8 @@
)
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(
code,
@@ -276,7 +283,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('abstract class Api'));
expect(code, contains('static void setup(Api'));
@@ -310,7 +318,8 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<void> doSomething'));
expect(code, contains('return;'));
@@ -343,7 +352,8 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
// The next line verifies that we're not setting a variable to the value of "doSomething", but
// ignores the line where we assert the value of the argument isn't null, since on that line
@@ -373,7 +383,8 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, matches('output.*=.*doSomething[(][)]'));
expect(code, contains('Output doSomething();'));
@@ -415,7 +426,8 @@
)
]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('enum1?.index,'));
expect(code, contains('? Enum.values[result[0]! as int]'));
@@ -442,7 +454,8 @@
])
]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('enum Foo {'));
expect(code, contains('Future<void> bar(Foo? arg_foo) async'));
@@ -485,7 +498,8 @@
)
]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('enum1.index,'));
expect(code, contains('enum1: Enum.values[result[0]! as int]'));
@@ -512,7 +526,8 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, matches('channel.send[(]null[)]'));
});
@@ -570,7 +585,8 @@
], enums: <Enum>[]);
final StringBuffer mainCodeSink = StringBuffer();
final StringBuffer testCodeSink = StringBuffer();
- generateDart(DartOptions(), root, mainCodeSink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, mainCodeSink);
final String mainCode = mainCodeSink.toString();
expect(mainCode, isNot(contains(r"import 'fo\'o.dart';")));
expect(mainCode, contains('class Api {'));
@@ -578,12 +594,15 @@
expect(mainCode, isNot(contains('.ApiMock.doSomething')));
expect(mainCode, isNot(contains("'${Keys.result}': output")));
expect(mainCode, isNot(contains('return <Object>[];')));
- generateTestDart(
- DartOptions(),
+
+ const DartGenerator testGenerator = DartGenerator();
+ testGenerator.generateTest(
+ DartOptions(
+ sourceOutPath: "fo'o.dart",
+ testOutPath: 'test.dart',
+ ),
root,
testCodeSink,
- sourceOutPath: "fo'o.dart",
- testOutPath: 'test.dart',
);
final String testCode = testCodeSink.toString();
expect(testCode, contains(r"import 'fo\'o.dart';"));
@@ -631,7 +650,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('abstract class Api'));
expect(code, contains('Future<Output> doSomething(Input arg0);'));
@@ -675,7 +695,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.s*doSomething')));
expect(code, contains('await api.doSomething('));
@@ -719,7 +740,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, matches('Output.*doSomething.*Input'));
@@ -747,7 +769,8 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, matches('channel.send[(]null[)]'));
});
@@ -759,7 +782,9 @@
test('header', () {
final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(
+
+ const DartGenerator generator = DartGenerator();
+ generator.generate(
DartOptions(copyrightHeader: makeIterable('hello world')),
root,
sink,
@@ -788,7 +813,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains(' List<int?>? field1;'));
@@ -815,7 +841,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains(' Map<String?, int?>? field1;'));
@@ -844,7 +871,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('doit(List<int?> arg'));
});
@@ -872,7 +900,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('doit(List<int?> arg'));
});
@@ -896,7 +925,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<List<int?>> doit('));
expect(code,
@@ -931,7 +961,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('List<int?> doit('));
expect(
@@ -958,7 +989,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<int?> doit()'));
expect(code, contains('return (replyList[0] as int?);'));
@@ -983,7 +1015,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<List<int?>?> doit()'));
expect(code,
@@ -1008,7 +1041,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<int?> doit()'));
expect(code, contains('return (replyList[0] as int?);'));
@@ -1031,7 +1065,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('int? doit();'));
expect(code, contains('final int? output = api.doit();'));
@@ -1055,7 +1090,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<int?> doit();'));
expect(code, contains('final int? output = await api.doit();'));
@@ -1078,7 +1114,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(
code,
@@ -1107,7 +1144,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('Future<void> doit(int? arg_foo) async {'));
});
@@ -1133,7 +1171,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('void doit(int? foo);'));
});
@@ -1150,12 +1189,14 @@
final Root root =
Root(classes: <Class>[], apis: <Api>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateTestDart(
- DartOptions(),
+ const DartGenerator testGenerator = DartGenerator();
+ testGenerator.generateTest(
+ DartOptions(
+ sourceOutPath: path.join(foo.path, 'bar.dart'),
+ testOutPath: path.join(tempDir.path, 'test', 'bar_test.dart'),
+ ),
root,
sink,
- sourceOutPath: path.join(foo.path, 'bar.dart'),
- testOutPath: path.join(tempDir.path, 'test', 'bar_test.dart'),
);
final String code = sink.toString();
expect(code, contains("import 'package:foobar/foo/bar.dart';"));
@@ -1238,7 +1279,8 @@
],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
for (final String comment in comments) {
expect(code, contains('///$comment'));
@@ -1273,7 +1315,8 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, isNot(contains('extends StandardMessageCodec')));
expect(code, contains('StandardMessageCodec'));
@@ -1316,7 +1359,8 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateDart(DartOptions(), root, sink);
+ const DartGenerator generator = DartGenerator();
+ generator.generate(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, contains('extends StandardMessageCodec'));
});
@@ -1354,13 +1398,17 @@
],
);
final StringBuffer sink = StringBuffer();
- generateTestDart(
- DartOptions(),
+
+ const DartGenerator testGenerator = DartGenerator();
+ testGenerator.generateTest(
+ DartOptions(
+ sourceOutPath: 'code.dart',
+ testOutPath: 'test.dart',
+ ),
root,
sink,
- sourceOutPath: 'code.dart',
- testOutPath: 'test.dart',
);
+
final String testCode = sink.toString();
expect(
testCode,
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index bfcea98..40ec005 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -27,7 +27,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public class Messages'));
expect(code, contains('public static class Foobar'));
@@ -55,7 +56,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public enum Foobar'));
expect(code, contains(' ONE(0),'));
@@ -86,7 +88,8 @@
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions =
JavaOptions(className: 'Messages', package: 'com.google.foobar');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('package com.google.foobar;'));
expect(code, contains('ArrayList<Object> toList()'));
@@ -129,7 +132,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public interface Api'));
expect(code, matches('Output.*doSomething.*Input'));
@@ -200,7 +204,8 @@
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('private @Nullable Boolean aBool;'));
expect(code, contains('private @Nullable Long aInt;'));
@@ -249,7 +254,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public static class Api'));
expect(code, matches('doSomething.*Input.*Output'));
@@ -283,7 +289,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.*doSomething')));
expect(code, contains('doSomething('));
@@ -317,7 +324,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('Reply<Void>'));
expect(code, contains('callback.reply(null)'));
@@ -345,7 +353,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('Output doSomething()'));
expect(code, contains('api.doSomething()'));
@@ -373,7 +382,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doSomething(Reply<Output>'));
expect(code, contains('channel.send(null'));
@@ -392,7 +402,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public static class Foobar'));
expect(code, contains('private @Nullable List<Object> field1;'));
@@ -411,7 +422,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public static class Foobar'));
expect(code, contains('private @Nullable Map<Object, Object> field1;'));
@@ -447,7 +459,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public class Messages'));
expect(code, contains('public static class Outer'));
@@ -498,7 +511,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public interface Api'));
expect(code, contains('public interface Result<T> {'));
@@ -549,7 +563,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public static class Api'));
expect(code, matches('doSomething.*Input.*Output'));
@@ -582,7 +597,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public enum Enum1'));
expect(code, contains(' ONE(0),'));
@@ -621,7 +637,8 @@
]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('public enum Foo'));
expect(
@@ -641,7 +658,8 @@
className: 'Messages',
copyrightHeader: makeIterable('hello world'),
);
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
@@ -667,7 +685,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains('List<Long> field1;'));
@@ -695,7 +714,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Foobar'));
expect(code, contains('Map<String, String> field1;'));
@@ -725,7 +745,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doit(@NonNull List<Long> arg'));
});
@@ -754,7 +775,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doit(@NonNull List<Long> arg'));
});
@@ -779,7 +801,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('List<Long> doit('));
expect(code, contains('List<Long> output ='));
@@ -805,7 +828,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doit(Reply<List<Long>> callback)'));
expect(code, contains('List<Long> output ='));
@@ -828,7 +852,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doit(Reply<Long> callback)'));
expect(
@@ -858,7 +883,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Messages'));
expect(code, contains('Long add(@NonNull Long x, @NonNull Long y)'));
@@ -889,7 +915,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Api');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('Object xArg = args.get(0)'));
});
@@ -915,7 +942,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Messages'));
expect(code, contains('BasicMessageChannel<Object> channel'));
@@ -947,7 +975,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -973,7 +1002,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@Nullable Long doit();'));
});
@@ -997,7 +1027,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
// Java doesn't accept nullability annotations in type arguments.
expect(code, contains('Result<Long>'));
@@ -1025,7 +1056,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains(' void doit(@Nullable Long foo);'));
});
@@ -1052,7 +1084,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1083,7 +1116,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1108,7 +1142,8 @@
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions =
JavaOptions(className: 'Messages', useGeneratedAnnotation: true);
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@javax.annotation.Generated("dev.flutter.pigeon")'));
});
@@ -1125,7 +1160,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code,
isNot(contains('@javax.annotation.Generated("dev.flutter.pigeon")')));
@@ -1207,7 +1243,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
for (final String comment in comments) {
// This regex finds the comment only between the open and close comment block
@@ -1247,7 +1284,8 @@
);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains(' extends StandardMessageCodec')));
expect(code, contains('StandardMessageCodec'));
@@ -1291,7 +1329,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const JavaOptions javaOptions = JavaOptions(className: 'Messages');
- generateJava(javaOptions, root, sink);
+ const JavaGenerator generator = JavaGenerator();
+ generator.generate(javaOptions, root, sink);
final String code = sink.toString();
expect(code, contains(' extends StandardMessageCodec'));
});
diff --git a/packages/pigeon/test/kotlin_generator_test.dart b/packages/pigeon/test/kotlin_generator_test.dart
index 4653410..aa4ec3e 100644
--- a/packages/pigeon/test/kotlin_generator_test.dart
+++ b/packages/pigeon/test/kotlin_generator_test.dart
@@ -27,7 +27,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Foobar ('));
expect(code, contains('val field1: Long? = null'));
@@ -50,7 +51,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum class Foobar(val raw: Int) {'));
expect(code, contains('ONE(0)'));
@@ -78,7 +80,8 @@
]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum class Foo(val raw: Int) {'));
expect(code, contains('val fooArg = Foo.ofRaw(args[0] as Int)'));
@@ -124,7 +127,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('interface Api'));
expect(code, contains('fun doSomething(input: Input): Output'));
@@ -213,7 +217,8 @@
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('val aBool: Boolean? = null'));
expect(code, contains('val aInt: Long? = null'));
@@ -265,7 +270,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code,
contains('class Api(private val binaryMessenger: BinaryMessenger)'));
@@ -302,7 +308,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(matches('.*doSomething(.*) ->')));
expect(code, matches('doSomething(.*)'));
@@ -338,7 +345,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('callback: () -> Unit'));
expect(code, contains('callback()'));
@@ -367,7 +375,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doSomething(): Output'));
expect(code, contains('wrapped = listOf<Any?>(api.doSomething())'));
@@ -398,7 +407,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doSomething(callback: (Output) -> Unit)'));
expect(code, contains('channel.send(null)'));
@@ -418,7 +428,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Foobar'));
expect(code, contains('val field1: List<Any?>? = null'));
@@ -438,7 +449,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Foobar'));
expect(code, contains('val field1: Map<Any, Any?>? = null'));
@@ -476,7 +488,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Outer'));
expect(code, contains('data class Nested'));
@@ -529,7 +542,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('interface Api'));
expect(code, contains('api.doSomething(argArg) {'));
@@ -577,7 +591,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, matches('fun doSomething.*Input.*callback.*Output.*Unit'));
@@ -610,7 +625,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum class Enum1(val raw: Int)'));
expect(code, contains('ONE(0)'));
@@ -627,7 +643,8 @@
final KotlinOptions kotlinOptions = KotlinOptions(
copyrightHeader: makeIterable('hello world'),
);
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
@@ -654,7 +671,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Foobar'));
expect(code, contains('val field1: List<Long?>'));
@@ -683,7 +701,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('data class Foobar'));
expect(code, contains('val field1: Map<String?, String?>'));
@@ -714,7 +733,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(arg: List<Long?>'));
});
@@ -744,7 +764,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(argArg: List<Long?>'));
});
@@ -769,7 +790,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(): List<Long?>'));
expect(code, contains('wrapped = listOf<Any?>(api.doit())'));
@@ -796,7 +818,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(callback: (List<Long?>) -> Unit'));
expect(code, contains('val result = it as List<Long?>'));
@@ -824,7 +847,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun add(x: Long, y: Long): Long'));
expect(code, contains('val args = message as List<Any?>'));
@@ -861,7 +885,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('val channel = BasicMessageChannel'));
expect(code, contains('val result = it as Long'));
@@ -889,7 +914,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(): Long?'));
});
@@ -913,7 +939,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(callback: (Long?) -> Unit'));
});
@@ -940,7 +967,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -970,7 +998,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('fun doit(fooArg: Long?, callback: () -> Unit'));
});
@@ -1005,7 +1034,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains('val input: String\n'));
});
@@ -1086,7 +1116,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
for (final String comment in comments) {
// This regex finds the comment only between the open and close comment block
@@ -1126,7 +1157,8 @@
);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains(' : StandardMessageCodec() ')));
expect(code, contains('StandardMessageCodec'));
@@ -1170,7 +1202,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const KotlinOptions kotlinOptions = KotlinOptions();
- generateKotlin(kotlinOptions, root, sink);
+ const KotlinGenerator generator = KotlinGenerator();
+ generator.generate(kotlinOptions, root, sink);
final String code = sink.toString();
expect(code, contains(' : StandardMessageCodec() '));
});
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index aeb2db6..64f92c8 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:pigeon/ast.dart';
+import 'package:pigeon/generator_tools.dart';
import 'package:pigeon/objc_generator.dart';
import 'package:pigeon/pigeon_lib.dart';
import 'package:test/test.dart';
@@ -17,7 +18,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(code, matches('@property.*NSString.*field1'));
@@ -32,8 +39,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('#import "foo.h"'));
expect(code, contains('@implementation Foobar'));
@@ -50,7 +62,13 @@
)
]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('typedef NS_ENUM(NSUInteger, Enum1) {'));
expect(code, contains(' Enum1One = 0,'));
@@ -68,7 +86,13 @@
)
]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(prefix: 'PREFIX'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(prefix: 'PREFIX'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('typedef NS_ENUM(NSUInteger, PREFIXEnum1) {'));
expect(code, contains(' PREFIXEnum1One = 0,'));
@@ -104,8 +128,13 @@
],
);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('#import "foo.h"'));
expect(code, contains('@implementation Foobar'));
@@ -138,13 +167,25 @@
const ObjcOptions options =
ObjcOptions(headerIncludePath: 'foo.h', prefix: 'AC');
{
- generateObjcHeader(options, root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: options,
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('typedef NS_ENUM(NSUInteger, ACFoo)'));
expect(code, contains(':(ACFoo)foo error:'));
}
{
- generateObjcSource(options, root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: options,
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -207,8 +248,13 @@
],
);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@property(nonatomic, assign) Enum1 enum1'));
});
@@ -240,7 +286,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Input'));
expect(code, contains('@interface Output'));
@@ -279,8 +331,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('#import "foo.h"'));
expect(code, contains('@implementation Input'));
@@ -327,8 +384,13 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(code, contains('@class FlutterStandardTypedData;'));
@@ -356,8 +418,13 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@implementation Foobar'));
expect(code,
@@ -378,8 +445,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code,
contains('@property(nonatomic, strong, nullable) Input * nested;'));
@@ -399,8 +471,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -419,7 +496,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface ABCFoobar'));
});
@@ -433,7 +516,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@implementation ABCFoobar'));
});
@@ -467,7 +556,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, matches('property.*ABCInput'));
expect(code, matches('ABCNested.*doSomething.*ABCInput'));
@@ -503,7 +598,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('ABCInput fromList'));
expect(code, matches(r'ABCInput.*=.*args.*0.*\;'));
@@ -539,8 +640,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Api : NSObject'));
expect(
@@ -579,8 +685,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h'), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(headerIncludePath: 'foo.h'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@implementation Api'));
expect(code, matches('void.*doSomething.*Input.*Output.*{'));
@@ -609,10 +720,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('(void)doSomething:'));
});
@@ -640,10 +755,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.*doSomething')));
expect(code, matches('[.*doSomething:.*]'));
@@ -673,10 +792,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('completion:(void(^)(NSError *_Nullable))'));
});
@@ -704,10 +827,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('completion:(void(^)(NSError *_Nullable))'));
expect(code, contains('completion(nil)'));
@@ -730,10 +857,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, matches('ABCOutput.*doSomethingWithError:[(]FlutterError'));
});
@@ -755,10 +886,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, matches('output.*=.*api doSomethingWithError:&error'));
});
@@ -780,10 +915,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -808,10 +947,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -829,7 +972,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(code, matches('@property.*NSArray.*field1'));
@@ -844,7 +993,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(code, matches('@property.*NSDictionary.*field1'));
@@ -865,7 +1020,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(
@@ -894,7 +1055,13 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('(NSDictionary<NSString *, id> *)foo'));
});
@@ -928,10 +1095,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -969,10 +1140,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -998,10 +1173,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1020,10 +1199,14 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1061,10 +1244,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1101,10 +1288,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1123,10 +1314,14 @@
])
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1152,10 +1347,14 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1170,14 +1369,16 @@
test('source copyright', () {
final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- ObjcOptions(
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: ObjcOptions(
headerIncludePath: 'foo.h',
prefix: 'ABC',
copyrightHeader: makeIterable('hello world')),
- root,
- sink,
);
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
@@ -1185,14 +1386,16 @@
test('header copyright', () {
final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- ObjcOptions(
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: ObjcOptions(
headerIncludePath: 'foo.h',
prefix: 'ABC',
copyrightHeader: makeIterable('hello world')),
- root,
- sink,
);
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
@@ -1217,10 +1420,14 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('NSArray<NSNumber *> * field1'));
});
@@ -1249,19 +1456,27 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitArg:(NSArray<NSNumber *> *)arg'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1294,19 +1509,27 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitArg:(NSArray<NSNumber *> *)arg'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitArg:(NSArray<NSNumber *> *)arg'));
}
@@ -1342,10 +1565,14 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitArg:(NSArray<NSArray<NSNumber *> *> *)arg'));
}
@@ -1371,20 +1598,28 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code, contains('- (nullable NSArray<NSNumber *> *)doitWithError:'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('NSArray<NSNumber *> *output ='));
}
@@ -1410,20 +1645,28 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code, contains('doitWithCompletion:(void(^)(NSArray<NSNumber *> *'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code, contains('doitWithCompletion:(void(^)(NSArray<NSNumber *> *'));
@@ -1451,10 +1694,14 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1463,10 +1710,14 @@
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('NSArray *args = message;'));
expect(code,
@@ -1500,10 +1751,14 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1512,10 +1767,14 @@
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('NSArray *args = message;'));
expect(code,
@@ -1547,10 +1806,14 @@
], classes: <Class>[], enums: <Enum>[]);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1559,10 +1822,14 @@
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- root,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1605,19 +1872,27 @@
final Root divideRoot = getDivideRoot(ApiLocation.host);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- divideRoot,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, divideRoot, sink);
final String code = sink.toString();
expect(code, matches('divideValue:.*by:.*error.*;'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- divideRoot,
- sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
+ );
+ generator.generate(generatorOptions, divideRoot, sink);
final String code = sink.toString();
expect(code, matches('divideValue:.*by:.*error.*;'));
}
@@ -1627,21 +1902,27 @@
final Root divideRoot = getDivideRoot(ApiLocation.flutter);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- divideRoot,
- sink,
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
);
+ generator.generate(generatorOptions, divideRoot, sink);
final String code = sink.toString();
expect(code, matches('divideValue:.*by:.*completion.*;'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(
- const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
- divideRoot,
- sink,
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions:
+ const ObjcOptions(headerIncludePath: 'foo.h', prefix: 'ABC'),
);
+ generator.generate(generatorOptions, divideRoot, sink);
final String code = sink.toString();
expect(code, matches('divideValue:.*by:.*completion.*{'));
}
@@ -1656,7 +1937,13 @@
]),
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('@interface Foobar'));
expect(code, contains('@property(nonatomic, copy) NSString * field1'));
@@ -1679,7 +1966,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1704,7 +1997,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, matches(r'doitWithCompletion.*NSNumber \*_Nullable'));
});
@@ -1726,7 +2025,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, matches(r'nullable NSNumber.*doitWithError'));
});
@@ -1753,13 +2058,25 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitFoo:(nullable NSNumber *)foo'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code,
contains('NSNumber *arg_foo = GetNullableObjectAtIndex(args, 0);'));
@@ -1788,13 +2105,25 @@
);
{
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('doitFoo:(nullable NSNumber *)foo'));
}
{
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains('- (void)doitFoo:(nullable NSNumber *)arg_foo'));
}
@@ -1818,7 +2147,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -1902,7 +2237,13 @@
],
);
final StringBuffer sink = StringBuffer();
- generateObjcHeader(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.header,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
for (final String comment in comments) {
expect(code, contains('///$comment'));
@@ -1937,7 +2278,13 @@
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains(' : FlutterStandardReader')));
});
@@ -1979,7 +2326,13 @@
])
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
- generateObjcSource(const ObjcOptions(), root, sink);
+ const ObjcGenerator generator = ObjcGenerator();
+ final OutputFileOptions<ObjcOptions> generatorOptions =
+ OutputFileOptions<ObjcOptions>(
+ fileType: FileType.source,
+ languageOptions: const ObjcOptions(),
+ );
+ generator.generate(generatorOptions, root, sink);
final String code = sink.toString();
expect(code, contains(' : FlutterStandardReader'));
});
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 64c4af7..3d5c706 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -938,10 +938,10 @@
dartTestOut: 'stdout',
dartOut: 'stdout',
);
- final DartTestGeneratorAdapter dartGeneratorAdapter =
+ final DartTestGeneratorAdapter dartTestGeneratorAdapter =
DartTestGeneratorAdapter();
final StringBuffer buffer = StringBuffer();
- dartGeneratorAdapter.generate(buffer, options, root, FileType.source);
+ dartTestGeneratorAdapter.generate(buffer, options, root, FileType.source);
expect(buffer.toString(), startsWith('// Copyright 2013'));
});
diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart
index 74c8aad..dfd2dfb 100644
--- a/packages/pigeon/test/swift_generator_test.dart
+++ b/packages/pigeon/test/swift_generator_test.dart
@@ -26,7 +26,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Foobar'));
expect(code, contains('var field1: Int32? = nil'));
@@ -49,7 +50,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum Foobar: Int'));
expect(code, contains(' case one = 0'));
@@ -77,7 +79,8 @@
]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum Foo: Int'));
expect(code, contains('let fooArg = Foo(rawValue: args[0] as! Int)!'));
@@ -120,7 +123,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('protocol Api'));
expect(code, matches('func doSomething.*Input.*Output'));
@@ -183,7 +187,8 @@
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('var aBool: Bool? = nil'));
expect(code, contains('var aInt: Int32? = nil'));
@@ -232,7 +237,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, contains('init(binaryMessenger: FlutterBinaryMessenger)'));
@@ -267,7 +273,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(matches('.*doSomething(.*) ->')));
expect(code, matches('doSomething(.*)'));
@@ -301,7 +308,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('completion: @escaping () -> Void'));
expect(code, contains('completion()'));
@@ -329,7 +337,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doSomething() -> Output'));
expect(code, contains('let result = api.doSomething()'));
@@ -358,7 +367,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code,
contains('func doSomething(completion: @escaping (Output) -> Void)'));
@@ -378,7 +388,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Foobar'));
expect(code, contains('var field1: [Any?]? = nil'));
@@ -397,7 +408,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Foobar'));
expect(code, contains('var field1: [AnyHashable: Any?]? = nil'));
@@ -433,7 +445,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Outer'));
expect(code, contains('struct Nested'));
@@ -481,7 +494,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('protocol Api'));
expect(code, contains('api.doSomething(arg: argArg) { result in'));
@@ -526,7 +540,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('class Api'));
expect(code, matches('func doSomething.*Input.*completion.*Output.*Void'));
@@ -558,7 +573,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('enum Enum1: Int'));
expect(code, contains('case one = 0'));
@@ -575,7 +591,8 @@
final SwiftOptions swiftOptions = SwiftOptions(
copyrightHeader: makeIterable('hello world'),
);
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, startsWith('// hello world'));
});
@@ -601,7 +618,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Foobar'));
expect(code, contains('var field1: [Int32?]'));
@@ -629,7 +647,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('struct Foobar'));
expect(code, contains('var field1: [String?: String?]'));
@@ -659,7 +678,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doit(arg: [Int32?]'));
});
@@ -688,7 +708,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doit(arg argArg: [Int32?]'));
});
@@ -713,7 +734,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doit() -> [Int32?]'));
expect(code, contains('let result = api.doit()'));
@@ -740,7 +762,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(
code, contains('func doit(completion: @escaping ([Int32?]) -> Void'));
@@ -769,7 +792,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func add(x: Int32, y: Int32) -> Int32'));
expect(code, contains('let args = message as! [Any?]'));
@@ -800,7 +824,8 @@
], classes: <Class>[], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('let channel = FlutterBasicMessageChannel'));
expect(code, contains('let result = response as! Int32'));
@@ -830,7 +855,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doit() -> Int32?'));
});
@@ -854,7 +880,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('func doit(completion: @escaping (Int32?) -> Void'));
});
@@ -881,7 +908,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('let fooArg = args[0] as? Int32'));
});
@@ -908,7 +936,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(
code,
@@ -944,7 +973,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains('var input: String\n'));
});
@@ -1025,7 +1055,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
for (final String comment in comments) {
expect(code, contains('///$comment'));
@@ -1061,7 +1092,8 @@
);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, isNot(contains(': FlutterStandardReader ')));
});
@@ -1104,7 +1136,8 @@
], enums: <Enum>[]);
final StringBuffer sink = StringBuffer();
const SwiftOptions swiftOptions = SwiftOptions();
- generateSwift(swiftOptions, root, sink);
+ const SwiftGenerator generator = SwiftGenerator();
+ generator.generate(swiftOptions, root, sink);
final String code = sink.toString();
expect(code, contains(': FlutterStandardReader '));
});