| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of protoc; | 5 part of protoc; |
| 6 | 6 |
| 7 /// A Dart function called on each item added to a repeated list | 7 /// A Dart function called on each item added to a repeated list |
| 8 /// to check its type and range. | 8 /// to check its type and range. |
| 9 const checkItem = '\$checkItem'; | 9 const checkItem = '\$checkItem'; |
| 10 | 10 |
| 11 class MessageGenerator extends ProtobufContainer { | 11 class MessageGenerator extends ProtobufContainer { |
| 12 // List of Dart language reserved words in names which cannot be used in a | 12 // List of Dart language reserved words in names which cannot be used in a |
| 13 // subclass of GeneratedMessage. | 13 // subclass of GeneratedMessage. |
| 14 static final List<String> reservedWords = | 14 static final List<String> reservedWords = [ |
| 15 ['assert', 'break', 'case', 'catch', 'class', 'const', 'continue', | 15 'assert', |
| 16 'default', 'do', 'else', 'enum', 'extends', 'false', 'final', | 16 'break', |
| 17 'finally', 'for', 'if', 'in', 'is', 'new', 'null', 'rethrow', 'return', | 17 'case', |
| 18 'super', 'switch', 'this', 'throw', 'true', 'try', 'var', 'void', | 18 'catch', |
| 19 'while', 'with']; | 19 'class', |
| 20 'const', |
| 21 'continue', |
| 22 'default', |
| 23 'do', |
| 24 'else', |
| 25 'enum', |
| 26 'extends', |
| 27 'false', |
| 28 'final', |
| 29 'finally', |
| 30 'for', |
| 31 'if', |
| 32 'in', |
| 33 'is', |
| 34 'new', |
| 35 'null', |
| 36 'rethrow', |
| 37 'return', |
| 38 'super', |
| 39 'switch', |
| 40 'this', |
| 41 'throw', |
| 42 'true', |
| 43 'try', |
| 44 'var', |
| 45 'void', |
| 46 'while', |
| 47 'with' |
| 48 ]; |
| 20 | 49 |
| 21 // List of names used in the generated class itself | 50 // List of names used in the generated class itself |
| 22 static final List<String> generatedNames = | 51 static final List<String> generatedNames = [ |
| 23 ['create', 'createRepeated', 'getDefault', checkItem]; | 52 'create', |
| 53 'createRepeated', |
| 54 'getDefault', |
| 55 checkItem |
| 56 ]; |
| 24 | 57 |
| 25 // Returns the mixin for this message, or null if none. | 58 // Returns the mixin for this message, or null if none. |
| 26 static PbMixin _getMixin(DescriptorProto desc, PbMixin defaultValue) { | 59 static PbMixin _getMixin(DescriptorProto desc, PbMixin defaultValue) { |
| 27 if (!desc.hasOptions()) return defaultValue; | 60 if (!desc.hasOptions()) return defaultValue; |
| 28 if (!desc.options.hasExtension(Dart_options.mixin)) return defaultValue; | 61 if (!desc.options.hasExtension(Dart_options.mixin)) return defaultValue; |
| 29 | 62 |
| 30 String name = desc.options.getExtension(Dart_options.mixin); | 63 String name = desc.options.getExtension(Dart_options.mixin); |
| 31 if (name.isEmpty) return null; // don't use a mixin (override any default) | 64 if (name.isEmpty) return null; // don't use a mixin (override any default) |
| 32 var mixin = findMixin(name); | 65 var mixin = findMixin(name); |
| 33 if (mixin == null) { | 66 if (mixin == null) { |
| 34 throw("unknown mixin class: ${name}"); | 67 throw ("unknown mixin class: ${name}"); |
| 35 } | 68 } |
| 36 return mixin; | 69 return mixin; |
| 37 } | 70 } |
| 38 | 71 |
| 39 final String classname; | 72 final String classname; |
| 40 final String fqname; | 73 final String fqname; |
| 41 final PbMixin mixin; | 74 final PbMixin mixin; |
| 42 | 75 |
| 43 final ProtobufContainer _parent; | 76 final ProtobufContainer _parent; |
| 44 final DescriptorProto _descriptor; | 77 final DescriptorProto _descriptor; |
| 45 final List<EnumGenerator> _enumGenerators = <EnumGenerator>[]; | 78 final List<EnumGenerator> _enumGenerators = <EnumGenerator>[]; |
| 46 final List<MessageGenerator> _messageGenerators = <MessageGenerator>[]; | 79 final List<MessageGenerator> _messageGenerators = <MessageGenerator>[]; |
| 47 final List<ExtensionGenerator> _extensionGenerators = <ExtensionGenerator>[]; | 80 final List<ExtensionGenerator> _extensionGenerators = <ExtensionGenerator>[]; |
| 48 | 81 |
| 49 // populated by resolve() | 82 // populated by resolve() |
| 50 List<ProtobufField> _fieldList; | 83 List<ProtobufField> _fieldList; |
| 51 | 84 |
| 52 // Used during generation. | 85 // Used during generation. |
| 53 final Set<String> _methodNames = new Set<String>(); | 86 final Set<String> _methodNames = new Set<String>(); |
| 54 | 87 |
| 55 MessageGenerator( | 88 MessageGenerator(DescriptorProto descriptor, ProtobufContainer parent, |
| 56 DescriptorProto descriptor, ProtobufContainer parent, PbMixin defaultMixin
) | 89 PbMixin defaultMixin) |
| 57 : _descriptor = descriptor, | 90 : _descriptor = descriptor, |
| 58 _parent = parent, | 91 _parent = parent, |
| 59 classname = (parent.classname == '') ? | 92 classname = (parent.classname == '') |
| 60 descriptor.name : '${parent.classname}_${descriptor.name}', | 93 ? descriptor.name |
| 61 fqname = (parent == null || parent.fqname == null) ? descriptor.name : | 94 : '${parent.classname}_${descriptor.name}', |
| 62 (parent.fqname == '.' ? | 95 fqname = (parent == null || parent.fqname == null) |
| 63 '.${descriptor.name}' : '${parent.fqname}.${descriptor.name}'), | 96 ? descriptor.name |
| 97 : (parent.fqname == '.' |
| 98 ? '.${descriptor.name}' |
| 99 : '${parent.fqname}.${descriptor.name}'), |
| 64 mixin = _getMixin(descriptor, defaultMixin) { | 100 mixin = _getMixin(descriptor, defaultMixin) { |
| 65 | |
| 66 for (EnumDescriptorProto e in _descriptor.enumType) { | 101 for (EnumDescriptorProto e in _descriptor.enumType) { |
| 67 _enumGenerators.add(new EnumGenerator(e, this)); | 102 _enumGenerators.add(new EnumGenerator(e, this)); |
| 68 } | 103 } |
| 69 | 104 |
| 70 for (DescriptorProto n in _descriptor.nestedType) { | 105 for (DescriptorProto n in _descriptor.nestedType) { |
| 71 _messageGenerators.add( | 106 _messageGenerators.add(new MessageGenerator(n, this, defaultMixin)); |
| 72 new MessageGenerator(n, this, defaultMixin)); | |
| 73 } | 107 } |
| 74 | 108 |
| 75 for (FieldDescriptorProto x in _descriptor.extension) { | 109 for (FieldDescriptorProto x in _descriptor.extension) { |
| 76 _extensionGenerators.add(new ExtensionGenerator(x, this)); | 110 _extensionGenerators.add(new ExtensionGenerator(x, this)); |
| 77 } | 111 } |
| 78 } | 112 } |
| 79 | 113 |
| 80 String get package => _parent.package; | 114 String get package => _parent.package; |
| 81 | 115 |
| 82 /// The generator of the .pb.dart file that will declare this type. | 116 /// The generator of the .pb.dart file that will declare this type. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 105 output.addAll(mixin.findMixinsToApply()); | 139 output.addAll(mixin.findMixinsToApply()); |
| 106 } | 140 } |
| 107 for (var m in _messageGenerators) { | 141 for (var m in _messageGenerators) { |
| 108 m.addMixinsTo(output); | 142 m.addMixinsTo(output); |
| 109 } | 143 } |
| 110 } | 144 } |
| 111 | 145 |
| 112 // Registers message and enum types that can be used elsewhere. | 146 // Registers message and enum types that can be used elsewhere. |
| 113 void register(GenerationContext ctx) { | 147 void register(GenerationContext ctx) { |
| 114 ctx.registerFieldType(fqname, this); | 148 ctx.registerFieldType(fqname, this); |
| 115 for (var m in _messageGenerators) { | 149 for (var m in _messageGenerators) { |
| 116 m.register(ctx); | 150 m.register(ctx); |
| 117 } | 151 } |
| 118 for (var e in _enumGenerators) { | 152 for (var e in _enumGenerators) { |
| 119 e.register(ctx); | 153 e.register(ctx); |
| 120 } | 154 } |
| 121 } | 155 } |
| 122 | 156 |
| 123 // Creates fields and resolves extension targets. | 157 // Creates fields and resolves extension targets. |
| 124 void resolve(GenerationContext ctx) { | 158 void resolve(GenerationContext ctx) { |
| 125 if (_fieldList != null) throw new StateError("message already resolved"); | 159 if (_fieldList != null) throw new StateError("message already resolved"); |
| 126 | 160 |
| 127 var sorted = new List<FieldDescriptorProto>.from(_descriptor.field) | 161 var sorted = new List<FieldDescriptorProto>.from(_descriptor.field) |
| 128 ..sort((FieldDescriptorProto a, FieldDescriptorProto b) { | 162 ..sort((FieldDescriptorProto a, FieldDescriptorProto b) { |
| 129 if (a.number < b.number) return -1; | 163 if (a.number < b.number) return -1; |
| 130 if (a.number > b.number) return 1; | 164 if (a.number > b.number) return 1; |
| 131 throw "multiple fields defined for tag ${a.number} in $fqname"; | 165 throw "multiple fields defined for tag ${a.number} in $fqname"; |
| 132 }); | 166 }); |
| 133 | 167 |
| 134 _fieldList = <ProtobufField>[]; | 168 _fieldList = <ProtobufField>[]; |
| 135 for (FieldDescriptorProto field in sorted) { | 169 for (FieldDescriptorProto field in sorted) { |
| 136 int index = _fieldList.length; | 170 int index = _fieldList.length; |
| 137 _fieldList.add(new ProtobufField(field, index, this, ctx)); | 171 _fieldList.add(new ProtobufField(field, index, this, ctx)); |
| 138 } | 172 } |
| 139 | 173 |
| 140 for (var m in _messageGenerators) { | 174 for (var m in _messageGenerators) { |
| 141 m.resolve(ctx); | 175 m.resolve(ctx); |
| 142 } | 176 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 for (MessageGenerator m in _messageGenerators) { | 231 for (MessageGenerator m in _messageGenerators) { |
| 198 m.generate(out); | 232 m.generate(out); |
| 199 } | 233 } |
| 200 | 234 |
| 201 var mixinClause = ''; | 235 var mixinClause = ''; |
| 202 if (mixin != null) { | 236 if (mixin != null) { |
| 203 var mixinNames = mixin.findMixinsToApply().map((m) => m.name); | 237 var mixinNames = mixin.findMixinsToApply().map((m) => m.name); |
| 204 mixinClause = ' with ${mixinNames.join(", ")}'; | 238 mixinClause = ' with ${mixinNames.join(", ")}'; |
| 205 } | 239 } |
| 206 | 240 |
| 207 out.addBlock('class ${classname} extends GeneratedMessage${mixinClause} {', | 241 out.addBlock( |
| 208 '}', () | 242 'class ${classname} extends GeneratedMessage${mixinClause} {', '}', () { |
| 209 { | |
| 210 out.addBlock( | 243 out.addBlock( |
| 211 'static final BuilderInfo _i = new BuilderInfo(\'${classname}\')', | 244 'static final BuilderInfo _i = new BuilderInfo(\'${classname}\')', |
| 212 ';', () { | 245 ';', () { |
| 213 for (ProtobufField field in _fieldList) { | 246 for (ProtobufField field in _fieldList) { |
| 214 out.println(field.generateBuilderInfoCall(package)); | 247 out.println(field.generateBuilderInfoCall(package)); |
| 215 } | 248 } |
| 216 | 249 |
| 217 if (_descriptor.extensionRange.length > 0) { | 250 if (_descriptor.extensionRange.length > 0) { |
| 218 out.println('..hasExtensions = true'); | 251 out.println('..hasExtensions = true'); |
| 219 } | 252 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 238 out.println('${classname} clone() =>' | 271 out.println('${classname} clone() =>' |
| 239 ' new ${classname}()..mergeFromMessage(this);'); | 272 ' new ${classname}()..mergeFromMessage(this);'); |
| 240 | 273 |
| 241 out.println('BuilderInfo get info_ => _i;'); | 274 out.println('BuilderInfo get info_ => _i;'); |
| 242 | 275 |
| 243 // Factory functions which can be used as default value closures. | 276 // Factory functions which can be used as default value closures. |
| 244 out.println('static ${classname} create() =>' | 277 out.println('static ${classname} create() =>' |
| 245 ' new ${classname}();'); | 278 ' new ${classname}();'); |
| 246 out.println('static PbList<${classname}> createRepeated() =>' | 279 out.println('static PbList<${classname}> createRepeated() =>' |
| 247 ' new PbList<${classname}>();'); | 280 ' new PbList<${classname}>();'); |
| 248 out.addBlock('static ${classname} getDefault() {', | 281 out.addBlock('static ${classname} getDefault() {', '}', () { |
| 249 '}', () { | 282 out.println( |
| 250 out.println('if (_defaultInstance == null) _defaultInstance = new _Reado
nly${classname}();'); | 283 'if (_defaultInstance == null) _defaultInstance = new _Readonly${cla
ssname}();'); |
| 251 out.println('return _defaultInstance;'); | 284 out.println('return _defaultInstance;'); |
| 252 }); | 285 }); |
| 253 out.println('static ${classname} _defaultInstance;'); | 286 out.println('static ${classname} _defaultInstance;'); |
| 254 out.addBlock('static void $checkItem($classname v) {', '}', () { | 287 out.addBlock('static void $checkItem($classname v) {', '}', () { |
| 255 out.println('if (v is !$classname)' | 288 out.println('if (v is !$classname)' |
| 256 " checkItemFailed(v, '$classname');"); | 289 " checkItemFailed(v, '$classname');"); |
| 257 }); | 290 }); |
| 258 generateFieldsAccessorsMutators(out); | 291 generateFieldsAccessorsMutators(out); |
| 259 }); | 292 }); |
| 260 out.println(); | 293 out.println(); |
| 261 | 294 |
| 262 out.println('class _Readonly${classname} extends ${classname} with ReadonlyM
essageMixin {}'); | 295 out.println( |
| 296 'class _Readonly${classname} extends ${classname} with ReadonlyMessageMi
xin {}'); |
| 263 out.println(); | 297 out.println(); |
| 264 } | 298 } |
| 265 | 299 |
| 266 // Returns true if the message type has any required fields. If it doesn't, | 300 // Returns true if the message type has any required fields. If it doesn't, |
| 267 // we can optimize out calls to its isInitialized()/_findInvalidFields() | 301 // we can optimize out calls to its isInitialized()/_findInvalidFields() |
| 268 // methods. | 302 // methods. |
| 269 // | 303 // |
| 270 // already_seen is used to avoid checking the same type multiple times | 304 // already_seen is used to avoid checking the same type multiple times |
| 271 // (and also to protect against unbounded recursion). | 305 // (and also to protect against unbounded recursion). |
| 272 bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) { | 306 bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 308 void generateFieldsAccessorsMutators(IndentingWriter out) { | 342 void generateFieldsAccessorsMutators(IndentingWriter out) { |
| 309 for (ProtobufField field in _fieldList) { | 343 for (ProtobufField field in _fieldList) { |
| 310 out.println(); | 344 out.println(); |
| 311 | 345 |
| 312 // Choose non-conflicting names. | 346 // Choose non-conflicting names. |
| 313 String identifier = field.dartFieldName; | 347 String identifier = field.dartFieldName; |
| 314 String hasIdentifier = field.hasMethodName; | 348 String hasIdentifier = field.hasMethodName; |
| 315 String clearIdentifier = field.clearMethodName; | 349 String clearIdentifier = field.clearMethodName; |
| 316 if (!field.isRepeated) { | 350 if (!field.isRepeated) { |
| 317 while (_methodNames.contains(identifier) || | 351 while (_methodNames.contains(identifier) || |
| 318 _methodNames.contains(hasIdentifier) || | 352 _methodNames.contains(hasIdentifier) || |
| 319 _methodNames.contains(clearIdentifier)) { | 353 _methodNames.contains(clearIdentifier)) { |
| 320 identifier += '_' + field.number.toString(); | 354 identifier += '_' + field.number.toString(); |
| 321 hasIdentifier += '_' + field.number.toString(); | 355 hasIdentifier += '_' + field.number.toString(); |
| 322 clearIdentifier += '_' + field.number.toString(); | 356 clearIdentifier += '_' + field.number.toString(); |
| 323 } | 357 } |
| 324 _methodNames.add(identifier); | 358 _methodNames.add(identifier); |
| 325 _methodNames.add(hasIdentifier); | 359 _methodNames.add(hasIdentifier); |
| 326 _methodNames.add(clearIdentifier); | 360 _methodNames.add(clearIdentifier); |
| 327 } else { | 361 } else { |
| 328 while (_methodNames.contains(identifier)) { | 362 while (_methodNames.contains(identifier)) { |
| 329 identifier += '_' + field.number.toString(); | 363 identifier += '_' + field.number.toString(); |
| 330 } | 364 } |
| 331 _methodNames.add(identifier); | 365 _methodNames.add(identifier); |
| 332 } | 366 } |
| 333 | 367 |
| 334 var fieldTypeString = field.getDartType(package); | 368 var fieldTypeString = field.getDartType(package); |
| 335 var defaultExpr = field.getDefaultExpr(); | 369 var defaultExpr = field.getDefaultExpr(); |
| 336 out.println('${fieldTypeString} get ${identifier}' | 370 out.println('${fieldTypeString} get ${identifier}' |
| 337 ' => \$_get(${field.index}, ${field.number}, $defaultExpr);'); | 371 ' => \$_get(' |
| 372 '${field.index}, ${field.number}, $defaultExpr);'); |
| 338 if (!field.isRepeated) { | 373 if (!field.isRepeated) { |
| 339 var fastSetter = field.baseType.setter; | 374 var fastSetter = field.baseType.setter; |
| 340 if (fastSetter != null) { | 375 if (fastSetter != null) { |
| 341 out.println('void set $identifier' | 376 out.println('void set $identifier' |
| 342 '($fieldTypeString v) { ' | 377 '($fieldTypeString v) { ' |
| 343 '$fastSetter(${field.index}, ${field.number}, v);' | 378 '$fastSetter(${field.index}, ${field.number}, v);' |
| 344 ' }'); | 379 ' }'); |
| 345 } else { | 380 } else { |
| 346 out.println('void set $identifier' | 381 out.println('void set $identifier' |
| 347 '($fieldTypeString v) { ' | 382 '($fieldTypeString v) { ' |
| (...skipping 13 matching lines...) Expand all Loading... |
| 361 /// to avoid duplication. | 396 /// to avoid duplication. |
| 362 void generateConstants(IndentingWriter out) { | 397 void generateConstants(IndentingWriter out) { |
| 363 const nestedTypeTag = 3; | 398 const nestedTypeTag = 3; |
| 364 const enumTypeTag = 4; | 399 const enumTypeTag = 4; |
| 365 assert(_descriptor.info_.fieldInfo[nestedTypeTag].name == "nestedType"); | 400 assert(_descriptor.info_.fieldInfo[nestedTypeTag].name == "nestedType"); |
| 366 assert(_descriptor.info_.fieldInfo[enumTypeTag].name == "enumType"); | 401 assert(_descriptor.info_.fieldInfo[enumTypeTag].name == "enumType"); |
| 367 | 402 |
| 368 var name = getJsonConstant(fileGen); | 403 var name = getJsonConstant(fileGen); |
| 369 var json = _descriptor.writeToJsonMap(); | 404 var json = _descriptor.writeToJsonMap(); |
| 370 var nestedTypeNames = | 405 var nestedTypeNames = |
| 371 _messageGenerators.map((m) => m.getJsonConstant(fileGen)) | 406 _messageGenerators.map((m) => m.getJsonConstant(fileGen)).toList(); |
| 372 .toList(); | |
| 373 var nestedEnumNames = | 407 var nestedEnumNames = |
| 374 _enumGenerators.map((e) => e.getJsonConstant(fileGen)) | 408 _enumGenerators.map((e) => e.getJsonConstant(fileGen)).toList(); |
| 375 .toList(); | |
| 376 | 409 |
| 377 out.addBlock("const $name = const {", "};", () { | 410 out.addBlock("const $name = const {", "};", () { |
| 378 for (var key in json.keys) { | 411 for (var key in json.keys) { |
| 379 out.print("'$key': "); | 412 out.print("'$key': "); |
| 380 if (key == "$nestedTypeTag") { | 413 if (key == "$nestedTypeTag") { |
| 381 // refer to message constants by name instead of repeating each value | 414 // refer to message constants by name instead of repeating each value |
| 382 out.println("const [${nestedTypeNames.join(", ")}],"); | 415 out.println("const [${nestedTypeNames.join(", ")}],"); |
| 383 continue; | 416 continue; |
| 384 } else if (key == "$enumTypeTag") { | 417 } else if (key == "$enumTypeTag") { |
| 385 // refer to enum constants by name | 418 // refer to enum constants by name |
| 386 out.println("const [${nestedEnumNames.join(", ")}],"); | 419 out.println("const [${nestedEnumNames.join(", ")}],"); |
| 387 continue; | 420 continue; |
| 388 } | 421 } |
| 389 writeJsonConst(out, json[key]); | 422 writeJsonConst(out, json[key]); |
| 390 out.println(","); | 423 out.println(","); |
| 391 } | 424 } |
| 392 }); | 425 }); |
| 393 out.println(); | 426 out.println(); |
| 394 | 427 |
| 395 for (var m in _messageGenerators) { | 428 for (var m in _messageGenerators) { |
| 396 m.generateConstants(out); | 429 m.generateConstants(out); |
| 397 } | 430 } |
| 398 | 431 |
| 399 for (var e in _enumGenerators) { | 432 for (var e in _enumGenerators) { |
| 400 e.generateConstants(out); | 433 e.generateConstants(out); |
| 401 } | 434 } |
| 402 } | 435 } |
| 403 } | 436 } |
| OLD | NEW |