OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * This file contains code to generate serialization/deserialization logic for | 6 * This file contains code to generate serialization/deserialization logic for |
7 * summaries based on an "IDL" description of the summary format (written in | 7 * summaries based on an "IDL" description of the summary format (written in |
8 * stylized Dart). | 8 * stylized Dart). |
9 * | 9 * |
10 * For each class in the "IDL" input, two corresponding classes are generated: | 10 * For each class in the "IDL" input, two corresponding classes are generated: |
11 * - A class with the same name which represents deserialized summary data in | 11 * - A class with the same name which represents deserialized summary data in |
12 * memory. This class has read-only semantics. | 12 * memory. This class has read-only semantics. |
13 * - A "builder" class which can be used to generate serialized summary data. | 13 * - A "builder" class which can be used to generate serialized summary data. |
14 * This class has write-only semantics. | 14 * This class has write-only semantics. |
15 * | 15 * |
16 * Each of the "builder" classes has a single `finish` method which writes | 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` | 17 * the entity being built into the given FlatBuffer and returns the `Offset` |
18 * reference to it. | 18 * reference to it. |
19 */ | 19 */ |
20 library analyzer.tool.summary.generate; | 20 library analyzer.tool.summary.generate; |
21 | 21 |
22 import 'dart:convert'; | 22 import 'dart:convert'; |
23 import 'dart:io' hide File; | 23 import 'dart:io' hide File; |
24 | 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'; | 25 import 'package:analyzer/file_system/file_system.dart'; |
29 import 'package:analyzer/file_system/physical_file_system.dart'; | 26 import 'package:analyzer/file_system/physical_file_system.dart'; |
30 import 'package:analyzer/src/codegen/tools.dart'; | 27 import 'package:analyzer/src/codegen/tools.dart'; |
31 import 'package:analyzer/src/dart/scanner/reader.dart'; | 28 import 'package:front_end/src/fasta/scanner/string_scanner.dart'; |
32 import 'package:analyzer/src/dart/scanner/scanner.dart'; | 29 import 'package:front_end/src/fasta/scanner/token.dart'; |
33 import 'package:analyzer/src/generated/parser.dart'; | |
34 import 'package:analyzer/src/generated/source.dart'; | |
35 import 'package:path/path.dart'; | 30 import 'package:path/path.dart'; |
36 | 31 |
37 import 'idl_model.dart' as idlModel; | 32 import 'idl_model.dart' as idlModel; |
| 33 import 'mini_ast.dart'; |
38 | 34 |
39 main() { | 35 main() { |
40 String script = Platform.script.toFilePath(windows: Platform.isWindows); | 36 String script = Platform.script.toFilePath(windows: Platform.isWindows); |
41 String pkgPath = normalize(join(dirname(script), '..', '..')); | 37 String pkgPath = normalize(join(dirname(script), '..', '..')); |
42 GeneratedContent.generateAll(pkgPath, allTargets); | 38 GeneratedContent.generateAll(pkgPath, allTargets); |
43 } | 39 } |
44 | 40 |
45 final List<GeneratedContent> allTargets = <GeneratedContent>[ | 41 final List<GeneratedContent> allTargets = <GeneratedContent>[ |
46 formatTarget, | 42 formatTarget, |
47 schemaTarget | 43 schemaTarget |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 * Semantic model of the "IDL" input file. | 77 * Semantic model of the "IDL" input file. |
82 */ | 78 */ |
83 idlModel.Idl _idl; | 79 idlModel.Idl _idl; |
84 | 80 |
85 _CodeGenerator(String pkgPath) { | 81 _CodeGenerator(String pkgPath) { |
86 // Parse the input "IDL" file. | 82 // Parse the input "IDL" file. |
87 PhysicalResourceProvider provider = new PhysicalResourceProvider( | 83 PhysicalResourceProvider provider = new PhysicalResourceProvider( |
88 PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS); | 84 PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS); |
89 String idlPath = join(pkgPath, 'lib', 'src', 'summary', 'idl.dart'); | 85 String idlPath = join(pkgPath, 'lib', 'src', 'summary', 'idl.dart'); |
90 File idlFile = provider.getFile(idlPath); | 86 File idlFile = provider.getFile(idlPath); |
91 Source idlSource = provider.getFile(idlPath).createSource(); | |
92 String idlText = idlFile.readAsStringSync(); | 87 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. | 88 // Extract a description of the IDL and make sure it is valid. |
101 extractIdl(lineInfo, idlParsed); | 89 var scanner = new StringScanner(idlText, includeComments: true); |
| 90 var startingToken = scanner.tokenize(); |
| 91 var listener = new MiniAstBuilder(); |
| 92 var parser = new MiniAstParser(listener); |
| 93 parser.parseUnit(startingToken); |
| 94 extractIdl(listener.compilationUnit); |
102 checkIdl(); | 95 checkIdl(); |
103 } | 96 } |
104 | 97 |
105 /** | 98 /** |
106 * Perform basic sanity checking of the IDL (over and above that done by | 99 * Perform basic sanity checking of the IDL (over and above that done by |
107 * [extractIdl]). | 100 * [extractIdl]). |
108 */ | 101 */ |
109 void checkIdl() { | 102 void checkIdl() { |
110 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) { | 103 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) { |
111 if (cls.fileIdentifier != null) { | 104 if (cls.fileIdentifier != null) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 return 'List<$typeStr>'; | 206 return 'List<$typeStr>'; |
214 } else { | 207 } else { |
215 return typeStr; | 208 return typeStr; |
216 } | 209 } |
217 } | 210 } |
218 | 211 |
219 /** | 212 /** |
220 * Process the AST in [idlParsed] and store the resulting semantic model in | 213 * Process the AST in [idlParsed] and store the resulting semantic model in |
221 * [_idl]. Also perform some error checking. | 214 * [_idl]. Also perform some error checking. |
222 */ | 215 */ |
223 void extractIdl(LineInfo lineInfo, CompilationUnit idlParsed) { | 216 void extractIdl(CompilationUnit idlParsed) { |
224 _idl = new idlModel.Idl(); | 217 _idl = new idlModel.Idl(); |
225 for (CompilationUnitMember decl in idlParsed.declarations) { | 218 for (CompilationUnitMember decl in idlParsed.declarations) { |
226 if (decl is ClassDeclaration) { | 219 if (decl is ClassDeclaration) { |
227 bool isTopLevel = false; | 220 bool isTopLevel = false; |
228 String fileIdentifier; | 221 String fileIdentifier; |
229 String clsName = decl.name.name; | 222 String clsName = decl.name; |
230 for (Annotation annotation in decl.metadata) { | 223 for (Annotation annotation in decl.metadata) { |
231 if (annotation.arguments != null && | 224 if (annotation.arguments != null && |
232 annotation.name.name == 'TopLevel' && | 225 annotation.name == 'TopLevel' && |
233 annotation.constructorName == null) { | 226 annotation.constructorName == null) { |
234 isTopLevel = true; | 227 isTopLevel = true; |
235 if (annotation.arguments == null) { | 228 if (annotation.arguments == null) { |
236 throw new Exception( | 229 throw new Exception( |
237 'Class `$clsName`: TopLevel requires parenthesis'); | 230 'Class `$clsName`: TopLevel requires parenthesis'); |
238 } | 231 } |
239 if (annotation.constructorName != null) { | 232 if (annotation.constructorName != null) { |
240 throw new Exception( | 233 throw new Exception( |
241 "Class `$clsName`: TopLevel doesn't have named constructors"); | 234 "Class `$clsName`: TopLevel doesn't have named constructors"); |
242 } | 235 } |
243 if (annotation.arguments.arguments.length == 1) { | 236 if (annotation.arguments.length == 1) { |
244 Expression arg = annotation.arguments.arguments[0]; | 237 Expression arg = annotation.arguments[0]; |
245 if (arg is StringLiteral) { | 238 if (arg is StringLiteral) { |
246 fileIdentifier = arg.stringValue; | 239 fileIdentifier = arg.stringValue; |
247 } else { | 240 } else { |
248 throw new Exception( | 241 throw new Exception( |
249 'Class `$clsName`: TopLevel argument must be a string' | 242 'Class `$clsName`: TopLevel argument must be a string' |
250 ' literal'); | 243 ' literal'); |
251 } | 244 } |
252 } else if (annotation.arguments.arguments.length != 0) { | 245 } else if (annotation.arguments.length != 0) { |
253 throw new Exception( | 246 throw new Exception( |
254 'Class `$clsName`: TopLevel requires 0 or 1 arguments'); | 247 'Class `$clsName`: TopLevel requires 0 or 1 arguments'); |
255 } | 248 } |
256 } | 249 } |
257 } | 250 } |
258 String doc = _getNodeDoc(lineInfo, decl); | 251 String doc = _getNodeDoc(decl); |
259 idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration( | 252 idlModel.ClassDeclaration cls = new idlModel.ClassDeclaration( |
260 doc, clsName, isTopLevel, fileIdentifier); | 253 doc, clsName, isTopLevel, fileIdentifier); |
261 _idl.classes[clsName] = cls; | 254 _idl.classes[clsName] = cls; |
262 String expectedBase = 'base.SummaryClass'; | 255 String expectedBase = 'base.SummaryClass'; |
263 if (decl.extendsClause == null || | 256 if (decl.superclass == null || decl.superclass.name != expectedBase) { |
264 decl.extendsClause.superclass.name.name != expectedBase) { | |
265 throw new Exception( | 257 throw new Exception( |
266 'Class `$clsName` needs to extend `$expectedBase`'); | 258 'Class `$clsName` needs to extend `$expectedBase`'); |
267 } | 259 } |
268 for (ClassMember classMember in decl.members) { | 260 for (ClassMember classMember in decl.members) { |
269 if (classMember is MethodDeclaration && classMember.isGetter) { | 261 if (classMember is MethodDeclaration && classMember.isGetter) { |
270 String desc = '$clsName.${classMember.name.name}'; | 262 String desc = '$clsName.${classMember.name}'; |
271 if (classMember.returnType is! TypeName) { | 263 if (classMember.returnType == null) { |
272 if (classMember.returnType == null) { | 264 throw new Exception('Class member needs a type: $desc'); |
273 throw new Exception('Class member needs a type: $desc'); | |
274 } | |
275 throw new Exception('Class member needs a class type: $desc'); | |
276 } | 265 } |
277 TypeName type = classMember.returnType; | 266 TypeName type = classMember.returnType; |
278 bool isList = false; | 267 bool isList = false; |
279 if (type.name.name == 'List' && | 268 if (type.name == 'List' && |
280 type.typeArguments != null && | 269 type.typeArguments != null && |
281 type.typeArguments.arguments.length == 1) { | 270 type.typeArguments.length == 1) { |
282 isList = true; | 271 isList = true; |
283 type = type.typeArguments.arguments[0]; | 272 type = type.typeArguments[0]; |
284 } | 273 } |
285 if (type.typeArguments != null) { | 274 if (type.typeArguments != null) { |
286 throw new Exception('Cannot handle type arguments in `$type`'); | 275 throw new Exception('Cannot handle type arguments in `$type`'); |
287 } | 276 } |
288 int id; | 277 int id; |
289 bool isDeprecated = false; | 278 bool isDeprecated = false; |
290 bool isInformative = false; | 279 bool isInformative = false; |
291 for (Annotation annotation in classMember.metadata) { | 280 for (Annotation annotation in classMember.metadata) { |
292 if (annotation.name.name == 'Id') { | 281 if (annotation.name == 'Id') { |
293 if (id != null) { | 282 if (id != null) { |
294 throw new Exception( | 283 throw new Exception( |
295 'Duplicate @id annotation ($classMember)'); | 284 'Duplicate @id annotation ($classMember)'); |
296 } | 285 } |
297 if (annotation.arguments.arguments.length != 1) { | 286 if (annotation.arguments == null) { |
| 287 throw new Exception('@Id must be passed an argument'); |
| 288 } |
| 289 if (annotation.arguments.length != 1) { |
298 throw new Exception( | 290 throw new Exception( |
299 '@Id must be passed exactly one argument ($desc)'); | 291 '@Id must be passed exactly one argument ($desc)'); |
300 } | 292 } |
301 Expression expression = annotation.arguments.arguments[0]; | 293 Expression expression = annotation.arguments[0]; |
302 if (expression is IntegerLiteral) { | 294 if (expression is IntegerLiteral) { |
303 id = expression.value; | 295 id = expression.value; |
304 } else { | 296 } else { |
305 throw new Exception( | 297 throw new Exception( |
306 '@Id parameter must be an integer literal ($desc)'); | 298 '@Id parameter must be an integer literal ($desc)'); |
307 } | 299 } |
308 } else if (annotation.name.name == 'deprecated') { | 300 } else if (annotation.name == 'deprecated') { |
309 if (annotation.arguments != null) { | 301 if (annotation.arguments != null) { |
310 throw new Exception('@deprecated does not take args ($desc)'); | 302 throw new Exception('@deprecated does not take args ($desc)'); |
311 } | 303 } |
312 isDeprecated = true; | 304 isDeprecated = true; |
313 } else if (annotation.name.name == 'informative') { | 305 } else if (annotation.name == 'informative') { |
314 isInformative = true; | 306 isInformative = true; |
315 } | 307 } |
316 } | 308 } |
317 if (id == null) { | 309 if (id == null) { |
318 throw new Exception('Missing @id annotation ($desc)'); | 310 throw new Exception('Missing @id annotation ($desc)'); |
319 } | 311 } |
320 String doc = _getNodeDoc(lineInfo, classMember); | 312 String doc = _getNodeDoc(classMember); |
321 idlModel.FieldType fieldType = | 313 idlModel.FieldType fieldType = |
322 new idlModel.FieldType(type.name.name, isList); | 314 new idlModel.FieldType(type.name, isList); |
323 cls.allFields.add(new idlModel.FieldDeclaration( | 315 cls.allFields.add(new idlModel.FieldDeclaration(doc, |
324 doc, | 316 classMember.name, fieldType, id, isDeprecated, isInformative)); |
325 classMember.name.name, | |
326 fieldType, | |
327 id, | |
328 isDeprecated, | |
329 isInformative)); | |
330 } else if (classMember is ConstructorDeclaration && | 317 } else if (classMember is ConstructorDeclaration && |
331 classMember.name.name == 'fromBuffer') { | 318 classMember.name.name.endsWith('fromBuffer')) { |
332 // Ignore `fromBuffer` declarations; they simply forward to the | 319 // Ignore `fromBuffer` declarations; they simply forward to the |
333 // read functions generated by [_generateReadFunction]. | 320 // read functions generated by [_generateReadFunction]. |
334 } else { | 321 } else { |
335 throw new Exception('Unexpected class member `$classMember`'); | 322 throw new Exception('Unexpected class member `$classMember`'); |
336 } | 323 } |
337 } | 324 } |
338 } else if (decl is EnumDeclaration) { | 325 } else if (decl is EnumDeclaration) { |
339 String doc = _getNodeDoc(lineInfo, decl); | 326 String doc = _getNodeDoc(decl); |
340 idlModel.EnumDeclaration enm = | 327 idlModel.EnumDeclaration enm = |
341 new idlModel.EnumDeclaration(doc, decl.name.name); | 328 new idlModel.EnumDeclaration(doc, decl.name); |
342 _idl.enums[enm.name] = enm; | 329 _idl.enums[enm.name] = enm; |
343 for (EnumConstantDeclaration constDecl in decl.constants) { | 330 for (EnumConstantDeclaration constDecl in decl.constants) { |
344 String doc = _getNodeDoc(lineInfo, constDecl); | 331 String doc = _getNodeDoc(constDecl); |
345 enm.values | 332 enm.values |
346 .add(new idlModel.EnumValueDeclaration(doc, constDecl.name.name)); | 333 .add(new idlModel.EnumValueDeclaration(doc, constDecl.name)); |
347 } | 334 } |
348 } else if (decl is TopLevelVariableDeclaration) { | |
349 // Ignore top level variable declarations; they are present just to make | |
350 // the IDL analyze without warnings. | |
351 } else { | 335 } else { |
352 throw new Exception('Unexpected declaration `$decl`'); | 336 throw new Exception('Unexpected declaration `$decl`'); |
353 } | 337 } |
354 } | 338 } |
355 } | 339 } |
356 | 340 |
357 /** | 341 /** |
358 * Generate a string representing the FlatBuffer schema type which should be | 342 * Generate a string representing the FlatBuffer schema type which should be |
359 * used to represent [type]. | 343 * used to represent [type]. |
360 */ | 344 */ |
(...skipping 647 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1008 default: | 992 default: |
1009 throw "Don't know how to generate signature call for $typeName"; | 993 throw "Don't know how to generate signature call for $typeName"; |
1010 } | 994 } |
1011 } | 995 } |
1012 } | 996 } |
1013 | 997 |
1014 /** | 998 /** |
1015 * Return the documentation text of the given [node], or `null` if the [node] | 999 * Return the documentation text of the given [node], or `null` if the [node] |
1016 * does not have a comment. Each line is `\n` separated. | 1000 * does not have a comment. Each line is `\n` separated. |
1017 */ | 1001 */ |
1018 String _getNodeDoc(LineInfo lineInfo, AnnotatedNode node) { | 1002 String _getNodeDoc(AnnotatedNode node) { |
1019 Comment comment = node.documentationComment; | 1003 Comment comment = node.documentationComment; |
1020 if (comment != null && | 1004 if (comment != null && |
1021 comment.isDocumentation && | 1005 comment.isDocumentation && |
1022 comment.tokens.length == 1 && | 1006 comment.tokens.length == 1 && |
1023 comment.tokens.first.type == TokenType.MULTI_LINE_COMMENT) { | 1007 comment.tokens.first.lexeme.startsWith('/*')) { |
1024 Token token = comment.tokens.first; | 1008 Token token = comment.tokens.first; |
1025 int column = lineInfo.getLocation(token.offset).columnNumber; | |
1026 String indent = ' ' * (column - 1); | |
1027 return token.lexeme.split('\n').map((String line) { | 1009 return token.lexeme.split('\n').map((String line) { |
1028 if (line.startsWith(indent)) { | 1010 line = line.trimLeft(); |
1029 line = line.substring(indent.length); | 1011 if (line.startsWith('*')) line = ' ' + line; |
1030 } | |
1031 return line; | 1012 return line; |
1032 }).join('\n'); | 1013 }).join('\n'); |
1033 } | 1014 } |
1034 return null; | 1015 return null; |
1035 } | 1016 } |
1036 } | 1017 } |
OLD | NEW |