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

Unified Diff: packages/analyzer/tool/summary/generate.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 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 side-by-side diff with in-line comments
Download patch
Index: packages/analyzer/tool/summary/generate.dart
diff --git a/packages/analyzer/tool/summary/generate.dart b/packages/analyzer/tool/summary/generate.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d3fd86372118f1a537ee09e71d8f2b0e9be5a735
--- /dev/null
+++ b/packages/analyzer/tool/summary/generate.dart
@@ -0,0 +1,1033 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * This file contains code to generate serialization/deserialization logic for
+ * summaries based on an "IDL" description of the summary format (written in
+ * stylized Dart).
+ *
+ * For each class in the "IDL" input, two corresponding classes are generated:
+ * - A class with the same name which represents deserialized summary data in
+ * memory. This class has read-only semantics.
+ * - A "builder" class which can be used to generate serialized summary data.
+ * This class has write-only semantics.
+ *
+ * Each of the "builder" classes has a single `finish` method which writes
+ * the entity being built into the given FlatBuffer and returns the `Offset`
+ * reference to it.
+ */
+library analyzer.tool.summary.generate;
+
+import 'dart:convert';
+import 'dart:io' hide File;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/codegen/tools.dart';
+import 'package:analyzer/src/dart/scanner/reader.dart';
+import 'package:analyzer/src/dart/scanner/scanner.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:path/path.dart';
+
+import 'idl_model.dart' as idlModel;
+
+main() {
+ String script = Platform.script.toFilePath(windows: Platform.isWindows);
+ String pkgPath = normalize(join(dirname(script), '..', '..'));
+ GeneratedContent.generateAll(pkgPath, allTargets);
+}
+
+final List<GeneratedContent> allTargets = <GeneratedContent>[
+ formatTarget,
+ schemaTarget
+];
+
+final GeneratedFile formatTarget =
+ new GeneratedFile('lib/src/summary/format.dart', (String pkgPath) {
+ _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath);
+ codeGenerator.generateFormatCode();
+ return codeGenerator._outBuffer.toString();
+});
+
+final GeneratedFile schemaTarget =
+ new GeneratedFile('lib/src/summary/format.fbs', (String pkgPath) {
+ _CodeGenerator codeGenerator = new _CodeGenerator(pkgPath);
+ codeGenerator.generateFlatBufferSchema();
+ return codeGenerator._outBuffer.toString();
+});
+
+typedef String _StringToString(String s);
+
+class _CodeGenerator {
+ static const String _throwDeprecated =
+ "throw new UnimplementedError('attempt to access deprecated field')";
+
+ /**
+ * Buffer in which generated code is accumulated.
+ */
+ final StringBuffer _outBuffer = new StringBuffer();
+
+ /**
+ * Current indentation level.
+ */
+ String _indentation = '';
+
+ /**
+ * Semantic model of the "IDL" input file.
+ */
+ idlModel.Idl _idl;
+
+ _CodeGenerator(String pkgPath) {
+ // Parse the input "IDL" file.
+ PhysicalResourceProvider provider = new PhysicalResourceProvider(
+ PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS);
+ String idlPath = join(pkgPath, 'lib', 'src', 'summary', 'idl.dart');
+ File idlFile = provider.getFile(idlPath);
+ Source idlSource = provider.getFile(idlPath).createSource();
+ String idlText = idlFile.readAsStringSync();
+ BooleanErrorListener errorListener = new BooleanErrorListener();
+ CharacterReader idlReader = new CharSequenceReader(idlText);
+ Scanner scanner = new Scanner(idlSource, idlReader, errorListener);
+ Token tokenStream = scanner.tokenize();
+ LineInfo lineInfo = new LineInfo(scanner.lineStarts);
+ Parser parser = new Parser(idlSource, new BooleanErrorListener());
+ CompilationUnit idlParsed = parser.parseCompilationUnit(tokenStream);
+ // Extract a description of the IDL and make sure it is valid.
+ extractIdl(lineInfo, idlParsed);
+ checkIdl();
+ }
+
+ /**
+ * Perform basic sanity checking of the IDL (over and above that done by
+ * [extractIdl]).
+ */
+ void checkIdl() {
+ _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
+ if (cls.fileIdentifier != null) {
+ if (cls.fileIdentifier.length != 4) {
+ throw new Exception('$name: file identifier must be 4 characters');
+ }
+ for (int i = 0; i < cls.fileIdentifier.length; i++) {
+ if (cls.fileIdentifier.codeUnitAt(i) >= 256) {
+ throw new Exception(
+ '$name: file identifier must be encodable as Latin-1');
+ }
+ }
+ }
+ Map<int, String> idsUsed = <int, String>{};
+ for (idlModel.FieldDeclaration field in cls.allFields) {
+ String fieldName = field.name;
+ idlModel.FieldType type = field.type;
+ if (type.isList) {
+ if (_idl.classes.containsKey(type.typeName)) {
+ // List of classes is ok
+ } else if (_idl.enums.containsKey(type.typeName)) {
+ // List of enums is ok
+ } else if (type.typeName == 'bool') {
+ // List of booleans is ok
+ } else if (type.typeName == 'int') {
+ // List of ints is ok
+ } else if (type.typeName == 'double') {
+ // List of doubles is ok
+ } else if (type.typeName == 'String') {
+ // List of strings is ok
+ } else {
+ throw new Exception(
+ '$name.$fieldName: illegal type (list of ${type.typeName})');
+ }
+ }
+ if (idsUsed.containsKey(field.id)) {
+ throw new Exception('$name.$fieldName: id ${field.id} already used by'
+ ' ${idsUsed[field.id]}');
+ }
+ idsUsed[field.id] = fieldName;
+ }
+ for (int i = 0; i < idsUsed.length; i++) {
+ if (!idsUsed.containsKey(i)) {
+ throw new Exception('$name: no field uses id $i');
+ }
+ }
+ });
+ }
+
+ /**
+ * Generate a string representing the Dart type which should be used to
+ * represent [type] when deserialized.
+ */
+ String dartType(idlModel.FieldType type) {
+ String baseType = idlPrefix(type.typeName);
+ if (type.isList) {
+ return 'List<$baseType>';
+ } else {
+ return baseType;
+ }
+ }
+
+ /**
+ * Generate a Dart expression representing the default value for a field
+ * having the given [type], or `null` if there is no default value.
+ *
+ * If [builder] is `true`, the returned type should be appropriate for use in
+ * a builder class.
+ */
+ String defaultValue(idlModel.FieldType type, bool builder) {
+ if (type.isList) {
+ if (builder) {
+ idlModel.FieldType elementType =
+ new idlModel.FieldType(type.typeName, false);
+ return '<${encodedType(elementType)}>[]';
+ } else {
+ return 'const <${idlPrefix(type.typeName)}>[]';
+ }
+ } else if (_idl.enums.containsKey(type.typeName)) {
+ return '${idlPrefix(type.typeName)}.'
+ '${_idl.enums[type.typeName].values[0].name}';
+ } else if (type.typeName == 'int') {
+ return '0';
+ } else if (type.typeName == 'String') {
+ return "''";
+ } else if (type.typeName == 'bool') {
+ return 'false';
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Generate a string representing the Dart type which should be used to
+ * represent [type] while building a serialized data structure.
+ */
+ String encodedType(idlModel.FieldType type) {
+ String typeStr;
+ if (_idl.classes.containsKey(type.typeName)) {
+ typeStr = '${type.typeName}Builder';
+ } else {
+ typeStr = idlPrefix(type.typeName);
+ }
+ if (type.isList) {
+ return 'List<$typeStr>';
+ } else {
+ return typeStr;
+ }
+ }
+
+ /**
+ * Process the AST in [idlParsed] and store the resulting semantic model in
+ * [_idl]. Also perform some error checking.
+ */
+ void extractIdl(LineInfo lineInfo, CompilationUnit idlParsed) {
+ _idl = new idlModel.Idl();
+ for (CompilationUnitMember decl in idlParsed.declarations) {
+ if (decl is ClassDeclaration) {
+ bool isTopLevel = false;
+ String fileIdentifier;
+ String clsName = decl.name.name;
+ for (Annotation annotation in decl.metadata) {
+ if (annotation.arguments != null &&
+ annotation.name.name == 'TopLevel' &&
+ annotation.constructorName == null) {
+ isTopLevel = true;
+ if (annotation.arguments == null) {
+ throw new Exception(
+ 'Class `$clsName`: TopLevel requires parenthesis');
+ }
+ if (annotation.constructorName != null) {
+ throw new Exception(
+ "Class `$clsName`: TopLevel doesn't have named constructors");
+ }
+ if (annotation.arguments.arguments.length == 1) {
+ Expression arg = annotation.arguments.arguments[0];
+ if (arg is StringLiteral) {
+ fileIdentifier = arg.stringValue;
+ } else {
+ throw new Exception(
+ 'Class `$clsName`: TopLevel argument must be a string'
+ ' literal');
+ }
+ } else if (annotation.arguments.arguments.length != 0) {
+ throw new Exception(
+ 'Class `$clsName`: TopLevel requires 0 or 1 arguments');
+ }
+ }
+ }
+ String doc = _getNodeDoc(lineInfo, decl);
+ idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration(
+ doc, clsName, isTopLevel, fileIdentifier);
+ _idl.classes[clsName] = cls;
+ String expectedBase = 'base.SummaryClass';
+ if (decl.extendsClause == null ||
+ decl.extendsClause.superclass.name.name != expectedBase) {
+ throw new Exception(
+ 'Class `$clsName` needs to extend `$expectedBase`');
+ }
+ for (ClassMember classMember in decl.members) {
+ if (classMember is MethodDeclaration && classMember.isGetter) {
+ String desc = '$clsName.${classMember.name.name}';
+ TypeName type = classMember.returnType;
+ if (type == null) {
+ throw new Exception('Class member needs a type: $desc');
+ }
+ bool isList = false;
+ if (type.name.name == 'List' &&
+ type.typeArguments != null &&
+ type.typeArguments.arguments.length == 1) {
+ isList = true;
+ type = type.typeArguments.arguments[0];
+ }
+ if (type.typeArguments != null) {
+ throw new Exception('Cannot handle type arguments in `$type`');
+ }
+ int id;
+ bool isDeprecated = false;
+ bool isInformative = false;
+ for (Annotation annotation in classMember.metadata) {
+ if (annotation.name.name == 'Id') {
+ if (id != null) {
+ throw new Exception(
+ 'Duplicate @id annotation ($classMember)');
+ }
+ if (annotation.arguments.arguments.length != 1) {
+ throw new Exception(
+ '@Id must be passed exactly one argument ($desc)');
+ }
+ Expression expression = annotation.arguments.arguments[0];
+ if (expression is IntegerLiteral) {
+ id = expression.value;
+ } else {
+ throw new Exception(
+ '@Id parameter must be an integer literal ($desc)');
+ }
+ } else if (annotation.name.name == 'deprecated') {
+ if (annotation.arguments != null) {
+ throw new Exception('@deprecated does not take args ($desc)');
+ }
+ isDeprecated = true;
+ } else if (annotation.name.name == 'informative') {
+ isInformative = true;
+ }
+ }
+ if (id == null) {
+ throw new Exception('Missing @id annotation ($desc)');
+ }
+ String doc = _getNodeDoc(lineInfo, classMember);
+ idlModel.FieldType fieldType =
+ new idlModel.FieldType(type.name.name, isList);
+ cls.allFields.add(new idlModel.FieldDeclaration(
+ doc,
+ classMember.name.name,
+ fieldType,
+ id,
+ isDeprecated,
+ isInformative));
+ } else if (classMember is ConstructorDeclaration &&
+ classMember.name.name == 'fromBuffer') {
+ // Ignore `fromBuffer` declarations; they simply forward to the
+ // read functions generated by [_generateReadFunction].
+ } else {
+ throw new Exception('Unexpected class member `$classMember`');
+ }
+ }
+ } else if (decl is EnumDeclaration) {
+ String doc = _getNodeDoc(lineInfo, decl);
+ idlModel.EnumDeclaration enm =
+ new idlModel.EnumDeclaration(doc, decl.name.name);
+ _idl.enums[enm.name] = enm;
+ for (EnumConstantDeclaration constDecl in decl.constants) {
+ String doc = _getNodeDoc(lineInfo, constDecl);
+ enm.values
+ .add(new idlModel.EnumValueDeclaration(doc, constDecl.name.name));
+ }
+ } else if (decl is TopLevelVariableDeclaration) {
+ // Ignore top level variable declarations; they are present just to make
+ // the IDL analyze without warnings.
+ } else {
+ throw new Exception('Unexpected declaration `$decl`');
+ }
+ }
+ }
+
+ /**
+ * Generate a string representing the FlatBuffer schema type which should be
+ * used to represent [type].
+ */
+ String fbsType(idlModel.FieldType type) {
+ String typeStr;
+ switch (type.typeName) {
+ case 'bool':
+ typeStr = 'bool';
+ break;
+ case 'double':
+ typeStr = 'double';
+ break;
+ case 'int':
+ typeStr = 'uint';
+ break;
+ case 'String':
+ typeStr = 'string';
+ break;
+ default:
+ typeStr = type.typeName;
+ break;
+ }
+ if (type.isList) {
+ // FlatBuffers don't natively support a packed list of booleans, so we
+ // treat it as a list of unsigned bytes, which is a compatible data
+ // structure.
+ if (typeStr == 'bool') {
+ typeStr = 'ubyte';
+ }
+ return '[$typeStr]';
+ } else {
+ return typeStr;
+ }
+ }
+
+ /**
+ * Entry point to the code generator when generating the "format.fbs" file.
+ */
+ void generateFlatBufferSchema() {
+ outputHeader();
+ for (idlModel.EnumDeclaration enm in _idl.enums.values) {
+ out();
+ outDoc(enm.documentation);
+ out('enum ${enm.name} : byte {');
+ indent(() {
+ for (int i = 0; i < enm.values.length; i++) {
+ idlModel.EnumValueDeclaration value = enm.values[i];
+ if (i != 0) {
+ out();
+ }
+ String suffix = i < enm.values.length - 1 ? ',' : '';
+ outDoc(value.documentation);
+ out('${value.name}$suffix');
+ }
+ });
+ out('}');
+ }
+ for (idlModel.ClassDeclaration cls in _idl.classes.values) {
+ out();
+ outDoc(cls.documentation);
+ out('table ${cls.name} {');
+ indent(() {
+ for (int i = 0; i < cls.allFields.length; i++) {
+ idlModel.FieldDeclaration field = cls.allFields[i];
+ if (i != 0) {
+ out();
+ }
+ outDoc(field.documentation);
+ List<String> attributes = <String>['id: ${field.id}'];
+ if (field.isDeprecated) {
+ attributes.add('deprecated');
+ }
+ String attrText = attributes.join(', ');
+ out('${field.name}:${fbsType(field.type)} ($attrText);');
+ }
+ });
+ out('}');
+ }
+ out();
+ // Standard flatbuffers only support one root type. We support multiple
+ // root types. For now work around this by forcing PackageBundle to be the
+ // root type. TODO(paulberry): come up with a better solution.
+ idlModel.ClassDeclaration rootType = _idl.classes['PackageBundle'];
+ out('root_type ${rootType.name};');
+ if (rootType.fileIdentifier != null) {
+ out();
+ out('file_identifier ${quoted(rootType.fileIdentifier)};');
+ }
+ }
+
+ /**
+ * Entry point to the code generator when generating the "format.dart" file.
+ */
+ void generateFormatCode() {
+ outputHeader();
+ out('library analyzer.src.summary.format;');
+ out();
+ out("import 'flat_buffers.dart' as fb;");
+ out("import 'idl.dart' as idl;");
+ out("import 'dart:convert' as convert;");
+ out("import 'api_signature.dart' as api_sig;");
+ out();
+ for (idlModel.EnumDeclaration enm in _idl.enums.values) {
+ _generateEnumReader(enm);
+ out();
+ }
+ for (idlModel.ClassDeclaration cls in _idl.classes.values) {
+ _generateBuilder(cls);
+ out();
+ if (cls.isTopLevel) {
+ _generateReadFunction(cls);
+ out();
+ }
+ _generateReader(cls);
+ out();
+ _generateImpl(cls);
+ out();
+ _generateMixin(cls);
+ out();
+ }
+ }
+
+ /**
+ * Add the prefix `idl.` to a type name, unless that type name is the name of
+ * a built-in type.
+ */
+ String idlPrefix(String s) {
+ switch (s) {
+ case 'bool':
+ case 'double':
+ case 'int':
+ case 'String':
+ return s;
+ default:
+ return 'idl.$s';
+ }
+ }
+
+ /**
+ * Execute [callback] with two spaces added to [_indentation].
+ */
+ void indent(void callback()) {
+ String oldIndentation = _indentation;
+ try {
+ _indentation += ' ';
+ callback();
+ } finally {
+ _indentation = oldIndentation;
+ }
+ }
+
+ /**
+ * Add the string [s] to the output as a single line, indenting as
+ * appropriate.
+ */
+ void out([String s = '']) {
+ if (s == '') {
+ _outBuffer.writeln('');
+ } else {
+ _outBuffer.writeln('$_indentation$s');
+ }
+ }
+
+ void outDoc(String documentation) {
+ if (documentation != null) {
+ documentation.split('\n').forEach(out);
+ }
+ }
+
+ void outputHeader() {
+ out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file');
+ out('// for details. All rights reserved. Use of this source code is governed by a');
+ out('// BSD-style license that can be found in the LICENSE file.');
+ out('//');
+ out('// This file has been automatically generated. Please do not edit it manually.');
+ out('// To regenerate the file, use the script "pkg/analyzer/tool/generate_files".');
+ out();
+ }
+
+ /**
+ * Enclose [s] in quotes, escaping as necessary.
+ */
+ String quoted(String s) {
+ return JSON.encode(s);
+ }
+
+ void _generateBuilder(idlModel.ClassDeclaration cls) {
+ String name = cls.name;
+ String builderName = name + 'Builder';
+ String mixinName = '_${name}Mixin';
+ List<String> constructorParams = <String>[];
+ out('class $builderName extends Object with $mixinName '
+ 'implements ${idlPrefix(name)} {');
+ indent(() {
+ // Generate fields.
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ String fieldName = field.name;
+ idlModel.FieldType type = field.type;
+ String typeStr = encodedType(type);
+ out('$typeStr _$fieldName;');
+ }
+ // Generate getters and setters.
+ for (idlModel.FieldDeclaration field in cls.allFields) {
+ String fieldName = field.name;
+ idlModel.FieldType fieldType = field.type;
+ String typeStr = encodedType(fieldType);
+ String def = defaultValue(fieldType, true);
+ String defSuffix = def == null ? '' : ' ??= $def';
+ out();
+ out('@override');
+ if (field.isDeprecated) {
+ out('$typeStr get $fieldName => $_throwDeprecated;');
+ } else {
+ out('$typeStr get $fieldName => _$fieldName$defSuffix;');
+ out();
+ outDoc(field.documentation);
+ constructorParams.add('$typeStr $fieldName');
+ out('void set $fieldName($typeStr value) {');
+ indent(() {
+ String stateFieldName = '_' + fieldName;
+ // Validate that int(s) are non-negative.
+ if (fieldType.typeName == 'int') {
+ if (!fieldType.isList) {
+ out('assert(value == null || value >= 0);');
+ } else {
+ out('assert(value == null || value.every((e) => e >= 0));');
+ }
+ }
+ // Set the value.
+ out('this.$stateFieldName = value;');
+ });
+ out('}');
+ }
+ }
+ // Generate constructor.
+ out();
+ out('$builderName({${constructorParams.join(', ')}})');
+ List<idlModel.FieldDeclaration> fields = cls.fields.toList();
+ for (int i = 0; i < fields.length; i++) {
+ idlModel.FieldDeclaration field = fields[i];
+ String prefix = i == 0 ? ' : ' : ' ';
+ String suffix = i == fields.length - 1 ? ';' : ',';
+ out('${prefix}_${field.name} = ${field.name}$suffix');
+ }
+ // Generate flushInformative().
+ {
+ out();
+ out('/**');
+ out(' * Flush [informative] data recursively.');
+ out(' */');
+ out('void flushInformative() {');
+ indent(() {
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ idlModel.FieldType fieldType = field.type;
+ String valueName = '_' + field.name;
+ if (field.isInformative) {
+ out('$valueName = null;');
+ } else if (_idl.classes.containsKey(fieldType.typeName)) {
+ if (fieldType.isList) {
+ out('$valueName?.forEach((b) => b.flushInformative());');
+ } else {
+ out('$valueName?.flushInformative();');
+ }
+ }
+ }
+ });
+ out('}');
+ }
+ // Generate collectApiSignature().
+ {
+ out();
+ out('/**');
+ out(' * Accumulate non-[informative] data into [signature].');
+ out(' */');
+ out('void collectApiSignature(api_sig.ApiSignature signature) {');
+ indent(() {
+ List<idlModel.FieldDeclaration> sortedFields = cls.fields.toList()
+ ..sort((idlModel.FieldDeclaration a, idlModel.FieldDeclaration b) =>
+ a.id.compareTo(b.id));
+ for (idlModel.FieldDeclaration field in sortedFields) {
+ if (field.isInformative) {
+ continue;
+ }
+ String ref = 'this._${field.name}';
+ if (field.type.isList) {
+ out('if ($ref == null) {');
+ indent(() {
+ out('signature.addInt(0);');
+ });
+ out('} else {');
+ indent(() {
+ out('signature.addInt($ref.length);');
+ out('for (var x in $ref) {');
+ indent(() {
+ _generateSignatureCall(field.type.typeName, 'x', false);
+ });
+ out('}');
+ });
+ out('}');
+ } else {
+ _generateSignatureCall(field.type.typeName, ref, true);
+ }
+ }
+ });
+ out('}');
+ }
+ // Generate finish.
+ if (cls.isTopLevel) {
+ out();
+ out('List<int> toBuffer() {');
+ indent(() {
+ out('fb.Builder fbBuilder = new fb.Builder();');
+ String fileId = cls.fileIdentifier == null
+ ? ''
+ : ', ${quoted(cls.fileIdentifier)}';
+ out('return fbBuilder.finish(finish(fbBuilder)$fileId);');
+ });
+ out('}');
+ }
+ out();
+ out('fb.Offset finish(fb.Builder fbBuilder) {');
+ indent(() {
+ // Write objects and remember Offset(s).
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ idlModel.FieldType fieldType = field.type;
+ String offsetName = 'offset_' + field.name;
+ if (fieldType.isList ||
+ fieldType.typeName == 'String' ||
+ _idl.classes.containsKey(fieldType.typeName)) {
+ out('fb.Offset $offsetName;');
+ }
+ }
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ idlModel.FieldType fieldType = field.type;
+ String valueName = '_' + field.name;
+ String offsetName = 'offset_' + field.name;
+ String condition;
+ String writeCode;
+ if (fieldType.isList) {
+ condition = ' || $valueName.isEmpty';
+ if (_idl.classes.containsKey(fieldType.typeName)) {
+ String itemCode = 'b.finish(fbBuilder)';
+ String listCode = '$valueName.map((b) => $itemCode).toList()';
+ writeCode = '$offsetName = fbBuilder.writeList($listCode);';
+ } else if (_idl.enums.containsKey(fieldType.typeName)) {
+ String itemCode = 'b.index';
+ String listCode = '$valueName.map((b) => $itemCode).toList()';
+ writeCode = '$offsetName = fbBuilder.writeListUint8($listCode);';
+ } else if (fieldType.typeName == 'bool') {
+ writeCode = '$offsetName = fbBuilder.writeListBool($valueName);';
+ } else if (fieldType.typeName == 'int') {
+ writeCode =
+ '$offsetName = fbBuilder.writeListUint32($valueName);';
+ } else if (fieldType.typeName == 'double') {
+ writeCode =
+ '$offsetName = fbBuilder.writeListFloat64($valueName);';
+ } else {
+ assert(fieldType.typeName == 'String');
+ String itemCode = 'fbBuilder.writeString(b)';
+ String listCode = '$valueName.map((b) => $itemCode).toList()';
+ writeCode = '$offsetName = fbBuilder.writeList($listCode);';
+ }
+ } else if (fieldType.typeName == 'String') {
+ writeCode = '$offsetName = fbBuilder.writeString($valueName);';
+ } else if (_idl.classes.containsKey(fieldType.typeName)) {
+ writeCode = '$offsetName = $valueName.finish(fbBuilder);';
+ }
+ if (writeCode != null) {
+ if (condition == null) {
+ out('if ($valueName != null) {');
+ } else {
+ out('if (!($valueName == null$condition)) {');
+ }
+ indent(() {
+ out(writeCode);
+ });
+ out('}');
+ }
+ }
+ // Write the table.
+ out('fbBuilder.startTable();');
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ int index = field.id;
+ idlModel.FieldType fieldType = field.type;
+ String valueName = '_' + field.name;
+ String condition = '$valueName != null';
+ String writeCode;
+ if (fieldType.isList ||
+ fieldType.typeName == 'String' ||
+ _idl.classes.containsKey(fieldType.typeName)) {
+ String offsetName = 'offset_' + field.name;
+ condition = '$offsetName != null';
+ writeCode = 'fbBuilder.addOffset($index, $offsetName);';
+ } else if (fieldType.typeName == 'bool') {
+ condition = '$valueName == true';
+ writeCode = 'fbBuilder.addBool($index, true);';
+ } else if (fieldType.typeName == 'int') {
+ condition += ' && $valueName != ${defaultValue(fieldType, true)}';
+ writeCode = 'fbBuilder.addUint32($index, $valueName);';
+ } else if (_idl.enums.containsKey(fieldType.typeName)) {
+ condition += ' && $valueName != ${defaultValue(fieldType, true)}';
+ writeCode = 'fbBuilder.addUint8($index, $valueName.index);';
+ }
+ if (writeCode == null) {
+ throw new UnimplementedError('Writing type ${fieldType.typeName}');
+ }
+ out('if ($condition) {');
+ indent(() {
+ out(writeCode);
+ });
+ out('}');
+ }
+ out('return fbBuilder.endTable();');
+ });
+ out('}');
+ });
+ out('}');
+ }
+
+ void _generateEnumReader(idlModel.EnumDeclaration enm) {
+ String name = enm.name;
+ String readerName = '_${name}Reader';
+ String count = '${idlPrefix(name)}.values.length';
+ String def = '${idlPrefix(name)}.${enm.values[0].name}';
+ out('class $readerName extends fb.Reader<${idlPrefix(name)}> {');
+ indent(() {
+ out('const $readerName() : super();');
+ out();
+ out('@override');
+ out('int get size => 1;');
+ out();
+ out('@override');
+ out('${idlPrefix(name)} read(fb.BufferContext bc, int offset) {');
+ indent(() {
+ out('int index = const fb.Uint8Reader().read(bc, offset);');
+ out('return index < $count ? ${idlPrefix(name)}.values[index] : $def;');
+ });
+ out('}');
+ });
+ out('}');
+ }
+
+ void _generateImpl(idlModel.ClassDeclaration cls) {
+ String name = cls.name;
+ String implName = '_${name}Impl';
+ String mixinName = '_${name}Mixin';
+ out('class $implName extends Object with $mixinName'
+ ' implements ${idlPrefix(name)} {');
+ indent(() {
+ out('final fb.BufferContext _bc;');
+ out('final int _bcOffset;');
+ out();
+ out('$implName(this._bc, this._bcOffset);');
+ out();
+ // Write cache fields.
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ String returnType = dartType(field.type);
+ String fieldName = field.name;
+ out('$returnType _$fieldName;');
+ }
+ // Write getters.
+ for (idlModel.FieldDeclaration field in cls.allFields) {
+ int index = field.id;
+ String fieldName = field.name;
+ idlModel.FieldType type = field.type;
+ String typeName = type.typeName;
+ // Prepare "readCode" + "def"
+ String readCode;
+ String def = defaultValue(type, false);
+ if (type.isList) {
+ if (typeName == 'bool') {
+ readCode = 'const fb.BoolListReader()';
+ } else if (typeName == 'int') {
+ readCode = 'const fb.Uint32ListReader()';
+ } else if (typeName == 'double') {
+ readCode = 'const fb.Float64ListReader()';
+ } else if (typeName == 'String') {
+ String itemCode = 'const fb.StringReader()';
+ readCode = 'const fb.ListReader<String>($itemCode)';
+ } else if (_idl.classes.containsKey(typeName)) {
+ String itemCode = 'const _${typeName}Reader()';
+ readCode = 'const fb.ListReader<${idlPrefix(typeName)}>($itemCode)';
+ } else {
+ assert(_idl.enums.containsKey(typeName));
+ String itemCode = 'const _${typeName}Reader()';
+ readCode = 'const fb.ListReader<${idlPrefix(typeName)}>($itemCode)';
+ }
+ } else if (typeName == 'bool') {
+ readCode = 'const fb.BoolReader()';
+ } else if (typeName == 'int') {
+ readCode = 'const fb.Uint32Reader()';
+ } else if (typeName == 'String') {
+ readCode = 'const fb.StringReader()';
+ } else if (_idl.enums.containsKey(typeName)) {
+ readCode = 'const _${typeName}Reader()';
+ } else if (_idl.classes.containsKey(typeName)) {
+ readCode = 'const _${typeName}Reader()';
+ }
+ assert(readCode != null);
+ // Write the getter implementation.
+ out();
+ out('@override');
+ String returnType = dartType(type);
+ if (field.isDeprecated) {
+ out('$returnType get $fieldName => $_throwDeprecated;');
+ } else {
+ out('$returnType get $fieldName {');
+ indent(() {
+ String readExpr =
+ '$readCode.vTableGet(_bc, _bcOffset, $index, $def)';
+ out('_$fieldName ??= $readExpr;');
+ out('return _$fieldName;');
+ });
+ out('}');
+ }
+ }
+ });
+ out('}');
+ }
+
+ void _generateMixin(idlModel.ClassDeclaration cls) {
+ String name = cls.name;
+ String mixinName = '_${name}Mixin';
+ out('abstract class $mixinName implements ${idlPrefix(name)} {');
+ indent(() {
+ // Write toJson().
+ out('@override');
+ out('Map<String, Object> toJson() {');
+ indent(() {
+ out('Map<String, Object> _result = <String, Object>{};');
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ String condition;
+ if (field.type.isList) {
+ condition = '${field.name}.isNotEmpty';
+ } else {
+ condition = '${field.name} != ${defaultValue(field.type, false)}';
+ }
+ _StringToString convertItem;
+ if (_idl.classes.containsKey(field.type.typeName)) {
+ convertItem = (String name) => '$name.toJson()';
+ } else if (_idl.enums.containsKey(field.type.typeName)) {
+ // TODO(paulberry): it would be better to generate a const list of
+ // strings so that we don't have to do this kludge.
+ convertItem = (String name) => "$name.toString().split('.')[1]";
+ } else if (field.type.typeName == 'double') {
+ convertItem =
+ (String name) => '$name.isFinite ? $name : $name.toString()';
+ }
+ String convertField;
+ if (convertItem == null) {
+ convertField = field.name;
+ } else if (field.type.isList) {
+ convertField = '${field.name}.map((_value) =>'
+ ' ${convertItem('_value')}).toList()';
+ } else {
+ convertField = convertItem(field.name);
+ }
+ String storeField = '_result[${quoted(field.name)}] = $convertField';
+ out('if ($condition) $storeField;');
+ }
+ out('return _result;');
+ });
+ out('}');
+ out();
+ // Write toMap().
+ out('@override');
+ out('Map<String, Object> toMap() => {');
+ indent(() {
+ for (idlModel.FieldDeclaration field in cls.fields) {
+ String fieldName = field.name;
+ out('${quoted(fieldName)}: $fieldName,');
+ }
+ });
+ out('};');
+ out();
+ // Write toString().
+ out('@override');
+ out('String toString() => convert.JSON.encode(toJson());');
+ });
+ out('}');
+ }
+
+ void _generateReader(idlModel.ClassDeclaration cls) {
+ String name = cls.name;
+ String readerName = '_${name}Reader';
+ String implName = '_${name}Impl';
+ out('class $readerName extends fb.TableReader<$implName> {');
+ indent(() {
+ out('const $readerName();');
+ out();
+ out('@override');
+ out('$implName createObject(fb.BufferContext bc, int offset) => new $implName(bc, offset);');
+ });
+ out('}');
+ }
+
+ void _generateReadFunction(idlModel.ClassDeclaration cls) {
+ String name = cls.name;
+ out('${idlPrefix(name)} read$name(List<int> buffer) {');
+ indent(() {
+ out('fb.BufferContext rootRef = new fb.BufferContext.fromBytes(buffer);');
+ out('return const _${name}Reader().read(rootRef, 0);');
+ });
+ out('}');
+ }
+
+ /**
+ * Generate a call to the appropriate method of [ApiSignature] for the type
+ * [typeName], using the data named by [ref]. If [couldBeNull] is `true`,
+ * generate code to handle the possibility that [ref] is `null` (substituting
+ * in the appropriate default value).
+ */
+ void _generateSignatureCall(String typeName, String ref, bool couldBeNull) {
+ if (_idl.enums.containsKey(typeName)) {
+ if (couldBeNull) {
+ out('signature.addInt($ref == null ? 0 : $ref.index);');
+ } else {
+ out('signature.addInt($ref.index);');
+ }
+ } else if (_idl.classes.containsKey(typeName)) {
+ if (couldBeNull) {
+ out('signature.addBool($ref != null);');
+ }
+ out('$ref?.collectApiSignature(signature);');
+ } else {
+ switch (typeName) {
+ case 'String':
+ if (couldBeNull) {
+ ref += " ?? ''";
+ }
+ out("signature.addString($ref);");
+ break;
+ case 'int':
+ if (couldBeNull) {
+ ref += ' ?? 0';
+ }
+ out('signature.addInt($ref);');
+ break;
+ case 'bool':
+ if (couldBeNull) {
+ ref += ' == true';
+ }
+ out('signature.addBool($ref);');
+ break;
+ case 'double':
+ if (couldBeNull) {
+ ref += ' ?? 0.0';
+ }
+ out('signature.addDouble($ref);');
+ break;
+ default:
+ throw "Don't know how to generate signature call for $typeName";
+ }
+ }
+ }
+
+ /**
+ * Return the documentation text of the given [node], or `null` if the [node]
+ * does not have a comment. Each line is `\n` separated.
+ */
+ String _getNodeDoc(LineInfo lineInfo, AnnotatedNode node) {
+ Comment comment = node.documentationComment;
+ if (comment != null &&
+ comment.isDocumentation &&
+ comment.tokens.length == 1 &&
+ comment.tokens.first.type == TokenType.MULTI_LINE_COMMENT) {
+ Token token = comment.tokens.first;
+ int column = lineInfo.getLocation(token.offset).columnNumber;
+ String indent = ' ' * (column - 1);
+ return token.lexeme.split('\n').map((String line) {
+ if (line.startsWith(indent)) {
+ line = line.substring(indent.length);
+ }
+ return line;
+ }).join('\n');
+ }
+ return null;
+ }
+}
« 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