Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart2js.ir_builder; | 5 part of dart2js.ir_builder; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This task iterates through all resolved elements and builds [ir.Node]s. The | 8 * This task iterates through all resolved elements and builds [ir.Node]s. The |
| 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and | 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and |
| 10 * [getIr]. | 10 * [getIr]. |
| (...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 444 // where (C', x) = Build(e, C) | 444 // where (C', x) = Build(e, C) |
| 445 // | 445 // |
| 446 // Return without a subexpression is translated as if it were return null. | 446 // Return without a subexpression is translated as if it were return null. |
| 447 ir.Primitive visitReturn(ast.Return node) { | 447 ir.Primitive visitReturn(ast.Return node) { |
| 448 assert(irBuilder.isOpen); | 448 assert(irBuilder.isOpen); |
| 449 assert(invariant(node, node.beginToken.value != 'native')); | 449 assert(invariant(node, node.beginToken.value != 'native')); |
| 450 irBuilder.buildReturn(build(node.expression)); | 450 irBuilder.buildReturn(build(node.expression)); |
| 451 return null; | 451 return null; |
| 452 } | 452 } |
| 453 | 453 |
| 454 ir.Primitive visitTryStatement(ast.TryStatement node) { | |
| 455 assert(this.irBuilder.isOpen); | |
| 456 // Try/catch is not yet implemented in the JS backend. | |
| 457 if (this.irBuilder.tryStatements == null) { | |
| 458 return giveup(node, 'try/catch in the JS backend'); | |
| 459 } | |
| 460 // Multiple catch blocks are not yet implemented. | |
| 461 if (node.catchBlocks.isEmpty || | |
| 462 node.catchBlocks.nodes.tail == null) { | |
| 463 return giveup(node, 'not exactly one catch block'); | |
| 464 } | |
| 465 // 'on T' catch blocks are not yet implemented. | |
| 466 if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { | |
| 467 return giveup(node, '"on T" catch block'); | |
| 468 } | |
| 469 // Finally blocks are not yet implemented. | |
| 470 if (node.finallyBlock != null) { | |
| 471 return giveup(node, 'try/finally'); | |
| 472 } | |
| 473 | |
| 474 // Catch handlers are in scope for their body. The CPS translation of | |
| 475 // [[try tryBlock catch (e) catchBlock; successor]] is: | |
| 476 // | |
| 477 // let cont join(v0, v1, ...) = [[successor]] in | |
| 478 // let mutable m0 = x0 in | |
| 479 // let mutable m1 = x1 in | |
| 480 // ... | |
| 481 // let handler catch_(e) = | |
| 482 // let prim p0 = GetMutable(m0) in | |
| 483 // let prim p1 = GetMutable(m1) in | |
| 484 // ... | |
| 485 // [[catchBlock]] | |
| 486 // join(p0, p1, ...) | |
| 487 // in | |
| 488 // [[tryBlock]] | |
| 489 // let prim p0' = GetMutable(m0) in | |
| 490 // let prim p1' = GetMutable(m1) in | |
| 491 // ... | |
| 492 // join(p0', p1', ...) | |
| 493 // | |
| 494 // In other words, both the try and catch block are in the scope of the | |
| 495 // join-point continuation, and they are both in the scope of a sequence | |
| 496 // of mutable bindings for the variables assigned in the try. The join- | |
| 497 // point continuation is not in the scope of these mutable bindings. | |
| 498 // The tryBlock is in the scope of a binding for the catch handler. Each | |
| 499 // instruction (specifically, each call) in the tryBlock is in the dynamic | |
| 500 // scope of the handler. The mutable bindings are dereferenced at the end | |
| 501 // of the try block and at the beginning of the catch block, so th | |
|
floitsch
2015/02/16 14:54:07
the
| |
| 502 // variables are unboxed in the catch block and at the join point. | |
|
floitsch
2015/02/16 14:54:07
I guess they are also unboxed before calls to "con
Kevin Millikin (Google)
2015/02/24 11:59:25
It does require an update to the comment, and a fi
| |
| 503 | |
| 504 IrBuilder tryCatchBuilder = irBuilder.makeDelimitedBuilder(); | |
| 505 TryStatementInfo tryInfo = tryCatchBuilder.tryStatements[node]; | |
| 506 // Variables that are boxed due to being captured in a closure are boxed | |
| 507 // for their entire lifetime, and so they do not need to be boxed on entry | |
| 508 // to any try block. They are only removed here because we cannot | |
| 509 // identify all of them in the same pass where we identify the variables | |
| 510 // assigned in the try (the may be captured by a closure after the try | |
| 511 // statement). | |
| 512 tryInfo.boxedOnEntry.removeAll(tryCatchBuilder.mutableCapturedVariables); | |
|
floitsch
2015/02/16 14:54:07
I'm not a fan of these kind of side-effects in a b
Kevin Millikin (Google)
2015/02/24 11:59:25
Hmmm. The other approach that we use a lot is to
| |
| 513 for (LocalVariableElement variable in tryInfo.boxedOnEntry) { | |
| 514 assert(!tryCatchBuilder.isInMutableVariable(variable)); | |
| 515 ir.Primitive value = tryCatchBuilder.buildLocalGet(variable); | |
| 516 tryCatchBuilder.makeMutableVariable(variable); | |
| 517 tryCatchBuilder.declareLocalVariable(variable, initialValue: value); | |
| 518 } | |
| 519 | |
| 520 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
| 521 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
| 522 List<ir.Parameter> joinParameters = | |
| 523 new List<ir.Parameter>.generate(irBuilder.environment.length, (i) { | |
| 524 return new ir.Parameter(irBuilder.environment.index2variable[i]); | |
| 525 }); | |
| 526 ir.Continuation joinContinuation = new ir.Continuation(joinParameters); | |
| 527 withBuilder(tryBuilder, () { | |
| 528 visit(node.tryBlock); | |
| 529 }); | |
| 530 if (tryBuilder.isOpen) { | |
| 531 for (LocalVariableElement variable in tryInfo.boxedOnEntry) { | |
| 532 assert(tryBuilder.isInMutableVariable(variable)); | |
| 533 ir.Primitive value = tryBuilder.buildLocalGet(variable); | |
| 534 tryBuilder.environment.update(variable, value); | |
| 535 } | |
| 536 assert(tryBuilder.environment.length >= irBuilder.environment.length); | |
| 537 ir.InvokeContinuation jump = new ir.InvokeContinuation.uninitialized(); | |
| 538 jump.continuation = new ir.Reference(joinContinuation); | |
| 539 jump.arguments = new List<ir.Reference>.generate( | |
| 540 irBuilder.environment.length, (i) { | |
| 541 return new ir.Reference(tryBuilder.environment[i]); | |
| 542 }); | |
| 543 tryBuilder.add(jump); | |
| 544 tryBuilder._current = null; | |
|
asgerf
2015/02/20 10:10:07
Would it make sense to extract these 8 lines into
Kevin Millikin (Google)
2015/02/24 11:59:25
Yes. It also occurs when breaking from a labeled
| |
| 545 } | |
| 546 | |
| 547 for (LocalVariableElement variable in tryInfo.boxedOnEntry) { | |
| 548 assert(catchBuilder.isInMutableVariable(variable)); | |
| 549 ir.Primitive value = catchBuilder.buildLocalGet(variable); | |
| 550 // Note that we remove the variable from the set of mutable variables | |
| 551 // here (and not above for the try body). This is because the set of | |
| 552 // mutable variables is global for the whole function and not local to | |
| 553 // a delimited builder. | |
| 554 catchBuilder.removeMutableVariable(variable); | |
| 555 catchBuilder.environment.update(variable, value); | |
| 556 } | |
| 557 ast.CatchBlock catchClause = node.catchBlocks.nodes.head; | |
| 558 assert(catchClause.exception != null); | |
| 559 List<ir.Parameter> catchParameters = | |
| 560 <ir.Parameter>[new ir.Parameter(elements[catchClause.exception])]; | |
| 561 catchBuilder.environment.extend(elements[catchClause.exception] as Local, | |
|
karlklose
2015/02/16 10:15:48
Why do you cast to Local here and below?
Kevin Millikin (Google)
2015/02/24 11:59:25
Otherwise the editor reports "The argument type 'E
| |
| 562 catchParameters[0]); | |
| 563 if (catchClause.trace != null) { | |
| 564 catchParameters.add(new ir.Parameter(elements[catchClause.trace])); | |
| 565 catchBuilder.environment.extend(elements[catchClause.trace] as Local, | |
| 566 catchParameters[1]); | |
| 567 } | |
| 568 withBuilder(catchBuilder, () { | |
| 569 visit(catchClause.block); | |
| 570 }); | |
| 571 if (catchBuilder.isOpen) { | |
| 572 assert(catchBuilder.environment.length >= irBuilder.environment.length); | |
| 573 ir.InvokeContinuation jump = new ir.InvokeContinuation.uninitialized(); | |
| 574 jump.continuation = new ir.Reference(joinContinuation); | |
| 575 jump.arguments = new List<ir.Reference>.generate( | |
| 576 irBuilder.environment.length, (i) { | |
| 577 return new ir.Reference(catchBuilder.environment[i]); | |
| 578 }); | |
| 579 catchBuilder.add(jump); | |
| 580 catchBuilder._current = null; | |
| 581 } | |
| 582 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); | |
| 583 catchContinuation.body = catchBuilder._root; | |
| 584 | |
| 585 tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder._root)); | |
| 586 tryCatchBuilder._current = null; | |
| 587 | |
| 588 irBuilder.add(new ir.LetCont(joinContinuation, tryCatchBuilder._root)); | |
| 589 for (int i = 0; i < irBuilder.environment.length; ++i) { | |
| 590 irBuilder.environment.index2value[i] = joinParameters[i]; | |
| 591 } | |
| 592 return null; | |
| 593 } | |
| 594 | |
| 454 // ==== Expressions ==== | 595 // ==== Expressions ==== |
| 455 ir.Primitive visitConditional(ast.Conditional node) { | 596 ir.Primitive visitConditional(ast.Conditional node) { |
| 456 return irBuilder.buildConditional( | 597 return irBuilder.buildConditional( |
| 457 build(node.condition), | 598 build(node.condition), |
| 458 subbuild(node.thenExpression), | 599 subbuild(node.thenExpression), |
| 459 subbuild(node.elseExpression)); | 600 subbuild(node.elseExpression)); |
| 460 } | 601 } |
| 461 | 602 |
| 462 // For all simple literals: | 603 // For all simple literals: |
| 463 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] | 604 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] |
| (...skipping 489 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 953 throw ABORT_IRNODE_BUILDER; | 1094 throw ABORT_IRNODE_BUILDER; |
| 954 } | 1095 } |
| 955 | 1096 |
| 956 /// Classifies local variables and local functions as captured, if they | 1097 /// Classifies local variables and local functions as captured, if they |
| 957 /// are accessed from within a nested function. | 1098 /// are accessed from within a nested function. |
| 958 /// | 1099 /// |
| 959 /// This class is specific to the [DartIrBuilder], in that it gives up if it | 1100 /// This class is specific to the [DartIrBuilder], in that it gives up if it |
| 960 /// sees a feature that is currently unsupport by that builder. In particular, | 1101 /// sees a feature that is currently unsupport by that builder. In particular, |
| 961 /// loop variables captured in a for-loop initializer, condition, or update | 1102 /// loop variables captured in a for-loop initializer, condition, or update |
| 962 /// expression are unsupported. | 1103 /// expression are unsupported. |
| 963 class DartCapturedVariables extends ast.Visitor | 1104 class DartCapturedVariables extends ast.Visitor { |
| 964 implements DartCapturedVariableInfo { | |
| 965 final TreeElements elements; | 1105 final TreeElements elements; |
| 966 DartCapturedVariables(this.elements); | 1106 DartCapturedVariables(this.elements); |
| 967 | 1107 |
| 968 FunctionElement currentFunction; | 1108 FunctionElement currentFunction; |
| 969 bool insideInitializer = false; | 1109 bool insideInitializer = false; |
| 970 Set<Local> capturedVariables = new Set<Local>(); | 1110 Set<Local> capturedVariables = new Set<Local>(); |
| 971 | 1111 |
| 1112 Map<ast.TryStatement, TryStatementInfo> tryStatements = | |
| 1113 <ast.TryStatement, TryStatementInfo>{}; | |
| 1114 | |
| 1115 TryStatementInfo currentTryInfo; | |
| 1116 bool get inTryStatement => currentTryInfo != null; | |
| 1117 | |
| 972 void markAsCaptured(Local local) { | 1118 void markAsCaptured(Local local) { |
| 973 capturedVariables.add(local); | 1119 capturedVariables.add(local); |
| 974 } | 1120 } |
| 975 | 1121 |
| 976 visit(ast.Node node) => node.accept(this); | 1122 visit(ast.Node node) => node.accept(this); |
| 977 | 1123 |
| 978 visitNode(ast.Node node) { | 1124 visitNode(ast.Node node) { |
| 979 node.visitChildren(this); | 1125 node.visitChildren(this); |
| 980 } | 1126 } |
| 981 | 1127 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 1009 } | 1155 } |
| 1010 | 1156 |
| 1011 visitSend(ast.Send node) { | 1157 visitSend(ast.Send node) { |
| 1012 handleSend(node); | 1158 handleSend(node); |
| 1013 node.visitChildren(this); | 1159 node.visitChildren(this); |
| 1014 } | 1160 } |
| 1015 | 1161 |
| 1016 visitSendSet(ast.SendSet node) { | 1162 visitSendSet(ast.SendSet node) { |
| 1017 handleSend(node); | 1163 handleSend(node); |
| 1018 Element element = elements[node]; | 1164 Element element = elements[node]; |
| 1019 // Initializers in an initializer-list can communicate via parameters. | 1165 if (Elements.isLocal(element)) { |
| 1020 // If a parameter is stored in an initializer list we box it. | |
| 1021 if (insideInitializer && | |
| 1022 Elements.isLocal(element) && | |
| 1023 element.isParameter) { | |
| 1024 LocalElement local = element; | 1166 LocalElement local = element; |
| 1025 // TODO(sigurdm): Fix this. | 1167 if (insideInitializer) { |
| 1026 // Though these variables do not outlive the activation of the function, | 1168 assert(local.isParameter); |
| 1027 // they still need to be boxed. As a simplification, we treat them as if | 1169 // Initializers in an initializer-list can communicate via parameters. |
| 1028 // they are captured by a closure (i.e., they do outlive the activation of | 1170 // If a parameter is stored in an initializer list we box it. |
| 1029 // the function). | 1171 // TODO(sigurdm): Fix this. |
| 1030 markAsCaptured(local); | 1172 // Though these variables do not outlive the activation of the |
| 1173 // function, they still need to be boxed. As a simplification, we | |
| 1174 // treat them as if they are captured by a closure (i.e., they do | |
| 1175 // outlive the activation of the function). | |
| 1176 markAsCaptured(local); | |
| 1177 } else if (inTryStatement) { | |
| 1178 assert(local.isParameter || local.isVariable); | |
| 1179 if (!currentTryInfo.declared.contains(local)) { | |
| 1180 // If a variable is assigned in a try and not declared in that try | |
| 1181 // then it has to be boxed on entry to the try. | |
| 1182 // Later we will remove such variables that will be boxed in an | |
| 1183 // enclosing try. | |
| 1184 currentTryInfo.boxedOnEntry.add(local); | |
| 1185 } | |
| 1186 } | |
| 1031 } | 1187 } |
| 1032 node.visitChildren(this); | 1188 node.visitChildren(this); |
| 1033 } | 1189 } |
| 1034 | 1190 |
| 1035 visitFunctionExpression(ast.FunctionExpression node) { | 1191 visitFunctionExpression(ast.FunctionExpression node) { |
| 1036 FunctionElement oldFunction = currentFunction; | 1192 FunctionElement oldFunction = currentFunction; |
| 1037 currentFunction = elements[node]; | 1193 currentFunction = elements[node]; |
| 1038 if (currentFunction.asyncMarker != AsyncMarker.SYNC) { | 1194 if (currentFunction.asyncMarker != AsyncMarker.SYNC) { |
| 1039 giveup(node, "cannot handle async/sync*/async* functions"); | 1195 giveup(node, "cannot handle async/sync*/async* functions"); |
| 1040 } | 1196 } |
| 1041 if (node.initializers != null) { | 1197 if (node.initializers != null) { |
| 1042 insideInitializer = true; | 1198 insideInitializer = true; |
| 1043 visit(node.initializers); | 1199 visit(node.initializers); |
| 1044 insideInitializer = false; | 1200 insideInitializer = false; |
| 1045 } | 1201 } |
| 1046 visit(node.body); | 1202 visit(node.body); |
| 1047 currentFunction = oldFunction; | 1203 currentFunction = oldFunction; |
| 1048 } | 1204 } |
| 1205 | |
| 1206 visitTryStatement(ast.TryStatement node) { | |
| 1207 TryStatementInfo outer = currentTryInfo; | |
| 1208 tryStatements[node] = currentTryInfo = new TryStatementInfo(outer); | |
| 1209 visit(node.tryBlock); | |
| 1210 if (outer == null) { | |
| 1211 // For each top-level try, compute the variables boxed on entry to it | |
| 1212 // and each nested try in a top-down manner. | |
| 1213 currentTryInfo.computeVariablesBoxedOnEntry(null); | |
| 1214 } | |
| 1215 currentTryInfo = outer; | |
| 1216 | |
| 1217 visit(node.catchBlocks); | |
| 1218 if (node.finallyBlock != null) visit(node.finallyBlock); | |
| 1219 } | |
| 1220 | |
| 1221 visitVariableDefinitions(ast.VariableDefinitions node) { | |
| 1222 if (inTryStatement) { | |
| 1223 for (ast.Node definition in node.definitions.nodes) { | |
| 1224 LocalVariableElement local = elements[definition]; | |
| 1225 assert(local != null); | |
| 1226 // In the closure conversion pass we check for isInitializingFormal, | |
| 1227 // but I'm not sure it can arise. | |
| 1228 assert(!local.isInitializingFormal); | |
| 1229 currentTryInfo.declared.add(local); | |
| 1230 } | |
| 1231 } | |
| 1232 node.visitChildren(this); | |
| 1233 } | |
| 1049 } | 1234 } |
| 1050 | 1235 |
| 1051 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. | 1236 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. |
| 1052 class DartIrBuilderVisitor extends IrBuilderVisitor { | 1237 class DartIrBuilderVisitor extends IrBuilderVisitor { |
| 1053 /// Promote the type of [irBuilder] to [DartIrBuilder]. | 1238 /// Promote the type of [irBuilder] to [DartIrBuilder]. |
| 1054 DartIrBuilder get irBuilder => super.irBuilder; | 1239 DartIrBuilder get irBuilder => super.irBuilder; |
| 1055 | 1240 |
| 1056 DartIrBuilderVisitor(TreeElements elements, | 1241 DartIrBuilderVisitor(TreeElements elements, |
| 1057 Compiler compiler, | 1242 Compiler compiler, |
| 1058 SourceFile sourceFile) | 1243 SourceFile sourceFile) |
| (...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1683 for (String argName in selector.getOrderedNamedArguments()) { | 1868 for (String argName in selector.getOrderedNamedArguments()) { |
| 1684 int nameIndex = selector.namedArguments.indexOf(argName); | 1869 int nameIndex = selector.namedArguments.indexOf(argName); |
| 1685 int translatedIndex = selector.positionalArgumentCount + nameIndex; | 1870 int translatedIndex = selector.positionalArgumentCount + nameIndex; |
| 1686 result.add(arguments[translatedIndex]); | 1871 result.add(arguments[translatedIndex]); |
| 1687 } | 1872 } |
| 1688 return result; | 1873 return result; |
| 1689 } | 1874 } |
| 1690 | 1875 |
| 1691 } | 1876 } |
| 1692 | 1877 |
| OLD | NEW |