| 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 the |
| 502 // variables are unboxed in the catch block and at the join point. |
| 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 |
| 508 // entry to any try block. We check for them here because we can not |
| 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 Iterable<LocalVariableElement> boxedOnEntry = |
| 513 tryInfo.boxedOnEntry.where((LocalVariableElement variable) { |
| 514 return !tryCatchBuilder.mutableCapturedVariables.contains(variable); |
| 515 }); |
| 516 for (LocalVariableElement variable in boxedOnEntry) { |
| 517 assert(!tryCatchBuilder.isInMutableVariable(variable)); |
| 518 ir.Primitive value = tryCatchBuilder.buildLocalGet(variable); |
| 519 tryCatchBuilder.makeMutableVariable(variable); |
| 520 tryCatchBuilder.declareLocalVariable(variable, initialValue: value); |
| 521 } |
| 522 |
| 523 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); |
| 524 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); |
| 525 List<ir.Parameter> joinParameters = |
| 526 new List<ir.Parameter>.generate(irBuilder.environment.length, (i) { |
| 527 return new ir.Parameter(irBuilder.environment.index2variable[i]); |
| 528 }); |
| 529 ir.Continuation joinContinuation = new ir.Continuation(joinParameters); |
| 530 |
| 531 void interceptJumps(JumpCollector collector) { |
| 532 collector.enterTry(boxedOnEntry); |
| 533 } |
| 534 void restoreJumps(JumpCollector collector) { |
| 535 collector.leaveTry(); |
| 536 } |
| 537 tryBuilder.state.breakCollectors.forEach(interceptJumps); |
| 538 tryBuilder.state.continueCollectors.forEach(interceptJumps); |
| 539 withBuilder(tryBuilder, () { |
| 540 visit(node.tryBlock); |
| 541 }); |
| 542 tryBuilder.state.breakCollectors.forEach(restoreJumps); |
| 543 tryBuilder.state.continueCollectors.forEach(restoreJumps); |
| 544 if (tryBuilder.isOpen) { |
| 545 for (LocalVariableElement variable in boxedOnEntry) { |
| 546 assert(tryBuilder.isInMutableVariable(variable)); |
| 547 ir.Primitive value = tryBuilder.buildLocalGet(variable); |
| 548 tryBuilder.environment.update(variable, value); |
| 549 } |
| 550 tryBuilder.jumpTo(joinContinuation); |
| 551 } |
| 552 |
| 553 for (LocalVariableElement variable in boxedOnEntry) { |
| 554 assert(catchBuilder.isInMutableVariable(variable)); |
| 555 ir.Primitive value = catchBuilder.buildLocalGet(variable); |
| 556 // Note that we remove the variable from the set of mutable variables |
| 557 // here (and not above for the try body). This is because the set of |
| 558 // mutable variables is global for the whole function and not local to |
| 559 // a delimited builder. |
| 560 catchBuilder.removeMutableVariable(variable); |
| 561 catchBuilder.environment.update(variable, value); |
| 562 } |
| 563 ast.CatchBlock catchClause = node.catchBlocks.nodes.head; |
| 564 assert(catchClause.exception != null); |
| 565 LocalVariableElement exceptionElement = elements[catchClause.exception]; |
| 566 ir.Parameter exceptionParameter = new ir.Parameter(exceptionElement); |
| 567 catchBuilder.environment.extend(exceptionElement, exceptionParameter); |
| 568 ir.Parameter traceParameter; |
| 569 if (catchClause.trace != null) { |
| 570 LocalVariableElement traceElement = elements[catchClause.trace]; |
| 571 traceParameter = new ir.Parameter(traceElement); |
| 572 catchBuilder.environment.extend(traceElement, traceParameter); |
| 573 } else { |
| 574 // Use a dummy continuation parameter for the stack trace parameter. |
| 575 // This will ensure that all handlers have two parameters and so they |
| 576 // can be treated uniformly. |
| 577 traceParameter = new ir.Parameter(null); |
| 578 } |
| 579 withBuilder(catchBuilder, () { |
| 580 visit(catchClause.block); |
| 581 }); |
| 582 if (catchBuilder.isOpen) { |
| 583 catchBuilder.jumpTo(joinContinuation); |
| 584 } |
| 585 List<ir.Parameter> catchParameters = |
| 586 <ir.Parameter>[exceptionParameter, traceParameter]; |
| 587 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); |
| 588 catchContinuation.body = catchBuilder._root; |
| 589 |
| 590 tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder._root)); |
| 591 tryCatchBuilder._current = null; |
| 592 |
| 593 irBuilder.add(new ir.LetCont(joinContinuation, tryCatchBuilder._root)); |
| 594 for (int i = 0; i < irBuilder.environment.length; ++i) { |
| 595 irBuilder.environment.index2value[i] = joinParameters[i]; |
| 596 } |
| 597 return null; |
| 598 } |
| 599 |
| 454 // ==== Expressions ==== | 600 // ==== Expressions ==== |
| 455 ir.Primitive visitConditional(ast.Conditional node) { | 601 ir.Primitive visitConditional(ast.Conditional node) { |
| 456 return irBuilder.buildConditional( | 602 return irBuilder.buildConditional( |
| 457 build(node.condition), | 603 build(node.condition), |
| 458 subbuild(node.thenExpression), | 604 subbuild(node.thenExpression), |
| 459 subbuild(node.elseExpression)); | 605 subbuild(node.elseExpression)); |
| 460 } | 606 } |
| 461 | 607 |
| 462 // For all simple literals: | 608 // For all simple literals: |
| 463 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] | 609 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] |
| (...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 977 throw ABORT_IRNODE_BUILDER; | 1123 throw ABORT_IRNODE_BUILDER; |
| 978 } | 1124 } |
| 979 | 1125 |
| 980 /// Classifies local variables and local functions as captured, if they | 1126 /// Classifies local variables and local functions as captured, if they |
| 981 /// are accessed from within a nested function. | 1127 /// are accessed from within a nested function. |
| 982 /// | 1128 /// |
| 983 /// This class is specific to the [DartIrBuilder], in that it gives up if it | 1129 /// This class is specific to the [DartIrBuilder], in that it gives up if it |
| 984 /// sees a feature that is currently unsupport by that builder. In particular, | 1130 /// sees a feature that is currently unsupport by that builder. In particular, |
| 985 /// loop variables captured in a for-loop initializer, condition, or update | 1131 /// loop variables captured in a for-loop initializer, condition, or update |
| 986 /// expression are unsupported. | 1132 /// expression are unsupported. |
| 987 class DartCapturedVariables extends ast.Visitor | 1133 class DartCapturedVariables extends ast.Visitor { |
| 988 implements DartCapturedVariableInfo { | |
| 989 final TreeElements elements; | 1134 final TreeElements elements; |
| 990 DartCapturedVariables(this.elements); | 1135 DartCapturedVariables(this.elements); |
| 991 | 1136 |
| 992 FunctionElement currentFunction; | 1137 FunctionElement currentFunction; |
| 993 bool insideInitializer = false; | 1138 bool insideInitializer = false; |
| 994 Set<Local> capturedVariables = new Set<Local>(); | 1139 Set<Local> capturedVariables = new Set<Local>(); |
| 995 | 1140 |
| 1141 Map<ast.TryStatement, TryStatementInfo> tryStatements = |
| 1142 <ast.TryStatement, TryStatementInfo>{}; |
| 1143 |
| 1144 List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[]; |
| 1145 bool get inTryStatement => tryNestingStack.isNotEmpty; |
| 1146 |
| 996 void markAsCaptured(Local local) { | 1147 void markAsCaptured(Local local) { |
| 997 capturedVariables.add(local); | 1148 capturedVariables.add(local); |
| 998 } | 1149 } |
| 999 | 1150 |
| 1000 visit(ast.Node node) => node.accept(this); | 1151 visit(ast.Node node) => node.accept(this); |
| 1001 | 1152 |
| 1002 visitNode(ast.Node node) { | 1153 visitNode(ast.Node node) { |
| 1003 node.visitChildren(this); | 1154 node.visitChildren(this); |
| 1004 } | 1155 } |
| 1005 | 1156 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1033 } | 1184 } |
| 1034 | 1185 |
| 1035 visitSend(ast.Send node) { | 1186 visitSend(ast.Send node) { |
| 1036 handleSend(node); | 1187 handleSend(node); |
| 1037 node.visitChildren(this); | 1188 node.visitChildren(this); |
| 1038 } | 1189 } |
| 1039 | 1190 |
| 1040 visitSendSet(ast.SendSet node) { | 1191 visitSendSet(ast.SendSet node) { |
| 1041 handleSend(node); | 1192 handleSend(node); |
| 1042 Element element = elements[node]; | 1193 Element element = elements[node]; |
| 1043 // Initializers in an initializer-list can communicate via parameters. | 1194 if (Elements.isLocal(element)) { |
| 1044 // If a parameter is stored in an initializer list we box it. | |
| 1045 if (insideInitializer && | |
| 1046 Elements.isLocal(element) && | |
| 1047 element.isParameter) { | |
| 1048 LocalElement local = element; | 1195 LocalElement local = element; |
| 1049 // TODO(sigurdm): Fix this. | 1196 if (insideInitializer) { |
| 1050 // Though these variables do not outlive the activation of the function, | 1197 assert(local.isParameter); |
| 1051 // they still need to be boxed. As a simplification, we treat them as if | 1198 // Initializers in an initializer-list can communicate via parameters. |
| 1052 // they are captured by a closure (i.e., they do outlive the activation of | 1199 // If a parameter is stored in an initializer list we box it. |
| 1053 // the function). | 1200 // TODO(sigurdm): Fix this. |
| 1054 markAsCaptured(local); | 1201 // Though these variables do not outlive the activation of the |
| 1202 // function, they still need to be boxed. As a simplification, we |
| 1203 // treat them as if they are captured by a closure (i.e., they do |
| 1204 // outlive the activation of the function). |
| 1205 markAsCaptured(local); |
| 1206 } else if (inTryStatement) { |
| 1207 assert(local.isParameter || local.isVariable); |
| 1208 // Search for the position of the try block containing the variable |
| 1209 // declaration, or -1 if it is declared outside the outermost try. |
| 1210 int i = tryNestingStack.length - 1; |
| 1211 while (i >= 0 && !tryNestingStack[i].declared.contains(local)) { |
| 1212 --i; |
| 1213 } |
| 1214 // If there is a next inner try, then the variable should be boxed on |
| 1215 // entry to it. |
| 1216 if (i + 1 < tryNestingStack.length) { |
| 1217 tryNestingStack[i + 1].boxedOnEntry.add(local); |
| 1218 } |
| 1219 } |
| 1055 } | 1220 } |
| 1056 node.visitChildren(this); | 1221 node.visitChildren(this); |
| 1057 } | 1222 } |
| 1058 | 1223 |
| 1059 visitFunctionExpression(ast.FunctionExpression node) { | 1224 visitFunctionExpression(ast.FunctionExpression node) { |
| 1060 FunctionElement oldFunction = currentFunction; | 1225 FunctionElement oldFunction = currentFunction; |
| 1061 currentFunction = elements[node]; | 1226 currentFunction = elements[node]; |
| 1062 if (currentFunction.asyncMarker != AsyncMarker.SYNC) { | 1227 if (currentFunction.asyncMarker != AsyncMarker.SYNC) { |
| 1063 giveup(node, "cannot handle async/sync*/async* functions"); | 1228 giveup(node, "cannot handle async/sync*/async* functions"); |
| 1064 } | 1229 } |
| 1065 if (node.initializers != null) { | 1230 if (node.initializers != null) { |
| 1066 insideInitializer = true; | 1231 insideInitializer = true; |
| 1067 visit(node.initializers); | 1232 visit(node.initializers); |
| 1068 insideInitializer = false; | 1233 insideInitializer = false; |
| 1069 } | 1234 } |
| 1070 visit(node.body); | 1235 visit(node.body); |
| 1071 currentFunction = oldFunction; | 1236 currentFunction = oldFunction; |
| 1072 } | 1237 } |
| 1238 |
| 1239 visitTryStatement(ast.TryStatement node) { |
| 1240 TryStatementInfo info = new TryStatementInfo(); |
| 1241 tryStatements[node] = info; |
| 1242 tryNestingStack.add(info); |
| 1243 visit(node.tryBlock); |
| 1244 assert(tryNestingStack.last == info); |
| 1245 tryNestingStack.removeLast(); |
| 1246 |
| 1247 visit(node.catchBlocks); |
| 1248 if (node.finallyBlock != null) visit(node.finallyBlock); |
| 1249 } |
| 1250 |
| 1251 visitVariableDefinitions(ast.VariableDefinitions node) { |
| 1252 if (inTryStatement) { |
| 1253 for (ast.Node definition in node.definitions.nodes) { |
| 1254 LocalVariableElement local = elements[definition]; |
| 1255 assert(local != null); |
| 1256 // In the closure conversion pass we check for isInitializingFormal, |
| 1257 // but I'm not sure it can arise. |
| 1258 assert(!local.isInitializingFormal); |
| 1259 tryNestingStack.last.declared.add(local); |
| 1260 } |
| 1261 } |
| 1262 node.visitChildren(this); |
| 1263 } |
| 1073 } | 1264 } |
| 1074 | 1265 |
| 1075 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. | 1266 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. |
| 1076 class DartIrBuilderVisitor extends IrBuilderVisitor { | 1267 class DartIrBuilderVisitor extends IrBuilderVisitor { |
| 1077 /// Promote the type of [irBuilder] to [DartIrBuilder]. | 1268 /// Promote the type of [irBuilder] to [DartIrBuilder]. |
| 1078 DartIrBuilder get irBuilder => super.irBuilder; | 1269 DartIrBuilder get irBuilder => super.irBuilder; |
| 1079 | 1270 |
| 1080 DartIrBuilderVisitor(TreeElements elements, | 1271 DartIrBuilderVisitor(TreeElements elements, |
| 1081 Compiler compiler, | 1272 Compiler compiler, |
| 1082 SourceFile sourceFile) | 1273 SourceFile sourceFile) |
| (...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1707 for (String argName in selector.getOrderedNamedArguments()) { | 1898 for (String argName in selector.getOrderedNamedArguments()) { |
| 1708 int nameIndex = selector.namedArguments.indexOf(argName); | 1899 int nameIndex = selector.namedArguments.indexOf(argName); |
| 1709 int translatedIndex = selector.positionalArgumentCount + nameIndex; | 1900 int translatedIndex = selector.positionalArgumentCount + nameIndex; |
| 1710 result.add(arguments[translatedIndex]); | 1901 result.add(arguments[translatedIndex]); |
| 1711 } | 1902 } |
| 1712 return result; | 1903 return result; |
| 1713 } | 1904 } |
| 1714 | 1905 |
| 1715 } | 1906 } |
| 1716 | 1907 |
| OLD | NEW |