From 1f9c3c3810408dbdfa1188ce60667107bbba5386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Wed, 6 Aug 2025 11:17:28 +0100 Subject: [PATCH 1/5] Make proto3_json.dart aware of well known types This syncs cl/629836458. From the CL: This removes the requirement for proto3_json.dart to invoke outside code (either via BuilderInfo or inheritance). This also improves tree shaking of the proto3json helpers, since the proto3json callbacks attached to BuilderInfo no longer retain those helpers. --- protobuf/lib/protobuf.dart | 4 +- protobuf/lib/src/protobuf/builder_info.dart | 17 +- protobuf/lib/src/protobuf/internal.dart | 1 + .../lib/src/protobuf/mixins/well_known.dart | 54 ++- protobuf/lib/src/protobuf/proto3_json.dart | 437 ++++++++++++++---- protoc_plugin/lib/mixins.dart | 10 +- .../src/gen/google/protobuf/duration.pb.dart | 3 +- protoc_plugin/lib/src/message_generator.dart | 5 +- protoc_plugin/lib/src/well_known_types.dart | 32 +- 9 files changed, 413 insertions(+), 150 deletions(-) diff --git a/protobuf/lib/protobuf.dart b/protobuf/lib/protobuf.dart index d1e569bd..67bfad99 100644 --- a/protobuf/lib/protobuf.dart +++ b/protobuf/lib/protobuf.dart @@ -18,4 +18,6 @@ export 'src/protobuf/internal.dart' GeneratedMessageInternalExtension, MapFieldInfoInternalExtension, mapKeyFieldNumber, - mapValueFieldNumber; + mapValueFieldNumber, + mergeFromProto3JsonAny, + writeToProto3JsonAny; diff --git a/protobuf/lib/src/protobuf/builder_info.dart b/protobuf/lib/src/protobuf/builder_info.dart index d21b3dfa..d98858f3 100644 --- a/protobuf/lib/src/protobuf/builder_info.dart +++ b/protobuf/lib/src/protobuf/builder_info.dart @@ -42,24 +42,17 @@ class BuilderInfo { List? _sortedByTag; // For well-known types. - final Object? Function(GeneratedMessage message, TypeRegistry typeRegistry)? - toProto3Json; - final Function( - GeneratedMessage targetMessage, - Object json, - TypeRegistry typeRegistry, - JsonParsingContext context, - )? - fromProto3Json; + final WellKnownType? _wellKnownType; + final CreateBuilderFunc? createEmptyInstance; BuilderInfo( String? messageName, { PackageName package = const PackageName(''), this.createEmptyInstance, - this.toProto3Json, - this.fromProto3Json, - }) : qualifiedMessageName = '${package.prefix}$messageName'; + WellKnownType? wellKnownType, + }) : qualifiedMessageName = '${package.prefix}$messageName', + _wellKnownType = wellKnownType; void add( int tagNumber, diff --git a/protobuf/lib/src/protobuf/internal.dart b/protobuf/lib/src/protobuf/internal.dart index e3a29d07..95d491ef 100644 --- a/protobuf/lib/src/protobuf/internal.dart +++ b/protobuf/lib/src/protobuf/internal.dart @@ -18,6 +18,7 @@ import 'package:meta/meta.dart' show UseResult; import 'consts.dart'; import 'json/json.dart' as json_lib; import 'json_parsing_context.dart'; +import 'mixins/well_known.dart'; import 'permissive_compare.dart'; import 'type_registry.dart'; import 'utils.dart'; diff --git a/protobuf/lib/src/protobuf/mixins/well_known.dart b/protobuf/lib/src/protobuf/mixins/well_known.dart index 644d92a0..bf63a434 100644 --- a/protobuf/lib/src/protobuf/mixins/well_known.dart +++ b/protobuf/lib/src/protobuf/mixins/well_known.dart @@ -6,9 +6,30 @@ import 'dart:convert'; import 'package:fixnum/fixnum.dart'; -import '../../../protobuf.dart'; +import '../internal.dart'; import '../json_parsing_context.dart'; +/// The set of well known protobuf message types which may have customized +/// serialization logic. +enum WellKnownType { + any, + timestamp, + duration, + struct, + value, + listValue, + fieldMask, + doubleValue, + floatValue, + int64Value, + uint64Value, + int32Value, + uint32Value, + boolValue, + stringValue, + bytesValue, +} + mixin AnyMixin implements GeneratedMessage { String get typeUrl; set typeUrl(String value); @@ -98,14 +119,7 @@ mixin AnyMixin implements GeneratedMessage { ); } final unpacked = info.createEmptyInstance!()..mergeFromBuffer(any.value); - final proto3Json = unpacked.toProto3Json(typeRegistry: typeRegistry); - if (info.toProto3Json == null) { - final map = proto3Json as Map; - map['@type'] = any.typeUrl; - return map; - } else { - return {'@type': any.typeUrl, 'value': proto3Json}; - } + return writeToProto3JsonAny(unpacked.fieldSet, any.typeUrl, typeRegistry); } static void fromProto3JsonHelper( @@ -133,21 +147,13 @@ mixin AnyMixin implements GeneratedMessage { ); } - final Object? subJson = - info.fromProto3Json == null - // TODO(sigurdm): avoid cloning [object] here. - ? (Map.from(object)..remove('@type')) - : object['value']; - // TODO(sigurdm): We lose [context.path]. - final packedMessage = - info.createEmptyInstance!()..mergeFromProto3Json( - subJson, - typeRegistry: typeRegistry, - supportNamesWithUnderscores: context.supportNamesWithUnderscores, - ignoreUnknownFields: context.ignoreUnknownFields, - permissiveEnums: context.permissiveEnums, - ); - + final packedMessage = info.createEmptyInstance!(); + mergeFromProto3JsonAny( + json, + packedMessage.fieldSet, + typeRegistry, + context, + ); any.value = packedMessage.writeToBuffer(); any.typeUrl = typeUrl; } else { diff --git a/protobuf/lib/src/protobuf/proto3_json.dart b/protobuf/lib/src/protobuf/proto3_json.dart index 0fe4849c..de0c2428 100644 --- a/protobuf/lib/src/protobuf/proto3_json.dart +++ b/protobuf/lib/src/protobuf/proto3_json.dart @@ -4,6 +4,40 @@ part of 'internal.dart'; +// Public because this is called from the mixins library. +Object writeToProto3JsonAny( + FieldSet fs, + String typeUrl, + TypeRegistry typeRegistry, +) { + final result = _writeToProto3Json(fs, typeRegistry); + final wellKnownType = fs._meta._wellKnownType; + if (wellKnownType != null) { + switch (wellKnownType) { + case WellKnownType.any: + case WellKnownType.timestamp: + case WellKnownType.duration: + case WellKnownType.struct: + case WellKnownType.value: + case WellKnownType.listValue: + case WellKnownType.fieldMask: + case WellKnownType.doubleValue: + case WellKnownType.floatValue: + case WellKnownType.int64Value: + case WellKnownType.uint64Value: + case WellKnownType.int32Value: + case WellKnownType.uint32Value: + case WellKnownType.boolValue: + case WellKnownType.stringValue: + case WellKnownType.bytesValue: + return {'@type': typeUrl, 'value': result}; + } + } + + (result as Map)['@type'] = typeUrl; + return result; +} + Object? _writeToProto3Json(FieldSet fs, TypeRegistry typeRegistry) { String? convertToMapKey(dynamic key, int keyType) { final baseType = PbFieldType.baseType(keyType); @@ -86,8 +120,46 @@ Object? _writeToProto3Json(FieldSet fs, TypeRegistry typeRegistry) { } final meta = fs._meta; - if (meta.toProto3Json != null) { - return meta.toProto3Json!(fs._message!, typeRegistry); + final wellKnownType = meta._wellKnownType; + if (wellKnownType != null) { + switch (wellKnownType) { + case WellKnownType.any: + return AnyMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.timestamp: + return TimestampMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.duration: + return DurationMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.struct: + return StructMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.value: + return ValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.listValue: + return ListValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.fieldMask: + return FieldMaskMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.doubleValue: + return DoubleValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.floatValue: + return FloatValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.int64Value: + return Int64ValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.uint64Value: + return UInt64ValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.int32Value: + return Int32ValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.uint32Value: + return UInt32ValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.boolValue: + return BoolValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.stringValue: + return StringValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + case WellKnownType.bytesValue: + return BytesValueMixin.toProto3JsonHelper(fs._message!, typeRegistry); + } + // [WellKnownType] could be used to for messages which have special + // encodings in other codecs. The set of messages which special encodings in + // proto3json is handled here, so we intentionally fall through to the + // default message handling rather than throwing. } final result = {}; @@ -156,6 +228,56 @@ extension _FindFirst on Iterable { } } +// Public because this is called from the mixins library. +void mergeFromProto3JsonAny( + Object? json, + FieldSet fieldSet, + TypeRegistry typeRegistry, + JsonParsingContext context, +) { + if (json is! Map) { + throw context.parseException('Expected JSON object', json); + } + + final wellKnownType = fieldSet._meta._wellKnownType; + if (wellKnownType != null) { + switch (wellKnownType) { + case WellKnownType.any: + case WellKnownType.timestamp: + case WellKnownType.duration: + case WellKnownType.struct: + case WellKnownType.value: + case WellKnownType.listValue: + case WellKnownType.fieldMask: + case WellKnownType.doubleValue: + case WellKnownType.floatValue: + case WellKnownType.int64Value: + case WellKnownType.uint64Value: + case WellKnownType.int32Value: + case WellKnownType.uint32Value: + case WellKnownType.boolValue: + case WellKnownType.stringValue: + case WellKnownType.bytesValue: + final value = json['value']; + return _mergeFromProto3JsonWithContext( + value, + fieldSet, + typeRegistry, + context, + ); + } + } + + // TODO(sigurdm): avoid cloning [object] here. + final withoutType = Map.from(json)..remove('@type'); + return _mergeFromProto3JsonWithContext( + withoutType, + fieldSet, + typeRegistry, + context, + ); +} + /// Merge a JSON object representing a message in proto3 JSON format ([json]) /// to [fieldSet]. void _mergeFromProto3Json( @@ -166,12 +288,23 @@ void _mergeFromProto3Json( bool supportNamesWithUnderscores, bool permissiveEnums, ) { - fieldSet._ensureWritable(); final context = JsonParsingContext( ignoreUnknownFields, supportNamesWithUnderscores, permissiveEnums, ); + return _mergeFromProto3JsonWithContext(json, fieldSet, typeRegistry, context); +} + +/// Merge a JSON object representing a message in proto3 JSON format ([json]) +/// to [fieldSet]. +void _mergeFromProto3JsonWithContext( + Object? json, + FieldSet fieldSet, + TypeRegistry typeRegistry, + JsonParsingContext context, +) { + fieldSet._ensureWritable(); void recursionHelper(Object? json, FieldSet fieldSet) { Object? convertProto3JsonValue(Object value, FieldInfo fieldInfo) { @@ -225,16 +358,16 @@ void _mergeFromProto3Json( if (value is String) { // TODO(sigurdm): Do we want to avoid linear search here? Measure... final result = - permissiveEnums + context.permissiveEnums ? fieldInfo.enumValues!.findFirst( (e) => permissiveCompare(e.name, value), ) : fieldInfo.enumValues!.findFirst((e) => e.name == value); - if ((result != null) || ignoreUnknownFields) return result; + if ((result != null) || context.ignoreUnknownFields) return result; throw context.parseException('Unknown enum value', value); } else if (value is int) { return fieldInfo.valueOf!(value) ?? - (ignoreUnknownFields + (context.ignoreUnknownFields ? null : (throw context.parseException( 'Unknown enum value', @@ -368,99 +501,227 @@ void _mergeFromProto3Json( } final meta = fieldSet._meta; - final wellKnownConverter = meta.fromProto3Json; - if (wellKnownConverter != null) { - wellKnownConverter(fieldSet._message!, json, typeRegistry, context); - } else { - if (json is Map) { - final byName = meta.byName; + final wellKnownType = meta._wellKnownType; + if (wellKnownType != null) { + switch (wellKnownType) { + case WellKnownType.any: + AnyMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.timestamp: + TimestampMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.duration: + DurationMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.struct: + StructMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.value: + ValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.listValue: + ListValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.fieldMask: + FieldMaskMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.doubleValue: + DoubleValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.floatValue: + FloatValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.int64Value: + Int64ValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.uint64Value: + UInt64ValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.int32Value: + Int32ValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.uint32Value: + UInt32ValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.boolValue: + BoolValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.stringValue: + StringValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + case WellKnownType.bytesValue: + BytesValueMixin.fromProto3JsonHelper( + fieldSet._message!, + json, + typeRegistry, + context, + ); + return; + } - json.forEach((key, Object? value) { - if (value == null) { - return; - } - if (key is! String) { - throw context.parseException('Key was not a String', key); - } - context.addMapIndex(key); + // [WellKnownType] could be used to for messages which have special + // encodings in other codecs. The set of messages which special encodings + // in proto3json is handled here, so we intentionally fall through to the + // default message handling rather than throwing. + } - var fieldInfo = byName[key]; - if (fieldInfo == null && supportNamesWithUnderscores) { - // We don't optimize for field names with underscores, instead do a - // linear search for the index. - fieldInfo = byName.values.findFirst( - (FieldInfo info) => info.protoName == key, - ); - } - if (fieldInfo == null) { - if (ignoreUnknownFields) { - return; - } else { - throw context.parseException('Unknown field name \'$key\'', key); - } + if (json is Map) { + final byName = meta.byName; + + json.forEach((key, Object? value) { + if (value == null) { + return; + } + if (key is! String) { + throw context.parseException('Key was not a String', key); + } + context.addMapIndex(key); + + var fieldInfo = byName[key]; + if (fieldInfo == null && context.supportNamesWithUnderscores) { + // We don't optimize for field names with underscores, instead do a + // linear search for the index. + fieldInfo = byName.values.findFirst( + (FieldInfo info) => info.protoName == key, + ); + } + if (fieldInfo == null) { + if (context.ignoreUnknownFields) { + return; + } else { + throw context.parseException('Unknown field name \'$key\'', key); } + } - if (PbFieldType.isMapField(fieldInfo.type)) { - if (value is Map) { - final mapFieldInfo = fieldInfo as MapFieldInfo; - final Map fieldValues = fieldSet._ensureMapField(meta, fieldInfo); - value.forEach((subKey, subValue) { - if (subKey is! String) { - throw context.parseException('Expected a String key', subKey); - } - context.addMapIndex(subKey); - fieldValues[decodeMapKey( - subKey, - mapFieldInfo.keyFieldType, - )] = convertProto3JsonValue( - subValue, - mapFieldInfo.valueFieldInfo, - ); - context.popIndex(); - }); - } else { - throw context.parseException('Expected a map', value); - } - } else if (PbFieldType.isRepeated(fieldInfo.type)) { - if (value is List) { - final values = fieldSet._ensureRepeatedField(meta, fieldInfo); - for (var i = 0; i < value.length; i++) { - final entry = value[i]; - context.addListIndex(i); - values.add(convertProto3JsonValue(entry, fieldInfo)); - context.popIndex(); + if (PbFieldType.isMapField(fieldInfo.type)) { + if (value is Map) { + final mapFieldInfo = fieldInfo as MapFieldInfo; + final Map fieldValues = fieldSet._ensureMapField(meta, fieldInfo); + value.forEach((subKey, subValue) { + if (subKey is! String) { + throw context.parseException('Expected a String key', subKey); } - } else { - throw context.parseException('Expected a list', value); - } - } else if (PbFieldType.isGroupOrMessage(fieldInfo.type)) { - // TODO(sigurdm) consider a cleaner separation between parsing and - // merging. - final parsedSubMessage = - convertProto3JsonValue(value, fieldInfo) as GeneratedMessage; - final GeneratedMessage? original = - fieldSet._values[fieldInfo.index!]; - if (original == null) { - fieldSet._setNonExtensionFieldUnchecked( - meta, - fieldInfo, - parsedSubMessage, - ); - } else { - original.mergeFromMessage(parsedSubMessage); + context.addMapIndex(subKey); + fieldValues[decodeMapKey(subKey, mapFieldInfo.keyFieldType)] = + convertProto3JsonValue(subValue, mapFieldInfo.valueFieldInfo); + context.popIndex(); + }); + } else { + throw context.parseException('Expected a map', value); + } + } else if (PbFieldType.isRepeated(fieldInfo.type)) { + if (value is List) { + final values = fieldSet._ensureRepeatedField(meta, fieldInfo); + for (var i = 0; i < value.length; i++) { + final entry = value[i]; + context.addListIndex(i); + values.add(convertProto3JsonValue(entry, fieldInfo)); + context.popIndex(); } } else { - fieldSet._setFieldUnchecked( + throw context.parseException('Expected a list', value); + } + } else if (PbFieldType.isGroupOrMessage(fieldInfo.type)) { + // TODO(sigurdm) consider a cleaner separation between parsing and + // merging. + final parsedSubMessage = + convertProto3JsonValue(value, fieldInfo) as GeneratedMessage; + final GeneratedMessage? original = fieldSet._values[fieldInfo.index!]; + if (original == null) { + fieldSet._setNonExtensionFieldUnchecked( meta, fieldInfo, - convertProto3JsonValue(value, fieldInfo), + parsedSubMessage, ); + } else { + original.mergeFromMessage(parsedSubMessage); } - context.popIndex(); - }); - } else { - throw context.parseException('Expected JSON object', json); - } + } else { + fieldSet._setFieldUnchecked( + meta, + fieldInfo, + convertProto3JsonValue(value, fieldInfo), + ); + } + context.popIndex(); + }); + } else { + throw context.parseException('Expected JSON object', json); } } diff --git a/protoc_plugin/lib/mixins.dart b/protoc_plugin/lib/mixins.dart index 6d969158..73eb2ff4 100644 --- a/protoc_plugin/lib/mixins.dart +++ b/protoc_plugin/lib/mixins.dart @@ -38,9 +38,11 @@ class PbMixin { /// Typically used for static helpers since you cannot mix in static members. final List? injectedHelpers; - /// Whether the mixin should have static methods for converting to and from - /// proto3 Json. - final bool hasProto3JsonHelpers; + /// The well known type name. + /// + /// Values should match enumerations in the `WellKnownType` enum from + /// `package:protobuf`. + final String? wellKnownType; const PbMixin( this.name, { @@ -48,7 +50,7 @@ class PbMixin { this.parent, this.reservedNames, this.injectedHelpers, - this.hasProto3JsonHelpers = false, + this.wellKnownType, }); /// Returns the mixin and its ancestors, in the order they should be applied. diff --git a/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart b/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart index 5cba2308..a60ed95b 100644 --- a/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart +++ b/protoc_plugin/lib/src/gen/google/protobuf/duration.pb.dart @@ -102,8 +102,7 @@ class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin { package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), createEmptyInstance: create, - toProto3Json: $mixin.DurationMixin.toProto3JsonHelper, - fromProto3Json: $mixin.DurationMixin.fromProto3JsonHelper) + wellKnownType: $mixin.WellKnownType.duration) ..aInt64(1, _omitFieldNames ? '' : 'seconds') ..a<$core.int>(2, _omitFieldNames ? '' : 'nanos', $pb.PbFieldType.O3) ..hasRequiredFields = false; diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart index f301ea2f..d3fb0ebb 100644 --- a/protoc_plugin/lib/src/message_generator.dart +++ b/protoc_plugin/lib/src/message_generator.dart @@ -356,9 +356,8 @@ class MessageGenerator extends ProtobufContainer { final packageClause = package == '' ? '' : ', package: $conditionalPackageName'; final proto3JsonClause = - (mixin?.hasProto3JsonHelpers ?? false) - ? ', toProto3Json: $mixinImportPrefix.${mixin!.name}.toProto3JsonHelper, ' - 'fromProto3Json: $mixinImportPrefix.${mixin!.name}.fromProto3JsonHelper' + (mixin?.wellKnownType != null) + ? ', wellKnownType: $mixinImportPrefix.WellKnownType.${mixin!.wellKnownType}' : ''; final String extendedClass; diff --git a/protoc_plugin/lib/src/well_known_types.dart b/protoc_plugin/lib/src/well_known_types.dart index 3b322239..3d93d45e 100644 --- a/protoc_plugin/lib/src/well_known_types.dart +++ b/protoc_plugin/lib/src/well_known_types.dart @@ -28,7 +28,7 @@ static Any pack($protobufImportPrefix.GeneratedMessage message, return result; }''', ], - hasProto3JsonHelpers: true, + wellKnownType: 'any', ), 'google.protobuf.Timestamp': PbMixin( 'TimestampMixin', @@ -44,7 +44,7 @@ static Timestamp fromDateTime($coreImportPrefix.DateTime dateTime) { return result; }''', ], - hasProto3JsonHelpers: true, + wellKnownType: 'timestamp', ), 'google.protobuf.Duration': PbMixin( 'DurationMixin', @@ -67,71 +67,71 @@ static Duration fromDart($coreImportPrefix.Duration duration) => Duration() ..nanos = (duration.inMicroseconds % $coreImportPrefix.Duration.microsecondsPerSecond) * 1000; ''', ], - hasProto3JsonHelpers: true, + wellKnownType: 'duration', ), 'google.protobuf.Struct': PbMixin( 'StructMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'struct', ), 'google.protobuf.Value': PbMixin( 'ValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'value', ), 'google.protobuf.ListValue': PbMixin( 'ListValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'listValue', ), 'google.protobuf.DoubleValue': PbMixin( 'DoubleValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'doubleValue', ), 'google.protobuf.FloatValue': PbMixin( 'FloatValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'floatValue', ), 'google.protobuf.Int64Value': PbMixin( 'Int64ValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'int64Value', ), 'google.protobuf.UInt64Value': PbMixin( 'UInt64ValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'uint64Value', ), 'google.protobuf.Int32Value': PbMixin( 'Int32ValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'int32Value', ), 'google.protobuf.UInt32Value': PbMixin( 'UInt32ValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'uint32Value', ), 'google.protobuf.BoolValue': PbMixin( 'BoolValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'boolValue', ), 'google.protobuf.StringValue': PbMixin( 'StringValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'stringValue', ), 'google.protobuf.BytesValue': PbMixin( 'BytesValueMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'bytesValue', ), 'google.protobuf.FieldMask': PbMixin( 'FieldMaskMixin', importFrom: _wellKnownImportPath, - hasProto3JsonHelpers: true, + wellKnownType: 'fieldMask', ), }; From 2b42758080657cc440fdced44985fc184f21a553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Wed, 6 Aug 2025 11:21:26 +0100 Subject: [PATCH 2/5] Update changelogs and versions --- protobuf/CHANGELOG.md | 5 ++++- protobuf/pubspec.yaml | 2 +- protoc_plugin/CHANGELOG.md | 4 ++++ protoc_plugin/pubspec.yaml | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md index 209c21b9..bd3217e6 100644 --- a/protobuf/CHANGELOG.md +++ b/protobuf/CHANGELOG.md @@ -1,4 +1,4 @@ -## 4.2.0-wip +## 5.0.0-wip * Internal refactoring to split the package into libraries. This allows conditionally importing different libraries and improving performance by @@ -11,6 +11,9 @@ * Improve performance of `GeneratedMessage` members: `writeToJsonMap`, `writeToJson`, `mergeFromJson`, `mergeFromJsonMap`. ([#1028]) +* Remove `BuilderInfo.fromProto3Json` and `BuilderInfo.toProto3Json` as a part + of an internal refactoring. + [#1026]: https://github.com/google/protobuf.dart/pull/1026 [#1027]: https://github.com/google/protobuf.dart/pull/1027 [#1028]: https://github.com/google/protobuf.dart/pull/1028 diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml index 50d99cf2..ed172704 100644 --- a/protobuf/pubspec.yaml +++ b/protobuf/pubspec.yaml @@ -1,5 +1,5 @@ name: protobuf -version: 4.2.0-wip +version: 5.0.0-wip description: >- Runtime library for protocol buffers support. Use with package:protoc_plugin to generate Dart code for your '.proto' files. diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md index b12de704..1935a8f3 100644 --- a/protoc_plugin/CHANGELOG.md +++ b/protoc_plugin/CHANGELOG.md @@ -1,3 +1,7 @@ +## 23.0.0 + +* Update generated code for protobuf-5.0.0. + ## 22.5.0 * Generated files are now formatted using the Dart formatter. The code is diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml index 3f6fa260..249a653a 100644 --- a/protoc_plugin/pubspec.yaml +++ b/protoc_plugin/pubspec.yaml @@ -1,5 +1,5 @@ name: protoc_plugin -version: 22.5.0 +version: 23.0.0 description: A protobuf protoc compiler plugin used to generate Dart code. repository: https://github.com/google/protobuf.dart/tree/master/protoc_plugin From c99b83584a05cbada4af808aeb902f53a34c51fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Wed, 6 Aug 2025 11:24:09 +0100 Subject: [PATCH 3/5] Update plugin protobuf dep --- protoc_plugin/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml index 249a653a..3a44417c 100644 --- a/protoc_plugin/pubspec.yaml +++ b/protoc_plugin/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: dart_style: ^3.0.0 fixnum: ^1.0.0 path: ^1.8.0 - protobuf: ^4.1.0 + protobuf: ^5.0.0 pub_semver: ^2.2.0 dev_dependencies: From d4579338dd7601214c4b7a365e861e5572b37af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Wed, 6 Aug 2025 11:25:04 +0100 Subject: [PATCH 4/5] Change version name? --- protobuf/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml index ed172704..162dadcc 100644 --- a/protobuf/pubspec.yaml +++ b/protobuf/pubspec.yaml @@ -1,5 +1,5 @@ name: protobuf -version: 5.0.0-wip +version: 5.0.0 description: >- Runtime library for protocol buffers support. Use with package:protoc_plugin to generate Dart code for your '.proto' files. From d8dcea1a09d3d11e7e64cc404cc3f0083375f408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20A=C4=9Facan?= Date: Mon, 11 Aug 2025 10:28:09 +0100 Subject: [PATCH 5/5] Changelog comment --- protoc_plugin/CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md index 1935a8f3..bf34074d 100644 --- a/protoc_plugin/CHANGELOG.md +++ b/protoc_plugin/CHANGELOG.md @@ -1,6 +1,6 @@ ## 23.0.0 -* Update generated code for protobuf-5.0.0. +* Update generated code for protobuf 5.0.0. ## 22.5.0 @@ -46,30 +46,30 @@ We now parse and generate code cooresponding to the proto options `google.api.default_host` and `google.api.oauth_scopes`: - + ```proto service Firestore { option (google.api.default_host) = "firestore.googleapis.com"; option (google.api.oauth_scopes) = "https://www.googleapis.com/auth/cloud-platform," "https://www.googleapis.com/auth/datastore"; - + ... ``` - + Will generate as: - + ```dart class FirestoreClient extends $grpc.Client { /// The hostname for this service. static const $core.String defaultHost = 'firestore.googleapis.com'; - + /// OAuth scopes needed for the client. static const $core.List<$core.String> oauthScopes = [ 'https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/datastore', ]; - + ... ``` * Minimum SDK dependency bumped from 3.3.0 to 3.6.0. ([#1001])