| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /** | 5 /** |
| 6 * This file contains code to generate serialization/deserialization logic for | 6 * This file contains code to generate serialization/deserialization logic for |
| 7 * summaries based on an "IDL" description of the summary format (written in | 7 * summaries based on an "IDL" description of the summary format (written in |
| 8 * stylized Dart). | 8 * stylized Dart). |
| 9 * | 9 * |
| 10 * For each class in the "IDL" input, two corresponding classes are generated: | 10 * For each class in the "IDL" input, two corresponding classes are generated: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 final GeneratedFile schemaTarget = | 56 final GeneratedFile schemaTarget = |
| 57 new GeneratedFile('lib/src/summary/format.fbs', (String pkgPath) { | 57 new GeneratedFile('lib/src/summary/format.fbs', (String pkgPath) { |
| 58 _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath); | 58 _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath); |
| 59 codeGenerator.generateFlatBufferSchema(); | 59 codeGenerator.generateFlatBufferSchema(); |
| 60 return codeGenerator._outBuffer.toString(); | 60 return codeGenerator._outBuffer.toString(); |
| 61 }); | 61 }); |
| 62 | 62 |
| 63 typedef String _StringToString(String s); | 63 typedef String _StringToString(String s); |
| 64 | 64 |
| 65 class _CodeGenerator { | 65 class _CodeGenerator { |
| 66 static const String _throwDeprecated = |
| 67 "throw new UnimplementedError('attempt to access deprecated field')"; |
| 68 |
| 66 /** | 69 /** |
| 67 * Buffer in which generated code is accumulated. | 70 * Buffer in which generated code is accumulated. |
| 68 */ | 71 */ |
| 69 final StringBuffer _outBuffer = new StringBuffer(); | 72 final StringBuffer _outBuffer = new StringBuffer(); |
| 70 | 73 |
| 71 /** | 74 /** |
| 72 * Current indentation level. | 75 * Current indentation level. |
| 73 */ | 76 */ |
| 74 String _indentation = ''; | 77 String _indentation = ''; |
| 75 | 78 |
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 270 if (type.name.name == 'List' && | 273 if (type.name.name == 'List' && |
| 271 type.typeArguments != null && | 274 type.typeArguments != null && |
| 272 type.typeArguments.arguments.length == 1) { | 275 type.typeArguments.arguments.length == 1) { |
| 273 isList = true; | 276 isList = true; |
| 274 type = type.typeArguments.arguments[0]; | 277 type = type.typeArguments.arguments[0]; |
| 275 } | 278 } |
| 276 if (type.typeArguments != null) { | 279 if (type.typeArguments != null) { |
| 277 throw new Exception('Cannot handle type arguments in `$type`'); | 280 throw new Exception('Cannot handle type arguments in `$type`'); |
| 278 } | 281 } |
| 279 int id; | 282 int id; |
| 283 bool isDeprecated = false; |
| 280 for (Annotation annotation in classMember.metadata) { | 284 for (Annotation annotation in classMember.metadata) { |
| 281 if (annotation.name.name == 'Id') { | 285 if (annotation.name.name == 'Id') { |
| 282 if (id != null) { | 286 if (id != null) { |
| 283 throw new Exception( | 287 throw new Exception( |
| 284 'Duplicate @id annotation ($classMember)'); | 288 'Duplicate @id annotation ($classMember)'); |
| 285 } | 289 } |
| 286 if (annotation.arguments.arguments.length != 1) { | 290 if (annotation.arguments.arguments.length != 1) { |
| 287 throw new Exception( | 291 throw new Exception( |
| 288 '@Id must be passed exactly one argument ($desc)'); | 292 '@Id must be passed exactly one argument ($desc)'); |
| 289 } | 293 } |
| 290 Expression expression = annotation.arguments.arguments[0]; | 294 Expression expression = annotation.arguments.arguments[0]; |
| 291 if (expression is IntegerLiteral) { | 295 if (expression is IntegerLiteral) { |
| 292 id = expression.value; | 296 id = expression.value; |
| 293 } else { | 297 } else { |
| 294 throw new Exception( | 298 throw new Exception( |
| 295 '@Id parameter must be an integer literal ($desc)'); | 299 '@Id parameter must be an integer literal ($desc)'); |
| 296 } | 300 } |
| 301 } else if (annotation.name.name == 'deprecated') { |
| 302 if (annotation.arguments != null) { |
| 303 throw new Exception('@deprecated does not take args ($desc)'); |
| 304 } |
| 305 isDeprecated = true; |
| 297 } | 306 } |
| 298 } | 307 } |
| 299 if (id == null) { | 308 if (id == null) { |
| 300 throw new Exception('Missing @id annotation ($desc)'); | 309 throw new Exception('Missing @id annotation ($desc)'); |
| 301 } | 310 } |
| 302 String doc = _getNodeDoc(lineInfo, classMember); | 311 String doc = _getNodeDoc(lineInfo, classMember); |
| 303 idlModel.FieldType fieldType = | 312 idlModel.FieldType fieldType = |
| 304 new idlModel.FieldType(type.name.name, isList); | 313 new idlModel.FieldType(type.name.name, isList); |
| 305 cls.fields.add(new idlModel.FieldDeclaration( | 314 cls.allFields.add(new idlModel.FieldDeclaration( |
| 306 doc, classMember.name.name, fieldType, id)); | 315 doc, classMember.name.name, fieldType, id, isDeprecated)); |
| 307 } else if (classMember is ConstructorDeclaration && | 316 } else if (classMember is ConstructorDeclaration && |
| 308 classMember.name.name == 'fromBuffer') { | 317 classMember.name.name == 'fromBuffer') { |
| 309 // Ignore `fromBuffer` declarations; they simply forward to the | 318 // Ignore `fromBuffer` declarations; they simply forward to the |
| 310 // read functions generated by [_generateReadFunction]. | 319 // read functions generated by [_generateReadFunction]. |
| 311 } else { | 320 } else { |
| 312 throw new Exception('Unexpected class member `$classMember`'); | 321 throw new Exception('Unexpected class member `$classMember`'); |
| 313 } | 322 } |
| 314 } | 323 } |
| 315 } else if (decl is EnumDeclaration) { | 324 } else if (decl is EnumDeclaration) { |
| 316 String doc = _getNodeDoc(lineInfo, decl); | 325 String doc = _getNodeDoc(lineInfo, decl); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 out('${value.name}$suffix'); | 390 out('${value.name}$suffix'); |
| 382 } | 391 } |
| 383 }); | 392 }); |
| 384 out('}'); | 393 out('}'); |
| 385 } | 394 } |
| 386 for (idlModel.ClassDeclaration cls in _idl.classes.values) { | 395 for (idlModel.ClassDeclaration cls in _idl.classes.values) { |
| 387 out(); | 396 out(); |
| 388 outDoc(cls.documentation); | 397 outDoc(cls.documentation); |
| 389 out('table ${cls.name} {'); | 398 out('table ${cls.name} {'); |
| 390 indent(() { | 399 indent(() { |
| 391 for (int i = 0; i < cls.fields.length; i++) { | 400 for (int i = 0; i < cls.allFields.length; i++) { |
| 392 idlModel.FieldDeclaration field = cls.fields[i]; | 401 idlModel.FieldDeclaration field = cls.allFields[i]; |
| 393 if (i != 0) { | 402 if (i != 0) { |
| 394 out(); | 403 out(); |
| 395 } | 404 } |
| 396 outDoc(field.documentation); | 405 outDoc(field.documentation); |
| 397 out('${field.name}:${fbsType(field.type)} (id: ${field.id});'); | 406 List<String> attributes = <String>['id: ${field.id}']; |
| 407 if (field.isDeprecated) { |
| 408 attributes.add('deprecated'); |
| 409 } |
| 410 String attrText = attributes.join(', '); |
| 411 out('${field.name}:${fbsType(field.type)} ($attrText);'); |
| 398 } | 412 } |
| 399 }); | 413 }); |
| 400 out('}'); | 414 out('}'); |
| 401 } | 415 } |
| 402 out(); | 416 out(); |
| 403 // Standard flatbuffers only support one root type. We support multiple | 417 // Standard flatbuffers only support one root type. We support multiple |
| 404 // root types. For now work around this by forcing PackageBundle to be the | 418 // root types. For now work around this by forcing PackageBundle to be the |
| 405 // root type. TODO(paulberry): come up with a better solution. | 419 // root type. TODO(paulberry): come up with a better solution. |
| 406 idlModel.ClassDeclaration rootType = _idl.classes['PackageBundle']; | 420 idlModel.ClassDeclaration rootType = _idl.classes['PackageBundle']; |
| 407 out('root_type ${rootType.name};'); | 421 out('root_type ${rootType.name};'); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 out('bool _finished = false;'); | 531 out('bool _finished = false;'); |
| 518 // Generate fields. | 532 // Generate fields. |
| 519 out(); | 533 out(); |
| 520 for (idlModel.FieldDeclaration field in cls.fields) { | 534 for (idlModel.FieldDeclaration field in cls.fields) { |
| 521 String fieldName = field.name; | 535 String fieldName = field.name; |
| 522 idlModel.FieldType type = field.type; | 536 idlModel.FieldType type = field.type; |
| 523 String typeStr = encodedType(type); | 537 String typeStr = encodedType(type); |
| 524 out('$typeStr _$fieldName;'); | 538 out('$typeStr _$fieldName;'); |
| 525 } | 539 } |
| 526 // Generate getters and setters. | 540 // Generate getters and setters. |
| 527 for (idlModel.FieldDeclaration field in cls.fields) { | 541 for (idlModel.FieldDeclaration field in cls.allFields) { |
| 528 String fieldName = field.name; | 542 String fieldName = field.name; |
| 529 idlModel.FieldType fieldType = field.type; | 543 idlModel.FieldType fieldType = field.type; |
| 530 String typeStr = encodedType(fieldType); | 544 String typeStr = encodedType(fieldType); |
| 531 String def = defaultValue(fieldType, true); | 545 String def = defaultValue(fieldType, true); |
| 532 String defSuffix = def == null ? '' : ' ??= $def'; | 546 String defSuffix = def == null ? '' : ' ??= $def'; |
| 533 out(); | 547 out(); |
| 534 out('@override'); | 548 out('@override'); |
| 535 out('$typeStr get $fieldName => _$fieldName$defSuffix;'); | 549 if (field.isDeprecated) { |
| 536 out(); | 550 out('$typeStr get $fieldName => $_throwDeprecated;'); |
| 537 outDoc(field.documentation); | 551 } else { |
| 538 constructorParams.add('$typeStr $fieldName'); | 552 out('$typeStr get $fieldName => _$fieldName$defSuffix;'); |
| 539 out('void set $fieldName($typeStr _value) {'); | 553 out(); |
| 540 indent(() { | 554 outDoc(field.documentation); |
| 541 String stateFieldName = '_' + fieldName; | 555 constructorParams.add('$typeStr $fieldName'); |
| 542 out('assert(!_finished);'); | 556 out('void set $fieldName($typeStr _value) {'); |
| 543 // Validate that int(s) are non-negative. | 557 indent(() { |
| 544 if (fieldType.typeName == 'int') { | 558 String stateFieldName = '_' + fieldName; |
| 545 if (!fieldType.isList) { | 559 out('assert(!_finished);'); |
| 546 out('assert(_value == null || _value >= 0);'); | 560 // Validate that int(s) are non-negative. |
| 547 } else { | 561 if (fieldType.typeName == 'int') { |
| 548 out('assert(_value == null || _value.every((e) => e >= 0));'); | 562 if (!fieldType.isList) { |
| 563 out('assert(_value == null || _value >= 0);'); |
| 564 } else { |
| 565 out('assert(_value == null || _value.every((e) => e >= 0));'); |
| 566 } |
| 549 } | 567 } |
| 550 } | 568 // Set the value. |
| 551 // Set the value. | 569 out('$stateFieldName = _value;'); |
| 552 out('$stateFieldName = _value;'); | 570 }); |
| 553 }); | 571 out('}'); |
| 554 out('}'); | 572 } |
| 555 } | 573 } |
| 556 // Generate constructor. | 574 // Generate constructor. |
| 557 out(); | 575 out(); |
| 558 out('$builderName({${constructorParams.join(', ')}})'); | 576 out('$builderName({${constructorParams.join(', ')}})'); |
| 559 for (int i = 0; i < cls.fields.length; i++) { | 577 List<idlModel.FieldDeclaration> fields = cls.fields.toList(); |
| 560 idlModel.FieldDeclaration field = cls.fields[i]; | 578 for (int i = 0; i < fields.length; i++) { |
| 579 idlModel.FieldDeclaration field = fields[i]; |
| 561 String prefix = i == 0 ? ' : ' : ' '; | 580 String prefix = i == 0 ? ' : ' : ' '; |
| 562 String suffix = i == cls.fields.length - 1 ? ';' : ','; | 581 String suffix = i == fields.length - 1 ? ';' : ','; |
| 563 out('${prefix}_${field.name} = ${field.name}$suffix'); | 582 out('${prefix}_${field.name} = ${field.name}$suffix'); |
| 564 } | 583 } |
| 565 // Generate finish. | 584 // Generate finish. |
| 566 if (cls.isTopLevel) { | 585 if (cls.isTopLevel) { |
| 567 out(); | 586 out(); |
| 568 out('List<int> toBuffer() {'); | 587 out('List<int> toBuffer() {'); |
| 569 indent(() { | 588 indent(() { |
| 570 out('fb.Builder fbBuilder = new fb.Builder();'); | 589 out('fb.Builder fbBuilder = new fb.Builder();'); |
| 571 String fileId = cls.fileIdentifier == null | 590 String fileId = cls.fileIdentifier == null |
| 572 ? '' | 591 ? '' |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 707 out(); | 726 out(); |
| 708 out('$implName(this._bp);'); | 727 out('$implName(this._bp);'); |
| 709 out(); | 728 out(); |
| 710 // Write cache fields. | 729 // Write cache fields. |
| 711 for (idlModel.FieldDeclaration field in cls.fields) { | 730 for (idlModel.FieldDeclaration field in cls.fields) { |
| 712 String returnType = dartType(field.type); | 731 String returnType = dartType(field.type); |
| 713 String fieldName = field.name; | 732 String fieldName = field.name; |
| 714 out('$returnType _$fieldName;'); | 733 out('$returnType _$fieldName;'); |
| 715 } | 734 } |
| 716 // Write getters. | 735 // Write getters. |
| 717 for (idlModel.FieldDeclaration field in cls.fields) { | 736 for (idlModel.FieldDeclaration field in cls.allFields) { |
| 718 int index = field.id; | 737 int index = field.id; |
| 719 String fieldName = field.name; | 738 String fieldName = field.name; |
| 720 idlModel.FieldType type = field.type; | 739 idlModel.FieldType type = field.type; |
| 721 String typeName = type.typeName; | 740 String typeName = type.typeName; |
| 722 // Prepare "readCode" + "def" | 741 // Prepare "readCode" + "def" |
| 723 String readCode; | 742 String readCode; |
| 724 String def = defaultValue(type, false); | 743 String def = defaultValue(type, false); |
| 725 if (type.isList) { | 744 if (type.isList) { |
| 726 if (typeName == 'int') { | 745 if (typeName == 'int') { |
| 727 readCode = 'const fb.Uint32ListReader()'; | 746 readCode = 'const fb.Uint32ListReader()'; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 747 } else if (_idl.enums.containsKey(typeName)) { | 766 } else if (_idl.enums.containsKey(typeName)) { |
| 748 readCode = 'const _${typeName}Reader()'; | 767 readCode = 'const _${typeName}Reader()'; |
| 749 } else if (_idl.classes.containsKey(typeName)) { | 768 } else if (_idl.classes.containsKey(typeName)) { |
| 750 readCode = 'const _${typeName}Reader()'; | 769 readCode = 'const _${typeName}Reader()'; |
| 751 } | 770 } |
| 752 assert(readCode != null); | 771 assert(readCode != null); |
| 753 // Write the getter implementation. | 772 // Write the getter implementation. |
| 754 out(); | 773 out(); |
| 755 out('@override'); | 774 out('@override'); |
| 756 String returnType = dartType(type); | 775 String returnType = dartType(type); |
| 757 out('$returnType get $fieldName {'); | 776 if (field.isDeprecated) { |
| 758 indent(() { | 777 out('$returnType get $fieldName => $_throwDeprecated;'); |
| 759 String readExpr = '$readCode.vTableGet(_bp, $index, $def)'; | 778 } else { |
| 760 out('_$fieldName ??= $readExpr;'); | 779 out('$returnType get $fieldName {'); |
| 761 out('return _$fieldName;'); | 780 indent(() { |
| 762 }); | 781 String readExpr = '$readCode.vTableGet(_bp, $index, $def)'; |
| 763 out('}'); | 782 out('_$fieldName ??= $readExpr;'); |
| 783 out('return _$fieldName;'); |
| 784 }); |
| 785 out('}'); |
| 786 } |
| 764 } | 787 } |
| 765 }); | 788 }); |
| 766 out('}'); | 789 out('}'); |
| 767 } | 790 } |
| 768 | 791 |
| 769 void _generateMixin(idlModel.ClassDeclaration cls) { | 792 void _generateMixin(idlModel.ClassDeclaration cls) { |
| 770 String name = cls.name; | 793 String name = cls.name; |
| 771 String mixinName = '_${name}Mixin'; | 794 String mixinName = '_${name}Mixin'; |
| 772 out('abstract class $mixinName implements ${idlPrefix(name)} {'); | 795 out('abstract class $mixinName implements ${idlPrefix(name)} {'); |
| 773 indent(() { | 796 indent(() { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 868 return token.lexeme.split('\n').map((String line) { | 891 return token.lexeme.split('\n').map((String line) { |
| 869 if (line.startsWith(indent)) { | 892 if (line.startsWith(indent)) { |
| 870 line = line.substring(indent.length); | 893 line = line.substring(indent.length); |
| 871 } | 894 } |
| 872 return line; | 895 return line; |
| 873 }).join('\n'); | 896 }).join('\n'); |
| 874 } | 897 } |
| 875 return null; | 898 return null; |
| 876 } | 899 } |
| 877 } | 900 } |
| OLD | NEW |