| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| index e7353091daa31b8eabdef88e094c5435d6fad923..76753954f0eb83493e5ec02b875e5802401ba7ff 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
|
| @@ -111,7 +111,7 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
|
| /// The analysis information includes the set of variables that must be
|
| /// copied into [ir.MutableVariable]s on entry to the try and copied out on
|
| /// exit.
|
| - Map<ast.TryStatement, TryStatementInfo> tryStatements = null;
|
| + Map<ast.Node, TryStatementInfo> tryStatements = null;
|
|
|
| // In SSA terms, join-point continuation parameters are the phis and the
|
| // continuation invocation arguments are the corresponding phi inputs. To
|
| @@ -431,11 +431,6 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
|
| }
|
|
|
| visitTryStatement(ast.TryStatement node) {
|
| - // Try/catch/finally is not yet implemented.
|
| - if (!node.catchBlocks.isEmpty && node.finallyBlock != null) {
|
| - return giveup(node, 'try/catch/finally');
|
| - }
|
| -
|
| List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
|
| for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
|
| assert(catchClause.exception != null);
|
| @@ -455,14 +450,29 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
|
| buildCatchBlock: subbuild(catchClause.block)));
|
| }
|
|
|
| - SubbuildFunction buildFinallyBlock =
|
| - node.finallyBlock == null ? null : subbuild(node.finallyBlock);
|
| - irBuilder.buildTry(
|
| - tryStatementInfo: tryStatements[node],
|
| - buildTryBlock: subbuild(node.tryBlock),
|
| - catchClauseInfos: catchClauseInfos,
|
| - buildFinallyBlock: buildFinallyBlock,
|
| - closureClassMap: closureClassMap);
|
| + assert(!node.catchBlocks.isEmpty || node.finallyBlock != null);
|
| + if (!node.catchBlocks.isEmpty && node.finallyBlock != null) {
|
| + // Try/catch/finally is encoded in terms of try/catch and try/finally:
|
| + //
|
| + // try tryBlock catch (ex, st) catchBlock finally finallyBlock
|
| + // ==>
|
| + // try { try tryBlock catch (ex, st) catchBlock } finally finallyBlock
|
| + irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
|
| + (IrBuilder inner) {
|
| + inner.buildTryCatch(tryStatements[node.catchBlocks],
|
| + subbuild(node.tryBlock),
|
| + catchClauseInfos);
|
| + },
|
| + subbuild(node.finallyBlock));
|
| + } else if (!node.catchBlocks.isEmpty) {
|
| + irBuilder.buildTryCatch(tryStatements[node.catchBlocks],
|
| + subbuild(node.tryBlock),
|
| + catchClauseInfos);
|
| + } else {
|
| + irBuilder.buildTryFinally(tryStatements[node.finallyBlock],
|
| + subbuild(node.tryBlock),
|
| + subbuild(node.finallyBlock));
|
| + }
|
| }
|
|
|
| // ## Expressions ##
|
| @@ -1961,8 +1971,13 @@ class DartCapturedVariables extends ast.Visitor {
|
| bool insideInitializer = false;
|
| Set<Local> capturedVariables = new Set<Local>();
|
|
|
| - Map<ast.TryStatement, TryStatementInfo> tryStatements =
|
| - <ast.TryStatement, TryStatementInfo>{};
|
| + /// A map containing variables boxed inside try blocks.
|
| + ///
|
| + /// The map is keyed by the [NodeList] of catch clauses for try/catch and
|
| + /// by the finally block for try/finally. try/catch/finally is treated
|
| + /// as a try/catch nested in the try block of a try/finally.
|
| + Map<ast.Node, TryStatementInfo> tryStatements =
|
| + <ast.Node, TryStatementInfo>{};
|
|
|
| List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[];
|
| bool get inTryStatement => tryNestingStack.isNotEmpty;
|
| @@ -2079,15 +2094,42 @@ class DartCapturedVariables extends ast.Visitor {
|
| }
|
|
|
| visitTryStatement(ast.TryStatement node) {
|
| - TryStatementInfo info = new TryStatementInfo();
|
| - tryStatements[node] = info;
|
| - tryNestingStack.add(info);
|
| + // Try/catch/finally is treated as two simpler constructs: try/catch and
|
| + // try/finally. The encoding is:
|
| + //
|
| + // try S0 catch (ex, st) S1 finally S2
|
| + // ==>
|
| + // try { try S0 catch (ex, st) S1 } finally S2
|
| + //
|
| + // The analysis associates variables assigned in S0 with the catch clauses
|
| + // and variables assigned in S0 and S1 with the finally block.
|
| + TryStatementInfo enterTryFor(ast.Node node) {
|
| + TryStatementInfo info = new TryStatementInfo();
|
| + tryStatements[node] = info;
|
| + tryNestingStack.add(info);
|
| + return info;
|
| + }
|
| + void leaveTryFor(TryStatementInfo info) {
|
| + assert(tryNestingStack.last == info);
|
| + tryNestingStack.removeLast();
|
| + }
|
| + bool hasCatch = !node.catchBlocks.isEmpty;
|
| + bool hasFinally = node.finallyBlock != null;
|
| + TryStatementInfo catchInfo, finallyInfo;
|
| + // There is a nesting stack of try blocks, so the outer try/finally block
|
| + // is added first.
|
| + if (hasFinally) finallyInfo = enterTryFor(node.finallyBlock);
|
| + if (hasCatch) catchInfo = enterTryFor(node.catchBlocks);
|
| visit(node.tryBlock);
|
| - assert(tryNestingStack.last == info);
|
| - tryNestingStack.removeLast();
|
|
|
| - visit(node.catchBlocks);
|
| - if (node.finallyBlock != null) visit(node.finallyBlock);
|
| + if (hasCatch) {
|
| + leaveTryFor(catchInfo);
|
| + visit(node.catchBlocks);
|
| + }
|
| + if (hasFinally) {
|
| + leaveTryFor(finallyInfo);
|
| + visit(node.finallyBlock);
|
| + }
|
| }
|
|
|
| visitVariableDefinitions(ast.VariableDefinitions node) {
|
|
|