| 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 |