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

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

Issue 1414903005: Begin generating code for summary serialization/deserialization. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix handling of List<int> Created 5 years, 1 month 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
« no previous file with comments | « pkg/analyzer/tool/summary/check_test.dart ('k') | pkg/analyzer/tool/summary/idl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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" classess has a single `finish` method which finalizes
17 * the entity being built and returns it as an [Object]. This object should
18 * only be passed to other builders (or to [BuilderContext.getBuffer]);
19 * otherwise the client should treat it as opaque, since it exposes
20 * implementation details of the underlying summary infrastructure.
21 */
22 library analyzer.tool.summary.generate;
23
24 import 'dart:convert';
25 import 'dart:io' hide File;
26
27 import 'package:analyzer/analyzer.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/generated/parser.dart';
32 import 'package:analyzer/src/generated/scanner.dart';
33 import 'package:analyzer/src/generated/source.dart';
34 import 'package:path/path.dart';
35
36 import 'idl_model.dart' as idlModel;
37
38 main() {
39 String script = Platform.script.toFilePath(windows: Platform.isWindows);
40 String pkgPath = normalize(join(dirname(script), '..', '..'));
41 GeneratedContent.generateAll(pkgPath, <GeneratedContent>[target]);
42 }
43
44 final GeneratedFile target =
45 new GeneratedFile('lib/src/summary/format.dart', (String pkgPath) {
46 // Parse the input "IDL" file and pass it to the [_CodeGenerator].
47 PhysicalResourceProvider provider = new PhysicalResourceProvider(
48 PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS);
49 String idlPath = join(pkgPath, 'tool', 'summary', 'idl.dart');
50 File idlFile = provider.getFile(idlPath);
51 Source idlSource = provider.getFile(idlPath).createSource();
52 String idlText = idlFile.readAsStringSync();
53 BooleanErrorListener errorListener = new BooleanErrorListener();
54 CharacterReader idlReader = new CharSequenceReader(idlText);
55 Scanner scanner = new Scanner(idlSource, idlReader, errorListener);
56 Token tokenStream = scanner.tokenize();
57 Parser parser = new Parser(idlSource, new BooleanErrorListener());
58 CompilationUnit idlParsed = parser.parseCompilationUnit(tokenStream);
59 _CodeGenerator codeGenerator = new _CodeGenerator();
60 codeGenerator.processCompilationUnit(idlParsed);
61 return codeGenerator._outBuffer.toString();
62 });
63
64 class _CodeGenerator {
65 /**
66 * Buffer in which generated code is accumulated.
67 */
68 final StringBuffer _outBuffer = new StringBuffer();
69
70 /**
71 * Current indentation level.
72 */
73 String _indentation = '';
74
75 /**
76 * Semantic model of the "IDL" input file.
77 */
78 idlModel.Idl _idl;
79
80 /**
81 * Perform basic sanity checking of the IDL (over and above that done by
82 * [extractIdl]).
83 */
84 void checkIdl() {
85 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
86 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
87 if (type.isList) {
88 if (_idl.classes.containsKey(type.typeName)) {
89 // List of classes is ok
90 } else if (type.typeName == 'int') {
91 // List of ints is ok
92 } else {
93 throw new Exception(
94 '$name.$fieldName: illegal type (list of ${type.typeName})');
95 }
96 }
97 });
98 });
99 }
100
101 /**
102 * Generate a string representing the Dart type which should be used to
103 * represent [type] when deserialized.
104 */
105 String dartType(idlModel.FieldType type) {
106 if (type.isList) {
107 return 'List<${type.typeName}>';
108 } else {
109 return type.typeName;
110 }
111 }
112
113 /**
114 * Generate a Dart expression representing the default value for a field
115 * having the given [type], or `null` if there is no default value.
116 */
117 String defaultValue(idlModel.FieldType type) {
118 if (type.isList) {
119 return 'const <${type.typeName}>[]';
120 } else if (_idl.enums.containsKey(type.typeName)) {
121 return '${type.typeName}.${_idl.enums[type.typeName].values[0]}';
122 } else if (type.typeName == 'int') {
123 return '0';
124 } else if (type.typeName == 'String') {
125 return "''";
126 } else if (type.typeName == 'bool') {
127 return 'false';
128 } else {
129 return null;
130 }
131 }
132
133 /**
134 * Generate a string representing the Dart type which should be used to
135 * represent [type] while building a serialized data structure.
136 */
137 String encodedType(idlModel.FieldType type) {
138 if (type.isList) {
139 if (type.typeName == 'int') {
140 return 'List<int>';
141 } else {
142 return 'List<Object>';
143 }
144 } else if (_idl.classes.containsKey(type.typeName)) {
145 return 'Object';
146 } else {
147 return dartType(type);
148 }
149 }
150
151 /**
152 * Process the AST in [idlParsed] and store the resulting semantic model in
153 * [_idl]. Also perform some error checking.
154 */
155 void extractIdl(CompilationUnit idlParsed) {
156 _idl = new idlModel.Idl();
157 for (CompilationUnitMember decl in idlParsed.declarations) {
158 if (decl is ClassDeclaration) {
159 idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration();
160 _idl.classes[decl.name.name] = cls;
161 for (ClassMember classMember in decl.members) {
162 if (classMember is FieldDeclaration) {
163 TypeName type = classMember.fields.type;
164 bool isList = false;
165 if (type.name.name == 'List' &&
166 type.typeArguments != null &&
167 type.typeArguments.arguments.length == 1) {
168 isList = true;
169 type = type.typeArguments.arguments[0];
170 }
171 if (type.typeArguments != null) {
172 throw new Exception('Cannot handle type arguments in `$type`');
173 }
174 idlModel.FieldType fieldType =
175 new idlModel.FieldType(type.name.name, isList);
176 for (VariableDeclaration field in classMember.fields.variables) {
177 cls.fields[field.name.name] = fieldType;
178 }
179 } else {
180 throw new Exception('Unexpected class member `$classMember`');
181 }
182 }
183 } else if (decl is EnumDeclaration) {
184 idlModel.EnumDeclaration enm = new idlModel.EnumDeclaration();
185 _idl.enums[decl.name.name] = enm;
186 for (EnumConstantDeclaration constDecl in decl.constants) {
187 enm.values.add(constDecl.name.name);
188 }
189 } else if (decl is TopLevelVariableDeclaration) {
190 // Ignore top leve variable declarations; they are present just to make
191 // the IDL analyze without warnings.
192 } else {
193 throw new Exception('Unexpected declaration `$decl`');
194 }
195 }
196 }
197
198 /**
199 * Execute [callback] with two spaces added to [_indentation].
200 */
201 void indent(void callback()) {
202 String oldIndentation = _indentation;
203 try {
204 _indentation += ' ';
205 callback();
206 } finally {
207 _indentation = oldIndentation;
208 }
209 }
210
211 /**
212 * Add the string [s] to the output as a single line, indenting as
213 * appropriate.
214 */
215 void out([String s = '']) {
216 if (s == '') {
217 _outBuffer.writeln('');
218 } else {
219 _outBuffer.writeln('$_indentation$s');
220 }
221 }
222
223 /**
224 * Entry point to the code generator. Interpret the AST in [idlParsed],
225 * generate code, and output it to [_outBuffer].
226 */
227 void processCompilationUnit(CompilationUnit idlParsed) {
228 extractIdl(idlParsed);
229 checkIdl();
230 out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHOR S file');
231 out('// for details. All rights reserved. Use of this source code is governe d by a');
232 out('// BSD-style license that can be found in the LICENSE file.');
233 out('//');
234 out('// This file has been automatically generated. Please do not edit it m anually.');
235 out('// To regenerate the file, use the script "pkg/analyzer/tool/generate_f iles".');
236 out();
237 out('library analyzer.src.summary.format;');
238 out();
239 out("import 'builder.dart' as builder;");
240 out();
241 _idl.enums.forEach((String name, idlModel.EnumDeclaration enm) {
242 out('enum $name {');
243 indent(() {
244 for (String value in enm.values) {
245 out('$value,');
246 }
247 });
248 out('}');
249 out();
250 });
251 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) {
252 out('class $name {');
253 indent(() {
254 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
255 out('${dartType(type)} _$fieldName;');
256 });
257 out();
258 out('$name.fromJson(Map json)');
259 indent(() {
260 List<String> initializers = <String>[];
261 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
262 String convert = 'json[${quoted(fieldName)}]';
263 if (type.isList && type.typeName == 'int') {
264 // No conversion necessary.
265 } else if (type.isList) {
266 convert =
267 '$convert?.map((x) => new ${type.typeName}.fromJson(x))?.toLis t()';
268 } else if (_idl.classes.containsKey(type.typeName)) {
269 convert =
270 '$convert == null ? null : new ${type.typeName}.fromJson($conv ert)';
271 } else if (_idl.enums.containsKey(type.typeName)) {
272 convert =
273 '$convert == null ? null : ${type.typeName}.values[$convert]';
274 }
275 initializers.add('_$fieldName = $convert');
276 });
277 for (int i = 0; i < initializers.length; i++) {
278 String prefix = i == 0 ? ': ' : ' ';
279 String suffix = i == initializers.length - 1 ? ';' : ',';
280 out('$prefix${initializers[i]}$suffix');
281 }
282 });
283 out();
284 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
285 String def = defaultValue(type);
286 String defaultSuffix = def == null ? '' : ' ?? $def';
287 out('${dartType(type)} get $fieldName => _$fieldName$defaultSuffix;');
288 });
289 });
290 out('}');
291 out();
292 List<String> builderParams = <String>[];
293 out('class ${name}Builder {');
294 indent(() {
295 out('final Map _json = {};');
296 out();
297 out('${name}Builder(builder.BuilderContext context);');
298 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
299 out();
300 String conversion = '_value';
301 String condition = '';
302 if (type.isList) {
303 conversion = '$conversion.toList()';
304 condition = ' || _value.isEmpty';
305 } else if (_idl.enums.containsKey(type.typeName)) {
306 conversion = '$conversion.index';
307 condition = ' || _value == ${defaultValue(type)}';
308 }
309 builderParams.add('${encodedType(type)} $fieldName');
310 out('void set $fieldName(${encodedType(type)} _value) {');
311 indent(() {
312 out('assert(!_json.containsKey(${quoted(fieldName)}));');
313 out('if (_value != null$condition) {');
314 indent(() {
315 out('_json[${quoted(fieldName)}] = $conversion;');
316 });
317 out('}');
318 });
319 out('}');
320 });
321 out();
322 out('Object finish() => _json;');
323 });
324 out('}');
325 out();
326 out('Object encode$name(builder.BuilderContext builderContext, {${builderP arams.join(', ')}}) {');
327 indent(() {
328 out('${name}Builder builder = new ${name}Builder(builderContext);');
329 cls.fields.forEach((String fieldName, idlModel.FieldType type) {
330 out('builder.$fieldName = $fieldName;');
331 });
332 out('return builder.finish();');
333 });
334 out('}');
335 out();
336 });
337 }
338
339 /**
340 * Enclose [s] in quotes, escaping as necessary.
341 */
342 String quoted(String s) {
343 return JSON.encode(s);
344 }
345 }
OLDNEW
« no previous file with comments | « pkg/analyzer/tool/summary/check_test.dart ('k') | pkg/analyzer/tool/summary/idl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698