| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, 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 part of dart_backend; | |
| 6 | |
| 7 // TODO(ahe): This class is simply wrong. This backend should use | |
| 8 // elements when it can, not AST nodes. Perhaps a [Map<Element, | |
| 9 // TreeElements>] is what is needed. | |
| 10 class ElementAst { | |
| 11 final Node ast; | |
| 12 final TreeElements treeElements; | |
| 13 | |
| 14 ElementAst(AstElement element) | |
| 15 : this.internal(element.resolvedAst.node, element.resolvedAst.elements); | |
| 16 | |
| 17 ElementAst.internal(this.ast, this.treeElements); | |
| 18 } | |
| 19 | |
| 20 class DartBackend extends Backend { | |
| 21 final List<CompilerTask> tasks; | |
| 22 final bool stripAsserts; | |
| 23 | |
| 24 // TODO(zarah) Maybe change this to a command-line option. | |
| 25 // Right now, it is set by the tests. | |
| 26 bool useMirrorHelperLibrary = false; | |
| 27 | |
| 28 /// Updated to a [MirrorRenamerImpl] instance if the [useMirrorHelperLibrary] | |
| 29 /// field is set and mirror are needed. | |
| 30 MirrorRenamer mirrorRenamer = const MirrorRenamer(); | |
| 31 | |
| 32 final DartOutputter outputter; | |
| 33 | |
| 34 // Used in test. | |
| 35 PlaceholderRenamer get placeholderRenamer => outputter.renamer; | |
| 36 Map<ClassNode, List<Node>> get memberNodes => outputter.output.memberNodes; | |
| 37 | |
| 38 ConstantSystem get constantSystem { | |
| 39 return constantCompilerTask.constantCompiler.constantSystem; | |
| 40 } | |
| 41 | |
| 42 BackendConstantEnvironment get constants => constantCompilerTask; | |
| 43 | |
| 44 DartConstantTask constantCompilerTask; | |
| 45 | |
| 46 DartResolutionCallbacks resolutionCallbacks; | |
| 47 | |
| 48 final Set<ClassElement> usedTypeLiterals = new Set<ClassElement>(); | |
| 49 | |
| 50 /** | |
| 51 * Tells whether it is safe to remove type declarations from variables, | |
| 52 * functions parameters. It becomes not safe if: | |
| 53 * 1) TypeError is used somewhere in the code, | |
| 54 * 2) The code has typedefs in right hand side of IS checks, | |
| 55 * 3) The code has classes which extend typedefs, have type arguments typedefs | |
| 56 * or type variable bounds typedefs. | |
| 57 * These restrictions can be less strict. | |
| 58 */ | |
| 59 bool isSafeToRemoveTypeDeclarations( | |
| 60 Map<ClassElement, Iterable<Element>> classMembers) { | |
| 61 ClassElement typeErrorElement = compiler.coreLibrary.find('TypeError'); | |
| 62 if (classMembers.containsKey(typeErrorElement) || | |
| 63 compiler.resolverWorld.isChecks.any( | |
| 64 (DartType type) => type.element == typeErrorElement)) { | |
| 65 return false; | |
| 66 } | |
| 67 Set<DartType> processedTypes = new Set<DartType>(); | |
| 68 List<DartType> workQueue = new List<DartType>(); | |
| 69 workQueue.addAll( | |
| 70 classMembers.keys.map((classElement) => classElement.thisType)); | |
| 71 workQueue.addAll(compiler.resolverWorld.isChecks); | |
| 72 | |
| 73 while (!workQueue.isEmpty) { | |
| 74 DartType type = workQueue.removeLast(); | |
| 75 if (processedTypes.contains(type)) continue; | |
| 76 processedTypes.add(type); | |
| 77 if (type is FunctionType) return false; | |
| 78 if (type is TypedefType) return false; | |
| 79 if (type is InterfaceType) { | |
| 80 InterfaceType interfaceType = type; | |
| 81 // Check all type arguments. | |
| 82 interfaceType.typeArguments.forEach(workQueue.add); | |
| 83 ClassElement element = type.element; | |
| 84 // Check all supertypes. | |
| 85 if (element.allSupertypes != null) { | |
| 86 element.allSupertypes.forEach(workQueue.add); | |
| 87 } | |
| 88 } | |
| 89 } | |
| 90 return true; | |
| 91 } | |
| 92 | |
| 93 DartBackend(Compiler compiler, List<String> strips, {bool multiFile}) | |
| 94 : tasks = <CompilerTask>[], | |
| 95 stripAsserts = strips.indexOf('asserts') != -1, | |
| 96 constantCompilerTask = new DartConstantTask(compiler), | |
| 97 outputter = new DartOutputter( | |
| 98 compiler, compiler.outputProvider, | |
| 99 forceStripTypes: strips.indexOf('types') != -1, | |
| 100 multiFile: multiFile, | |
| 101 enableMinification: compiler.enableMinification), | |
| 102 super(compiler) { | |
| 103 resolutionCallbacks = new DartResolutionCallbacks(this); | |
| 104 } | |
| 105 | |
| 106 bool classNeedsRti(ClassElement cls) => false; | |
| 107 bool methodNeedsRti(FunctionElement function) => false; | |
| 108 | |
| 109 void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { | |
| 110 // Right now resolver doesn't always resolve interfaces needed | |
| 111 // for literals, so force them. TODO(antonm): fix in the resolver. | |
| 112 final LITERAL_TYPE_NAMES = const [ | |
| 113 'Map', 'List', 'num', 'int', 'double', 'bool' | |
| 114 ]; | |
| 115 final coreLibrary = compiler.coreLibrary; | |
| 116 for (final name in LITERAL_TYPE_NAMES) { | |
| 117 ClassElement classElement = coreLibrary.findLocal(name); | |
| 118 classElement.ensureResolved(compiler); | |
| 119 } | |
| 120 // Enqueue the methods that the VM might invoke on user objects because | |
| 121 // we don't trust the resolution to always get these included. | |
| 122 world.registerInvocation(new Selector.call("toString", null, 0)); | |
| 123 world.registerInvokedGetter(new Selector.getter("hashCode", null)); | |
| 124 world.registerInvocation(new Selector.binaryOperator("==")); | |
| 125 world.registerInvocation(new Selector.call("compareTo", null, 1)); | |
| 126 } | |
| 127 | |
| 128 void codegen(CodegenWorkItem work) { } | |
| 129 | |
| 130 /// Create an [ElementAst] from the CPS IR. | |
| 131 static ElementAst createElementAst(Compiler compiler, | |
| 132 Tracer tracer, | |
| 133 ConstantSystem constantSystem, | |
| 134 Element element, | |
| 135 cps_ir.FunctionDefinition function) { | |
| 136 // Transformations on the CPS IR. | |
| 137 if (tracer != null) { | |
| 138 tracer.traceCompilation(element.name, null); | |
| 139 } | |
| 140 | |
| 141 void traceGraph(String title, var irObject) { | |
| 142 if (tracer != null) { | |
| 143 tracer.traceGraph(title, irObject); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 new ConstantPropagator(compiler, constantSystem).rewrite(function); | |
| 148 traceGraph("Sparse constant propagation", function); | |
| 149 new RedundantPhiEliminator().rewrite(function); | |
| 150 traceGraph("Redundant phi elimination", function); | |
| 151 new ShrinkingReducer().rewrite(function); | |
| 152 traceGraph("Shrinking reductions", function); | |
| 153 | |
| 154 // Do not rewrite the IR after variable allocation. Allocation | |
| 155 // makes decisions based on an approximation of IR variable live | |
| 156 // ranges that can be invalidated by transforming the IR. | |
| 157 new cps_ir.RegisterAllocator().visit(function); | |
| 158 | |
| 159 tree_builder.Builder builder = new tree_builder.Builder(compiler); | |
| 160 tree_ir.FunctionDefinition definition = builder.build(function); | |
| 161 assert(definition != null); | |
| 162 traceGraph('Tree builder', definition); | |
| 163 | |
| 164 // Transformations on the Tree IR. | |
| 165 new StatementRewriter().rewrite(definition); | |
| 166 traceGraph('Statement rewriter', definition); | |
| 167 new CopyPropagator().rewrite(definition); | |
| 168 traceGraph('Copy propagation', definition); | |
| 169 new LoopRewriter().rewrite(definition); | |
| 170 traceGraph('Loop rewriter', definition); | |
| 171 new LogicalRewriter().rewrite(definition); | |
| 172 traceGraph('Logical rewriter', definition); | |
| 173 new backend_ast_emitter.UnshadowParameters().unshadow(definition); | |
| 174 traceGraph('Unshadow parameters', definition); | |
| 175 | |
| 176 TreeElementMapping treeElements = new TreeElementMapping(element); | |
| 177 backend_ast.Node backendAst = | |
| 178 backend_ast_emitter.emit(definition); | |
| 179 Node frontend_ast = backend2frontend.emit(treeElements, backendAst); | |
| 180 return new ElementAst.internal(frontend_ast, treeElements); | |
| 181 | |
| 182 } | |
| 183 | |
| 184 /** | |
| 185 * Tells whether we should output given element. Corelib classes like | |
| 186 * Object should not be in the resulting code. | |
| 187 */ | |
| 188 @override | |
| 189 bool shouldOutput(Element element) { | |
| 190 return (!element.library.isPlatformLibrary && | |
| 191 !element.isSynthesized && | |
| 192 element is! AbstractFieldElement) | |
| 193 || mirrorRenamer.isMirrorHelperLibrary(element.library); | |
| 194 } | |
| 195 | |
| 196 void assembleProgram() { | |
| 197 | |
| 198 ElementAst computeElementAst(AstElement element) { | |
| 199 if (!compiler.irBuilder.hasIr(element)) { | |
| 200 return new ElementAst(element); | |
| 201 } else { | |
| 202 cps_ir.FunctionDefinition function = compiler.irBuilder.getIr(element); | |
| 203 return createElementAst(compiler, | |
| 204 compiler.tracer, constantSystem, element, function); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 // TODO(johnniwinther): Remove the need for this method. | |
| 209 void postProcessElementAst( | |
| 210 AstElement element, ElementAst elementAst, | |
| 211 newTypedefElementCallback, | |
| 212 newClassElementCallback) { | |
| 213 ReferencedElementCollector collector = | |
| 214 new ReferencedElementCollector(compiler, | |
| 215 element, | |
| 216 elementAst, | |
| 217 newTypedefElementCallback, | |
| 218 newClassElementCallback); | |
| 219 collector.collect(); | |
| 220 } | |
| 221 | |
| 222 String assembledCode = outputter.assembleProgram( | |
| 223 libraries: compiler.libraryLoader.libraries, | |
| 224 instantiatedClasses: compiler.resolverWorld.directlyInstantiatedClasses, | |
| 225 resolvedElements: compiler.enqueuer.resolution.resolvedElements, | |
| 226 usedTypeLiterals: usedTypeLiterals, | |
| 227 postProcessElementAst: postProcessElementAst, | |
| 228 computeElementAst: computeElementAst, | |
| 229 shouldOutput: shouldOutput, | |
| 230 isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations, | |
| 231 sortElements: sortElements, | |
| 232 mirrorRenamer: mirrorRenamer, | |
| 233 mainFunction: compiler.mainFunction, | |
| 234 outputUri: compiler.outputUri); | |
| 235 compiler.assembledCode = assembledCode; | |
| 236 | |
| 237 int totalSize = assembledCode.length; | |
| 238 | |
| 239 // Output verbose info about size ratio of resulting bundle to all | |
| 240 // referenced non-platform sources. | |
| 241 logResultBundleSizeInfo( | |
| 242 outputter.libraryInfo.userLibraries, | |
| 243 outputter.elementInfo.topLevelElements, | |
| 244 assembledCode.length); | |
| 245 } | |
| 246 | |
| 247 void logResultBundleSizeInfo(Iterable<LibraryElement> userLibraries, | |
| 248 Iterable<Element> topLevelElements, | |
| 249 int totalOutputSize) { | |
| 250 // Sum total size of scripts in each referenced library. | |
| 251 int nonPlatformSize = 0; | |
| 252 for (LibraryElement lib in userLibraries) { | |
| 253 for (CompilationUnitElement compilationUnit in lib.compilationUnits) { | |
| 254 nonPlatformSize += compilationUnit.script.file.length; | |
| 255 } | |
| 256 } | |
| 257 int percentage = totalOutputSize * 100 ~/ nonPlatformSize; | |
| 258 log('Total used non-platform files size: ${nonPlatformSize} bytes, ' | |
| 259 'Output total size: $totalOutputSize bytes (${percentage}%)'); | |
| 260 } | |
| 261 | |
| 262 log(String message) => compiler.log('[DartBackend] $message'); | |
| 263 | |
| 264 Future onLibrariesLoaded(Map<Uri, LibraryElement> loadedLibraries) { | |
| 265 // All platform classes must be resolved to ensure that their member names | |
| 266 // are preserved. | |
| 267 loadedLibraries.values.forEach((LibraryElement library) { | |
| 268 if (library.isPlatformLibrary) { | |
| 269 library.forEachLocalMember((Element element) { | |
| 270 if (element.isClass) { | |
| 271 ClassElement classElement = element; | |
| 272 classElement.ensureResolved(compiler); | |
| 273 } | |
| 274 }); | |
| 275 } | |
| 276 }); | |
| 277 if (useMirrorHelperLibrary && | |
| 278 loadedLibraries.containsKey(Compiler.DART_MIRRORS)) { | |
| 279 return compiler.libraryLoader.loadLibrary( | |
| 280 compiler.translateResolvedUri( | |
| 281 loadedLibraries[Compiler.DART_MIRRORS], | |
| 282 MirrorRenamerImpl.DART_MIRROR_HELPER, null)). | |
| 283 then((LibraryElement library) { | |
| 284 mirrorRenamer = new MirrorRenamerImpl(compiler, this, library); | |
| 285 }); | |
| 286 } | |
| 287 return new Future.value(); | |
| 288 } | |
| 289 | |
| 290 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
| 291 if (element == compiler.mirrorSystemGetNameFunction) { | |
| 292 FunctionElement getNameFunction = mirrorRenamer.getNameFunction; | |
| 293 if (getNameFunction != null) { | |
| 294 enqueuer.addToWorkList(getNameFunction); | |
| 295 } | |
| 296 } | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 class DartResolutionCallbacks extends ResolutionCallbacks { | |
| 301 final DartBackend backend; | |
| 302 | |
| 303 DartResolutionCallbacks(this.backend); | |
| 304 | |
| 305 void onTypeLiteral(DartType type, Registry registry) { | |
| 306 if (type.isInterfaceType) { | |
| 307 backend.usedTypeLiterals.add(type.element); | |
| 308 } | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 class EmitterUnparser extends Unparser { | |
| 313 final Map<Node, String> renames; | |
| 314 | |
| 315 EmitterUnparser(this.renames, {bool minify, bool stripTypes}) | |
| 316 : super(minify: minify, stripTypes: stripTypes); | |
| 317 | |
| 318 visit(Node node) { | |
| 319 if (node != null && renames.containsKey(node)) { | |
| 320 write(renames[node]); | |
| 321 } else { | |
| 322 super.visit(node); | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 unparseSendReceiver(Send node, {bool spacesNeeded: false}) { | |
| 327 // TODO(smok): Remove ugly hack for library prefices. | |
| 328 if (node.receiver != null && renames[node.receiver] == '') return; | |
| 329 super.unparseSendReceiver(node, spacesNeeded: spacesNeeded); | |
| 330 } | |
| 331 | |
| 332 unparseFunctionName(Node name) { | |
| 333 if (name != null && renames.containsKey(name)) { | |
| 334 write(renames[name]); | |
| 335 } else { | |
| 336 super.unparseFunctionName(name); | |
| 337 } | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 | |
| 342 /** | |
| 343 * Some elements are not recorded by resolver now, | |
| 344 * for example, typedefs or classes which are only | |
| 345 * used in signatures, as/is operators or in super clauses | |
| 346 * (just to name a few). Retraverse AST to pick those up. | |
| 347 */ | |
| 348 class ReferencedElementCollector extends Visitor { | |
| 349 final Compiler compiler; | |
| 350 final Element element; | |
| 351 final ElementAst elementAst; | |
| 352 final newTypedefElementCallback; | |
| 353 final newClassElementCallback; | |
| 354 | |
| 355 ReferencedElementCollector(this.compiler, | |
| 356 this.element, | |
| 357 this.elementAst, | |
| 358 this.newTypedefElementCallback, | |
| 359 this.newClassElementCallback); | |
| 360 | |
| 361 visitNode(Node node) { | |
| 362 node.visitChildren(this); | |
| 363 } | |
| 364 | |
| 365 visitTypeAnnotation(TypeAnnotation typeAnnotation) { | |
| 366 TreeElements treeElements = elementAst.treeElements; | |
| 367 final DartType type = treeElements.getType(typeAnnotation); | |
| 368 assert(invariant(typeAnnotation, type != null, | |
| 369 message: "Missing type for type annotation: $treeElements.")); | |
| 370 if (type.isTypedef) newTypedefElementCallback(type.element); | |
| 371 if (type.isInterfaceType) newClassElementCallback(type.element); | |
| 372 typeAnnotation.visitChildren(this); | |
| 373 } | |
| 374 | |
| 375 void collect() { | |
| 376 compiler.withCurrentElement(element, () { | |
| 377 elementAst.ast.accept(this); | |
| 378 }); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 Comparator compareBy(f) => (x, y) => f(x).compareTo(f(y)); | |
| 383 | |
| 384 List sorted(Iterable l, comparison) { | |
| 385 final result = new List.from(l); | |
| 386 result.sort(comparison); | |
| 387 return result; | |
| 388 } | |
| 389 | |
| 390 compareElements(e0, e1) { | |
| 391 int result = compareBy((e) => e.library.canonicalUri.toString())(e0, e1); | |
| 392 if (result != 0) return result; | |
| 393 return compareBy((e) => e.position.charOffset)(e0, e1); | |
| 394 } | |
| 395 | |
| 396 List<Element> sortElements(Iterable<Element> elements) => | |
| 397 sorted(elements, compareElements); | |
| 398 | |
| 399 /// [ConstantCompilerTask] for compilation of constants for the Dart backend. | |
| 400 /// | |
| 401 /// Since this task needs no distinction between frontend and backend constants | |
| 402 /// it also serves as the [BackendConstantEnvironment]. | |
| 403 class DartConstantTask extends ConstantCompilerTask | |
| 404 implements BackendConstantEnvironment { | |
| 405 final DartConstantCompiler constantCompiler; | |
| 406 | |
| 407 DartConstantTask(Compiler compiler) | |
| 408 : this.constantCompiler = new DartConstantCompiler(compiler), | |
| 409 super(compiler); | |
| 410 | |
| 411 String get name => 'ConstantHandler'; | |
| 412 | |
| 413 ConstantExpression getConstantForVariable(VariableElement element) { | |
| 414 return constantCompiler.getConstantForVariable(element); | |
| 415 } | |
| 416 | |
| 417 ConstantExpression getConstantForNode(Node node, TreeElements elements) { | |
| 418 return constantCompiler.getConstantForNode(node, elements); | |
| 419 } | |
| 420 | |
| 421 ConstantExpression getConstantForMetadata(MetadataAnnotation metadata) { | |
| 422 return metadata.constant; | |
| 423 } | |
| 424 | |
| 425 ConstantExpression compileConstant(VariableElement element) { | |
| 426 return measure(() { | |
| 427 return constantCompiler.compileConstant(element); | |
| 428 }); | |
| 429 } | |
| 430 | |
| 431 void compileVariable(VariableElement element) { | |
| 432 measure(() { | |
| 433 constantCompiler.compileVariable(element); | |
| 434 }); | |
| 435 } | |
| 436 | |
| 437 ConstantExpression compileNode(Node node, TreeElements elements) { | |
| 438 return measure(() { | |
| 439 return constantCompiler.compileNodeWithDefinitions(node, elements); | |
| 440 }); | |
| 441 } | |
| 442 | |
| 443 ConstantExpression compileMetadata(MetadataAnnotation metadata, | |
| 444 Node node, | |
| 445 TreeElements elements) { | |
| 446 return measure(() { | |
| 447 return constantCompiler.compileMetadata(metadata, node, elements); | |
| 448 }); | |
| 449 } | |
| 450 } | |
| OLD | NEW |