Skip to content

[pigeon] Fixed decoding code for null-safe classes. #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 22, 2021
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
5 changes: 5 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.1.21

* Fixed decode method on generated Flutter classes that use null-safety and have
null values.

## 0.1.20

* Implemented `@async` HostApi's for iOS.
Expand Down
9 changes: 4 additions & 5 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ void _writeFlutterApi(
String _addGenericTypes(String dataType, String nullTag) {
switch (dataType) {
case 'List':
return 'List<Object$nullTag>';
return 'List<Object$nullTag>$nullTag';
case 'Map':
return 'Map<Object$nullTag, Object$nullTag>';
return 'Map<Object$nullTag, Object$nullTag>$nullTag';
default:
return dataType;
return '$dataType$nullTag';
Comment on lines +181 to +185
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix, other stuff is just testing infrastructure / tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

}
}

Expand Down Expand Up @@ -211,8 +211,7 @@ void generateDart(DartOptions opt, Root root, StringSink sink) {
sink.write('class ${klass.name} ');
indent.scoped('{', '}', () {
for (Field field in klass.fields) {
final String datatype =
'${_addGenericTypes(field.dataType, nullTag)}$nullTag';
final String datatype = '${_addGenericTypes(field.dataType, nullTag)}';
indent.writeln('$datatype ${field.name};');
}
if (klass.fields.isNotEmpty) {
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 @@ -8,7 +8,7 @@ import 'dart:mirrors';
import 'ast.dart';

/// The current version of pigeon. This must match the version in pubspec.yaml.
const String pigeonVersion = '0.1.20';
const String pigeonVersion = '0.1.21';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/

# Web related
lib/generated_plugin_registrant.dart

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: 6db3d61ed4a58ba89140d7fe1fd294b598cc29c5
channel: master

project_type: app
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# flutter_unit_tests

Unit test scaffold for null safe Flutter projects.
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Autogenerated from Pigeon (v0.1.21), 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
// @dart = 2.12
Comment on lines +1 to +4
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to add this file otherwise CI analysis steps fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, we disable the analysis for flutter_null_safe_unit_tests? https://dart.dev/guides/language/analysis-options#the-analysis-options-file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pointer. I'm on the fence as to whether it is a good idea or not. I think I'll keep it this way for now.

import 'dart:async';
import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;

import 'package:flutter/services.dart';

class SearchReply {
String? result;
String? error;

Object encode() {
final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
pigeonMap['result'] = result;
pigeonMap['error'] = error;
return pigeonMap;
}

static SearchReply decode(Object message) {
final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
return SearchReply()
..result = pigeonMap['result'] as String?
..error = pigeonMap['error'] as String?;
}
}

class SearchRequest {
String? query;
int? anInt;
bool? aBool;

Object encode() {
final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
pigeonMap['query'] = query;
pigeonMap['anInt'] = anInt;
pigeonMap['aBool'] = aBool;
return pigeonMap;
}

static SearchRequest decode(Object message) {
final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
return SearchRequest()
..query = pigeonMap['query'] as String?
..anInt = pigeonMap['anInt'] as int?
..aBool = pigeonMap['aBool'] as bool?;
}
}

class Nested {
SearchRequest? request;

Object encode() {
final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
pigeonMap['request'] = request == null ? null : request!.encode();
return pigeonMap;
}

static Nested decode(Object message) {
final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
return Nested()
..request = pigeonMap['request'] != null
? SearchRequest.decode(pigeonMap['request']!)
: null;
}
}

abstract class FlutterSearchApi {
SearchReply search(SearchRequest arg);
static void setup(FlutterSearchApi? api) {
{
const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.FlutterSearchApi.search', StandardMessageCodec());
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null. Expected SearchRequest.');
final SearchRequest input = SearchRequest.decode(message!);
final SearchReply output = api.search(input);
return output.encode();
});
}
}
}
}

class NestedApi {
Future<SearchReply> search(Nested arg) async {
final Object encoded = arg.encode();
const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.NestedApi.search', StandardMessageCodec());
final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
replyMap['error'] as Map<Object?, Object?>;
throw PlatformException(
code: error['code'] as String,
message: error['message'] as String?,
details: error['details'],
);
} else {
return SearchReply.decode(replyMap['result']!);
}
}
}

class Api {
Future<void> initialize() async {
const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.initialize', StandardMessageCodec());
final Map<Object?, Object?>? replyMap =
await channel.send(null) as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
replyMap['error'] as Map<Object?, Object?>;
throw PlatformException(
code: error['code'] as String,
message: error['message'] as String?,
details: error['details'],
);
} else {
// noop
}
}

Future<SearchReply> search(SearchRequest arg) async {
final Object encoded = arg.encode();
const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.search', StandardMessageCodec());
final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
replyMap['error'] as Map<Object?, Object?>;
throw PlatformException(
code: error['code'] as String,
message: error['message'] as String?,
details: error['details'],
);
} else {
return SearchReply.decode(replyMap['result']!);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: flutter_unit_tests
description: Unit test scaffold for null safe Flutter projects.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"

dependencies:
flutter:
sdk: flutter

dev_dependencies:
flutter_test:
sdk: flutter

flutter:
uses-material-design: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_unit_tests/null_safe_pigeon.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('with values filled', () {
final SearchReply reply = SearchReply()
..result = 'foo'
..error = 'bar';
final Object encoded = reply.encode();
final SearchReply decoded = SearchReply.decode(encoded);
expect(reply.result, decoded.result);
expect(reply.error, decoded.error);
});

test('with null value', () {
final SearchReply reply = SearchReply()
..result = 'foo'
..error = null;
final Object encoded = reply.encode();
final SearchReply decoded = SearchReply.decode(encoded);
expect(reply.result, decoded.result);
expect(reply.error, decoded.error);
});
}
2 changes: 1 addition & 1 deletion packages/pigeon/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: pigeon
version: 0.1.20 # This must match the version in lib/generator_tools.dart
version: 0.1.21 # This must match the version in lib/generator_tools.dart
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
homepage: https://github.com/flutter/packages/tree/master/packages/pigeon
dependencies:
Expand Down
13 changes: 13 additions & 0 deletions packages/pigeon/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ pub run test test/
###############################################################################
pub run pigeon 1> /dev/null

###############################################################################
# Run unit tests on generated Dart code.
###############################################################################
pushd $PWD
pub run pigeon \
--input pigeons/message.dart \
--dart_null_safety \
--dart_out platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
cd platform_tests/flutter_null_safe_unit_tests
flutter pub get
flutter test test/null_safe_test.dart
popd

###############################################################################
# Mock handler flutter tests.
###############################################################################
Expand Down