Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 26.3.4

* [dart] Supports serializing Lists of Lists and Maps, and serializing Maps of
Lists and Maps.

## 26.3.3

* Updates `analyzer` dependency to support versions 10 through 12.
Expand Down
40 changes: 37 additions & 3 deletions packages/pigeon/lib/src/dart/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1597,9 +1597,43 @@ String _castValue(String value, TypeDeclaration type) {
}

final nullAwareOperator = type.isNullable ? '?' : '';
final castCall =
'$nullAwareOperator.cast<${_flattenTypeArguments(typeArguments)}>()';
return '($valueWithTypeCast)$castCall';
return '($valueWithTypeCast)$nullAwareOperator.${_castCall(type)}';
}

/// Returns the code that casts a `List<Object?>` or a `Map<Object?, Object?>`
/// to a specific List or Map [type], considering nested List or Map type
/// arguments.
///
/// Given a simple [type], like `List<int>`, the returned code is a simple call
/// to [List.cast]: `cast<int>()`.
///
/// Given a deep List type, like `List<List<int>>`, the returned code uses
/// [List.map] and [List.cast]:
/// `map((e) => (e! as List<Object?>).cast<int>()).toList()`.
///
/// Given a deep Map type, like `Map<int, List<int>>`, the returned code uses
/// [Map.map] and [Map.cast]:
/// `map((k, v) => MapEntry(k! as int, (v! as List<Object?>).cast<int>()))`.
String _castCall(TypeDeclaration type) {
if (type.baseName == 'List') {
final TypeDeclaration typeArgument = type.typeArguments.first;
if (typeArgument.baseName == 'List' || typeArgument.baseName == 'Map') {
// We must cast each element in `valueWithTypeCast`.
return 'map((e) => ${_castValue('e', typeArgument)}).toList()';
}
} else if (type.baseName == 'Map') {
if (type.typeArguments.any(
(e) => e.baseName == 'List' || e.baseName == 'Map',
)) {
final TypeDeclaration keyTypeArgument = type.typeArguments[0];
final TypeDeclaration valueTypeArgument = type.typeArguments[1];
return 'map((k, v) => MapEntry('
'${_castValue('k', keyTypeArgument)}, '
'${_castValue('v', valueTypeArgument)}))';
}
}

return 'cast<${_flattenTypeArguments(type.typeArguments)}>()';
}

/// Returns an argument name that can be used in a context where it is possible to collide.
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/src/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import 'generator.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '26.3.3';
const String pigeonVersion = '26.3.4';

/// Default plugin package name.
const String defaultPluginPackageName = 'dev.flutter.pigeon';
Expand Down
15 changes: 15 additions & 0 deletions packages/pigeon/pigeons/core_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ class AllTypes {
required this.enumList,
required this.objectList,
required this.listList,
required this.boolListList,
required this.mapList,
required this.boolMapList,

// Maps
required this.map,
Expand All @@ -53,6 +55,7 @@ class AllTypes {
required this.enumMap,
required this.objectMap,
required this.listMap,
required this.boolListMap,
required this.mapMap,
});

Expand All @@ -79,7 +82,9 @@ class AllTypes {
List<AnEnum> enumList;
List<Object> objectList;
List<List<Object?>> listList;
List<List<bool>> boolListList;
List<Map<Object?, Object?>> mapList;
List<Map<int, bool>> boolMapList;

// Maps
// ignore: strict_raw_type, always_specify_types
Expand All @@ -89,6 +94,7 @@ class AllTypes {
Map<AnEnum, AnEnum> enumMap;
Map<Object, Object> objectMap;
Map<int, List<Object?>> listMap;
Map<int, List<bool>> boolListMap;
Map<int, Map<Object?, Object?>> mapMap;
}

Expand Down Expand Up @@ -121,6 +127,7 @@ class AllNullableTypes {
this.enumList,
this.objectList,
this.listList,
this.boolListList,
this.mapList,
this.recursiveClassList,

Expand All @@ -131,6 +138,7 @@ class AllNullableTypes {
this.enumMap,
this.objectMap,
this.listMap,
this.boolListMap,
this.mapMap,
this.recursiveClassMap,
);
Expand Down Expand Up @@ -159,6 +167,7 @@ class AllNullableTypes {
List<AnEnum?>? enumList;
List<Object?>? objectList;
List<List<Object?>?>? listList;
List<List<bool?>?>? boolListList;
List<Map<Object?, Object?>?>? mapList;
List<AllNullableTypes?>? recursiveClassList;

Expand All @@ -170,6 +179,7 @@ class AllNullableTypes {
Map<AnEnum?, AnEnum?>? enumMap;
Map<Object?, Object?>? objectMap;
Map<int?, List<Object?>?>? listMap;
Map<int?, List<bool?>?>? boolListMap;
Map<int?, Map<Object?, Object?>?>? mapMap;
Map<int?, AllNullableTypes?>? recursiveClassMap;
}
Expand Down Expand Up @@ -354,6 +364,11 @@ abstract class HostIntegrationCoreApi {
@SwiftFunction('echoNonNull(classList:)')
List<AllNullableTypes> echoNonNullClassList(List<AllNullableTypes> classList);

/// Returns the passed list, to test serialization and deserialization.
@ObjCSelector('echoNonNullBoolListList:')
@SwiftFunction('echoNonNull(boolListList:)')
List<List<bool>> echoNonNullBoolListList(List<List<bool>> list);

/// Returns the passed map, to test serialization and deserialization.
@ObjCSelector('echoMap:')
@SwiftFunction('echo(_:)')
Expand Down
Loading
Loading