Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| index ea7bf3a1fe80c63bc88acd16a83908be7d0c4354..95030b63f4262584433946b16f7591d724965d90 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart |
| @@ -451,6 +451,147 @@ abstract class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> |
| return null; |
| } |
| + ir.Primitive visitTryStatement(ast.TryStatement node) { |
| + assert(this.irBuilder.isOpen); |
| + // Try/catch is not yet implemented in the JS backend. |
| + if (this.irBuilder.tryStatements == null) { |
| + return giveup(node, 'try/catch in the JS backend'); |
| + } |
| + // Multiple catch blocks are not yet implemented. |
| + if (node.catchBlocks.isEmpty || |
| + node.catchBlocks.nodes.tail == null) { |
| + return giveup(node, 'not exactly one catch block'); |
| + } |
| + // 'on T' catch blocks are not yet implemented. |
| + if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { |
| + return giveup(node, '"on T" catch block'); |
| + } |
| + // Finally blocks are not yet implemented. |
| + if (node.finallyBlock != null) { |
| + return giveup(node, 'try/finally'); |
| + } |
| + |
| + // Catch handlers are in scope for their body. The CPS translation of |
| + // [[try tryBlock catch (e) catchBlock; successor]] is: |
| + // |
| + // let cont join(v0, v1, ...) = [[successor]] in |
| + // let mutable m0 = x0 in |
| + // let mutable m1 = x1 in |
| + // ... |
| + // let handler catch_(e) = |
| + // let prim p0 = GetMutable(m0) in |
| + // let prim p1 = GetMutable(m1) in |
| + // ... |
| + // [[catchBlock]] |
| + // join(p0, p1, ...) |
| + // in |
| + // [[tryBlock]] |
| + // let prim p0' = GetMutable(m0) in |
| + // let prim p1' = GetMutable(m1) in |
| + // ... |
| + // join(p0', p1', ...) |
| + // |
| + // In other words, both the try and catch block are in the scope of the |
| + // join-point continuation, and they are both in the scope of a sequence |
| + // of mutable bindings for the variables assigned in the try. The join- |
| + // point continuation is not in the scope of these mutable bindings. |
| + // The tryBlock is in the scope of a binding for the catch handler. Each |
| + // instruction (specifically, each call) in the tryBlock is in the dynamic |
| + // scope of the handler. The mutable bindings are dereferenced at the end |
| + // of the try block and at the beginning of the catch block, so th |
|
floitsch
2015/02/16 14:54:07
the
|
| + // 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
|
| + |
| + IrBuilder tryCatchBuilder = irBuilder.makeDelimitedBuilder(); |
| + TryStatementInfo tryInfo = tryCatchBuilder.tryStatements[node]; |
| + // Variables that are boxed due to being captured in a closure are boxed |
| + // for their entire lifetime, and so they do not need to be boxed on entry |
| + // to any try block. They are only removed here because we cannot |
| + // identify all of them in the same pass where we identify the variables |
| + // assigned in the try (the may be captured by a closure after the try |
| + // statement). |
| + 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
|
| + for (LocalVariableElement variable in tryInfo.boxedOnEntry) { |
| + assert(!tryCatchBuilder.isInMutableVariable(variable)); |
| + ir.Primitive value = tryCatchBuilder.buildLocalGet(variable); |
| + tryCatchBuilder.makeMutableVariable(variable); |
| + tryCatchBuilder.declareLocalVariable(variable, initialValue: value); |
| + } |
| + |
| + IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); |
| + IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); |
| + List<ir.Parameter> joinParameters = |
| + new List<ir.Parameter>.generate(irBuilder.environment.length, (i) { |
| + return new ir.Parameter(irBuilder.environment.index2variable[i]); |
| + }); |
| + ir.Continuation joinContinuation = new ir.Continuation(joinParameters); |
| + withBuilder(tryBuilder, () { |
| + visit(node.tryBlock); |
| + }); |
| + if (tryBuilder.isOpen) { |
| + for (LocalVariableElement variable in tryInfo.boxedOnEntry) { |
| + assert(tryBuilder.isInMutableVariable(variable)); |
| + ir.Primitive value = tryBuilder.buildLocalGet(variable); |
| + tryBuilder.environment.update(variable, value); |
| + } |
| + assert(tryBuilder.environment.length >= irBuilder.environment.length); |
| + ir.InvokeContinuation jump = new ir.InvokeContinuation.uninitialized(); |
| + jump.continuation = new ir.Reference(joinContinuation); |
| + jump.arguments = new List<ir.Reference>.generate( |
| + irBuilder.environment.length, (i) { |
| + return new ir.Reference(tryBuilder.environment[i]); |
| + }); |
| + tryBuilder.add(jump); |
| + 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
|
| + } |
| + |
| + for (LocalVariableElement variable in tryInfo.boxedOnEntry) { |
| + assert(catchBuilder.isInMutableVariable(variable)); |
| + ir.Primitive value = catchBuilder.buildLocalGet(variable); |
| + // Note that we remove the variable from the set of mutable variables |
| + // here (and not above for the try body). This is because the set of |
| + // mutable variables is global for the whole function and not local to |
| + // a delimited builder. |
| + catchBuilder.removeMutableVariable(variable); |
| + catchBuilder.environment.update(variable, value); |
| + } |
| + ast.CatchBlock catchClause = node.catchBlocks.nodes.head; |
| + assert(catchClause.exception != null); |
| + List<ir.Parameter> catchParameters = |
| + <ir.Parameter>[new ir.Parameter(elements[catchClause.exception])]; |
| + 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
|
| + catchParameters[0]); |
| + if (catchClause.trace != null) { |
| + catchParameters.add(new ir.Parameter(elements[catchClause.trace])); |
| + catchBuilder.environment.extend(elements[catchClause.trace] as Local, |
| + catchParameters[1]); |
| + } |
| + withBuilder(catchBuilder, () { |
| + visit(catchClause.block); |
| + }); |
| + if (catchBuilder.isOpen) { |
| + assert(catchBuilder.environment.length >= irBuilder.environment.length); |
| + ir.InvokeContinuation jump = new ir.InvokeContinuation.uninitialized(); |
| + jump.continuation = new ir.Reference(joinContinuation); |
| + jump.arguments = new List<ir.Reference>.generate( |
| + irBuilder.environment.length, (i) { |
| + return new ir.Reference(catchBuilder.environment[i]); |
| + }); |
| + catchBuilder.add(jump); |
| + catchBuilder._current = null; |
| + } |
| + ir.Continuation catchContinuation = new ir.Continuation(catchParameters); |
| + catchContinuation.body = catchBuilder._root; |
| + |
| + tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder._root)); |
| + tryCatchBuilder._current = null; |
| + |
| + irBuilder.add(new ir.LetCont(joinContinuation, tryCatchBuilder._root)); |
| + for (int i = 0; i < irBuilder.environment.length; ++i) { |
| + irBuilder.environment.index2value[i] = joinParameters[i]; |
| + } |
| + return null; |
| + } |
| + |
| // ==== Expressions ==== |
| ir.Primitive visitConditional(ast.Conditional node) { |
| return irBuilder.buildConditional( |
| @@ -960,8 +1101,7 @@ dynamic giveup(ast.Node node, [String reason]) { |
| /// sees a feature that is currently unsupport by that builder. In particular, |
| /// loop variables captured in a for-loop initializer, condition, or update |
| /// expression are unsupported. |
| -class DartCapturedVariables extends ast.Visitor |
| - implements DartCapturedVariableInfo { |
| +class DartCapturedVariables extends ast.Visitor { |
| final TreeElements elements; |
| DartCapturedVariables(this.elements); |
| @@ -969,6 +1109,12 @@ class DartCapturedVariables extends ast.Visitor |
| bool insideInitializer = false; |
| Set<Local> capturedVariables = new Set<Local>(); |
| + Map<ast.TryStatement, TryStatementInfo> tryStatements = |
| + <ast.TryStatement, TryStatementInfo>{}; |
| + |
| + TryStatementInfo currentTryInfo; |
| + bool get inTryStatement => currentTryInfo != null; |
| + |
| void markAsCaptured(Local local) { |
| capturedVariables.add(local); |
| } |
| @@ -1016,18 +1162,28 @@ class DartCapturedVariables extends ast.Visitor |
| visitSendSet(ast.SendSet node) { |
| handleSend(node); |
| Element element = elements[node]; |
| - // Initializers in an initializer-list can communicate via parameters. |
| - // If a parameter is stored in an initializer list we box it. |
| - if (insideInitializer && |
| - Elements.isLocal(element) && |
| - element.isParameter) { |
| + if (Elements.isLocal(element)) { |
| LocalElement local = element; |
| - // TODO(sigurdm): Fix this. |
| - // Though these variables do not outlive the activation of the function, |
| - // they still need to be boxed. As a simplification, we treat them as if |
| - // they are captured by a closure (i.e., they do outlive the activation of |
| - // the function). |
| - markAsCaptured(local); |
| + if (insideInitializer) { |
| + assert(local.isParameter); |
| + // Initializers in an initializer-list can communicate via parameters. |
| + // If a parameter is stored in an initializer list we box it. |
| + // TODO(sigurdm): Fix this. |
| + // Though these variables do not outlive the activation of the |
| + // function, they still need to be boxed. As a simplification, we |
| + // treat them as if they are captured by a closure (i.e., they do |
| + // outlive the activation of the function). |
| + markAsCaptured(local); |
| + } else if (inTryStatement) { |
| + assert(local.isParameter || local.isVariable); |
| + if (!currentTryInfo.declared.contains(local)) { |
| + // If a variable is assigned in a try and not declared in that try |
| + // then it has to be boxed on entry to the try. |
| + // Later we will remove such variables that will be boxed in an |
| + // enclosing try. |
| + currentTryInfo.boxedOnEntry.add(local); |
| + } |
| + } |
| } |
| node.visitChildren(this); |
| } |
| @@ -1046,6 +1202,35 @@ class DartCapturedVariables extends ast.Visitor |
| visit(node.body); |
| currentFunction = oldFunction; |
| } |
| + |
| + visitTryStatement(ast.TryStatement node) { |
| + TryStatementInfo outer = currentTryInfo; |
| + tryStatements[node] = currentTryInfo = new TryStatementInfo(outer); |
| + visit(node.tryBlock); |
| + if (outer == null) { |
| + // For each top-level try, compute the variables boxed on entry to it |
| + // and each nested try in a top-down manner. |
| + currentTryInfo.computeVariablesBoxedOnEntry(null); |
| + } |
| + currentTryInfo = outer; |
| + |
| + visit(node.catchBlocks); |
| + if (node.finallyBlock != null) visit(node.finallyBlock); |
| + } |
| + |
| + visitVariableDefinitions(ast.VariableDefinitions node) { |
| + if (inTryStatement) { |
| + for (ast.Node definition in node.definitions.nodes) { |
| + LocalVariableElement local = elements[definition]; |
| + assert(local != null); |
| + // In the closure conversion pass we check for isInitializingFormal, |
| + // but I'm not sure it can arise. |
| + assert(!local.isInitializingFormal); |
| + currentTryInfo.declared.add(local); |
| + } |
| + } |
| + node.visitChildren(this); |
| + } |
| } |
| /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. |