Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(935)

Side by Side Diff: packages/analyzer/tool/summary/generate.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * This file contains code to generate serialization/deserialization logic for
7 * summaries based on an "IDL" description of the summary format (written in
8 * stylized Dart).
9 *
10 * For each class in the "IDL" input, two corresponding classes are generated:
11 * - A class with the same name which represents deserialized summary data in
12 * memory. This class has read-only semantics.
13 * - A "builder" class which can be used to generate serialized summary data.
14 * This class has write-only semantics.
15 *
16 * Each of the "builder" classes has a single `finish` method which writes
17 * the entity being built into the given FlatBuffer and returns the `Offset`
18 * reference to it.
19 */
20 library analyzer.tool.summary.generate;
21
22 import 'dart:convert';
23 import 'dart:io' hide File;
24
25 import 'package:analyzer/analyzer.dart';
26 import 'package:analyzer/dart/ast/token.dart';
27 import 'package:analyzer/error/listener.dart';
28 import 'package:analyzer/file_system/file_system.dart';
29 import 'package:analyzer/file_system/physical_file_system.dart';
30 import 'package:analyzer/src/codegen/tools.dart';
31 import 'package:analyzer/src/dart/scanner/reader.dart';
32 import 'package:analyzer/src/dart/scanner/scanner.dart';
33 import 'package:analyzer/src/generated/parser.dart';
34 import 'package:analyzer/src/generated/source.dart';
35 import 'package:path/path.dart';
36
37 import 'idl_model.dart' as idlModel;
38
39 main() {
40 String script = Platform.script.toFilePath(windows: Platform.isWindows);
41 String pkgPath = normalize(join(dirname(script), '..', '..'));
42 GeneratedContent.generateAll(pkgPath, allTargets);
43 }
44
45 final List<GeneratedContent> allTargets = <GeneratedContent>[
46 formatTarget,
47 schemaTarget
48 ];
49
50 final GeneratedFile formatTarget =
51 new GeneratedFile('lib/src/summary/format.dart', (String pkgPath) {
52 _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath);
53 codeGenerator.generateFormatCode();
54 return codeGenerator._outBuffer.toString();
55 });
56
57 final GeneratedFile schemaTarget =
58 new GeneratedFile('lib/src/summary/format.fbs', (String pkgPath) {
59 _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath);
60 codeGenerator.generateFlatBufferSchema();
61 return codeGenerator._outBuffer.toString();
62 });
63
64 typedef String _StringToString(String s);
65
66 class _CodeGenerator {
67 static const String _throwDeprecated =
68 "throw new UnimplementedError('attempt to access deprecated field')";
69
70 /**
71 * Buffer in which generated code is accumulated.
72 */
73 final StringBuffer _outBuffer = new StringBuffer();
74
75 /**
76 * Current indentation level.
77 */
78 String _indentation = '';
79
80 /**
81 * Semantic model of the "IDL" input file.
82 */
83 idlModel.Idl _idl;
84
85 _CodeGenerator(String pkgPath) {
86 // Parse the input "IDL" file.
87 PhysicalResourceProvider provider = new PhysicalResourceProvider(
88 PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS);
89 String idlPath = join(pkgPath, 'lib', 'src', 'summary', 'idl.dart');
90 File idlFile = provider.getFile(idlPath);
91 Source idlSource = provider.getFile(idlPath).createSource();
92 String idlText = idlFile.readAsStringSync();
93 BooleanErrorListener errorListener = new BooleanErrorListener();
94 CharacterReader idlReader = new CharSequenceReader(idlText);
95 Scanner scanner = new Scanner(idlSource, idlReader, errorListener);
96 Token tokenStream = scanner.tokenize();
97 LineInfo lineInfo = new LineInfo(scanner.lineStarts);
98 Parser parser = new Parser(idlSource, new BooleanErrorListener());
99 CompilationUnit idlParsed = parser.parseCompilationUnit(tokenStream);
100 // Extract a description of the IDL and make sure it is valid.
101 extractIdl(lineInfo, idlParsed);
102 checkIdl();
103 }
104
105 /**
106 * Perform basic sanity checking of the IDL (over and above that done by
107 * [extractIdl]).
108 */
109 void checkIdl() {
110 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
111 if (cls.fileIdentifier != null) {
112 if (cls.fileIdentifier.length != 4) {
113 throw new Exception('$name: file identifier must be 4 characters');
114 }
115 for (int i = 0; i < cls.fileIdentifier.length; i++) {
116 if (cls.fileIdentifier.codeUnitAt(i) >= 256) {
117 throw new Exception(
118 '$name: file identifier must be encodable as Latin-1');
119 }
120 }
121 }
122 Map<int, String> idsUsed = <int, String>{};
123 for (idlModel.FieldDeclaration field in cls.allFields) {
124 String fieldName = field.name;
125 idlModel.FieldType type = field.type;
126 if (type.isList) {
127 if (_idl.classes.containsKey(type.typeName)) {
128 // List of classes is ok
129 } else if (_idl.enums.containsKey(type.typeName)) {
130 // List of enums is ok
131 } else if (type.typeName == 'bool') {
132 // List of booleans is ok
133 } else if (type.typeName == 'int') {
134 // List of ints is ok
135 } else if (type.typeName == 'double') {
136 // List of doubles is ok
137 } else if (type.typeName == 'String') {
138 // List of strings is ok
139 } else {
140 throw new Exception(
141 '$name.$fieldName: illegal type (list of ${type.typeName})');
142 }
143 }
144 if (idsUsed.containsKey(field.id)) {
145 throw new Exception('$name.$fieldName: id ${field.id} already used by'
146 ' ${idsUsed[field.id]}');
147 }
148 idsUsed[field.id] = fieldName;
149 }
150 for (int i = 0; i < idsUsed.length; i++) {
151 if (!idsUsed.containsKey(i)) {
152 throw new Exception('$name: no field uses id $i');
153 }
154 }
155 });
156 }
157
158 /**
159 * Generate a string representing the Dart type which should be used to
160 * represent [type] when deserialized.
161 */
162 String dartType(idlModel.FieldType type) {
163 String baseType = idlPrefix(type.typeName);
164 if (type.isList) {
165 return 'List<$baseType>';
166 } else {
167 return baseType;
168 }
169 }
170
171 /**
172 * Generate a Dart expression representing the default value for a field
173 * having the given [type], or `null` if there is no default value.
174 *
175 * If [builder] is `true`, the returned type should be appropriate for use in
176 * a builder class.
177 */
178 String defaultValue(idlModel.FieldType type, bool builder) {
179 if (type.isList) {
180 if (builder) {
181 idlModel.FieldType elementType =
182 new idlModel.FieldType(type.typeName, false);
183 return '<${encodedType(elementType)}>[]';
184 } else {
185 return 'const <${idlPrefix(type.typeName)}>[]';
186 }
187 } else if (_idl.enums.containsKey(type.typeName)) {
188 return '${idlPrefix(type.typeName)}.'
189 '${_idl.enums[type.typeName].values[0].name}';
190 } else if (type.typeName == 'int') {
191 return '0';
192 } else if (type.typeName == 'String') {
193 return "''";
194 } else if (type.typeName == 'bool') {
195 return 'false';
196 } else {
197 return null;
198 }
199 }
200
201 /**
202 * Generate a string representing the Dart type which should be used to
203 * represent [type] while building a serialized data structure.
204 */
205 String encodedType(idlModel.FieldType type) {
206 String typeStr;
207 if (_idl.classes.containsKey(type.typeName)) {
208 typeStr = '${type.typeName}Builder';
209 } else {
210 typeStr = idlPrefix(type.typeName);
211 }
212 if (type.isList) {
213 return 'List<$typeStr>';
214 } else {
215 return typeStr;
216 }
217 }
218
219 /**
220 * Process the AST in [idlParsed] and store the resulting semantic model in
221 * [_idl]. Also perform some error checking.
222 */
223 void extractIdl(LineInfo lineInfo, CompilationUnit idlParsed) {
224 _idl = new idlModel.Idl();
225 for (CompilationUnitMember decl in idlParsed.declarations) {
226 if (decl is ClassDeclaration) {
227 bool isTopLevel = false;
228 String fileIdentifier;
229 String clsName = decl.name.name;
230 for (Annotation annotation in decl.metadata) {
231 if (annotation.arguments != null &&
232 annotation.name.name == 'TopLevel' &&
233 annotation.constructorName == null) {
234 isTopLevel = true;
235 if (annotation.arguments == null) {
236 throw new Exception(
237 'Class `$clsName`: TopLevel requires parenthesis');
238 }
239 if (annotation.constructorName != null) {
240 throw new Exception(
241 "Class `$clsName`: TopLevel doesn't have named constructors");
242 }
243 if (annotation.arguments.arguments.length == 1) {
244 Expression arg = annotation.arguments.arguments[0];
245 if (arg is StringLiteral) {
246 fileIdentifier = arg.stringValue;
247 } else {
248 throw new Exception(
249 'Class `$clsName`: TopLevel argument must be a string'
250 ' literal');
251 }
252 } else if (annotation.arguments.arguments.length != 0) {
253 throw new Exception(
254 'Class `$clsName`: TopLevel requires 0 or 1 arguments');
255 }
256 }
257 }
258 String doc = _getNodeDoc(lineInfo, decl);
259 idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration(
260 doc, clsName, isTopLevel, fileIdentifier);
261 _idl.classes[clsName] = cls;
262 String expectedBase = 'base.SummaryClass';
263 if (decl.extendsClause == null ||
264 decl.extendsClause.superclass.name.name != expectedBase) {
265 throw new Exception(
266 'Class `$clsName` needs to extend `$expectedBase`');
267 }
268 for (ClassMember classMember in decl.members) {
269 if (classMember is MethodDeclaration && classMember.isGetter) {
270 String desc = '$clsName.${classMember.name.name}';
271 TypeName type = classMember.returnType;
272 if (type == null) {
273 throw new Exception('Class member needs a type: $desc');
274 }
275 bool isList = false;
276 if (type.name.name == 'List' &&
277 type.typeArguments != null &&
278 type.typeArguments.arguments.length == 1) {
279 isList = true;
280 type = type.typeArguments.arguments[0];
281 }
282 if (type.typeArguments != null) {
283 throw new Exception('Cannot handle type arguments in `$type`');
284 }
285 int id;
286 bool isDeprecated = false;
287 bool isInformative = false;
288 for (Annotation annotation in classMember.metadata) {
289 if (annotation.name.name == 'Id') {
290 if (id != null) {
291 throw new Exception(
292 'Duplicate @id annotation ($classMember)');
293 }
294 if (annotation.arguments.arguments.length != 1) {
295 throw new Exception(
296 '@Id must be passed exactly one argument ($desc)');
297 }
298 Expression expression = annotation.arguments.arguments[0];
299 if (expression is IntegerLiteral) {
300 id = expression.value;
301 } else {
302 throw new Exception(
303 '@Id parameter must be an integer literal ($desc)');
304 }
305 } else if (annotation.name.name == 'deprecated') {
306 if (annotation.arguments != null) {
307 throw new Exception('@deprecated does not take args ($desc)');
308 }
309 isDeprecated = true;
310 } else if (annotation.name.name == 'informative') {
311 isInformative = true;
312 }
313 }
314 if (id == null) {
315 throw new Exception('Missing @id annotation ($desc)');
316 }
317 String doc = _getNodeDoc(lineInfo, classMember);
318 idlModel.FieldType fieldType =
319 new idlModel.FieldType(type.name.name, isList);
320 cls.allFields.add(new idlModel.FieldDeclaration(
321 doc,
322 classMember.name.name,
323 fieldType,
324 id,
325 isDeprecated,
326 isInformative));
327 } else if (classMember is ConstructorDeclaration &&
328 classMember.name.name == 'fromBuffer') {
329 // Ignore `fromBuffer` declarations; they simply forward to the
330 // read functions generated by [_generateReadFunction].
331 } else {
332 throw new Exception('Unexpected class member `$classMember`');
333 }
334 }
335 } else if (decl is EnumDeclaration) {
336 String doc = _getNodeDoc(lineInfo, decl);
337 idlModel.EnumDeclaration enm =
338 new idlModel.EnumDeclaration(doc, decl.name.name);
339 _idl.enums[enm.name] = enm;
340 for (EnumConstantDeclaration constDecl in decl.constants) {
341 String doc = _getNodeDoc(lineInfo, constDecl);
342 enm.values
343 .add(new idlModel.EnumValueDeclaration(doc, constDecl.name.name));
344 }
345 } else if (decl is TopLevelVariableDeclaration) {
346 // Ignore top level variable declarations; they are present just to make
347 // the IDL analyze without warnings.
348 } else {
349 throw new Exception('Unexpected declaration `$decl`');
350 }
351 }
352 }
353
354 /**
355 * Generate a string representing the FlatBuffer schema type which should be
356 * used to represent [type].
357 */
358 String fbsType(idlModel.FieldType type) {
359 String typeStr;
360 switch (type.typeName) {
361 case 'bool':
362 typeStr = 'bool';
363 break;
364 case 'double':
365 typeStr = 'double';
366 break;
367 case 'int':
368 typeStr = 'uint';
369 break;
370 case 'String':
371 typeStr = 'string';
372 break;
373 default:
374 typeStr = type.typeName;
375 break;
376 }
377 if (type.isList) {
378 // FlatBuffers don't natively support a packed list of booleans, so we
379 // treat it as a list of unsigned bytes, which is a compatible data
380 // structure.
381 if (typeStr == 'bool') {
382 typeStr = 'ubyte';
383 }
384 return '[$typeStr]';
385 } else {
386 return typeStr;
387 }
388 }
389
390 /**
391 * Entry point to the code generator when generating the "format.fbs" file.
392 */
393 void generateFlatBufferSchema() {
394 outputHeader();
395 for (idlModel.EnumDeclaration enm in _idl.enums.values) {
396 out();
397 outDoc(enm.documentation);
398 out('enum ${enm.name} : byte {');
399 indent(() {
400 for (int i = 0; i < enm.values.length; i++) {
401 idlModel.EnumValueDeclaration value = enm.values[i];
402 if (i != 0) {
403 out();
404 }
405 String suffix = i < enm.values.length - 1 ? ',' : '';
406 outDoc(value.documentation);
407 out('${value.name}$suffix');
408 }
409 });
410 out('}');
411 }
412 for (idlModel.ClassDeclaration cls in _idl.classes.values) {
413 out();
414 outDoc(cls.documentation);
415 out('table ${cls.name} {');
416 indent(() {
417 for (int i = 0; i < cls.allFields.length; i++) {
418 idlModel.FieldDeclaration field = cls.allFields[i];
419 if (i != 0) {
420 out();
421 }
422 outDoc(field.documentation);
423 List<String> attributes = <String>['id: ${field.id}'];
424 if (field.isDeprecated) {
425 attributes.add('deprecated');
426 }
427 String attrText = attributes.join(', ');
428 out('${field.name}:${fbsType(field.type)} ($attrText);');
429 }
430 });
431 out('}');
432 }
433 out();
434 // Standard flatbuffers only support one root type. We support multiple
435 // root types. For now work around this by forcing PackageBundle to be the
436 // root type. TODO(paulberry): come up with a better solution.
437 idlModel.ClassDeclaration rootType = _idl.classes['PackageBundle'];
438 out('root_type ${rootType.name};');
439 if (rootType.fileIdentifier != null) {
440 out();
441 out('file_identifier ${quoted(rootType.fileIdentifier)};');
442 }
443 }
444
445 /**
446 * Entry point to the code generator when generating the "format.dart" file.
447 */
448 void generateFormatCode() {
449 outputHeader();
450 out('library analyzer.src.summary.format;');
451 out();
452 out("import 'flat_buffers.dart' as fb;");
453 out("import 'idl.dart' as idl;");
454 out("import 'dart:convert' as convert;");
455 out("import 'api_signature.dart' as api_sig;");
456 out();
457 for (idlModel.EnumDeclaration enm in _idl.enums.values) {
458 _generateEnumReader(enm);
459 out();
460 }
461 for (idlModel.ClassDeclaration cls in _idl.classes.values) {
462 _generateBuilder(cls);
463 out();
464 if (cls.isTopLevel) {
465 _generateReadFunction(cls);
466 out();
467 }
468 _generateReader(cls);
469 out();
470 _generateImpl(cls);
471 out();
472 _generateMixin(cls);
473 out();
474 }
475 }
476
477 /**
478 * Add the prefix `idl.` to a type name, unless that type name is the name of
479 * a built-in type.
480 */
481 String idlPrefix(String s) {
482 switch (s) {
483 case 'bool':
484 case 'double':
485 case 'int':
486 case 'String':
487 return s;
488 default:
489 return 'idl.$s';
490 }
491 }
492
493 /**
494 * Execute [callback] with two spaces added to [_indentation].
495 */
496 void indent(void callback()) {
497 String oldIndentation = _indentation;
498 try {
499 _indentation += ' ';
500 callback();
501 } finally {
502 _indentation = oldIndentation;
503 }
504 }
505
506 /**
507 * Add the string [s] to the output as a single line, indenting as
508 * appropriate.
509 */
510 void out([String s = '']) {
511 if (s == '') {
512 _outBuffer.writeln('');
513 } else {
514 _outBuffer.writeln('$_indentation$s');
515 }
516 }
517
518 void outDoc(String documentation) {
519 if (documentation != null) {
520 documentation.split('\n').forEach(out);
521 }
522 }
523
524 void outputHeader() {
525 out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHOR S file');
526 out('// for details. All rights reserved. Use of this source code is governe d by a');
527 out('// BSD-style license that can be found in the LICENSE file.');
528 out('//');
529 out('// This file has been automatically generated. Please do not edit it m anually.');
530 out('// To regenerate the file, use the script "pkg/analyzer/tool/generate_f iles".');
531 out();
532 }
533
534 /**
535 * Enclose [s] in quotes, escaping as necessary.
536 */
537 String quoted(String s) {
538 return JSON.encode(s);
539 }
540
541 void _generateBuilder(idlModel.ClassDeclaration cls) {
542 String name = cls.name;
543 String builderName = name + 'Builder';
544 String mixinName = '_${name}Mixin';
545 List<String> constructorParams = <String>[];
546 out('class $builderName extends Object with $mixinName '
547 'implements ${idlPrefix(name)} {');
548 indent(() {
549 // Generate fields.
550 for (idlModel.FieldDeclaration field in cls.fields) {
551 String fieldName = field.name;
552 idlModel.FieldType type = field.type;
553 String typeStr = encodedType(type);
554 out('$typeStr _$fieldName;');
555 }
556 // Generate getters and setters.
557 for (idlModel.FieldDeclaration field in cls.allFields) {
558 String fieldName = field.name;
559 idlModel.FieldType fieldType = field.type;
560 String typeStr = encodedType(fieldType);
561 String def = defaultValue(fieldType, true);
562 String defSuffix = def == null ? '' : ' ??= $def';
563 out();
564 out('@override');
565 if (field.isDeprecated) {
566 out('$typeStr get $fieldName => $_throwDeprecated;');
567 } else {
568 out('$typeStr get $fieldName => _$fieldName$defSuffix;');
569 out();
570 outDoc(field.documentation);
571 constructorParams.add('$typeStr $fieldName');
572 out('void set $fieldName($typeStr value) {');
573 indent(() {
574 String stateFieldName = '_' + fieldName;
575 // Validate that int(s) are non-negative.
576 if (fieldType.typeName == 'int') {
577 if (!fieldType.isList) {
578 out('assert(value == null || value >= 0);');
579 } else {
580 out('assert(value == null || value.every((e) => e >= 0));');
581 }
582 }
583 // Set the value.
584 out('this.$stateFieldName = value;');
585 });
586 out('}');
587 }
588 }
589 // Generate constructor.
590 out();
591 out('$builderName({${constructorParams.join(', ')}})');
592 List<idlModel.FieldDeclaration> fields = cls.fields.toList();
593 for (int i = 0; i < fields.length; i++) {
594 idlModel.FieldDeclaration field = fields[i];
595 String prefix = i == 0 ? ' : ' : ' ';
596 String suffix = i == fields.length - 1 ? ';' : ',';
597 out('${prefix}_${field.name} = ${field.name}$suffix');
598 }
599 // Generate flushInformative().
600 {
601 out();
602 out('/**');
603 out(' * Flush [informative] data recursively.');
604 out(' */');
605 out('void flushInformative() {');
606 indent(() {
607 for (idlModel.FieldDeclaration field in cls.fields) {
608 idlModel.FieldType fieldType = field.type;
609 String valueName = '_' + field.name;
610 if (field.isInformative) {
611 out('$valueName = null;');
612 } else if (_idl.classes.containsKey(fieldType.typeName)) {
613 if (fieldType.isList) {
614 out('$valueName?.forEach((b) => b.flushInformative());');
615 } else {
616 out('$valueName?.flushInformative();');
617 }
618 }
619 }
620 });
621 out('}');
622 }
623 // Generate collectApiSignature().
624 {
625 out();
626 out('/**');
627 out(' * Accumulate non-[informative] data into [signature].');
628 out(' */');
629 out('void collectApiSignature(api_sig.ApiSignature signature) {');
630 indent(() {
631 List<idlModel.FieldDeclaration> sortedFields = cls.fields.toList()
632 ..sort((idlModel.FieldDeclaration a, idlModel.FieldDeclaration b) =>
633 a.id.compareTo(b.id));
634 for (idlModel.FieldDeclaration field in sortedFields) {
635 if (field.isInformative) {
636 continue;
637 }
638 String ref = 'this._${field.name}';
639 if (field.type.isList) {
640 out('if ($ref == null) {');
641 indent(() {
642 out('signature.addInt(0);');
643 });
644 out('} else {');
645 indent(() {
646 out('signature.addInt($ref.length);');
647 out('for (var x in $ref) {');
648 indent(() {
649 _generateSignatureCall(field.type.typeName, 'x', false);
650 });
651 out('}');
652 });
653 out('}');
654 } else {
655 _generateSignatureCall(field.type.typeName, ref, true);
656 }
657 }
658 });
659 out('}');
660 }
661 // Generate finish.
662 if (cls.isTopLevel) {
663 out();
664 out('List<int> toBuffer() {');
665 indent(() {
666 out('fb.Builder fbBuilder = new fb.Builder();');
667 String fileId = cls.fileIdentifier == null
668 ? ''
669 : ', ${quoted(cls.fileIdentifier)}';
670 out('return fbBuilder.finish(finish(fbBuilder)$fileId);');
671 });
672 out('}');
673 }
674 out();
675 out('fb.Offset finish(fb.Builder fbBuilder) {');
676 indent(() {
677 // Write objects and remember Offset(s).
678 for (idlModel.FieldDeclaration field in cls.fields) {
679 idlModel.FieldType fieldType = field.type;
680 String offsetName = 'offset_' + field.name;
681 if (fieldType.isList ||
682 fieldType.typeName == 'String' ||
683 _idl.classes.containsKey(fieldType.typeName)) {
684 out('fb.Offset $offsetName;');
685 }
686 }
687 for (idlModel.FieldDeclaration field in cls.fields) {
688 idlModel.FieldType fieldType = field.type;
689 String valueName = '_' + field.name;
690 String offsetName = 'offset_' + field.name;
691 String condition;
692 String writeCode;
693 if (fieldType.isList) {
694 condition = ' || $valueName.isEmpty';
695 if (_idl.classes.containsKey(fieldType.typeName)) {
696 String itemCode = 'b.finish(fbBuilder)';
697 String listCode = '$valueName.map((b) => $itemCode).toList()';
698 writeCode = '$offsetName = fbBuilder.writeList($listCode);';
699 } else if (_idl.enums.containsKey(fieldType.typeName)) {
700 String itemCode = 'b.index';
701 String listCode = '$valueName.map((b) => $itemCode).toList()';
702 writeCode = '$offsetName = fbBuilder.writeListUint8($listCode);';
703 } else if (fieldType.typeName == 'bool') {
704 writeCode = '$offsetName = fbBuilder.writeListBool($valueName);';
705 } else if (fieldType.typeName == 'int') {
706 writeCode =
707 '$offsetName = fbBuilder.writeListUint32($valueName);';
708 } else if (fieldType.typeName == 'double') {
709 writeCode =
710 '$offsetName = fbBuilder.writeListFloat64($valueName);';
711 } else {
712 assert(fieldType.typeName == 'String');
713 String itemCode = 'fbBuilder.writeString(b)';
714 String listCode = '$valueName.map((b) => $itemCode).toList()';
715 writeCode = '$offsetName = fbBuilder.writeList($listCode);';
716 }
717 } else if (fieldType.typeName == 'String') {
718 writeCode = '$offsetName = fbBuilder.writeString($valueName);';
719 } else if (_idl.classes.containsKey(fieldType.typeName)) {
720 writeCode = '$offsetName = $valueName.finish(fbBuilder);';
721 }
722 if (writeCode != null) {
723 if (condition == null) {
724 out('if ($valueName != null) {');
725 } else {
726 out('if (!($valueName == null$condition)) {');
727 }
728 indent(() {
729 out(writeCode);
730 });
731 out('}');
732 }
733 }
734 // Write the table.
735 out('fbBuilder.startTable();');
736 for (idlModel.FieldDeclaration field in cls.fields) {
737 int index = field.id;
738 idlModel.FieldType fieldType = field.type;
739 String valueName = '_' + field.name;
740 String condition = '$valueName != null';
741 String writeCode;
742 if (fieldType.isList ||
743 fieldType.typeName == 'String' ||
744 _idl.classes.containsKey(fieldType.typeName)) {
745 String offsetName = 'offset_' + field.name;
746 condition = '$offsetName != null';
747 writeCode = 'fbBuilder.addOffset($index, $offsetName);';
748 } else if (fieldType.typeName == 'bool') {
749 condition = '$valueName == true';
750 writeCode = 'fbBuilder.addBool($index, true);';
751 } else if (fieldType.typeName == 'int') {
752 condition += ' && $valueName != ${defaultValue(fieldType, true)}';
753 writeCode = 'fbBuilder.addUint32($index, $valueName);';
754 } else if (_idl.enums.containsKey(fieldType.typeName)) {
755 condition += ' && $valueName != ${defaultValue(fieldType, true)}';
756 writeCode = 'fbBuilder.addUint8($index, $valueName.index);';
757 }
758 if (writeCode == null) {
759 throw new UnimplementedError('Writing type ${fieldType.typeName}');
760 }
761 out('if ($condition) {');
762 indent(() {
763 out(writeCode);
764 });
765 out('}');
766 }
767 out('return fbBuilder.endTable();');
768 });
769 out('}');
770 });
771 out('}');
772 }
773
774 void _generateEnumReader(idlModel.EnumDeclaration enm) {
775 String name = enm.name;
776 String readerName = '_${name}Reader';
777 String count = '${idlPrefix(name)}.values.length';
778 String def = '${idlPrefix(name)}.${enm.values[0].name}';
779 out('class $readerName extends fb.Reader<${idlPrefix(name)}> {');
780 indent(() {
781 out('const $readerName() : super();');
782 out();
783 out('@override');
784 out('int get size => 1;');
785 out();
786 out('@override');
787 out('${idlPrefix(name)} read(fb.BufferContext bc, int offset) {');
788 indent(() {
789 out('int index = const fb.Uint8Reader().read(bc, offset);');
790 out('return index < $count ? ${idlPrefix(name)}.values[index] : $def;');
791 });
792 out('}');
793 });
794 out('}');
795 }
796
797 void _generateImpl(idlModel.ClassDeclaration cls) {
798 String name = cls.name;
799 String implName = '_${name}Impl';
800 String mixinName = '_${name}Mixin';
801 out('class $implName extends Object with $mixinName'
802 ' implements ${idlPrefix(name)} {');
803 indent(() {
804 out('final fb.BufferContext _bc;');
805 out('final int _bcOffset;');
806 out();
807 out('$implName(this._bc, this._bcOffset);');
808 out();
809 // Write cache fields.
810 for (idlModel.FieldDeclaration field in cls.fields) {
811 String returnType = dartType(field.type);
812 String fieldName = field.name;
813 out('$returnType _$fieldName;');
814 }
815 // Write getters.
816 for (idlModel.FieldDeclaration field in cls.allFields) {
817 int index = field.id;
818 String fieldName = field.name;
819 idlModel.FieldType type = field.type;
820 String typeName = type.typeName;
821 // Prepare "readCode" + "def"
822 String readCode;
823 String def = defaultValue(type, false);
824 if (type.isList) {
825 if (typeName == 'bool') {
826 readCode = 'const fb.BoolListReader()';
827 } else if (typeName == 'int') {
828 readCode = 'const fb.Uint32ListReader()';
829 } else if (typeName == 'double') {
830 readCode = 'const fb.Float64ListReader()';
831 } else if (typeName == 'String') {
832 String itemCode = 'const fb.StringReader()';
833 readCode = 'const fb.ListReader<String>($itemCode)';
834 } else if (_idl.classes.containsKey(typeName)) {
835 String itemCode = 'const _${typeName}Reader()';
836 readCode = 'const fb.ListReader<${idlPrefix(typeName)}>($itemCode)';
837 } else {
838 assert(_idl.enums.containsKey(typeName));
839 String itemCode = 'const _${typeName}Reader()';
840 readCode = 'const fb.ListReader<${idlPrefix(typeName)}>($itemCode)';
841 }
842 } else if (typeName == 'bool') {
843 readCode = 'const fb.BoolReader()';
844 } else if (typeName == 'int') {
845 readCode = 'const fb.Uint32Reader()';
846 } else if (typeName == 'String') {
847 readCode = 'const fb.StringReader()';
848 } else if (_idl.enums.containsKey(typeName)) {
849 readCode = 'const _${typeName}Reader()';
850 } else if (_idl.classes.containsKey(typeName)) {
851 readCode = 'const _${typeName}Reader()';
852 }
853 assert(readCode != null);
854 // Write the getter implementation.
855 out();
856 out('@override');
857 String returnType = dartType(type);
858 if (field.isDeprecated) {
859 out('$returnType get $fieldName => $_throwDeprecated;');
860 } else {
861 out('$returnType get $fieldName {');
862 indent(() {
863 String readExpr =
864 '$readCode.vTableGet(_bc, _bcOffset, $index, $def)';
865 out('_$fieldName ??= $readExpr;');
866 out('return _$fieldName;');
867 });
868 out('}');
869 }
870 }
871 });
872 out('}');
873 }
874
875 void _generateMixin(idlModel.ClassDeclaration cls) {
876 String name = cls.name;
877 String mixinName = '_${name}Mixin';
878 out('abstract class $mixinName implements ${idlPrefix(name)} {');
879 indent(() {
880 // Write toJson().
881 out('@override');
882 out('Map<String, Object> toJson() {');
883 indent(() {
884 out('Map<String, Object> _result = <String, Object>{};');
885 for (idlModel.FieldDeclaration field in cls.fields) {
886 String condition;
887 if (field.type.isList) {
888 condition = '${field.name}.isNotEmpty';
889 } else {
890 condition = '${field.name} != ${defaultValue(field.type, false)}';
891 }
892 _StringToString convertItem;
893 if (_idl.classes.containsKey(field.type.typeName)) {
894 convertItem = (String name) => '$name.toJson()';
895 } else if (_idl.enums.containsKey(field.type.typeName)) {
896 // TODO(paulberry): it would be better to generate a const list of
897 // strings so that we don't have to do this kludge.
898 convertItem = (String name) => "$name.toString().split('.')[1]";
899 } else if (field.type.typeName == 'double') {
900 convertItem =
901 (String name) => '$name.isFinite ? $name : $name.toString()';
902 }
903 String convertField;
904 if (convertItem == null) {
905 convertField = field.name;
906 } else if (field.type.isList) {
907 convertField = '${field.name}.map((_value) =>'
908 ' ${convertItem('_value')}).toList()';
909 } else {
910 convertField = convertItem(field.name);
911 }
912 String storeField = '_result[${quoted(field.name)}] = $convertField';
913 out('if ($condition) $storeField;');
914 }
915 out('return _result;');
916 });
917 out('}');
918 out();
919 // Write toMap().
920 out('@override');
921 out('Map<String, Object> toMap() => {');
922 indent(() {
923 for (idlModel.FieldDeclaration field in cls.fields) {
924 String fieldName = field.name;
925 out('${quoted(fieldName)}: $fieldName,');
926 }
927 });
928 out('};');
929 out();
930 // Write toString().
931 out('@override');
932 out('String toString() => convert.JSON.encode(toJson());');
933 });
934 out('}');
935 }
936
937 void _generateReader(idlModel.ClassDeclaration cls) {
938 String name = cls.name;
939 String readerName = '_${name}Reader';
940 String implName = '_${name}Impl';
941 out('class $readerName extends fb.TableReader<$implName> {');
942 indent(() {
943 out('const $readerName();');
944 out();
945 out('@override');
946 out('$implName createObject(fb.BufferContext bc, int offset) => new $implN ame(bc, offset);');
947 });
948 out('}');
949 }
950
951 void _generateReadFunction(idlModel.ClassDeclaration cls) {
952 String name = cls.name;
953 out('${idlPrefix(name)} read$name(List<int> buffer) {');
954 indent(() {
955 out('fb.BufferContext rootRef = new fb.BufferContext.fromBytes(buffer);');
956 out('return const _${name}Reader().read(rootRef, 0);');
957 });
958 out('}');
959 }
960
961 /**
962 * Generate a call to the appropriate method of [ApiSignature] for the type
963 * [typeName], using the data named by [ref]. If [couldBeNull] is `true`,
964 * generate code to handle the possibility that [ref] is `null` (substituting
965 * in the appropriate default value).
966 */
967 void _generateSignatureCall(String typeName, String ref, bool couldBeNull) {
968 if (_idl.enums.containsKey(typeName)) {
969 if (couldBeNull) {
970 out('signature.addInt($ref == null ? 0 : $ref.index);');
971 } else {
972 out('signature.addInt($ref.index);');
973 }
974 } else if (_idl.classes.containsKey(typeName)) {
975 if (couldBeNull) {
976 out('signature.addBool($ref != null);');
977 }
978 out('$ref?.collectApiSignature(signature);');
979 } else {
980 switch (typeName) {
981 case 'String':
982 if (couldBeNull) {
983 ref += " ?? ''";
984 }
985 out("signature.addString($ref);");
986 break;
987 case 'int':
988 if (couldBeNull) {
989 ref += ' ?? 0';
990 }
991 out('signature.addInt($ref);');
992 break;
993 case 'bool':
994 if (couldBeNull) {
995 ref += ' == true';
996 }
997 out('signature.addBool($ref);');
998 break;
999 case 'double':
1000 if (couldBeNull) {
1001 ref += ' ?? 0.0';
1002 }
1003 out('signature.addDouble($ref);');
1004 break;
1005 default:
1006 throw "Don't know how to generate signature call for $typeName";
1007 }
1008 }
1009 }
1010
1011 /**
1012 * Return the documentation text of the given [node], or `null` if the [node]
1013 * does not have a comment. Each line is `\n` separated.
1014 */
1015 String _getNodeDoc(LineInfo lineInfo, AnnotatedNode node) {
1016 Comment comment = node.documentationComment;
1017 if (comment != null &&
1018 comment.isDocumentation &&
1019 comment.tokens.length == 1 &&
1020 comment.tokens.first.type == TokenType.MULTI_LINE_COMMENT) {
1021 Token token = comment.tokens.first;
1022 int column = lineInfo.getLocation(token.offset).columnNumber;
1023 String indent = ' ' * (column - 1);
1024 return token.lexeme.split('\n').map((String line) {
1025 if (line.startsWith(indent)) {
1026 line = line.substring(indent.length);
1027 }
1028 return line;
1029 }).join('\n');
1030 }
1031 return null;
1032 }
1033 }
OLDNEW
« no previous file with comments | « packages/analyzer/tool/summary/dump_inferred_types.dart ('k') | packages/analyzer/tool/summary/idl_model.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698