Skip to content

Commit

Permalink
[pigeon] adds support for collections of enums and classes (#7476)
Browse files Browse the repository at this point in the history
adds support for collections of enums and classes
fixes flutter/flutter#133728
  • Loading branch information
tarrinneal authored Aug 30, 2024
1 parent 1f9b89b commit b835465
Show file tree
Hide file tree
Showing 35 changed files with 20,688 additions and 2,647 deletions.
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 22.3.0

* Adds support for enums and classes in collections.

## 22.2.0

* [kotlin] Adds implementation for `@ProxyApi`.
Expand Down
6 changes: 3 additions & 3 deletions packages/pigeon/example/app/ios/Runner/Messages.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private class MessagesPigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 129:
let enumResultAsInt: Int? = nilOrValue(self.readValue() as? Int)
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
if let enumResultAsInt = enumResultAsInt {
return Code(rawValue: enumResultAsInt)
}
Expand Down Expand Up @@ -191,8 +191,8 @@ class ExampleHostApiSetup {
if let api = api {
addChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let aArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let bArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let aArg = args[0] as! Int64
let bArg = args[1] as! Int64
do {
let result = try api.add(aArg, to: bArg)
reply(wrapResult(result))
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '22.2.0';
const String pigeonVersion = '22.3.0';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
7 changes: 0 additions & 7 deletions packages/pigeon/lib/pigeon_lib.dart
Original file line number Diff line number Diff line change
Expand Up @@ -979,13 +979,6 @@ List<Error> _validateAst(Root root, String source) {
lineNumber: _calculateLineNumberNullable(source, field.offset),
));
}
if (customEnums.contains(typeArgument.baseName)) {
result.add(Error(
message:
'Enum types aren\'t supported in type arguments in "${field.name}" in class "${classDefinition.name}".',
lineNumber: _calculateLineNumberNullable(source, field.offset),
));
}
}
if (!(validTypes.contains(field.type.baseName) ||
customClasses.contains(field.type.baseName) ||
Expand Down
58 changes: 39 additions & 19 deletions packages/pigeon/lib/swift_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class SwiftGenerator extends StructuredGenerator<SwiftOptions> {
indent.nest(1, () {
if (customType.type == CustomTypes.customEnum) {
indent.writeln(
'let enumResultAsInt: Int? = nilOrValue(self.readValue() as? Int)');
'let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)');
indent.writeScoped('if let enumResultAsInt = enumResultAsInt {', '}',
() {
indent.writeln(
Expand Down Expand Up @@ -419,7 +419,7 @@ if (wrapped == nil) {
}

void _writeClassField(Indent indent, NamedType field, {bool addNil = true}) {
indent.add('${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}');
indent.add('${field.name}: ${_nullSafeSwiftTypeForDartType(field.type)}');
final String defaultNil = field.type.isNullable && addNil ? ' = nil' : '';
indent.add(defaultNil);
}
Expand Down Expand Up @@ -487,7 +487,17 @@ if (wrapped == nil) {
getFieldsInSerializationOrder(classDefinition).last == field
? ''
: ',';
indent.writeln('${field.name}: ${field.name}$comma');
// Force-casting nullable enums in maps doesn't work the same as other types.
// It needs soft-casting followed by force unwrapping.
final String forceUnwrapMapWithNullableEnums =
(field.type.baseName == 'Map' &&
!field.type.isNullable &&
field.type.typeArguments
.any((TypeDeclaration type) => type.isEnum))
? '!'
: '';
indent.writeln(
'${field.name}: ${field.name}$forceUnwrapMapWithNullableEnums$comma');
}
});
});
Expand Down Expand Up @@ -650,14 +660,11 @@ if (wrapped == nil) {
assert(!type.isVoid);
if (type.baseName == 'Object') {
return value + (type.isNullable ? '' : '!');
} else if (type.baseName == 'int') {
if (type.isNullable) {
// Nullable ints need to check for NSNull, and Int32 before casting can be done safely.
// This nested ternary is a necessary evil to avoid less efficient conversions.
return 'isNullish($value) ? nil : ($value is Int64? ? $value as! Int64? : Int64($value as! Int32))';
} else {
return '$value is Int64 ? $value as! Int64 : Int64($value as! Int32)';
}
// Force-casting nullable enums in maps doesn't work the same as other types.
// It needs soft-casting followed by force unwrapping.
} else if (type.baseName == 'Map' &&
type.typeArguments.any((TypeDeclaration type) => type.isEnum)) {
return '$value as? ${_swiftTypeForDartType(type)}';
} else if (type.isNullable) {
return 'nilOrValue($value)';
} else {
Expand Down Expand Up @@ -846,7 +853,13 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
fieldType: fieldType,
type: returnType,
);
indent.writeln('completion(.success(result))');
// There is a swift bug with unwrapping maps of nullable Enums;
final String enumMapForceUnwrap = returnType.baseName == 'Map' &&
returnType.typeArguments
.any((TypeDeclaration type) => type.isEnum)
? '!'
: '';
indent.writeln('completion(.success(result$enumMapForceUnwrap))');
}
});
});
Expand Down Expand Up @@ -887,6 +900,12 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
final String argName = _getSafeArgumentName(index, arg.namedType);
final String argIndex = 'args[$index]';
final String fieldType = _swiftTypeForDartType(arg.type);
// There is a swift bug with unwrapping maps of nullable Enums;
final String enumMapForceUnwrap = arg.type.baseName == 'Map' &&
arg.type.typeArguments
.any((TypeDeclaration type) => type.isEnum)
? '!'
: '';

_writeGenericCasting(
indent: indent,
Expand All @@ -896,9 +915,10 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
type: arg.type);

if (arg.label == '_') {
methodArgument.add(argName);
methodArgument.add('$argName$enumMapForceUnwrap');
} else {
methodArgument.add('${arg.label ?? arg.name}: $argName');
methodArgument
.add('${arg.label ?? arg.name}: $argName$enumMapForceUnwrap');
}
});
}
Expand Down Expand Up @@ -1022,9 +1042,9 @@ String _swiftTypeForBuiltinGenericDartType(TypeDeclaration type) {
}
} else {
if (type.baseName == 'List') {
return '[${_nullsafeSwiftTypeForDartType(type.typeArguments.first)}]';
return '[${_nullSafeSwiftTypeForDartType(type.typeArguments.first)}]';
} else if (type.baseName == 'Map') {
return '[${_nullsafeSwiftTypeForDartType(type.typeArguments.first)}: ${_nullsafeSwiftTypeForDartType(type.typeArguments.last)}]';
return '[${_nullSafeSwiftTypeForDartType(type.typeArguments.first)}: ${_nullSafeSwiftTypeForDartType(type.typeArguments.last)}]';
} else {
return '${type.baseName}<${_flattenTypeArguments(type.typeArguments)}>';
}
Expand Down Expand Up @@ -1058,7 +1078,7 @@ String _swiftTypeForDartType(TypeDeclaration type) {
return _swiftTypeForBuiltinDartType(type) ?? type.baseName;
}

String _nullsafeSwiftTypeForDartType(TypeDeclaration type) {
String _nullSafeSwiftTypeForDartType(TypeDeclaration type) {
final String nullSafe = type.isNullable ? '?' : '';
return '${_swiftTypeForDartType(type)}$nullSafe';
}
Expand All @@ -1080,10 +1100,10 @@ String _getMethodSignature({
swiftFunction: swiftFunction,
);
final String returnTypeString =
returnType.isVoid ? 'Void' : _nullsafeSwiftTypeForDartType(returnType);
returnType.isVoid ? 'Void' : _nullSafeSwiftTypeForDartType(returnType);

final Iterable<String> types =
parameters.map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
parameters.map((NamedType e) => _nullSafeSwiftTypeForDartType(e.type));
final Iterable<String> labels = indexMap(components.arguments,
(int index, _SwiftFunctionArgument argument) {
return argument.label ?? _getArgumentName(index, argument.namedType);
Expand Down
Loading

0 comments on commit b835465

Please sign in to comment.