Skip to content

Commit 3e8f116

Browse files
authored
Support for Any (flutter#112)
* Support for Any
1 parent f2874bd commit 3e8f116

28 files changed

+559
-182
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.idea/
33
.packages
44
.pub
5+
.idea
56
out
67
packages
78
pubspec.lock

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.10.0
2+
3+
* Breaking change: Support for [any](https://developers.google.com/protocol-buffers/docs/proto3#any) messages.
4+
Generated files require package:protobuf version 0.10.1 or newer.
5+
`BuilderInfo.messageName` will now be the fully qualified name for generated messages.
6+
17
## 0.9.0
28

39
* Breaking change: Add `copyWith()` to message classes and update `getDefault()` to use `freeze()`.

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ PLUGIN_PATH=bin/$(PLUGIN_NAME)
1212
BENCHMARK_PROTOS = $(wildcard benchmark/protos/*.proto)
1313

1414
TEST_PROTO_LIST = \
15+
google/protobuf/any \
1516
google/protobuf/unittest_import \
1617
google/protobuf/unittest_optimize_for \
1718
google/protobuf/unittest \
@@ -36,7 +37,8 @@ TEST_PROTO_LIST = \
3637
service2 \
3738
service3 \
3839
toplevel_import \
39-
toplevel
40+
toplevel \
41+
using_any
4042
TEST_PROTO_DIR=$(OUTPUT_DIR)/protos
4143
TEST_PROTO_LIBS=$(foreach f, $(TEST_PROTO_LIST), \
4244
$(TEST_PROTO_DIR)/$(f).pb.dart \

lib/code_generator.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ part of protoc;
77
abstract class ProtobufContainer {
88
String get package;
99
String get classname;
10-
String get fqname;
10+
String get fullName;
11+
12+
/// The fully qualified name with a leading '.'.
13+
///
14+
/// This exists because names from protoc come like this.
15+
String get dottedName => '.$fullName';
16+
1117
String get packageImportPrefix =>
1218
_cachedImportPrefix ??= _calculateImportPrefix();
1319

@@ -83,6 +89,6 @@ class CodeGenerator extends ProtobufContainer {
8389

8490
String get package => '';
8591
String get classname => null;
86-
String get fqname => '';
92+
String get fullName => '';
8793
get fileGen => null;
8894
}

lib/enum_generator.dart

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,21 @@ class EnumAlias {
1313
class EnumGenerator extends ProtobufContainer {
1414
final ProtobufContainer _parent;
1515
final String classname;
16-
final String fqname;
16+
final String fullName;
1717
final EnumDescriptorProto _descriptor;
1818
final List<EnumValueDescriptorProto> _canonicalValues =
1919
<EnumValueDescriptorProto>[];
2020
final List<EnumAlias> _aliases = <EnumAlias>[];
2121

2222
EnumGenerator(EnumDescriptorProto descriptor, ProtobufContainer parent)
23-
: _parent = parent,
24-
classname = (parent == null || parent is FileGenerator)
23+
: assert(parent != null),
24+
_parent = parent,
25+
classname = (parent is FileGenerator)
2526
? descriptor.name
2627
: '${parent.classname}_${descriptor.name}',
27-
fqname = (parent == null || parent.fqname == null)
28+
fullName = parent.fullName == ''
2829
? descriptor.name
29-
: (parent.fqname == '.'
30-
? '.${descriptor.name}'
31-
: '${parent.fqname}.${descriptor.name}'),
30+
: '${parent.fullName}.${descriptor.name}',
3231
_descriptor = descriptor {
3332
for (EnumValueDescriptorProto value in descriptor.value) {
3433
EnumValueDescriptorProto canonicalValue =
@@ -46,7 +45,7 @@ class EnumGenerator extends ProtobufContainer {
4645

4746
/// Make this enum available as a field type.
4847
void register(GenerationContext ctx) {
49-
ctx.registerFieldType(fqname, this);
48+
ctx.registerFieldType(this);
5049
}
5150

5251
/// Returns a const expression that evaluates to the JSON for this message.

lib/extension_generator.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ExtensionGenerator {
1111
// populated by resolve()
1212
ProtobufField _field;
1313
String _extensionName;
14-
String _extendedClassName = "";
14+
String _extendedFullName = "";
1515

1616
ExtensionGenerator(this._descriptor, this._parent);
1717

@@ -22,7 +22,7 @@ class ExtensionGenerator {
2222
ProtobufContainer extendedType = ctx.getFieldType(_descriptor.extendee);
2323
// TODO(skybrian) When would this be null?
2424
if (extendedType != null) {
25-
_extendedClassName = extendedType.classname;
25+
_extendedFullName = extendedType.fullName;
2626
}
2727
}
2828

@@ -80,7 +80,7 @@ class ExtensionGenerator {
8080

8181
if (_field.isRepeated) {
8282
out.print('static final $_protobufImportPrefix.Extension $name = '
83-
'new $_protobufImportPrefix.Extension<$dartType>.repeated(\'$_extendedClassName\','
83+
'new $_protobufImportPrefix.Extension<$dartType>.repeated(\'$_extendedFullName\','
8484
' \'$name\', ${_field.number}, ${_field.typeConstant}');
8585
if (type.isMessage || type.isGroup) {
8686
out.println(', $dartType.$checkItem, $dartType.create);');
@@ -95,7 +95,7 @@ class ExtensionGenerator {
9595
}
9696

9797
out.print('static final $_protobufImportPrefix.Extension $name = '
98-
'new $_protobufImportPrefix.Extension<$dartType>(\'$_extendedClassName\', \'$name\', '
98+
'new $_protobufImportPrefix.Extension<$dartType>(\'$_extendedFullName\', \'$name\', '
9999
'${_field.number}, ${_field.typeConstant}');
100100

101101
String initializer = _field.generateDefaultFunction(package);

lib/file_generator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class FileGenerator extends ProtobufContainer {
163163

164164
String get package => descriptor.package;
165165
String get classname => '';
166-
String get fqname => '.${descriptor.package}';
166+
String get fullName => descriptor.package;
167167
FileGenerator get fileGen => this;
168168

169169
/// Generates all the Dart files for this .proto file.

lib/grpc_generator.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class GrpcServiceGenerator {
7373
return;
7474
}
7575
mg.checkResolved();
76-
_deps[mg.fqname] = mg;
76+
_deps[mg.dottedName] = mg;
7777
}
7878

7979
/// Adds dependencies of [generate] to [imports].

lib/linker.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ class GenerationContext {
5555
}
5656

5757
/// Makes a message, group, or enum available for reference.
58-
void registerFieldType(String name, ProtobufContainer type) {
59-
_typeRegistry[name] = type;
58+
void registerFieldType(ProtobufContainer type) {
59+
// Register the name with a leading '.' to be compatible with input from
60+
// protoc.
61+
_typeRegistry[type.dottedName] = type;
6062
}
6163

6264
/// Returns info about a .pb.dart being imported,

lib/message_generator.dart

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,27 @@ class MessageGenerator extends ProtobufContainer {
2727
/// The name of the Dart class to generate.
2828
final String classname;
2929

30-
/// The fully-qualified name of the message type.
30+
/// The fully-qualified name of the message (without any leading '.').
31+
final String fullName;
32+
33+
/// The part of the fully qualified name that comes after the package prefix.
34+
///
35+
/// For nested messages this will include the names of the parents.
3136
///
32-
/// (Used as a unique key and in error messages, not in Dart code.)
33-
final String fqname;
37+
/// For example:
38+
/// ```
39+
/// package foo;
40+
///
41+
/// message Container {
42+
/// message Nested {
43+
/// int32 int32_value = 1;
44+
/// }
45+
/// }
46+
/// ```
47+
/// The nested message will have a `fullName` of 'foo.Container.Nested', and a
48+
/// `messageName` of 'Container.Nested'.
49+
String get messageName =>
50+
fullName.substring(package.length == 0 ? 0 : package.length + 1);
3451

3552
final PbMixin mixin;
3653

@@ -48,11 +65,10 @@ class MessageGenerator extends ProtobufContainer {
4865
: _descriptor = descriptor,
4966
_parent = parent,
5067
classname = messageClassName(descriptor, parent: parent.classname),
51-
fqname = (parent == null || parent.fqname == null)
68+
assert(parent != null),
69+
fullName = parent.fullName == ''
5270
? descriptor.name
53-
: (parent.fqname == '.'
54-
? '.${descriptor.name}'
55-
: '${parent.fqname}.${descriptor.name}'),
71+
: '${parent.fullName}.${descriptor.name}',
5672
mixin = _getMixin(descriptor, parent.fileGen.descriptor, declaredMixins,
5773
defaultMixin) {
5874
for (EnumDescriptorProto e in _descriptor.enumType) {
@@ -77,7 +93,7 @@ class MessageGenerator extends ProtobufContainer {
7793
/// Throws an exception if [resolve] hasn't been called yet.
7894
void checkResolved() {
7995
if (_fieldList == null) {
80-
throw new StateError("message not resolved: ${fqname}");
96+
throw new StateError("message not resolved: ${fullName}");
8197
}
8298
}
8399

@@ -103,7 +119,7 @@ class MessageGenerator extends ProtobufContainer {
103119

104120
// Registers message and enum types that can be used elsewhere.
105121
void register(GenerationContext ctx) {
106-
ctx.registerFieldType(fqname, this);
122+
ctx.registerFieldType(this);
107123
for (var m in _messageGenerators) {
108124
m.register(ctx);
109125
}
@@ -205,11 +221,15 @@ class MessageGenerator extends ProtobufContainer {
205221
mixinClause = ' with ${mixinNames.join(", ")}';
206222
}
207223

224+
String packageClause = package == ''
225+
? ''
226+
: ', package: const $_protobufImportPrefix.PackageName(\'$package\')';
208227
out.addBlock(
209228
'class ${classname} extends $_protobufImportPrefix.GeneratedMessage${mixinClause} {',
210229
'}', () {
211230
out.addBlock(
212-
'static final $_protobufImportPrefix.BuilderInfo _i = new $_protobufImportPrefix.BuilderInfo(\'${classname}\')',
231+
'static final $_protobufImportPrefix.BuilderInfo _i = '
232+
'new $_protobufImportPrefix.BuilderInfo(\'${messageName}\'$packageClause)',
213233
';', () {
214234
for (ProtobufField field in _fieldList) {
215235
var dartFieldName = field.memberNames.fieldName;
@@ -255,9 +275,12 @@ class MessageGenerator extends ProtobufContainer {
255275
out.println('static ${classname} _defaultInstance;');
256276
out.addBlock('static void $checkItem($classname v) {', '}', () {
257277
out.println('if (v is! $classname)'
258-
" $_protobufImportPrefix.checkItemFailed(v, '$classname');");
278+
" $_protobufImportPrefix.checkItemFailed(v, _i.messageName);");
259279
});
260280
generateFieldsAccessorsMutators(out);
281+
if (fullName == 'google.protobuf.Any') {
282+
generateAnyMethods(out);
283+
}
261284
});
262285
out.println();
263286
}
@@ -271,7 +294,7 @@ class MessageGenerator extends ProtobufContainer {
271294
bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) {
272295
if (type._fieldList == null) throw new StateError("message not resolved");
273296

274-
if (alreadySeen.contains(type.fqname)) {
297+
if (alreadySeen.contains(type.fullName)) {
275298
// The type is already in cache. This means that either:
276299
// a. The type has no required fields.
277300
// b. We are in the midst of checking if the type has required fields,
@@ -282,7 +305,7 @@ class MessageGenerator extends ProtobufContainer {
282305
// here.
283306
return false;
284307
}
285-
alreadySeen.add(type.fqname);
308+
alreadySeen.add(type.fullName);
286309
// If the type has extensions, an extension with message type could contain
287310
// required fields, so we have to be conservative and assume such an
288311
// extension exists.
@@ -304,6 +327,45 @@ class MessageGenerator extends ProtobufContainer {
304327
return false;
305328
}
306329

330+
/// Generates methods for the Any message class for packing and unpacking
331+
/// values.
332+
void generateAnyMethods(IndentingWriter out) {
333+
out.println('''
334+
/// Unpacks the message in [value] into [instance].
335+
///
336+
/// Throws a [InvalidProtocolBufferException] if [typeUrl] does not correspond
337+
/// to the type of [instance].
338+
///
339+
/// A typical usage would be `any.unpackInto(new Message())`.
340+
///
341+
/// Returns [instance].
342+
T unpackInto<T extends $_protobufImportPrefix.GeneratedMessage>(T instance,
343+
{$_protobufImportPrefix.ExtensionRegistry extensionRegistry = $_protobufImportPrefix.ExtensionRegistry.EMPTY}) {
344+
$_protobufImportPrefix.unpackIntoHelper(value, instance, typeUrl,
345+
extensionRegistry: extensionRegistry);
346+
return instance;
347+
}
348+
349+
/// Returns `true` if the encoded message matches the type of [instance].
350+
///
351+
/// Can be used with a default instance:
352+
/// `any.canUnpackInto(Message.getDefault())`
353+
bool canUnpackInto($_protobufImportPrefix.GeneratedMessage instance) {
354+
return $_protobufImportPrefix.canUnpackIntoHelper(instance, typeUrl);
355+
}
356+
357+
/// Creates a new [Any] encoding [message].
358+
///
359+
/// The [typeUrl] will be [typeUrlPrefix]/`fullName` where `fullName` is
360+
/// the fully qualified name of the type of [message].
361+
static Any pack($_protobufImportPrefix.GeneratedMessage message,
362+
{String typeUrlPrefix = 'type.googleapis.com'}) {
363+
return new Any()
364+
..value = message.writeToBuffer()
365+
..typeUrl = '\${typeUrlPrefix}/\${message.info_.messageName}';
366+
}''');
367+
}
368+
307369
void generateFieldsAccessorsMutators(IndentingWriter out) {
308370
for (ProtobufField field in _fieldList) {
309371
out.println();
@@ -324,15 +386,15 @@ class MessageGenerator extends ProtobufContainer {
324386

325387
if (field.isRepeated) {
326388
if (field.overridesSetter) {
327-
throw 'Field ${field.fqname} cannot override a setter for '
389+
throw 'Field ${field.fullName} cannot override a setter for '
328390
'${names.fieldName} because it is repeated.';
329391
}
330392
if (field.overridesHasMethod) {
331-
throw 'Field ${field.fqname} cannot override '
393+
throw 'Field ${field.fullName} cannot override '
332394
'${names.hasMethodName}() because it is repeated.';
333395
}
334396
if (field.overridesClearMethod) {
335-
throw 'Field ${field.fqname} cannot override '
397+
throw 'Field ${field.fullName} cannot override '
336398
'${names.clearMethodName}() because it is repeated.';
337399
}
338400
} else {

0 commit comments

Comments
 (0)