| 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 492f1170833e9b4ae229a06f4d029581b42209d9..6cdec14b9abd7096ef4cddb4c1b865b65fdb8e32 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,152 @@ 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 the
|
| + // variables are unboxed in the catch block and at the join point.
|
| +
|
| + 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. We check for them here because we can not
|
| + // 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).
|
| + Iterable<LocalVariableElement> boxedOnEntry =
|
| + tryInfo.boxedOnEntry.where((LocalVariableElement variable) {
|
| + return !tryCatchBuilder.mutableCapturedVariables.contains(variable);
|
| + });
|
| + for (LocalVariableElement variable in 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);
|
| +
|
| + void interceptJumps(JumpCollector collector) {
|
| + collector.enterTry(boxedOnEntry);
|
| + }
|
| + void restoreJumps(JumpCollector collector) {
|
| + collector.leaveTry();
|
| + }
|
| + tryBuilder.state.breakCollectors.forEach(interceptJumps);
|
| + tryBuilder.state.continueCollectors.forEach(interceptJumps);
|
| + withBuilder(tryBuilder, () {
|
| + visit(node.tryBlock);
|
| + });
|
| + tryBuilder.state.breakCollectors.forEach(restoreJumps);
|
| + tryBuilder.state.continueCollectors.forEach(restoreJumps);
|
| + if (tryBuilder.isOpen) {
|
| + for (LocalVariableElement variable in boxedOnEntry) {
|
| + assert(tryBuilder.isInMutableVariable(variable));
|
| + ir.Primitive value = tryBuilder.buildLocalGet(variable);
|
| + tryBuilder.environment.update(variable, value);
|
| + }
|
| + tryBuilder.jumpTo(joinContinuation);
|
| + }
|
| +
|
| + for (LocalVariableElement variable in 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);
|
| + LocalVariableElement exceptionElement = elements[catchClause.exception];
|
| + ir.Parameter exceptionParameter = new ir.Parameter(exceptionElement);
|
| + catchBuilder.environment.extend(exceptionElement, exceptionParameter);
|
| + ir.Parameter traceParameter;
|
| + if (catchClause.trace != null) {
|
| + LocalVariableElement traceElement = elements[catchClause.trace];
|
| + traceParameter = new ir.Parameter(traceElement);
|
| + catchBuilder.environment.extend(traceElement, traceParameter);
|
| + } else {
|
| + // Use a dummy continuation parameter for the stack trace parameter.
|
| + // This will ensure that all handlers have two parameters and so they
|
| + // can be treated uniformly.
|
| + traceParameter = new ir.Parameter(null);
|
| + }
|
| + withBuilder(catchBuilder, () {
|
| + visit(catchClause.block);
|
| + });
|
| + if (catchBuilder.isOpen) {
|
| + catchBuilder.jumpTo(joinContinuation);
|
| + }
|
| + List<ir.Parameter> catchParameters =
|
| + <ir.Parameter>[exceptionParameter, traceParameter];
|
| + 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(
|
| @@ -984,8 +1130,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);
|
|
|
| @@ -993,6 +1138,12 @@ class DartCapturedVariables extends ast.Visitor
|
| bool insideInitializer = false;
|
| Set<Local> capturedVariables = new Set<Local>();
|
|
|
| + Map<ast.TryStatement, TryStatementInfo> tryStatements =
|
| + <ast.TryStatement, TryStatementInfo>{};
|
| +
|
| + List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[];
|
| + bool get inTryStatement => tryNestingStack.isNotEmpty;
|
| +
|
| void markAsCaptured(Local local) {
|
| capturedVariables.add(local);
|
| }
|
| @@ -1040,18 +1191,32 @@ 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);
|
| + // Search for the position of the try block containing the variable
|
| + // declaration, or -1 if it is declared outside the outermost try.
|
| + int i = tryNestingStack.length - 1;
|
| + while (i >= 0 && !tryNestingStack[i].declared.contains(local)) {
|
| + --i;
|
| + }
|
| + // If there is a next inner try, then the variable should be boxed on
|
| + // entry to it.
|
| + if (i + 1 < tryNestingStack.length) {
|
| + tryNestingStack[i + 1].boxedOnEntry.add(local);
|
| + }
|
| + }
|
| }
|
| node.visitChildren(this);
|
| }
|
| @@ -1070,6 +1235,32 @@ class DartCapturedVariables extends ast.Visitor
|
| visit(node.body);
|
| currentFunction = oldFunction;
|
| }
|
| +
|
| + visitTryStatement(ast.TryStatement node) {
|
| + TryStatementInfo info = new TryStatementInfo();
|
| + tryStatements[node] = info;
|
| + tryNestingStack.add(info);
|
| + visit(node.tryBlock);
|
| + assert(tryNestingStack.last == info);
|
| + tryNestingStack.removeLast();
|
| +
|
| + 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);
|
| + tryNestingStack.last.declared.add(local);
|
| + }
|
| + }
|
| + node.visitChildren(this);
|
| + }
|
| }
|
|
|
| /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder].
|
|
|