| 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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 * Semantic model of the "IDL" input file. | 75 * Semantic model of the "IDL" input file. |
| 76 */ | 76 */ |
| 77 idlModel.Idl _idl; | 77 idlModel.Idl _idl; |
| 78 | 78 |
| 79 /** | 79 /** |
| 80 * Perform basic sanity checking of the IDL (over and above that done by | 80 * Perform basic sanity checking of the IDL (over and above that done by |
| 81 * [extractIdl]). | 81 * [extractIdl]). |
| 82 */ | 82 */ |
| 83 void checkIdl() { | 83 void checkIdl() { |
| 84 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) { | 84 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) { |
| 85 Map<int, String> idsUsed = <int, String>{}; |
| 85 for (idlModel.FieldDeclaration field in cls.fields) { | 86 for (idlModel.FieldDeclaration field in cls.fields) { |
| 86 String fieldName = field.name; | 87 String fieldName = field.name; |
| 87 idlModel.FieldType type = field.type; | 88 idlModel.FieldType type = field.type; |
| 88 if (type.isList) { | 89 if (type.isList) { |
| 89 if (_idl.classes.containsKey(type.typeName)) { | 90 if (_idl.classes.containsKey(type.typeName)) { |
| 90 // List of classes is ok | 91 // List of classes is ok |
| 91 } else if (_idl.enums.containsKey(type.typeName)) { | 92 } else if (_idl.enums.containsKey(type.typeName)) { |
| 92 // List of enums is ok | 93 // List of enums is ok |
| 93 } else if (type.typeName == 'int') { | 94 } else if (type.typeName == 'int') { |
| 94 // List of ints is ok | 95 // List of ints is ok |
| 95 } else if (type.typeName == 'double') { | 96 } else if (type.typeName == 'double') { |
| 96 // List of doubles is ok | 97 // List of doubles is ok |
| 97 } else if (type.typeName == 'String') { | 98 } else if (type.typeName == 'String') { |
| 98 // List of strings is ok | 99 // List of strings is ok |
| 99 } else { | 100 } else { |
| 100 throw new Exception( | 101 throw new Exception( |
| 101 '$name.$fieldName: illegal type (list of ${type.typeName})'); | 102 '$name.$fieldName: illegal type (list of ${type.typeName})'); |
| 102 } | 103 } |
| 103 } | 104 } |
| 105 if (idsUsed.containsKey(field.id)) { |
| 106 throw new Exception('$name.$fieldName: id ${field.id} already used by' |
| 107 ' ${idsUsed[field.id]}'); |
| 108 } |
| 109 idsUsed[field.id] = fieldName; |
| 110 } |
| 111 for (int i = 0; i < idsUsed.length; i++) { |
| 112 if (!idsUsed.containsKey(i)) { |
| 113 throw new Exception('$name: no field uses id $i'); |
| 114 } |
| 104 } | 115 } |
| 105 }); | 116 }); |
| 106 } | 117 } |
| 107 | 118 |
| 108 /** | 119 /** |
| 109 * Generate a string representing the Dart type which should be used to | 120 * Generate a string representing the Dart type which should be used to |
| 110 * represent [type] when deserialized. | 121 * represent [type] when deserialized. |
| 111 */ | 122 */ |
| 112 String dartType(idlModel.FieldType type) { | 123 String dartType(idlModel.FieldType type) { |
| 113 String baseType = idlPrefix(type.typeName); | 124 String baseType = idlPrefix(type.typeName); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 new idlModel.ClassDeclaration(doc, decl.name.name, isTopLevel); | 197 new idlModel.ClassDeclaration(doc, decl.name.name, isTopLevel); |
| 187 _idl.classes[cls.name] = cls; | 198 _idl.classes[cls.name] = cls; |
| 188 String expectedBase = 'base.SummaryClass'; | 199 String expectedBase = 'base.SummaryClass'; |
| 189 if (decl.extendsClause == null || | 200 if (decl.extendsClause == null || |
| 190 decl.extendsClause.superclass.name.name != expectedBase) { | 201 decl.extendsClause.superclass.name.name != expectedBase) { |
| 191 throw new Exception( | 202 throw new Exception( |
| 192 'Class `${cls.name}` needs to extend `$expectedBase`'); | 203 'Class `${cls.name}` needs to extend `$expectedBase`'); |
| 193 } | 204 } |
| 194 for (ClassMember classMember in decl.members) { | 205 for (ClassMember classMember in decl.members) { |
| 195 if (classMember is MethodDeclaration && classMember.isGetter) { | 206 if (classMember is MethodDeclaration && classMember.isGetter) { |
| 207 String desc = '${cls.name}.${classMember.name.name}'; |
| 196 TypeName type = classMember.returnType; | 208 TypeName type = classMember.returnType; |
| 197 if (type == null) { | 209 if (type == null) { |
| 198 throw new Exception('Class member needs a type: $classMember'); | 210 throw new Exception('Class member needs a type: $desc'); |
| 199 } | 211 } |
| 200 bool isList = false; | 212 bool isList = false; |
| 201 if (type.name.name == 'List' && | 213 if (type.name.name == 'List' && |
| 202 type.typeArguments != null && | 214 type.typeArguments != null && |
| 203 type.typeArguments.arguments.length == 1) { | 215 type.typeArguments.arguments.length == 1) { |
| 204 isList = true; | 216 isList = true; |
| 205 type = type.typeArguments.arguments[0]; | 217 type = type.typeArguments.arguments[0]; |
| 206 } | 218 } |
| 207 if (type.typeArguments != null) { | 219 if (type.typeArguments != null) { |
| 208 throw new Exception('Cannot handle type arguments in `$type`'); | 220 throw new Exception('Cannot handle type arguments in `$type`'); |
| 209 } | 221 } |
| 222 int id; |
| 223 for (Annotation annotation in classMember.metadata) { |
| 224 if (annotation.name.name == 'Id') { |
| 225 if (id != null) { |
| 226 throw new Exception( |
| 227 'Duplicate @id annotation ($classMember)'); |
| 228 } |
| 229 if (annotation.arguments.arguments.length != 1) { |
| 230 throw new Exception( |
| 231 '@Id must be passed exactly one argument ($desc)'); |
| 232 } |
| 233 Expression expression = annotation.arguments.arguments[0]; |
| 234 if (expression is IntegerLiteral) { |
| 235 id = expression.value; |
| 236 } else { |
| 237 throw new Exception( |
| 238 '@Id parameter must be an integer literal ($desc)'); |
| 239 } |
| 240 } |
| 241 } |
| 242 if (id == null) { |
| 243 throw new Exception('Missing @id annotation ($desc)'); |
| 244 } |
| 210 String doc = _getNodeDoc(lineInfo, classMember); | 245 String doc = _getNodeDoc(lineInfo, classMember); |
| 211 idlModel.FieldType fieldType = | 246 idlModel.FieldType fieldType = |
| 212 new idlModel.FieldType(type.name.name, isList); | 247 new idlModel.FieldType(type.name.name, isList); |
| 213 cls.fields.add(new idlModel.FieldDeclaration( | 248 cls.fields.add(new idlModel.FieldDeclaration( |
| 214 doc, classMember.name.name, fieldType)); | 249 doc, classMember.name.name, fieldType, id)); |
| 215 } else if (classMember is ConstructorDeclaration && | 250 } else if (classMember is ConstructorDeclaration && |
| 216 classMember.name.name == 'fromBuffer') { | 251 classMember.name.name == 'fromBuffer') { |
| 217 // Ignore `fromBuffer` declarations; they simply forward to the | 252 // Ignore `fromBuffer` declarations; they simply forward to the |
| 218 // read functions generated by [_generateReadFunction]. | 253 // read functions generated by [_generateReadFunction]. |
| 219 } else { | 254 } else { |
| 220 throw new Exception('Unexpected class member `$classMember`'); | 255 throw new Exception('Unexpected class member `$classMember`'); |
| 221 } | 256 } |
| 222 } | 257 } |
| 223 } else if (decl is EnumDeclaration) { | 258 } else if (decl is EnumDeclaration) { |
| 224 String doc = _getNodeDoc(lineInfo, decl); | 259 String doc = _getNodeDoc(lineInfo, decl); |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 out('return fbBuilder.finish(finish(fbBuilder));'); | 432 out('return fbBuilder.finish(finish(fbBuilder));'); |
| 398 }); | 433 }); |
| 399 out('}'); | 434 out('}'); |
| 400 } | 435 } |
| 401 out(); | 436 out(); |
| 402 out('fb.Offset finish(fb.Builder fbBuilder) {'); | 437 out('fb.Offset finish(fb.Builder fbBuilder) {'); |
| 403 indent(() { | 438 indent(() { |
| 404 out('assert(!_finished);'); | 439 out('assert(!_finished);'); |
| 405 out('_finished = true;'); | 440 out('_finished = true;'); |
| 406 // Write objects and remember Offset(s). | 441 // Write objects and remember Offset(s). |
| 407 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { | 442 for (idlModel.FieldDeclaration field in cls.fields) { |
| 408 idlModel.FieldType fieldType = field.type; | 443 idlModel.FieldType fieldType = field.type; |
| 409 String offsetName = 'offset_' + field.name; | 444 String offsetName = 'offset_' + field.name; |
| 410 if (fieldType.isList || | 445 if (fieldType.isList || |
| 411 fieldType.typeName == 'String' || | 446 fieldType.typeName == 'String' || |
| 412 _idl.classes.containsKey(fieldType.typeName)) { | 447 _idl.classes.containsKey(fieldType.typeName)) { |
| 413 out('fb.Offset $offsetName;'); | 448 out('fb.Offset $offsetName;'); |
| 414 } | 449 } |
| 415 }); | 450 } |
| 416 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { | 451 for (idlModel.FieldDeclaration field in cls.fields) { |
| 417 idlModel.FieldType fieldType = field.type; | 452 idlModel.FieldType fieldType = field.type; |
| 418 String valueName = '_' + field.name; | 453 String valueName = '_' + field.name; |
| 419 String offsetName = 'offset_' + field.name; | 454 String offsetName = 'offset_' + field.name; |
| 420 String condition; | 455 String condition; |
| 421 String writeCode; | 456 String writeCode; |
| 422 if (fieldType.isList) { | 457 if (fieldType.isList) { |
| 423 condition = ' || $valueName.isEmpty'; | 458 condition = ' || $valueName.isEmpty'; |
| 424 if (_idl.classes.containsKey(fieldType.typeName)) { | 459 if (_idl.classes.containsKey(fieldType.typeName)) { |
| 425 String itemCode = 'b.finish(fbBuilder)'; | 460 String itemCode = 'b.finish(fbBuilder)'; |
| 426 String listCode = '$valueName.map((b) => $itemCode).toList()'; | 461 String listCode = '$valueName.map((b) => $itemCode).toList()'; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 450 if (condition == null) { | 485 if (condition == null) { |
| 451 out('if ($valueName != null) {'); | 486 out('if ($valueName != null) {'); |
| 452 } else { | 487 } else { |
| 453 out('if (!($valueName == null$condition)) {'); | 488 out('if (!($valueName == null$condition)) {'); |
| 454 } | 489 } |
| 455 indent(() { | 490 indent(() { |
| 456 out(writeCode); | 491 out(writeCode); |
| 457 }); | 492 }); |
| 458 out('}'); | 493 out('}'); |
| 459 } | 494 } |
| 460 }); | 495 } |
| 461 // Write the table. | 496 // Write the table. |
| 462 out('fbBuilder.startTable();'); | 497 out('fbBuilder.startTable();'); |
| 463 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { | 498 for (idlModel.FieldDeclaration field in cls.fields) { |
| 499 int index = field.id; |
| 464 idlModel.FieldType fieldType = field.type; | 500 idlModel.FieldType fieldType = field.type; |
| 465 String valueName = '_' + field.name; | 501 String valueName = '_' + field.name; |
| 466 String condition = '$valueName != null'; | 502 String condition = '$valueName != null'; |
| 467 String writeCode; | 503 String writeCode; |
| 468 if (fieldType.isList || | 504 if (fieldType.isList || |
| 469 fieldType.typeName == 'String' || | 505 fieldType.typeName == 'String' || |
| 470 _idl.classes.containsKey(fieldType.typeName)) { | 506 _idl.classes.containsKey(fieldType.typeName)) { |
| 471 String offsetName = 'offset_' + field.name; | 507 String offsetName = 'offset_' + field.name; |
| 472 condition = '$offsetName != null'; | 508 condition = '$offsetName != null'; |
| 473 writeCode = 'fbBuilder.addOffset($index, $offsetName);'; | 509 writeCode = 'fbBuilder.addOffset($index, $offsetName);'; |
| 474 } else if (fieldType.typeName == 'bool') { | 510 } else if (fieldType.typeName == 'bool') { |
| 475 condition = '$valueName == true'; | 511 condition = '$valueName == true'; |
| 476 writeCode = 'fbBuilder.addBool($index, true);'; | 512 writeCode = 'fbBuilder.addBool($index, true);'; |
| 477 } else if (fieldType.typeName == 'int') { | 513 } else if (fieldType.typeName == 'int') { |
| 478 condition += ' && $valueName != ${defaultValue(fieldType, true)}'; | 514 condition += ' && $valueName != ${defaultValue(fieldType, true)}'; |
| 479 writeCode = 'fbBuilder.addUint32($index, $valueName);'; | 515 writeCode = 'fbBuilder.addUint32($index, $valueName);'; |
| 480 } else if (_idl.enums.containsKey(fieldType.typeName)) { | 516 } else if (_idl.enums.containsKey(fieldType.typeName)) { |
| 481 condition += ' && $valueName != ${defaultValue(fieldType, true)}'; | 517 condition += ' && $valueName != ${defaultValue(fieldType, true)}'; |
| 482 writeCode = 'fbBuilder.addUint32($index, $valueName.index);'; | 518 writeCode = 'fbBuilder.addUint32($index, $valueName.index);'; |
| 483 } | 519 } |
| 484 if (writeCode == null) { | 520 if (writeCode == null) { |
| 485 throw new UnimplementedError('Writing type ${fieldType.typeName}'); | 521 throw new UnimplementedError('Writing type ${fieldType.typeName}'); |
| 486 } | 522 } |
| 487 out('if ($condition) {'); | 523 out('if ($condition) {'); |
| 488 indent(() { | 524 indent(() { |
| 489 out(writeCode); | 525 out(writeCode); |
| 490 }); | 526 }); |
| 491 out('}'); | 527 out('}'); |
| 492 }); | 528 } |
| 493 out('return fbBuilder.endTable();'); | 529 out('return fbBuilder.endTable();'); |
| 494 }); | 530 }); |
| 495 out('}'); | 531 out('}'); |
| 496 }); | 532 }); |
| 497 out('}'); | 533 out('}'); |
| 498 } | 534 } |
| 499 | 535 |
| 500 void _generateEnumReader(idlModel.EnumDeclaration enm) { | 536 void _generateEnumReader(idlModel.EnumDeclaration enm) { |
| 501 String name = enm.name; | 537 String name = enm.name; |
| 502 String readerName = '_${name}Reader'; | 538 String readerName = '_${name}Reader'; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 529 out(); | 565 out(); |
| 530 out('$implName(this._bp);'); | 566 out('$implName(this._bp);'); |
| 531 out(); | 567 out(); |
| 532 // Write cache fields. | 568 // Write cache fields. |
| 533 for (idlModel.FieldDeclaration field in cls.fields) { | 569 for (idlModel.FieldDeclaration field in cls.fields) { |
| 534 String returnType = dartType(field.type); | 570 String returnType = dartType(field.type); |
| 535 String fieldName = field.name; | 571 String fieldName = field.name; |
| 536 out('$returnType _$fieldName;'); | 572 out('$returnType _$fieldName;'); |
| 537 } | 573 } |
| 538 // Write getters. | 574 // Write getters. |
| 539 cls.fields.asMap().forEach((index, field) { | 575 for (idlModel.FieldDeclaration field in cls.fields) { |
| 576 int index = field.id; |
| 540 String fieldName = field.name; | 577 String fieldName = field.name; |
| 541 idlModel.FieldType type = field.type; | 578 idlModel.FieldType type = field.type; |
| 542 String typeName = type.typeName; | 579 String typeName = type.typeName; |
| 543 // Prepare "readCode" + "def" | 580 // Prepare "readCode" + "def" |
| 544 String readCode; | 581 String readCode; |
| 545 String def = defaultValue(type, false); | 582 String def = defaultValue(type, false); |
| 546 if (type.isList) { | 583 if (type.isList) { |
| 547 if (typeName == 'int') { | 584 if (typeName == 'int') { |
| 548 String itemCode = 'const fb.Uint32Reader()'; | 585 String itemCode = 'const fb.Uint32Reader()'; |
| 549 readCode = 'const fb.ListReader<int>($itemCode)'; | 586 readCode = 'const fb.ListReader<int>($itemCode)'; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 576 out(); | 613 out(); |
| 577 out('@override'); | 614 out('@override'); |
| 578 String returnType = dartType(type); | 615 String returnType = dartType(type); |
| 579 out('$returnType get $fieldName {'); | 616 out('$returnType get $fieldName {'); |
| 580 indent(() { | 617 indent(() { |
| 581 String readExpr = '$readCode.vTableGet(_bp, $index, $def)'; | 618 String readExpr = '$readCode.vTableGet(_bp, $index, $def)'; |
| 582 out('_$fieldName ??= $readExpr;'); | 619 out('_$fieldName ??= $readExpr;'); |
| 583 out('return _$fieldName;'); | 620 out('return _$fieldName;'); |
| 584 }); | 621 }); |
| 585 out('}'); | 622 out('}'); |
| 586 }); | 623 } |
| 587 }); | 624 }); |
| 588 out('}'); | 625 out('}'); |
| 589 } | 626 } |
| 590 | 627 |
| 591 void _generateMixin(idlModel.ClassDeclaration cls) { | 628 void _generateMixin(idlModel.ClassDeclaration cls) { |
| 592 String name = cls.name; | 629 String name = cls.name; |
| 593 String mixinName = '_${name}Mixin'; | 630 String mixinName = '_${name}Mixin'; |
| 594 out('abstract class $mixinName implements ${idlPrefix(name)} {'); | 631 out('abstract class $mixinName implements ${idlPrefix(name)} {'); |
| 595 indent(() { | 632 indent(() { |
| 596 // Write toMap(). | 633 // Write toMap(). |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 return token.lexeme.split('\n').map((String line) { | 684 return token.lexeme.split('\n').map((String line) { |
| 648 if (line.startsWith(indent)) { | 685 if (line.startsWith(indent)) { |
| 649 line = line.substring(indent.length); | 686 line = line.substring(indent.length); |
| 650 } | 687 } |
| 651 return line; | 688 return line; |
| 652 }).join('\n'); | 689 }).join('\n'); |
| 653 } | 690 } |
| 654 return null; | 691 return null; |
| 655 } | 692 } |
| 656 } | 693 } |
| OLD | NEW |