Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
84c6bc8
Avoid intermediate values when serializing proto3 JSON
osa1 Jun 2, 2022
662efa9
Fix formatting
osa1 Jun 2, 2022
81ddd0c
Fix reserved names test
osa1 Jun 2, 2022
3594c5d
Avoid intermediate JSON objects when merging proto3 JSON messages
osa1 Jun 2, 2022
a64b2f3
Update reserved names
osa1 Jun 3, 2022
a549b57
Update benchmarks to use new functions
osa1 Jun 7, 2022
b8ef37d
Implement well-known type conversions
osa1 Jun 7, 2022
fa4d2ea
Generalize JSON generator and parsers to generate and parse both stri…
osa1 Jun 7, 2022
f155f11
Move writer and reader code to separate files
osa1 Jun 7, 2022
24bffd5
Remove accidentally committed file
osa1 Jun 7, 2022
7c92089
Refactoring
osa1 Jun 7, 2022
6fa98c8
Stubs for well-known types
osa1 Jun 7, 2022
38079b0
Refactor function types, fix a jsontool use error
osa1 Jun 7, 2022
a737185
Rename jsonWriter -> jsonSink
osa1 Jun 7, 2022
9b62b5d
Start updating well-known types
osa1 Jun 8, 2022
d44a919
Update rest of the well-known type ser/deser code
osa1 Jun 8, 2022
78d495e
Update plugin for new BuilderInfo arg names
osa1 Jun 8, 2022
c8895c7
Update reserved names
osa1 Jun 8, 2022
e6bee93
Merge remote-tracking branch 'origin/master' into jsontool
osa1 Jun 8, 2022
add7cce
Fix control flow in a few places
osa1 Jun 8, 2022
513d01e
Fix a few bugs
osa1 Jun 9, 2022
951a2c9
Fix a bug
osa1 Jun 9, 2022
00003ce
Update two tests
osa1 Jun 9, 2022
48804f1
Fix a few tests
osa1 Jun 9, 2022
a5a3f98
More bugs
osa1 Jun 9, 2022
dd8cb85
Fix int bounds checks
osa1 Jun 9, 2022
4d43c95
A few fixups
osa1 Jun 9, 2022
6fe0731
Fix bugs, update tests
osa1 Jun 9, 2022
333f16c
Bump jsontool
osa1 Jun 9, 2022
a890105
Remove unused def
osa1 Jun 9, 2022
eaa0d35
Fix error messages
osa1 Jun 9, 2022
14bffa2
Disable a test for now
osa1 Jun 9, 2022
0e28eb3
Fix TODOs
osa1 Jun 9, 2022
02a9960
Handle unexpected input in parseFailure matcher
osa1 Jun 9, 2022
7899d1f
Renaming, docs
osa1 Jun 9, 2022
ee3cf27
More docs
osa1 Jun 9, 2022
617c30c
Bump jsontool
osa1 Jun 16, 2022
712cae7
Merge remote-tracking branch 'origin/master' into jsontool
osa1 Jun 17, 2022
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
14 changes: 7 additions & 7 deletions protobuf/benchmarks/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/// both on the VM and when compiled to JavaScript.
library common;

import 'dart:convert' show jsonDecode, jsonEncode;
import 'dart:convert' show jsonEncode;
import 'dart:typed_data';

import 'package:benchmark_harness/benchmark_harness.dart';
Expand Down Expand Up @@ -101,35 +101,35 @@ class Factories {
fromBuffer: (List<int> binary) => p2.GoogleMessage1.fromBuffer(binary),
fromJson: (String json) => p2.GoogleMessage1.fromJson(json),
fromProto3JsonString: (String json) =>
p2.GoogleMessage1.create()..mergeFromProto3Json(jsonDecode(json)),
p2.GoogleMessage1.create()..mergeFromProto3JsonString(json),
fromProto3JsonObject: (Object json) =>
p2.GoogleMessage1.create()..mergeFromProto3Json(json)),
'benchmarks.proto3.GoogleMessage1': Factories._(
fromBuffer: (List<int> binary) => p3.GoogleMessage1.fromBuffer(binary),
fromJson: (String json) => p3.GoogleMessage1.fromJson(json),
fromProto3JsonString: (String json) =>
p3.GoogleMessage1.create()..mergeFromProto3Json(jsonDecode(json)),
p3.GoogleMessage1.create()..mergeFromProto3JsonString(json),
fromProto3JsonObject: (Object json) =>
p3.GoogleMessage1.create()..mergeFromProto3Json(json)),
'benchmarks.proto2.GoogleMessage2': Factories._(
fromBuffer: (List<int> binary) => GoogleMessage2.fromBuffer(binary),
fromJson: (String json) => GoogleMessage2.fromJson(json),
fromProto3JsonString: (String json) =>
GoogleMessage2.create()..mergeFromProto3Json(jsonDecode(json)),
GoogleMessage2.create()..mergeFromProto3JsonString(json),
fromProto3JsonObject: (Object json) =>
GoogleMessage2.create()..mergeFromProto3Json(json)),
'benchmarks.google_message3.GoogleMessage3': Factories._(
fromBuffer: (List<int> binary) => GoogleMessage3.fromBuffer(binary),
fromJson: (String json) => GoogleMessage3.fromJson(json),
fromProto3JsonString: (String json) =>
GoogleMessage3.create()..mergeFromProto3Json(jsonDecode(json)),
GoogleMessage3.create()..mergeFromProto3JsonString(json),
fromProto3JsonObject: (Object json) =>
GoogleMessage3.create()..mergeFromProto3Json(json)),
'benchmarks.google_message4.GoogleMessage4': Factories._(
fromBuffer: (List<int> binary) => GoogleMessage4.fromBuffer(binary),
fromJson: (String json) => GoogleMessage4.fromJson(json),
fromProto3JsonString: (String json) =>
GoogleMessage4.create()..mergeFromProto3Json(jsonDecode(json)),
GoogleMessage4.create()..mergeFromProto3JsonString(json),
fromProto3JsonObject: (Object json) =>
GoogleMessage4.create()..mergeFromProto3Json(json)),
};
Expand Down Expand Up @@ -231,7 +231,7 @@ class ToProto3JsonStringBenchmark extends _ProtobufBenchmark {
void run() {
for (final ds in datasets) {
for (final unpacked in ds.unpacked) {
jsonEncode(unpacked.toProto3Json());
unpacked.toProto3JsonString();
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions protobuf/lib/meta.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const GeneratedMessage_reservedNames = <String>[
'mergeFromJsonMap',
'mergeFromMessage',
'mergeFromProto3Json',
'mergeFromProto3JsonReader',
'mergeFromProto3JsonString',
'mergeUnknownFields',
'noSuchMethod',
'runtimeType',
Expand All @@ -52,6 +54,8 @@ const GeneratedMessage_reservedNames = <String>[
'toBuilder',
'toDebugString',
'toProto3Json',
'toProto3JsonSink',
'toProto3JsonString',
'toString',
'unknownFields',
'writeToBuffer',
Expand Down
25 changes: 22 additions & 3 deletions protobuf/lib/protobuf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,19 @@ import 'dart:math' as math;
import 'dart:typed_data' show TypedData, Uint8List, ByteData, Endian;

import 'package:fixnum/fixnum.dart' show Int64;
import 'package:jsontool/jsontool.dart';
import 'package:meta/meta.dart' show UseResult;

import 'src/protobuf/json_parsing_context.dart';
import 'src/protobuf/permissive_compare.dart';
import 'src/protobuf/type_registry.dart';
export 'src/protobuf/type_registry.dart' show TypeRegistry;

part 'src/protobuf/builder_info.dart';
part 'src/protobuf/coded_buffer.dart';
part 'src/protobuf/coded_buffer_reader.dart';
part 'src/protobuf/coded_buffer_writer.dart';
part 'src/protobuf/consts.dart';
part 'src/protobuf/builder_info.dart';
part 'src/protobuf/event_plugin.dart';
part 'src/protobuf/exceptions.dart';
part 'src/protobuf/extension.dart';
Expand All @@ -37,14 +38,16 @@ part 'src/protobuf/generated_service.dart';
part 'src/protobuf/json.dart';
part 'src/protobuf/pb_list.dart';
part 'src/protobuf/pb_map.dart';
part 'src/protobuf/proto3_json_reader.dart';
part 'src/protobuf/proto3_json_writer.dart';
part 'src/protobuf/protobuf_enum.dart';
part 'src/protobuf/proto3_json.dart';
part 'src/protobuf/rpc_client.dart';
part 'src/protobuf/unknown_field_set.dart';
part 'src/protobuf/utils.dart';
part 'src/protobuf/unpack.dart';
part 'src/protobuf/utils.dart';
part 'src/protobuf/wire_format.dart';

/// Used by generated code, do not use.
// TODO(sra): Use Int64.parse() when available - see http://dartbug.com/21915.
Int64 parseLongInt(String text) {
if (text.startsWith('0x')) return Int64.parseHex(text.substring(2));
Expand All @@ -54,3 +57,19 @@ Int64 parseLongInt(String text) {
}

const _utf8 = Utf8Codec(allowMalformed: true);

/// Used by generated code, do not use.
///
/// Reads the next JSON object from the given [JsonReader].
Object? nextJsonObject(JsonReader jsonReader) {
Object? json;
try {
final sink = jsonObjectWriter((result) {
json = result;
});
jsonReader.expectAnyValue(sink);
} on FormatException {
json = '<invalid Dart JSON object>';
}
return json;
}
21 changes: 14 additions & 7 deletions protobuf/lib/src/protobuf/builder_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,25 @@ class BuilderInfo {

List<FieldInfo>? _sortedByTag;

// For well-known types.
final Object? Function(GeneratedMessage message, TypeRegistry typeRegistry)?
toProto3Json;
final Function(GeneratedMessage targetMessage, Object json,
TypeRegistry typeRegistry, JsonParsingContext context)? fromProto3Json;
/// JSON generator for well-known types.
final void Function(
GeneratedMessage msg, TypeRegistry typeRegistry, JsonSink jsonSink)?
writeToProto3JsonSink;

/// JSON parser for well-known types.
final Function(
GeneratedMessage msg,
JsonReader jsonReader,
TypeRegistry typeRegistry,
JsonParsingContext context)? mergeFromProto3JsonReader;

final CreateBuilderFunc? createEmptyInstance;

BuilderInfo(String? messageName,
{PackageName package = const PackageName(''),
this.createEmptyInstance,
this.toProto3Json,
this.fromProto3Json})
this.writeToProto3JsonSink,
this.mergeFromProto3JsonReader})
: qualifiedMessageName = '${package.prefix}$messageName';

void add<T>(
Expand Down
94 changes: 81 additions & 13 deletions protobuf/lib/src/protobuf/generated_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,24 +208,92 @@ abstract class GeneratedMessage {
/// For the proto3 JSON format use: [toProto3Json].
String writeToJson() => jsonEncode(writeToJsonMap());

/// Returns an Object representing Proto3 JSON serialization of `this`.
/// Returns Dart JSON object encoding this message following proto3 JSON
/// format.
///
/// The returned object will have the same format as objects returned by
/// [jsonEncode].
///
/// See [toProto3JsonSink] for details.
Object? toProto3Json(
{TypeRegistry typeRegistry = const TypeRegistry.empty()}) {
Object? object;
final objectSink = jsonObjectWriter((newObject) {
object = newObject;
});
_writeToProto3JsonSink(_fieldSet, typeRegistry, objectSink);
return object;
}

/// Returns a proto3 JSON string encoding this message.
///
/// See [toProto3JsonSink] for details.
String toProto3JsonString(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider passing an "indent" value

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this really be called writeToProto3Json to maintain consistency?

Copy link
Member Author

Choose a reason for hiding this comment

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

Additions in this PR will be inconsistent unless we want to do breaking changes.

Currently in master branch (and also in the latest release) we have these generators:

  • writeToBuffer
  • writeToCodedBufferWriter
  • writeToJson
  • writeToJsonMap
  • toProto3Json

So the proto3 method is already inconsistent.

Without changing it, we can name the new methods toProto3..., or writeToProto3.... Either way it will be inconsistent.

Should we rename toProto3Json? @sigurdm

Copy link
Collaborator

Choose a reason for hiding this comment

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

Argh - I see - I introduced that inconsistency - not sure what is the better path forwards.
I guess if we want to be consistent we do:

@Deprecated('Use `writeToProto3JsonMap`');
toProto3Json() => writeToProto3JsonMap();

writeToProto3JsonMap();
writeToProto3Json();

If we prefer to avoid churn over consistency we keep it as toProto3JsonString()....

I guess it's up to your judgement.

{TypeRegistry typeRegistry = const TypeRegistry.empty()}) {
final buf = StringBuffer();
final stringSink = jsonStringWriter(buf);
toProto3JsonSink(typeRegistry, stringSink);
return buf.toString();
}

/// Writes proto3 JSON serialization of this message to the given [JsonSink].
///
/// The key for each field is be the camel-cased name of the field.
///
/// Well-known types and their special JSON encoding are supported.
/// If a well-known type cannot be encoded (eg. a `google.protobuf.Timestamp`
/// with negative `nanoseconds`) an error is thrown.
/// Well-known types and their special JSON encoding are supported. If a
/// well-known type cannot be encoded (eg. a `google.protobuf.Timestamp` with
/// negative `nanoseconds`) an error is thrown.
///
/// Extensions and unknown fields are not encoded.
///
/// The [typeRegistry] is be used for encoding `Any` messages. If an `Any`
/// message encoding a type not in [typeRegistry] is encountered, an
/// error is thrown.
Object? toProto3Json(
{TypeRegistry typeRegistry = const TypeRegistry.empty()}) =>
_writeToProto3Json(_fieldSet, typeRegistry);
/// message encoding a type not in [typeRegistry] is encountered, an error is
/// thrown.
///
/// The [newMessage] argument is for use in generated code, do not use.
void toProto3JsonSink(TypeRegistry typeRegistry, JsonSink jsonSink,
{bool newMessage = true}) {
_writeToProto3JsonSink(_fieldSet, typeRegistry, jsonSink,
newMessage: newMessage);
}

/// Merges field values from Dart representation of JSON following proto3
/// JSON format.
///
/// [jsonObject] should be generated by [jsonDecode] or have the same format.
///
/// See [mergeFromProto3JsonReader] documentation for details.
void mergeFromProto3Json(Object? jsonObject,
{TypeRegistry typeRegistry = const TypeRegistry.empty(),
bool ignoreUnknownFields = false,
bool supportNamesWithUnderscores = true,
bool permissiveEnums = false}) =>
mergeFromProto3JsonReader(
JsonReader.fromObject(jsonObject),
typeRegistry: typeRegistry,
ignoreUnknownFields: ignoreUnknownFields,
supportNamesWithUnderscores: supportNamesWithUnderscores,
permissiveEnums: permissiveEnums,
);

/// Marges field values from a [String] following proto3 JSON format.
///
/// See [mergeFromProto3JsonReader] documentation for details.
void mergeFromProto3JsonString(String jsonString,
{TypeRegistry typeRegistry = const TypeRegistry.empty(),
bool ignoreUnknownFields = false,
bool supportNamesWithUnderscores = true,
bool permissiveEnums = false}) =>
mergeFromProto3JsonReader(
JsonReader.fromString(jsonString),
typeRegistry: typeRegistry,
ignoreUnknownFields: ignoreUnknownFields,
supportNamesWithUnderscores: supportNamesWithUnderscores,
permissiveEnums: permissiveEnums,
);

/// Merges field values from [json], a JSON object using proto3 encoding.
/// Merges field values from a [JsonReader] that reads from a JSON in proto3
/// JSON format.
///
/// Well-known types and their special JSON encodings are supported.
///
Expand All @@ -252,13 +320,13 @@ abstract class GeneratedMessage {
///
/// Throws [FormatException] if the JSON not formatted correctly (a String
/// where a number was expected etc.).
void mergeFromProto3Json(Object? json,
void mergeFromProto3JsonReader(JsonReader jsonReader,
{TypeRegistry typeRegistry = const TypeRegistry.empty(),
bool ignoreUnknownFields = false,
bool supportNamesWithUnderscores = true,
bool permissiveEnums = false}) =>
_mergeFromProto3Json(json, _fieldSet, typeRegistry, ignoreUnknownFields,
supportNamesWithUnderscores, permissiveEnums);
_mergeFromProto3JsonReader(jsonReader, _fieldSet, typeRegistry,
ignoreUnknownFields, supportNamesWithUnderscores, permissiveEnums);

/// Merges field values from [data], a JSON object, encoded as described by
/// [GeneratedMessage.writeToJson].
Expand Down
Loading