| Index: pkg/analyzer/tool/summary/generate.dart
|
| diff --git a/pkg/analyzer/tool/summary/generate.dart b/pkg/analyzer/tool/summary/generate.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5e4b2c2a9e37a2015fc173644b57c173b7ccfe29
|
| --- /dev/null
|
| +++ b/pkg/analyzer/tool/summary/generate.dart
|
| @@ -0,0 +1,345 @@
|
| +// 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" classess has a single `finish` method which finalizes
|
| + * the entity being built and returns it as an [Object]. This object should
|
| + * only be passed to other builders (or to [BuilderContext.getBuffer]);
|
| + * otherwise the client should treat it as opaque, since it exposes
|
| + * implementation details of the underlying summary infrastructure.
|
| + */
|
| +library analyzer.tool.summary.generate;
|
| +
|
| +import 'dart:convert';
|
| +import 'dart:io' hide File;
|
| +
|
| +import 'package:analyzer/analyzer.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/generated/parser.dart';
|
| +import 'package:analyzer/src/generated/scanner.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, <GeneratedContent>[target]);
|
| +}
|
| +
|
| +final GeneratedFile target =
|
| + new GeneratedFile('lib/src/summary/format.dart', (String pkgPath) {
|
| + // Parse the input "IDL" file and pass it to the [_CodeGenerator].
|
| + PhysicalResourceProvider provider = new PhysicalResourceProvider(
|
| + PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS);
|
| + String idlPath = join(pkgPath, 'tool', '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();
|
| + Parser parser = new Parser(idlSource, new BooleanErrorListener());
|
| + CompilationUnit idlParsed = parser.parseCompilationUnit(tokenStream);
|
| + _CodeGenerator codeGenerator = new _CodeGenerator();
|
| + codeGenerator.processCompilationUnit(idlParsed);
|
| + return codeGenerator._outBuffer.toString();
|
| +});
|
| +
|
| +class _CodeGenerator {
|
| + /**
|
| + * 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;
|
| +
|
| + /**
|
| + * Perform basic sanity checking of the IDL (over and above that done by
|
| + * [extractIdl]).
|
| + */
|
| + void checkIdl() {
|
| + _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + if (type.isList) {
|
| + if (_idl.classes.containsKey(type.typeName)) {
|
| + // List of classes is ok
|
| + } else if (type.typeName == 'int') {
|
| + // List of ints is ok
|
| + } else {
|
| + throw new Exception(
|
| + '$name.$fieldName: illegal type (list of ${type.typeName})');
|
| + }
|
| + }
|
| + });
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Generate a string representing the Dart type which should be used to
|
| + * represent [type] when deserialized.
|
| + */
|
| + String dartType(idlModel.FieldType type) {
|
| + if (type.isList) {
|
| + return 'List<${type.typeName}>';
|
| + } else {
|
| + return type.typeName;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Generate a Dart expression representing the default value for a field
|
| + * having the given [type], or `null` if there is no default value.
|
| + */
|
| + String defaultValue(idlModel.FieldType type) {
|
| + if (type.isList) {
|
| + return 'const <${type.typeName}>[]';
|
| + } else if (_idl.enums.containsKey(type.typeName)) {
|
| + return '${type.typeName}.${_idl.enums[type.typeName].values[0]}';
|
| + } 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) {
|
| + if (type.isList) {
|
| + if (type.typeName == 'int') {
|
| + return 'List<int>';
|
| + } else {
|
| + return 'List<Object>';
|
| + }
|
| + } else if (_idl.classes.containsKey(type.typeName)) {
|
| + return 'Object';
|
| + } else {
|
| + return dartType(type);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Process the AST in [idlParsed] and store the resulting semantic model in
|
| + * [_idl]. Also perform some error checking.
|
| + */
|
| + void extractIdl(CompilationUnit idlParsed) {
|
| + _idl = new idlModel.Idl();
|
| + for (CompilationUnitMember decl in idlParsed.declarations) {
|
| + if (decl is ClassDeclaration) {
|
| + idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration();
|
| + _idl.classes[decl.name.name] = cls;
|
| + for (ClassMember classMember in decl.members) {
|
| + if (classMember is FieldDeclaration) {
|
| + TypeName type = classMember.fields.type;
|
| + 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`');
|
| + }
|
| + idlModel.FieldType fieldType =
|
| + new idlModel.FieldType(type.name.name, isList);
|
| + for (VariableDeclaration field in classMember.fields.variables) {
|
| + cls.fields[field.name.name] = fieldType;
|
| + }
|
| + } else {
|
| + throw new Exception('Unexpected class member `$classMember`');
|
| + }
|
| + }
|
| + } else if (decl is EnumDeclaration) {
|
| + idlModel.EnumDeclaration enm = new idlModel.EnumDeclaration();
|
| + _idl.enums[decl.name.name] = enm;
|
| + for (EnumConstantDeclaration constDecl in decl.constants) {
|
| + enm.values.add(constDecl.name.name);
|
| + }
|
| + } else if (decl is TopLevelVariableDeclaration) {
|
| + // Ignore top leve variable declarations; they are present just to make
|
| + // the IDL analyze without warnings.
|
| + } else {
|
| + throw new Exception('Unexpected declaration `$decl`');
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * 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');
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Entry point to the code generator. Interpret the AST in [idlParsed],
|
| + * generate code, and output it to [_outBuffer].
|
| + */
|
| + void processCompilationUnit(CompilationUnit idlParsed) {
|
| + extractIdl(idlParsed);
|
| + checkIdl();
|
| + 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();
|
| + out('library analyzer.src.summary.format;');
|
| + out();
|
| + out("import 'builder.dart' as builder;");
|
| + out();
|
| + _idl.enums.forEach((String name, idlModel.EnumDeclaration enm) {
|
| + out('enum $name {');
|
| + indent(() {
|
| + for (String value in enm.values) {
|
| + out('$value,');
|
| + }
|
| + });
|
| + out('}');
|
| + out();
|
| + });
|
| + _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
|
| + out('class $name {');
|
| + indent(() {
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + out('${dartType(type)} _$fieldName;');
|
| + });
|
| + out();
|
| + out('$name.fromJson(Map json)');
|
| + indent(() {
|
| + List<String> initializers = <String>[];
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + String convert = 'json[${quoted(fieldName)}]';
|
| + if (type.isList && type.typeName == 'int') {
|
| + // No conversion necessary.
|
| + } else if (type.isList) {
|
| + convert =
|
| + '$convert?.map((x) => new ${type.typeName}.fromJson(x))?.toList()';
|
| + } else if (_idl.classes.containsKey(type.typeName)) {
|
| + convert =
|
| + '$convert == null ? null : new ${type.typeName}.fromJson($convert)';
|
| + } else if (_idl.enums.containsKey(type.typeName)) {
|
| + convert =
|
| + '$convert == null ? null : ${type.typeName}.values[$convert]';
|
| + }
|
| + initializers.add('_$fieldName = $convert');
|
| + });
|
| + for (int i = 0; i < initializers.length; i++) {
|
| + String prefix = i == 0 ? ': ' : ' ';
|
| + String suffix = i == initializers.length - 1 ? ';' : ',';
|
| + out('$prefix${initializers[i]}$suffix');
|
| + }
|
| + });
|
| + out();
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + String def = defaultValue(type);
|
| + String defaultSuffix = def == null ? '' : ' ?? $def';
|
| + out('${dartType(type)} get $fieldName => _$fieldName$defaultSuffix;');
|
| + });
|
| + });
|
| + out('}');
|
| + out();
|
| + List<String> builderParams = <String>[];
|
| + out('class ${name}Builder {');
|
| + indent(() {
|
| + out('final Map _json = {};');
|
| + out();
|
| + out('${name}Builder(builder.BuilderContext context);');
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + out();
|
| + String conversion = '_value';
|
| + String condition = '';
|
| + if (type.isList) {
|
| + conversion = '$conversion.toList()';
|
| + condition = ' || _value.isEmpty';
|
| + } else if (_idl.enums.containsKey(type.typeName)) {
|
| + conversion = '$conversion.index';
|
| + condition = ' || _value == ${defaultValue(type)}';
|
| + }
|
| + builderParams.add('${encodedType(type)} $fieldName');
|
| + out('void set $fieldName(${encodedType(type)} _value) {');
|
| + indent(() {
|
| + out('assert(!_json.containsKey(${quoted(fieldName)}));');
|
| + out('if (_value != null$condition) {');
|
| + indent(() {
|
| + out('_json[${quoted(fieldName)}] = $conversion;');
|
| + });
|
| + out('}');
|
| + });
|
| + out('}');
|
| + });
|
| + out();
|
| + out('Object finish() => _json;');
|
| + });
|
| + out('}');
|
| + out();
|
| + out('Object encode$name(builder.BuilderContext builderContext, {${builderParams.join(', ')}}) {');
|
| + indent(() {
|
| + out('${name}Builder builder = new ${name}Builder(builderContext);');
|
| + cls.fields.forEach((String fieldName, idlModel.FieldType type) {
|
| + out('builder.$fieldName = $fieldName;');
|
| + });
|
| + out('return builder.finish();');
|
| + });
|
| + out('}');
|
| + out();
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Enclose [s] in quotes, escaping as necessary.
|
| + */
|
| + String quoted(String s) {
|
| + return JSON.encode(s);
|
| + }
|
| +}
|
|
|