Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pigeon/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ gradlew.bat
local.properties
gradle-wrapper.jar
bin/pigeon.dart.dill

**/subdirectory/does/not/exist/**
8 changes: 6 additions & 2 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## NEXT
## 11.0.0

* Adds primitive enum support.
* Fixes Objective-C nullable enums.
* **Breaking Change** Changes all nullable enums in Objective-C to be wrapped in custom classes.
* **Breaking Change** Changes all enums names in Objective-C to have class prefix.
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.

## 10.1.6
Expand Down Expand Up @@ -91,7 +95,7 @@
## 9.1.1

* [swift] Removes experimental tags.
* [kotin] Removes experimental tags.
* [kotlin] Removes experimental tags.

## 9.1.0

Expand Down
7 changes: 2 additions & 5 deletions packages/pigeon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@ Currently pigeon supports generating:
Pigeon uses the `StandardMessageCodec` so it supports
[any datatype platform channels support](https://flutter.dev/docs/development/platform-integration/platform-channels#codec).

Custom classes and nested datatypes are also supported.
Custom classes, nested datatypes, and enums are also supported.

#### Enums

Pigeon currently supports enum generation in class fields only.
See issue: [87307](https://github.com/flutter/flutter/issues/87307).
Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability.

### Synchronous and Asynchronous methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ ArrayList<Object> toList() {
Object description = list.get(1);
pigeonResult.setDescription((String) description);
Object code = list.get(2);
pigeonResult.setCode(code == null ? null : Code.values()[(int) code]);
pigeonResult.setCode(Code.values()[(int) code]);
Object data = list.get(3);
pigeonResult.setData((Map<String, String>) data);
return pigeonResult;
Expand Down
4 changes: 4 additions & 0 deletions packages/pigeon/example/app/ios/Runner/Messages.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import FlutterMacOS
#error("Unsupported platform.")
#endif

private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}

private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
Expand Down
6 changes: 6 additions & 0 deletions packages/pigeon/example/app/macos/Runner/messages.g.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ typedef NS_ENUM(NSUInteger, PGNCode) {
PGNCodeTwo = 1,
};

/// Wrapper for PGNCode to allow for nullability.
@interface PGNCodeBox : NSObject
@property(nonatomic, assign) PGNCode value;
- (instancetype)initWithValue:(PGNCode)value;
@end

@class PGNMessageData;

@interface PGNMessageData : NSObject
Expand Down
10 changes: 10 additions & 0 deletions packages/pigeon/example/app/macos/Runner/messages.g.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
#error File requires ARC to be enabled.
#endif

@implementation PGNCodeBox
- (instancetype)initWithValue:(PGNCode)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
@end

static NSArray *wrapResult(id result, FlutterError *error) {
if (error) {
return @[
Expand Down
68 changes: 57 additions & 11 deletions packages/pigeon/lib/cpp_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -888,9 +888,14 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
indent.writeln(
'std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);');
indent.writeln('const auto& $encodedReplyName = *response;');
_writeEncodableValueArgumentUnwrapping(indent, returnType,
argName: successCallbackArgument,
encodableArgName: encodedReplyName);
_writeEncodableValueArgumentUnwrapping(
indent,
root,
returnType,
argName: successCallbackArgument,
encodableArgName: encodedReplyName,
apiType: ApiType.flutter,
);
}
indent.writeln('on_success($successCallbackArgument);');
});
Expand Down Expand Up @@ -968,9 +973,19 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
indent.writeln('return;');
});
}
_writeEncodableValueArgumentUnwrapping(indent, hostType,
argName: argName, encodableArgName: encodableArgName);
methodArgument.add(argName);
_writeEncodableValueArgumentUnwrapping(
indent,
root,
hostType,
argName: argName,
encodableArgName: encodableArgName,
apiType: ApiType.host,
);
final String unwrapEnum =
isEnum(root, arg.type) && arg.type.isNullable
? ' ? &(*$argName) : nullptr'
: '';
methodArgument.add('$argName$unwrapEnum');
});
}

Expand Down Expand Up @@ -1198,29 +1213,35 @@ return EncodableValue(EncodableList{
final String errorGetter;

const String nullValue = 'EncodableValue()';
String enumPrefix = '';
if (isEnum(root, returnType)) {
enumPrefix = '(int) ';
}
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, _shortBaseCppTypeForBuiltinDartType);

const String extractedValue = 'std::move(output).TakeValue()';
final String wrapperType =
hostType.isBuiltin ? 'EncodableValue' : 'CustomEncodableValue';
final String wrapperType = hostType.isBuiltin || isEnum(root, returnType)
? 'EncodableValue'
: '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\twrapped.push_back($wrapperType(${enumPrefix}std::move(output_optional).value()));
$prefix} else {
$prefix\twrapped.push_back($nullValue);
$prefix}''';
} else {
nonErrorPath =
'${prefix}wrapped.push_back($wrapperType($extractedValue));';
'${prefix}wrapped.push_back($wrapperType($enumPrefix$extractedValue));';
}
errorCondition = 'output.has_error()';
errorGetter = 'error';
Expand Down Expand Up @@ -1297,9 +1318,11 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
/// existing EncodableValue variable called [encodableArgName].
void _writeEncodableValueArgumentUnwrapping(
Indent indent,
Root root,
HostDatatype hostType, {
required String argName,
required String encodableArgName,
required ApiType apiType,
}) {
if (hostType.isNullable) {
// Nullable arguments are always pointers, with nullptr corresponding to
Expand All @@ -1320,6 +1343,22 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
} else if (hostType.isBuiltin) {
indent.writeln(
'const auto* $argName = std::get_if<${hostType.datatype}>(&$encodableArgName);');
} else if (hostType.isEnum) {
if (hostType.isNullable) {
final String valueVarName = '${argName}_value';
indent.writeln(
'const int64_t $valueVarName = $encodableArgName.IsNull() ? 0 : $encodableArgName.LongValue();');
if (apiType == ApiType.flutter) {
indent.writeln(
'const auto* $argName = $encodableArgName.IsNull() ? nullptr : &(${hostType.datatype})$valueVarName;');
} else {
indent.writeln(
'const auto $argName = $encodableArgName.IsNull() ? std::nullopt : std::make_optional<${hostType.datatype}>(static_cast<${hostType.datatype}>(${argName}_value));');
}
} else {
indent.writeln(
'const auto* $argName = &((${hostType.datatype})std::get<int>($encodableArgName));');
}
} else {
indent.writeln(
'const auto* $argName = &(std::any_cast<const ${hostType.datatype}&>(std::get<CustomEncodableValue>($encodableArgName)));');
Expand All @@ -1342,6 +1381,9 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
} else if (hostType.isBuiltin) {
indent.writeln(
'const auto& $argName = std::get<${hostType.datatype}>($encodableArgName);');
} else if (hostType.isEnum) {
indent.writeln(
'const ${hostType.datatype}& $argName = (${hostType.datatype})$encodableArgName.LongValue();');
} else {
indent.writeln(
'const auto& $argName = std::any_cast<const ${hostType.datatype}&>(std::get<CustomEncodableValue>($encodableArgName));');
Expand Down Expand Up @@ -1390,7 +1432,11 @@ String _getSafeArgumentName(int count, NamedType argument) =>
/// Returns a non-nullable variant of [type].
HostDatatype _nonNullableType(HostDatatype type) {
return HostDatatype(
datatype: type.datatype, isBuiltin: type.isBuiltin, isNullable: false);
datatype: type.datatype,
isBuiltin: type.isBuiltin,
isNullable: false,
isEnum: type.isEnum,
);
}

String _pascalCaseFromCamelCase(String camelCase) =>
Expand Down
31 changes: 25 additions & 6 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ $resultAt != null
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];');
'$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count]! as int];');
} else {
indent.writeln(
'$leftHandSide = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
Expand Down Expand Up @@ -442,10 +442,15 @@ $resultAt != null
} else {
indent.writeln('final $returnType output = $call;');
}

const String returnExpression = 'output';
final String nullability =
func.returnType.isNullable ? '?' : '';
final String valueExtraction =
isEnum(root, func.returnType) ? '$nullability.index' : '';
final String returnStatement = isMockHandler
? 'return <Object?>[$returnExpression];'
: 'return $returnExpression;';
? 'return <Object?>[$returnExpression$valueExtraction];'
: 'return $returnExpression$valueExtraction;';
indent.writeln(returnStatement);
}
});
Expand Down Expand Up @@ -487,6 +492,8 @@ $resultAt != null
codecName = _getCodecName(api);
_writeCodec(indent, codecName, api, root);
}
final List<String> customEnumNames =
root.enums.map((Enum x) => x.name).toList();
indent.newln();
bool first = true;
addDocumentationComments(
Expand Down Expand Up @@ -553,9 +560,21 @@ final BinaryMessenger? _binaryMessenger;
final String nullHandler = func.returnType.isNullable
? (genericCastCall.isEmpty ? '' : '?')
: '!';
final String returnStatement = func.returnType.isVoid
? 'return;'
: 'return $nullablyTypedAccessor$nullHandler$genericCastCall;';
String returnStatement = 'return';
if (customEnumNames.contains(returnType)) {
if (func.returnType.isNullable) {
returnStatement =
'$returnStatement ($accessor as int?) == null ? null : $returnType.values[$accessor! as int]';
} else {
returnStatement =
'$returnStatement $returnType.values[$accessor! as int]';
}
} else if (!func.returnType.isVoid) {
returnStatement =
'$returnStatement $nullablyTypedAccessor$nullHandler$genericCastCall';
}
returnStatement = '$returnStatement;';

indent.format('''
final List<Object?>? replyList =
\t\tawait channel.send($sendArgument) as List<Object?>?;
Expand Down
33 changes: 29 additions & 4 deletions packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '10.1.6';
const String pigeonVersion = '11.0.0';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down Expand Up @@ -176,6 +176,7 @@ class HostDatatype {
required this.datatype,
required this.isBuiltin,
required this.isNullable,
required this.isEnum,
});

/// The [String] that can be printed into host code to represent the type.
Expand All @@ -186,6 +187,9 @@ class HostDatatype {

/// `true` if the type corresponds to a nullable Dart datatype.
final bool isNullable;

/// `true if the type is a custom enum.
final bool isEnum;
}

/// Calculates the [HostDatatype] for the provided [NamedType].
Expand Down Expand Up @@ -226,20 +230,32 @@ HostDatatype _getHostDatatype(TypeDeclaration type, List<Class> classes,
? customResolver(type.baseName)
: type.baseName;
return HostDatatype(
datatype: customName, isBuiltin: false, isNullable: type.isNullable);
datatype: customName,
isBuiltin: false,
isNullable: type.isNullable,
isEnum: false,
);
} else if (enums.map((Enum x) => x.name).contains(type.baseName)) {
final String customName = customResolver != null
? customResolver(type.baseName)
: type.baseName;
return HostDatatype(
datatype: customName, isBuiltin: false, isNullable: type.isNullable);
datatype: customName,
isBuiltin: false,
isNullable: type.isNullable,
isEnum: true,
);
} else {
throw Exception(
'unrecognized datatype ${fieldName == null ? '' : 'for field:"$fieldName" '}of type:"${type.baseName}"');
}
} else {
return HostDatatype(
datatype: datatype, isBuiltin: true, isNullable: type.isNullable);
datatype: datatype,
isBuiltin: true,
isNullable: type.isNullable,
isEnum: false,
);
}
}

Expand Down Expand Up @@ -574,6 +590,15 @@ String? deducePackageName(String mainDartFile) {
}
}

/// Enum to specify api type when generating code.
enum ApiType {
/// Flutter api.
flutter,

/// Host api.
host,
}

/// Enum to specify which file will be generated for multi-file generators
enum FileType {
/// header file.
Expand Down
Loading