| Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| index c491237d77c5ef5996ee166a103224f0b060894f..8be9b21676f8a222743fbfb3fba93dc0357d7475 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
|
| @@ -68,24 +68,25 @@ class Environment {
|
|
|
| void extend(Local element, ir.Primitive value) {
|
| // Assert that the name is not already in the environment. `null` is used
|
| - // as the name of anonymous variables. Because the variable2index map is
|
| - // shared, `null` can already occur. This is safe because such variables
|
| - // are not looked up by name.
|
| - //
|
| - // TODO(kmillikin): This is still kind of fishy. Refactor to not share
|
| - // name maps or else garbage collect unneeded names.
|
| - assert(element == null || !variable2index.containsKey(element));
|
| - variable2index[element] = index2variable.length;
|
| + // as the name of anonymous variables.
|
| + assert(!variable2index.containsKey(element));
|
| + if (element != null) variable2index[element] = index2variable.length;
|
| index2variable.add(element);
|
| index2value.add(value);
|
| }
|
|
|
| - void discard(int count) {
|
| + /// Drop [count] values from the environment.
|
| + ///
|
| + /// Return the previous last value in the environment for convenience.
|
| + ir.Primitive discard(int count) {
|
| + assert(count > 0);
|
| assert(count <= index2variable.length);
|
| + ir.Primitive value = index2value.last;
|
| // The map from variables to their index are shared, so we cannot remove
|
| // the mapping in `variable2index`.
|
| index2variable.length -= count;
|
| index2value.length -= count;
|
| + return value;
|
| }
|
|
|
| ir.Primitive lookup(Local element) {
|
| @@ -108,10 +109,12 @@ class Environment {
|
| Local variable = index2variable[i];
|
| if (variable != other.index2variable[i]) return false;
|
|
|
| - // The variable maps to the same index in both environments.
|
| - int index = variable2index[variable];
|
| - if (index == null || index != other.variable2index[variable]) {
|
| - return false;
|
| + // A named variable maps to the same index in both environments.
|
| + if (variable != null) {
|
| + int index = variable2index[variable];
|
| + if (index == null || index != other.variable2index[variable]) {
|
| + return false;
|
| + }
|
| }
|
| }
|
| return true;
|
| @@ -131,7 +134,15 @@ abstract class JumpCollector {
|
| final List<Iterable<LocalVariableElement>> _boxedTryVariables =
|
| <Iterable<LocalVariableElement>>[];
|
|
|
| - JumpCollector(this._continuationEnvironment, this.target);
|
| + /// Construct a collector for a given environment and optionally a target.
|
| + ///
|
| + /// The environment is the one in effect at the point where the jump's
|
| + /// continuation will be bound. Continuations can take an extra argument
|
| + /// (see [addJump]).
|
| + JumpCollector(this._continuationEnvironment, this.target,
|
| + bool hasExtraArgument) {
|
| + if (hasExtraArgument) _continuationEnvironment.extend(null, null);
|
| + }
|
|
|
| /// True if the collector has not recorded any jumps to its continuation.
|
| bool get isEmpty;
|
| @@ -144,7 +155,12 @@ abstract class JumpCollector {
|
| Environment get environment;
|
|
|
| /// Emit a jump to the continuation for a given [IrBuilder].
|
| - void addJump(IrBuilder builder);
|
| + ///
|
| + /// Jumps can take a single extra argument. This is used to pass return
|
| + /// values to finally blocks for returns inside try/finally and to pass
|
| + /// values of expressions that have internal control flow to their join-point
|
| + /// continuations.
|
| + void addJump(IrBuilder builder, [ir.Primitive value]);
|
|
|
| /// Add a set of variables that were boxed on entry to a try block.
|
| ///
|
| @@ -206,8 +222,9 @@ class ForwardJumpCollector extends JumpCollector {
|
| /// continuation represented by this collector will be bound. The
|
| /// environment is copied by the collector. Subsequent mutation of the
|
| /// original environment will not affect the collector.
|
| - ForwardJumpCollector(Environment environment, {JumpTarget target: null})
|
| - : super(new Environment.from(environment), target);
|
| + ForwardJumpCollector(Environment environment,
|
| + {JumpTarget target, bool hasExtraArgument: false})
|
| + : super(new Environment.from(environment), target, hasExtraArgument);
|
|
|
| bool get isEmpty => _invocations.isEmpty;
|
|
|
| @@ -221,12 +238,20 @@ class ForwardJumpCollector extends JumpCollector {
|
| return _continuationEnvironment;
|
| }
|
|
|
| - void addJump(IrBuilder builder) {
|
| + void addJump(IrBuilder builder, [ir.Primitive value]) {
|
| assert(_continuation == null);
|
| _buildTryExit(builder);
|
| ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized();
|
| builder.add(invoke);
|
| _invocations.add(invoke);
|
| + // Truncate the environment at the invocation site so it only includes
|
| + // values that will be continuation arguments. If an extra value is passed
|
| + // it will already be included in the continuation environment, but it is
|
| + // not present in the invocation environment.
|
| + int delta = builder.environment.length - _continuationEnvironment.length;
|
| + if (value != null) ++delta;
|
| + if (delta > 0) builder.environment.discard(delta);
|
| + if (value != null) builder.environment.extend(null, value);
|
| _invocationEnvironments.add(builder.environment);
|
| builder._current = null;
|
| // TODO(kmillikin): Can we set builder.environment to null to make it
|
| @@ -308,8 +333,9 @@ class BackwardJumpCollector extends JumpCollector {
|
| /// continuation represented by this collector will be bound. The
|
| /// translation of the continuation body will use an environment with the
|
| /// same shape, but with fresh continuation parameters for each variable.
|
| - BackwardJumpCollector(Environment environment, {JumpTarget target: null})
|
| - : super(new Environment.fresh(environment), target) {
|
| + BackwardJumpCollector(Environment environment,
|
| + {JumpTarget target, bool hasExtraArgument: false})
|
| + : super(new Environment.fresh(environment), target, hasExtraArgument) {
|
| List<ir.Parameter> parameters =
|
| new List<ir.Parameter>.from(_continuationEnvironment.index2value);
|
| _continuation = new ir.Continuation(parameters, isRecursive: true);
|
| @@ -320,13 +346,20 @@ class BackwardJumpCollector extends JumpCollector {
|
| ir.Continuation get continuation => _continuation;
|
| Environment get environment => _continuationEnvironment;
|
|
|
| - void addJump(IrBuilder builder) {
|
| + void addJump(IrBuilder builder, [ir.Primitive value]) {
|
| assert(_continuation.parameters.length <= builder.environment.length);
|
| isEmpty = false;
|
| _buildTryExit(builder);
|
| + // Truncate the environment at the invocation site so it only includes
|
| + // values that will be continuation arguments. If an extra value is passed
|
| + // it will already be included in the continuation environment, but it is
|
| + // not present in the invocation environment.
|
| + int delta = builder.environment.length - _continuationEnvironment.length;
|
| + if (value != null) ++delta;
|
| + if (delta > 0) builder.environment.discard(delta);
|
| + if (value != null) builder.environment.extend(null, value);
|
| builder.add(new ir.InvokeContinuation(_continuation,
|
| - builder.environment.index2value.take(_continuation.parameters.length)
|
| - .toList(),
|
| + builder.environment.index2value,
|
| isRecursive: true));
|
| builder._current = null;
|
| }
|
| @@ -394,14 +427,22 @@ class IrBuilderSharedState {
|
| ConstantSystem get constantSystem => constants.constantSystem;
|
|
|
| /// A stack of collectors for breaks.
|
| - final List<JumpCollector> breakCollectors = <JumpCollector>[];
|
| + List<JumpCollector> breakCollectors = <JumpCollector>[];
|
|
|
| /// A stack of collectors for continues.
|
| - final List<JumpCollector> continueCollectors = <JumpCollector>[];
|
| + List<JumpCollector> continueCollectors = <JumpCollector>[];
|
|
|
| final ExecutableElement currentElement;
|
|
|
| final ir.Continuation returnContinuation = new ir.Continuation.retrn();
|
| +
|
| + /// The target of a return from the function.
|
| + ///
|
| + /// A null value indicates that the target is the function's return
|
| + /// continuation. Otherwise, when inside the try block of try/finally
|
| + /// a return is intercepted to give a place to generate the finally code.
|
| + JumpCollector returnCollector = null;
|
| +
|
| ir.Parameter _thisParameter;
|
| ir.Parameter enclosingMethodThisParameter;
|
|
|
| @@ -750,20 +791,10 @@ abstract class IrBuilder {
|
| // expressions cannot introduce variable bindings.
|
| assert(environment.length == thenBuilder.environment.length);
|
| assert(environment.length == elseBuilder.environment.length);
|
| - // Extend the join-point environment with a placeholder for the value of
|
| - // the expression. Optimistically assume that the value is the value of
|
| - // the first subexpression. This value might noe even be in scope at the
|
| - // join-point because it's bound in the first subexpression. However, if
|
| - // that is the case, it will necessarily differ from the value of the
|
| - // other subexpression and cause the introduction of a join-point
|
| - // continuation parameter. If the two values do happen to be the same,
|
| - // this will avoid inserting a useless continuation parameter.
|
| - environment.extend(null, thenValue);
|
| - thenBuilder.environment.extend(null, thenValue);
|
| - elseBuilder.environment.extend(null, elseValue);
|
| - JumpCollector join = new ForwardJumpCollector(environment);
|
| - thenBuilder.jumpTo(join);
|
| - elseBuilder.jumpTo(join);
|
| + JumpCollector join =
|
| + new ForwardJumpCollector(environment, hasExtraArgument: true);
|
| + thenBuilder.jumpTo(join, thenValue);
|
| + elseBuilder.jumpTo(join, elseValue);
|
|
|
| // Build the term
|
| // let cont join(x, ..., result) = [] in
|
| @@ -782,10 +813,7 @@ abstract class IrBuilder {
|
| thenContinuation,
|
| elseContinuation))));
|
| environment = join.environment;
|
| - environment.discard(1);
|
| - return (thenValue == elseValue)
|
| - ? thenValue
|
| - : join.continuation.parameters.last;
|
| + return environment.discard(1);
|
| }
|
|
|
| /**
|
| @@ -1169,8 +1197,8 @@ abstract class IrBuilder {
|
| }
|
| }
|
|
|
| - void jumpTo(JumpCollector collector) {
|
| - collector.addJump(this);
|
| + void jumpTo(JumpCollector collector, [ir.Primitive value]) {
|
| + collector.addJump(this, value);
|
| }
|
|
|
| void addRecursiveContinuation(BackwardJumpCollector collector) {
|
| @@ -1715,6 +1743,7 @@ abstract class IrBuilder {
|
| {TryStatementInfo tryStatementInfo,
|
| SubbuildFunction buildTryBlock,
|
| List<CatchClauseInfo> catchClauseInfos: const <CatchClauseInfo>[],
|
| + SubbuildFunction buildFinallyBlock,
|
| ClosureClassMap closureClassMap}) {
|
| assert(isOpen);
|
|
|
| @@ -1747,125 +1776,230 @@ abstract class IrBuilder {
|
| // 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.
|
| - JumpCollector join = new ForwardJumpCollector(environment);
|
| - IrBuilder tryCatchBuilder = makeDelimitedBuilder();
|
| -
|
| - // Variables treated as mutable in a try are not mutable outside of it.
|
| - // Work with a copy of the outer builder's mutable variables.
|
| - tryCatchBuilder.mutableVariables =
|
| - new Map<Local, ir.MutableVariable>.from(mutableVariables);
|
| - for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| - assert(!tryCatchBuilder.isInMutableVariable(variable));
|
| - ir.Primitive value = tryCatchBuilder.buildLocalVariableGet(variable);
|
| - tryCatchBuilder.makeMutableVariable(variable);
|
| - tryCatchBuilder.declareLocalVariable(variable, initialValue: value);
|
| - }
|
| + if (catchClauseInfos.isNotEmpty) {
|
| + JumpCollector join = new ForwardJumpCollector(environment);
|
| + IrBuilder tryCatchBuilder = makeDelimitedBuilder();
|
| +
|
| + // Variables treated as mutable in a try are not mutable outside of it.
|
| + // Work with a copy of the outer builder's mutable variables.
|
| + tryCatchBuilder.mutableVariables =
|
| + new Map<Local, ir.MutableVariable>.from(mutableVariables);
|
| + for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| + assert(!tryCatchBuilder.isInMutableVariable(variable));
|
| + ir.Primitive value = tryCatchBuilder.buildLocalVariableGet(variable);
|
| + tryCatchBuilder.makeMutableVariable(variable);
|
| + tryCatchBuilder.declareLocalVariable(variable, initialValue: value);
|
| + }
|
|
|
| - IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
| + IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
|
|
| - void interceptJumps(JumpCollector collector) {
|
| - collector.enterTry(tryStatementInfo.boxedOnEntry);
|
| - }
|
| - void restoreJumps(JumpCollector collector) {
|
| - collector.leaveTry();
|
| - }
|
| - tryBuilder.state.breakCollectors.forEach(interceptJumps);
|
| - tryBuilder.state.continueCollectors.forEach(interceptJumps);
|
| - buildTryBlock(tryBuilder);
|
| - if (tryBuilder.isOpen) {
|
| - interceptJumps(join);
|
| - tryBuilder.jumpTo(join);
|
| - restoreJumps(join);
|
| - }
|
| - tryBuilder.state.breakCollectors.forEach(restoreJumps);
|
| - tryBuilder.state.continueCollectors.forEach(restoreJumps);
|
| -
|
| - IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
| - for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| - assert(catchBuilder.isInMutableVariable(variable));
|
| - ir.Primitive value = catchBuilder.buildLocalVariableGet(variable);
|
| - // After this point, the variables that were boxed on entry to the try
|
| - // are no longer treated as mutable.
|
| - catchBuilder.removeMutableVariable(variable);
|
| - catchBuilder.environment.update(variable, value);
|
| - }
|
| + void interceptJump(JumpCollector collector) {
|
| + collector.enterTry(tryStatementInfo.boxedOnEntry);
|
| + }
|
| + void restoreJump(JumpCollector collector) {
|
| + collector.leaveTry();
|
| + }
|
| + tryBuilder.state.breakCollectors.forEach(interceptJump);
|
| + tryBuilder.state.continueCollectors.forEach(interceptJump);
|
| + buildTryBlock(tryBuilder);
|
| + if (tryBuilder.isOpen) {
|
| + interceptJump(join);
|
| + tryBuilder.jumpTo(join);
|
| + restoreJump(join);
|
| + }
|
| + tryBuilder.state.breakCollectors.forEach(restoreJump);
|
| + tryBuilder.state.continueCollectors.forEach(restoreJump);
|
| +
|
| + IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder();
|
| + for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| + assert(catchBuilder.isInMutableVariable(variable));
|
| + ir.Primitive value = catchBuilder.buildLocalVariableGet(variable);
|
| + // After this point, the variables that were boxed on entry to the try
|
| + // are no longer treated as mutable.
|
| + catchBuilder.removeMutableVariable(variable);
|
| + catchBuilder.environment.update(variable, value);
|
| + }
|
|
|
| - // Handlers are always translated as having both exception and stack trace
|
| - // parameters. Multiple clauses do not have to use the same names for
|
| - // them. Choose the first of each as the name hint for the respective
|
| - // handler parameter.
|
| - ir.Parameter exceptionParameter =
|
| - new ir.Parameter(catchClauseInfos.first.exceptionVariable);
|
| - LocalVariableElement traceVariable;
|
| - CatchClauseInfo catchAll;
|
| - for (int i = 0; i < catchClauseInfos.length; ++i) {
|
| - CatchClauseInfo info = catchClauseInfos[i];
|
| - if (info.type == null) {
|
| - catchAll = info;
|
| - catchClauseInfos.length = i;
|
| - break;
|
| + // Handlers are always translated as having both exception and stack trace
|
| + // parameters. Multiple clauses do not have to use the same names for
|
| + // them. Choose the first of each as the name hint for the respective
|
| + // handler parameter.
|
| + ir.Parameter exceptionParameter =
|
| + new ir.Parameter(catchClauseInfos.first.exceptionVariable);
|
| + LocalVariableElement traceVariable;
|
| + CatchClauseInfo catchAll;
|
| + for (int i = 0; i < catchClauseInfos.length; ++i) {
|
| + CatchClauseInfo info = catchClauseInfos[i];
|
| + if (info.type == null) {
|
| + catchAll = info;
|
| + catchClauseInfos.length = i;
|
| + break;
|
| + }
|
| + if (traceVariable == null) {
|
| + traceVariable = info.stackTraceVariable;
|
| + }
|
| }
|
| - if (traceVariable == null) {
|
| - traceVariable = info.stackTraceVariable;
|
| + ir.Parameter traceParameter = new ir.Parameter(traceVariable);
|
| + // Expand multiple catch clauses into an explicit if/then/else. Iterate
|
| + // them in reverse so the current block becomes the next else block.
|
| + ir.Expression catchBody;
|
| + if (catchAll == null) {
|
| + catchBody = new ir.Rethrow();
|
| + } else {
|
| + IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder();
|
| + clauseBuilder.declareLocalVariable(catchAll.exceptionVariable,
|
| + initialValue: exceptionParameter);
|
| + if (catchAll.stackTraceVariable != null) {
|
| + clauseBuilder.declareLocalVariable(catchAll.stackTraceVariable,
|
| + initialValue: traceParameter);
|
| + }
|
| + catchAll.buildCatchBlock(clauseBuilder);
|
| + if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
|
| + catchBody = clauseBuilder._root;
|
| }
|
| - }
|
| - ir.Parameter traceParameter = new ir.Parameter(traceVariable);
|
| - // Expand multiple catch clauses into an explicit if/then/else. Iterate
|
| - // them in reverse so the current block becomes the next else block.
|
| - ir.Expression catchBody;
|
| - if (catchAll == null) {
|
| - catchBody = new ir.Rethrow();
|
| - } else {
|
| - IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder();
|
| - clauseBuilder.declareLocalVariable(catchAll.exceptionVariable,
|
| - initialValue: exceptionParameter);
|
| - if (catchAll.stackTraceVariable != null) {
|
| - clauseBuilder.declareLocalVariable(catchAll.stackTraceVariable,
|
| - initialValue: traceParameter);
|
| + for (CatchClauseInfo clause in catchClauseInfos.reversed) {
|
| + IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder();
|
| + clauseBuilder.declareLocalVariable(clause.exceptionVariable,
|
| + initialValue: exceptionParameter);
|
| + if (clause.stackTraceVariable != null) {
|
| + clauseBuilder.declareLocalVariable(clause.stackTraceVariable,
|
| + initialValue: traceParameter);
|
| + }
|
| + clause.buildCatchBlock(clauseBuilder);
|
| + if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
|
| + ir.Continuation thenContinuation = new ir.Continuation([]);
|
| + thenContinuation.body = clauseBuilder._root;
|
| + ir.Continuation elseContinuation = new ir.Continuation([]);
|
| + elseContinuation.body = catchBody;
|
| +
|
| + // Build the type test guarding this clause. We can share the
|
| + // environment with the nested builder because this part cannot mutate
|
| + // it.
|
| + IrBuilder checkBuilder = catchBuilder.makeDelimitedBuilder(environment);
|
| + ir.Primitive typeMatches =
|
| + checkBuilder.buildTypeOperator(exceptionParameter,
|
| + clause.type,
|
| + isTypeTest: true);
|
| + checkBuilder.add(new ir.LetCont.many([thenContinuation,
|
| + elseContinuation],
|
| + new ir.Branch(new ir.IsTrue(typeMatches),
|
| + thenContinuation,
|
| + elseContinuation)));
|
| + catchBody = checkBuilder._root;
|
| }
|
| - catchAll.buildCatchBlock(clauseBuilder);
|
| - if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
|
| - catchBody = clauseBuilder._root;
|
| - }
|
| - for (CatchClauseInfo clause in catchClauseInfos.reversed) {
|
| - IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder();
|
| - clauseBuilder.declareLocalVariable(clause.exceptionVariable,
|
| - initialValue: exceptionParameter);
|
| - if (clause.stackTraceVariable != null) {
|
| - clauseBuilder.declareLocalVariable(clause.stackTraceVariable,
|
| - initialValue: traceParameter);
|
| +
|
| + List<ir.Parameter> catchParameters =
|
| + <ir.Parameter>[exceptionParameter, traceParameter];
|
| + ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
|
| + catchBuilder.add(catchBody);
|
| + catchContinuation.body = catchBuilder._root;
|
| +
|
| + tryCatchBuilder.add(
|
| + new ir.LetHandler(catchContinuation, tryBuilder._root));
|
| + add(new ir.LetCont(join.continuation, tryCatchBuilder._root));
|
| + environment = join.environment;
|
| + } else {
|
| + // Try/finally.
|
| + JumpCollector join = new ForwardJumpCollector(environment);
|
| + IrBuilder tryFinallyBuilder = makeDelimitedBuilder();
|
| +
|
| + tryFinallyBuilder.mutableVariables =
|
| + new Map<Local, ir.MutableVariable>.from(mutableVariables);
|
| + for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| + assert(!tryFinallyBuilder.isInMutableVariable(variable));
|
| + ir.Primitive value = tryFinallyBuilder.buildLocalVariableGet(variable);
|
| + tryFinallyBuilder.makeMutableVariable(variable);
|
| + tryFinallyBuilder.declareLocalVariable(variable, initialValue: value);
|
| }
|
| - clause.buildCatchBlock(clauseBuilder);
|
| - if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
|
| - ir.Continuation thenContinuation = new ir.Continuation([]);
|
| - thenContinuation.body = clauseBuilder._root;
|
| - ir.Continuation elseContinuation = new ir.Continuation([]);
|
| - elseContinuation.body = catchBody;
|
| -
|
| - // Build the type test guarding this clause. We can share the environment
|
| - // with the nested builder because this part cannot mutate it.
|
| - IrBuilder checkBuilder = catchBuilder.makeDelimitedBuilder(environment);
|
| - ir.Primitive typeMatches =
|
| - checkBuilder.buildTypeOperator(exceptionParameter,
|
| - clause.type,
|
| - isTypeTest: true);
|
| - checkBuilder.add(new ir.LetCont.many([thenContinuation, elseContinuation],
|
| - new ir.Branch(new ir.IsTrue(typeMatches),
|
| - thenContinuation,
|
| - elseContinuation)));
|
| - catchBody = checkBuilder._root;
|
| - }
|
|
|
| - List<ir.Parameter> catchParameters =
|
| - <ir.Parameter>[exceptionParameter, traceParameter];
|
| - ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
|
| - catchBuilder.add(catchBody);
|
| - catchContinuation.body = catchBuilder._root;
|
| + IrBuilder tryBuilder = tryFinallyBuilder.makeDelimitedBuilder();
|
|
|
| - tryCatchBuilder.add(
|
| - new ir.LetHandler(catchContinuation, tryBuilder._root));
|
| - add(new ir.LetCont(join.continuation, tryCatchBuilder._root));
|
| - environment = join.environment;
|
| + JumpCollector interceptJump(JumpCollector collector) {
|
| + JumpCollector result =
|
| + new ForwardJumpCollector(environment, target: collector.target);
|
| + result.enterTry(tryStatementInfo.boxedOnEntry);
|
| + return result;
|
| + }
|
| + void restoreJump(JumpCollector collector) {
|
| + collector.leaveTry();
|
| + }
|
| + List<JumpCollector> savedBreaks = tryBuilder.state.breakCollectors;
|
| + List<JumpCollector> savedContinues = tryBuilder.state.continueCollectors;
|
| + JumpCollector savedReturn = tryBuilder.state.returnCollector;
|
| +
|
| + List<JumpCollector> newBreaks = tryBuilder.state.breakCollectors =
|
| + savedBreaks.map(interceptJump).toList();
|
| + List<JumpCollector> newContinues = tryBuilder.state.continueCollectors =
|
| + savedContinues.map(interceptJump).toList();
|
| + JumpCollector newReturn = tryBuilder.state.returnCollector =
|
| + new ForwardJumpCollector(environment, hasExtraArgument: true);
|
| + newReturn.enterTry(tryStatementInfo.boxedOnEntry);
|
| + buildTryBlock(tryBuilder);
|
| + if (tryBuilder.isOpen) {
|
| + // To cover control falling off the end of the try block, the finally
|
| + // code is translated at the join point. This ensures that it is
|
| + // correctly outside the scope of the catch handler.
|
| + join.enterTry(tryStatementInfo.boxedOnEntry);
|
| + tryBuilder.jumpTo(join);
|
| + join.leaveTry();
|
| + }
|
| + newBreaks.forEach(restoreJump);
|
| + newContinues.forEach(restoreJump);
|
| + newReturn.leaveTry();
|
| + tryBuilder.state.breakCollectors = savedBreaks;
|
| + tryBuilder.state.continueCollectors = savedContinues;
|
| + tryBuilder.state.returnCollector = savedReturn;
|
| +
|
| + IrBuilder catchBuilder = tryFinallyBuilder.makeDelimitedBuilder();
|
| + for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) {
|
| + assert(catchBuilder.isInMutableVariable(variable));
|
| + ir.Primitive value = catchBuilder.buildLocalVariableGet(variable);
|
| + catchBuilder.removeMutableVariable(variable);
|
| + catchBuilder.environment.update(variable, value);
|
| + }
|
| +
|
| + buildFinallyBlock(catchBuilder);
|
| + if (catchBuilder.isOpen) {
|
| + catchBuilder.add(new ir.Rethrow());
|
| + catchBuilder._current = null;
|
| + }
|
| + List<ir.Parameter> catchParameters =
|
| + <ir.Parameter>[new ir.Parameter(null), new ir.Parameter(null)];
|
| + ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
|
| + catchContinuation.body = catchBuilder._root;
|
| + tryFinallyBuilder.add(
|
| + new ir.LetHandler(catchContinuation, tryBuilder._root));
|
| +
|
| + // Build a list of continuations for jumps from the try block and
|
| + // duplicate the finally code before jumping to the actual target.
|
| + List<ir.Continuation> exits = <ir.Continuation>[join.continuation];
|
| + void addJump(JumpCollector newCollector,
|
| + JumpCollector originalCollector) {
|
| + if (newCollector.isEmpty) return;
|
| + IrBuilder builder = makeDelimitedBuilder(newCollector.environment);
|
| + buildFinallyBlock(builder);
|
| + if (builder.isOpen) builder.jumpTo(originalCollector);
|
| + newCollector.continuation.body = builder._root;
|
| + exits.add(newCollector.continuation);
|
| + }
|
| + for (int i = 0; i < newBreaks.length; ++i) {
|
| + addJump(newBreaks[i], savedBreaks[i]);
|
| + }
|
| + for (int i = 0; i < newContinues.length; ++i) {
|
| + addJump(newContinues[i], savedContinues[i]);
|
| + }
|
| + if (!newReturn.isEmpty) {
|
| + IrBuilder builder = makeDelimitedBuilder(newReturn.environment);
|
| + ir.Primitive value = builder.environment.discard(1);
|
| + buildFinallyBlock(builder);
|
| + if (builder.isOpen) builder.buildReturn(value);
|
| + newReturn.continuation.body = builder._root;
|
| + exits.add(newReturn.continuation);
|
| + }
|
| + add(new ir.LetCont.many(exits, tryFinallyBuilder._root));
|
| + environment = join.environment;
|
| + buildFinallyBlock(this);
|
| + }
|
| }
|
|
|
| /// Create a return statement `return value;` or `return;` if [value] is
|
| @@ -1879,8 +2013,15 @@ abstract class IrBuilder {
|
| if (value == null) {
|
| value = buildNullConstant();
|
| }
|
| - add(new ir.InvokeContinuation(state.returnContinuation, [value]));
|
| - _current = null;
|
| + if (state.returnCollector == null) {
|
| + add(new ir.InvokeContinuation(state.returnContinuation, [value]));
|
| + _current = null;
|
| + } else {
|
| + // Inside the try block of try/finally, all returns go to a join-point
|
| + // continuation that contains the finally code. The return value is
|
| + // passed as an extra argument.
|
| + jumpTo(state.returnCollector, value);
|
| + }
|
| }
|
|
|
| /// Create a blocks of [statements] by applying [build] to all reachable
|
| @@ -2037,24 +2178,19 @@ abstract class IrBuilder {
|
| ir.Constant rightTrue = rightTrueBuilder.buildBooleanConstant(true);
|
| ir.Constant rightFalse = rightFalseBuilder.buildBooleanConstant(false);
|
|
|
| - // Treat the result values as named values in the environment, so they
|
| - // will be treated as arguments to the join-point continuation.
|
| + // Result values are passed as continuation arguments, which are
|
| + // constructed based on environments. These assertions are a sanity check.
|
| assert(environment.length == emptyBuilder.environment.length);
|
| assert(environment.length == rightTrueBuilder.environment.length);
|
| assert(environment.length == rightFalseBuilder.environment.length);
|
| - // Treat the value of the expression as a local variable so it will get
|
| - // a continuation parameter.
|
| - environment.extend(null, null);
|
| - emptyBuilder.environment.extend(null, leftBool);
|
| - rightTrueBuilder.environment.extend(null, rightTrue);
|
| - rightFalseBuilder.environment.extend(null, rightFalse);
|
|
|
| // Wire up two continuations for the left subexpression, two continuations
|
| // for the right subexpression, and a three-way join continuation.
|
| - JumpCollector join = new ForwardJumpCollector(environment);
|
| - emptyBuilder.jumpTo(join);
|
| - rightTrueBuilder.jumpTo(join);
|
| - rightFalseBuilder.jumpTo(join);
|
| + JumpCollector join =
|
| + new ForwardJumpCollector(environment, hasExtraArgument: true);
|
| + emptyBuilder.jumpTo(join, leftBool);
|
| + rightTrueBuilder.jumpTo(join, rightTrue);
|
| + rightFalseBuilder.jumpTo(join, rightFalse);
|
| ir.Continuation leftTrueContinuation = new ir.Continuation([]);
|
| ir.Continuation leftFalseContinuation = new ir.Continuation([]);
|
| ir.Continuation rightTrueContinuation = new ir.Continuation([]);
|
| @@ -2086,10 +2222,7 @@ abstract class IrBuilder {
|
| leftTrueContinuation,
|
| leftFalseContinuation))));
|
| environment = join.environment;
|
| - environment.discard(1);
|
| - // There is always a join parameter for the result value, because it
|
| - // is different on at least two paths.
|
| - return join.continuation.parameters.last;
|
| + return environment.discard(1);
|
| }
|
|
|
| ir.Primitive buildIdentical(ir.Primitive x, ir.Primitive y) {
|
| @@ -2630,4 +2763,4 @@ class SwitchCaseInfo {
|
| SwitchCaseInfo(this.buildBody);
|
|
|
| void addConstant(ir.Primitive constant) => constants.add(constant);
|
| -}
|
| +}
|
|
|