| OLD | NEW |
| (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 library dev_compiler.src.codegen.dart_codegen; | |
| 6 | |
| 7 import 'dart:io' show File; | |
| 8 | |
| 9 import 'package:analyzer/analyzer.dart' as analyzer; | |
| 10 import 'package:analyzer/src/generated/ast.dart'; | |
| 11 import 'package:analyzer/src/generated/element.dart'; | |
| 12 import 'package:analyzer/src/generated/java_core.dart' as java_core; | |
| 13 import 'package:analyzer/src/generated/scanner.dart' show Token; | |
| 14 import 'package:logging/logging.dart' as logger; | |
| 15 import 'package:path/path.dart' as path; | |
| 16 | |
| 17 import 'package:dev_compiler/devc.dart' show AbstractCompiler; | |
| 18 import 'package:dev_compiler/src/info.dart'; | |
| 19 import 'package:dev_compiler/src/options.dart'; | |
| 20 import 'package:dev_compiler/src/utils.dart' as utils; | |
| 21 import 'ast_builder.dart'; | |
| 22 import 'code_generator.dart' as codegenerator; | |
| 23 import 'reify_coercions.dart' | |
| 24 show CoercionReifier, NewTypeIdDesc, InstrumentedRuntime; | |
| 25 | |
| 26 final _log = new logger.Logger('dev_compiler.dart_codegen'); | |
| 27 | |
| 28 class DevCompilerRuntime extends InstrumentedRuntime { | |
| 29 Identifier _runtimeId = AstBuilder.identifierFromString("DEVC\$RT"); | |
| 30 | |
| 31 Identifier _castId; | |
| 32 Identifier _typeToTypeId; | |
| 33 Identifier _wrapId; | |
| 34 | |
| 35 DevCompilerRuntime() { | |
| 36 _castId = _prefixId(AstBuilder.identifierFromString("cast")); | |
| 37 _typeToTypeId = _prefixId(AstBuilder.identifierFromString("type")); | |
| 38 _wrapId = _prefixId(AstBuilder.identifierFromString("wrap")); | |
| 39 } | |
| 40 | |
| 41 String get importString { | |
| 42 var name = _runtimeId; | |
| 43 var uri = "package:dev_compiler/runtime/dart_logging_runtime.dart"; | |
| 44 return "import '$uri' as $name;"; | |
| 45 } | |
| 46 | |
| 47 Identifier _prefixId(Identifier id) => | |
| 48 AstBuilder.prefixedIdentifier(_runtimeId, id); | |
| 49 | |
| 50 Identifier runtimeId(RuntimeOperation oper) { | |
| 51 if (oper.operation == "cast") return _castId; | |
| 52 if (oper.operation == "wrap") return _wrapId; | |
| 53 if (oper.operation == "type") return _typeToTypeId; | |
| 54 assert(false); | |
| 55 return null; | |
| 56 } | |
| 57 | |
| 58 Expression runtimeOperation(RuntimeOperation oper) { | |
| 59 var id = runtimeId(oper); | |
| 60 var args = oper.arguments; | |
| 61 return AstBuilder.application(id, args); | |
| 62 } | |
| 63 | |
| 64 @override | |
| 65 Expression wrap(Expression coercion, Expression e, Expression fromType, | |
| 66 Expression toType, Expression dartIs, String kind, String location) { | |
| 67 var k = AstBuilder.stringLiteral(kind); | |
| 68 var key = AstBuilder.multiLineStringLiteral(location); | |
| 69 var arguments = <Expression>[coercion, e, fromType, toType, k, key, dartIs]; | |
| 70 return new RuntimeOperation("wrap", arguments); | |
| 71 } | |
| 72 | |
| 73 Expression cast(Expression e, Expression fromType, Expression toType, | |
| 74 Expression dartIs, String kind, String location, bool ground) { | |
| 75 var k = AstBuilder.stringLiteral(kind); | |
| 76 var key = AstBuilder.multiLineStringLiteral(location); | |
| 77 var g = AstBuilder.booleanLiteral(ground); | |
| 78 var arguments = <Expression>[e, fromType, toType, k, key, dartIs, g]; | |
| 79 return new RuntimeOperation("cast", arguments); | |
| 80 } | |
| 81 | |
| 82 Expression type(Expression witnessFunction) { | |
| 83 return new RuntimeOperation("type", <Expression>[witnessFunction]); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 // TODO(leafp) This is kind of a hack, but it works for now. | |
| 88 class FileWriter extends java_core.PrintStringWriter { | |
| 89 final CompilerOptions options; | |
| 90 String _path; | |
| 91 FileWriter(this.options, this._path); | |
| 92 int indent = 0; | |
| 93 int withinInterpolationExpression = 0; | |
| 94 bool insideForLoop = false; | |
| 95 | |
| 96 void print(x) { | |
| 97 if (!options.formatOutput) { | |
| 98 super.print(x); | |
| 99 return; | |
| 100 } | |
| 101 | |
| 102 switch (x) { | |
| 103 case '{': | |
| 104 indent++; | |
| 105 x = '{\n${" " * indent}'; | |
| 106 break; | |
| 107 case ';': | |
| 108 if (!insideForLoop) { | |
| 109 x = ';\n${" " * indent}'; | |
| 110 } | |
| 111 break; | |
| 112 case 'for (': | |
| 113 insideForLoop = true; | |
| 114 break; | |
| 115 case ') ': | |
| 116 insideForLoop = false; | |
| 117 break; | |
| 118 case r'${': | |
| 119 withinInterpolationExpression++; | |
| 120 break; | |
| 121 case '}': | |
| 122 if (withinInterpolationExpression > 0) { | |
| 123 withinInterpolationExpression--; | |
| 124 } else { | |
| 125 indent--; | |
| 126 x = '}\n${" " * indent}'; | |
| 127 } | |
| 128 break; | |
| 129 } | |
| 130 super.print(x); | |
| 131 } | |
| 132 | |
| 133 void finalize() { | |
| 134 String s = toString(); | |
| 135 _log.fine("Writing file $_path"); | |
| 136 new File(_path).writeAsStringSync(s); | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 bool _identifierNeedsQualification(Identifier id, NewTypeIdDesc desc) { | |
| 141 var library = desc.importedFrom; | |
| 142 if (library == null) return false; | |
| 143 if (library.isDartCore) return false; | |
| 144 if (desc.fromCurrent) return false; | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 // This class just holds some additional syntactic helpers and | |
| 149 // fixes to the general ToSourceVisitor for use by subclasses. | |
| 150 abstract class UnitGeneratorCommon extends analyzer.ToSourceVisitor { | |
| 151 UnitGeneratorCommon(java_core.PrintWriter out) : super(out); | |
| 152 | |
| 153 void output(String s); | |
| 154 void outputln(String s); | |
| 155 | |
| 156 // Copied from ast.dart | |
| 157 void visitNodeListWithSeparatorAndSuffix( | |
| 158 NodeList<AstNode> nodes, String separator, String suffix) { | |
| 159 if (nodes != null) { | |
| 160 int size = nodes.length; | |
| 161 if (size > 0) { | |
| 162 for (int i = 0; i < size; i++) { | |
| 163 if (i > 0) { | |
| 164 output(separator); | |
| 165 } | |
| 166 nodes[i].accept(this); | |
| 167 } | |
| 168 output(suffix); | |
| 169 } | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 // Copied from ast.dart | |
| 174 void visitListWithSeparatorAndPrefix( | |
| 175 String prefix, List<AstNode> nodes, String separator) { | |
| 176 if (nodes != null) { | |
| 177 int size = nodes.length; | |
| 178 if (size > 0) { | |
| 179 output(prefix); | |
| 180 for (int i = 0; i < size; i++) { | |
| 181 if (i > 0) { | |
| 182 output(separator); | |
| 183 } | |
| 184 nodes[i].accept(this); | |
| 185 } | |
| 186 } | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 // Copied from ast.dart | |
| 191 void visitNodeListWithSeparatorAndPrefix( | |
| 192 String prefix, NodeList<AstNode> nodes, String separator) { | |
| 193 visitListWithSeparatorAndPrefix(prefix, nodes, separator); | |
| 194 } | |
| 195 | |
| 196 // Copied from ast.dart | |
| 197 void visitTokenWithSuffix(Token token, String suffix) { | |
| 198 if (token != null) { | |
| 199 output(token.lexeme); | |
| 200 output(suffix); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 // Copied from ast.dart | |
| 205 void safelyVisitNode(AstNode node) { | |
| 206 if (node != null) { | |
| 207 node.accept(this); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 // Copied from ast.dart | |
| 212 void visitNodeWithPrefix(String prefix, AstNode node) { | |
| 213 if (node != null) { | |
| 214 output(prefix); | |
| 215 node.accept(this); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 // Copied from ast.dart | |
| 220 void visitNodeWithSuffix(AstNode node, String suffix) { | |
| 221 if (node != null) { | |
| 222 node.accept(this); | |
| 223 output(suffix); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 // Overridden to add external keyword if present | |
| 228 @override | |
| 229 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 230 visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " "); | |
| 231 visitTokenWithSuffix(node.externalKeyword, " "); | |
| 232 visitNodeWithSuffix(node.returnType, " "); | |
| 233 visitTokenWithSuffix(node.propertyKeyword, " "); | |
| 234 safelyVisitNode(node.name); | |
| 235 safelyVisitNode(node.functionExpression); | |
| 236 return null; | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 // TODO(leafp) Not sure if this is the right way to generate | |
| 241 // Dart source going forward, but it's a quick way to get started. | |
| 242 class UnitGenerator extends UnitGeneratorCommon with ConversionVisitor<Object> { | |
| 243 CompilationUnit unit; | |
| 244 final java_core.PrintWriter _out; | |
| 245 final String outDir; | |
| 246 Set<LibraryElement> _extraImports; | |
| 247 final _runtime; | |
| 248 bool _qualifyNames = true; | |
| 249 Map<Identifier, NewTypeIdDesc> _newIdentifiers; | |
| 250 | |
| 251 UnitGenerator(this.unit, java_core.PrintWriter out, String this.outDir, | |
| 252 this._extraImports, this._newIdentifiers, this._runtime) | |
| 253 : _out = out, | |
| 254 super(out); | |
| 255 | |
| 256 void output(String s) => _out.print(s); | |
| 257 void outputln(String s) => _out.println(s); | |
| 258 | |
| 259 // Choose a canonical prefix for a library that we are adding. | |
| 260 // Currently just chooses something unlikely to conflict with a user | |
| 261 // prefix. | |
| 262 // TODO(leafp): Make this robust. | |
| 263 String canonizeLibraryName(String name) { | |
| 264 name = name.replaceAll(".", "DOT"); | |
| 265 name = "DDC\$$name\$"; | |
| 266 return name; | |
| 267 } | |
| 268 | |
| 269 // Split the directives into the various kinds (since there are restrictions | |
| 270 // in the syntax as to order of directives). | |
| 271 Map<String, List<Directive>> splitDirectives(NodeList<Directive> directives) { | |
| 272 return { | |
| 273 'export': directives.where((d) => d is ExportDirective).toList(), | |
| 274 'import': directives.where((d) => d is ImportDirective).toList(), | |
| 275 'library': directives.where((d) => d is LibraryDirective).toList(), | |
| 276 'part': directives.where((d) => d is PartDirective).toList(), | |
| 277 'partof': directives.where((d) => d is PartOfDirective).toList(), | |
| 278 }; | |
| 279 } | |
| 280 | |
| 281 // Build the set of import directives corresponding to the | |
| 282 // extra imports required by the types that we add in via | |
| 283 // inference | |
| 284 List<String> buildExtraImportDirectives() { | |
| 285 return _extraImports.map((lib) { | |
| 286 var name = utils.canonicalLibraryName(lib); | |
| 287 name = canonizeLibraryName(name); | |
| 288 var uri = codegenerator.CodeGenerator.uriFor(lib); | |
| 289 return "import '$uri' as $name;"; | |
| 290 }).toList(); | |
| 291 } | |
| 292 | |
| 293 // Rewrite the import directives with additional library imports | |
| 294 // covering the inferred types added in as part of this pass. | |
| 295 void _visitDirectives(String prefix, List<Directive> directives) { | |
| 296 _qualifyNames = false; | |
| 297 var ds = splitDirectives(directives); | |
| 298 visitListWithSeparatorAndPrefix(prefix, ds['library'], " "); | |
| 299 if (ds['library'].length != 0) { | |
| 300 assert(ds['partof'].length == 0); | |
| 301 var es = buildExtraImportDirectives(); | |
| 302 es.add(_runtime.importString); | |
| 303 es.forEach(outputln); | |
| 304 } | |
| 305 visitListWithSeparatorAndPrefix(prefix, ds['partof'], " "); | |
| 306 visitListWithSeparatorAndPrefix(prefix, ds['import'], " "); | |
| 307 visitListWithSeparatorAndPrefix(prefix, ds['export'], " "); | |
| 308 visitListWithSeparatorAndPrefix(prefix, ds['part'], " "); | |
| 309 _qualifyNames = true; | |
| 310 } | |
| 311 | |
| 312 @override | |
| 313 Object visitNode(AstNode node) { | |
| 314 if (node != null) { | |
| 315 node.visitChildren(this); | |
| 316 } | |
| 317 return null; | |
| 318 } | |
| 319 | |
| 320 @override | |
| 321 Object visitRuntimeOperation(RuntimeOperation oper) { | |
| 322 var e = _runtime.runtimeOperation(oper); | |
| 323 e.accept(this); | |
| 324 return null; | |
| 325 } | |
| 326 | |
| 327 @override | |
| 328 Object visitAsExpression(AsExpression node) { | |
| 329 _log.severe("Unlowered as expression"); | |
| 330 assert(false); | |
| 331 return null; | |
| 332 } | |
| 333 | |
| 334 @override | |
| 335 Object visitCompilationUnit(CompilationUnit node) { | |
| 336 ScriptTag scriptTag = node.scriptTag; | |
| 337 NodeList<Directive> directives = node.directives; | |
| 338 safelyVisitNode(scriptTag); | |
| 339 String prefix = scriptTag == null ? "" : " "; | |
| 340 _visitDirectives(prefix, directives); | |
| 341 prefix = scriptTag == null && directives.isEmpty ? "" : " "; | |
| 342 visitNodeListWithSeparatorAndPrefix(prefix, node.declarations, " "); | |
| 343 return null; | |
| 344 } | |
| 345 | |
| 346 @override | |
| 347 Object visitPrefixedIdentifier(PrefixedIdentifier id) { | |
| 348 safelyVisitNode(id.prefix); | |
| 349 output('.'); | |
| 350 output(id.identifier.token.lexeme); | |
| 351 return null; | |
| 352 } | |
| 353 | |
| 354 @override | |
| 355 Object visitSimpleIdentifier(SimpleIdentifier id) { | |
| 356 if (!(_qualifyNames && | |
| 357 _newIdentifiers.containsKey(id) && | |
| 358 _identifierNeedsQualification(id, _newIdentifiers[id]))) { | |
| 359 return super.visitSimpleIdentifier(id); | |
| 360 } | |
| 361 var library = _newIdentifiers[id].importedFrom; | |
| 362 if (!utils.isDartPrivateLibrary(library)) { | |
| 363 var lib = utils.canonicalLibraryName(library); | |
| 364 var libname = canonizeLibraryName(lib); | |
| 365 output(libname); | |
| 366 output('.'); | |
| 367 } | |
| 368 output(id.name); | |
| 369 return null; | |
| 370 } | |
| 371 | |
| 372 void generate() { | |
| 373 visitCompilationUnit(unit); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 class DartGenerator extends codegenerator.CodeGenerator { | |
| 378 final DevCompilerRuntime _runtime = new DevCompilerRuntime(); | |
| 379 | |
| 380 DartGenerator(AbstractCompiler compiler) : super(compiler); | |
| 381 | |
| 382 Set<LibraryElement> computeExtraImports(Map<Identifier, NewTypeIdDesc> ids) { | |
| 383 var imports = new Set<LibraryElement>(); | |
| 384 void process(Identifier id, NewTypeIdDesc desc) { | |
| 385 if (_identifierNeedsQualification(id, desc)) { | |
| 386 var library = desc.importedFrom; | |
| 387 if (utils.isDartPrivateLibrary(library)) { | |
| 388 _log.severe("Dropping import of private library ${library}\n"); | |
| 389 return; | |
| 390 } | |
| 391 imports.add(library); | |
| 392 } | |
| 393 } | |
| 394 ids.forEach(process); | |
| 395 return imports; | |
| 396 } | |
| 397 | |
| 398 String generateLibrary(LibraryUnit library, LibraryInfo info) { | |
| 399 var r = new CoercionReifier(library, compiler, _runtime); | |
| 400 var ids = r.reify(); | |
| 401 var extraImports = computeExtraImports(ids); | |
| 402 | |
| 403 for (var unit in library.partsThenLibrary) { | |
| 404 var libraryDir = makeOutputDirectory(info, unit); | |
| 405 var uri = unit.element.source.uri; | |
| 406 _log.fine("Generating unit $uri"); | |
| 407 FileWriter out = new FileWriter( | |
| 408 options, path.join(libraryDir, '${uri.pathSegments.last}')); | |
| 409 var unitGen = | |
| 410 new UnitGenerator(unit, out, outDir, extraImports, ids, _runtime); | |
| 411 unitGen.generate(); | |
| 412 out.finalize(); | |
| 413 } | |
| 414 | |
| 415 return null; | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 class EmptyUnitGenerator extends UnitGeneratorCommon { | |
| 420 final java_core.PrintWriter _out; | |
| 421 CompilationUnit unit; | |
| 422 | |
| 423 EmptyUnitGenerator(this.unit, java_core.PrintWriter out) | |
| 424 : _out = out, | |
| 425 super(out); | |
| 426 | |
| 427 void output(String s) => _out.print(s); | |
| 428 void outputln(String s) => _out.println(s); | |
| 429 | |
| 430 void generate() { | |
| 431 unit.visitChildren(this); | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 // This class emits the code unchanged, for comparison purposes. | |
| 436 class EmptyDartGenerator extends codegenerator.CodeGenerator { | |
| 437 EmptyDartGenerator(AbstractCompiler compiler) : super(compiler); | |
| 438 | |
| 439 String generateLibrary(LibraryUnit library, LibraryInfo info) { | |
| 440 for (var unit in library.partsThenLibrary) { | |
| 441 var outputDir = makeOutputDirectory(info, unit); | |
| 442 generateUnit(unit, info, outputDir); | |
| 443 } | |
| 444 return null; | |
| 445 } | |
| 446 | |
| 447 void generateUnit(CompilationUnit unit, LibraryInfo info, String libraryDir) { | |
| 448 var uri = unit.element.source.uri; | |
| 449 _log.fine("Emitting original unit " + uri.toString()); | |
| 450 FileWriter out = new FileWriter( | |
| 451 options, path.join(libraryDir, '${uri.pathSegments.last}')); | |
| 452 var unitGen = new EmptyUnitGenerator(unit, out); | |
| 453 unitGen.generate(); | |
| 454 out.finalize(); | |
| 455 } | |
| 456 } | |
| OLD | NEW |