Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(454)

Unified Diff: pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart

Issue 1207703002: dart2js CPS: Support try/catch/finally. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Merge, and fix a test expectation. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 20a22af71ba0be30990ea56e5172145a3774abd6..b9ee61f3f627543eafab7554ac7d4263b40a5c26 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
@@ -1621,29 +1621,86 @@ class IrBuilder {
}
}
- /// Creates a try-statement.
+ /// Utility function to translate try/catch into the IR.
///
- /// [tryInfo] provides information on local variables declared and boxed
- /// within this try statement.
+ /// The translation treats try/finally and try/catch/finally as if they
+ /// were macro-expanded into try/catch. This utility function generates
+ /// that try/catch. The function is parameterized over a list of variables
+ /// that should be boxed on entry to the try, and over functions to emit
+ /// code for entering the try, building the try body, leaving the try body,
+ /// building the catch body, and leaving the entire try/catch.
+ ///
+ /// Please see the function's implementation for where these functions are
+ /// called.
+ void _helpBuildTryCatch(TryStatementInfo variables,
+ void enterTry(IrBuilder builder),
+ SubbuildFunction buildTryBlock,
+ void leaveTry(IrBuilder builder),
+ List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join),
+ void leaveTryCatch(IrBuilder builder, JumpCollector join,
+ ir.Expression body)) {
+ 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 variables.boxedOnEntry) {
+ assert(!tryCatchBuilder.isInMutableVariable(variable));
+ ir.Primitive value = tryCatchBuilder.buildLocalVariableGet(variable);
+ tryCatchBuilder.makeMutableVariable(variable);
+ tryCatchBuilder.declareLocalVariable(variable, initialValue: value);
+ }
+
+ IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder();
+ enterTry(tryBuilder);
+ buildTryBlock(tryBuilder);
+ if (tryBuilder.isOpen) {
+ join.enterTry(variables.boxedOnEntry);
+ tryBuilder.jumpTo(join);
+ join.leaveTry();
+ }
+ leaveTry(tryBuilder);
+
+ IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder();
+ for (LocalVariableElement variable in variables.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);
+ }
+
+ List<ir.Parameter> catchParameters = buildCatch(catchBuilder, join);
+ ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
+ catchContinuation.body = catchBuilder._root;
+ tryCatchBuilder.add(
+ new ir.LetHandler(catchContinuation, tryBuilder._root));
+
+ leaveTryCatch(this, join, tryCatchBuilder._root);
+ }
+
+ /// Translates a try/catch.
+ ///
+ /// [variables] provides information on local variables declared and boxed
+ /// within the try body.
/// [buildTryBlock] builds the try block.
/// [catchClauseInfos] provides access to the catch type, exception variable,
/// and stack trace variable, and a function for building the catch block.
- void buildTry(
- {TryStatementInfo tryStatementInfo,
- SubbuildFunction buildTryBlock,
- List<CatchClauseInfo> catchClauseInfos: const <CatchClauseInfo>[],
- SubbuildFunction buildFinallyBlock,
- ClosureClassMap closureClassMap}) {
+ void buildTryCatch(TryStatementInfo variables,
+ SubbuildFunction buildTryBlock,
+ List<CatchClauseInfo> catchClauseInfos) {
assert(isOpen);
-
// Catch handlers are in scope for their body. The CPS translation of
- // [[try tryBlock catch (e) catchBlock; successor]] is:
+ // [[try tryBlock catch (ex, st) 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 handler catch_(ex, st) =
// let prim p0 = GetMutable(m0) in
// let prim p1 = GetMutable(m1) in
// ...
@@ -1665,49 +1722,31 @@ 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.
- 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();
+ void enterTry(IrBuilder builder) {
+ // On entry to try of try/catch, update the builder's state to reflect the
+ // variables that have been boxed.
void interceptJump(JumpCollector collector) {
- collector.enterTry(tryStatementInfo.boxedOnEntry);
+ collector.enterTry(variables.boxedOnEntry);
}
+ builder.state.breakCollectors.forEach(interceptJump);
+ builder.state.continueCollectors.forEach(interceptJump);
+ }
+
+ void leaveTry(IrBuilder builder) {
+ // On exit from try of try/catch, update the builder's state to reflect
+ // the variables that are no longer boxed.
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);
- }
+ builder.state.breakCollectors.forEach(restoreJump);
+ builder.state.continueCollectors.forEach(restoreJump);
+ }
+
+ List<ir.Parameter> buildCatch(IrBuilder builder,
+ JumpCollector join) {
+ // Translate the catch clauses. Multiple clauses are translated as if
+ // they were explicitly cascaded if/else type tests.
// Handlers are always translated as having both exception and stack trace
// parameters. Multiple clauses do not have to use the same names for
@@ -1729,136 +1768,140 @@ class IrBuilder {
}
}
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;
- }
- for (CatchClauseInfo clause in catchClauseInfos.reversed) {
- IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder();
+
+ ir.Expression buildCatchClause(CatchClauseInfo clause) {
+ IrBuilder clauseBuilder = builder.makeDelimitedBuilder();
clauseBuilder.declareLocalVariable(clause.exceptionVariable,
- initialValue: exceptionParameter);
+ initialValue: exceptionParameter);
if (clause.stackTraceVariable != null) {
clauseBuilder.declareLocalVariable(clause.stackTraceVariable,
- initialValue: traceParameter);
+ initialValue: traceParameter);
}
clause.buildCatchBlock(clauseBuilder);
if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
+ return clauseBuilder._root;
+ }
+
+ // 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 = (catchAll == null)
+ ? new ir.Rethrow()
+ : buildCatchClause(catchAll);
+ for (CatchClauseInfo clause in catchClauseInfos.reversed) {
ir.Continuation thenContinuation = new ir.Continuation([]);
- thenContinuation.body = clauseBuilder._root;
ir.Continuation elseContinuation = new ir.Continuation([]);
+ thenContinuation.body = buildCatchClause(clause);
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);
+ IrBuilder checkBuilder = builder.makeDelimitedBuilder(environment);
ir.Primitive typeMatches =
checkBuilder.buildTypeOperator(exceptionParameter,
- clause.type,
- isTypeTest: true);
+ clause.type,
+ isTypeTest: true);
checkBuilder.add(new ir.LetCont.many([thenContinuation,
elseContinuation],
- new ir.Branch(new ir.IsTrue(typeMatches),
- thenContinuation,
- elseContinuation)));
+ new ir.Branch(new ir.IsTrue(typeMatches),
+ thenContinuation,
+ elseContinuation)));
catchBody = checkBuilder._root;
}
+ builder.add(catchBody);
- List<ir.Parameter> catchParameters =
- <ir.Parameter>[exceptionParameter, traceParameter];
- ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
- catchBuilder.add(catchBody);
- catchContinuation.body = catchBuilder._root;
+ return <ir.Parameter>[exceptionParameter, traceParameter];
+ }
- 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);
- }
+ void leaveTryCatch(IrBuilder builder, JumpCollector join,
+ ir.Expression body) {
+ // Add the binding for the join-point continuation and continue the
+ // translation in its body.
+ builder.add(new ir.LetCont(join.continuation, body));
+ builder.environment = join.environment;
+ }
- IrBuilder tryBuilder = tryFinallyBuilder.makeDelimitedBuilder();
+ _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry,
+ buildCatch, leaveTryCatch);
+ }
+ /// Translates a try/finally.
+ ///
+ /// [variables] provides information on local variables declared and boxed
+ /// within the try body.
+ /// [buildTryBlock] builds the try block.
+ /// [buildFinallyBlock] builds the finally block.
+ void buildTryFinally(TryStatementInfo variables,
+ SubbuildFunction buildTryBlock,
+ SubbuildFunction buildFinallyBlock) {
+ assert(isOpen);
+ // Try/finally is implemented in terms of try/catch and by duplicating the
+ // code for finally at all exits. The encoding is:
+ //
+ // try tryBlock finally finallyBlock
+ // ==>
+ // try tryBlock catch (ex, st) { finallyBlock; rethrow } finallyBlock
+ //
+ // Where in tryBlock, all of the break, continue, and return exits are
+ // translated as jumps to continuations (bound outside the catch handler)
+ // that include the finally code followed by a break, continue, or
+ // return respectively.
+
+ List<JumpCollector> savedBreaks, newBreaks, savedContinues, newContinues;
+ JumpCollector savedReturn, newReturn;
+ void enterTry(IrBuilder builder) {
+ // On entry to the try of try/finally, update the builder's state to
+ // relfect the variables that have been boxed. Then intercept all break,
+ // continue, and return jumps out of the try so that they can go to
+ // continuations that include the finally code.
JumpCollector interceptJump(JumpCollector collector) {
JumpCollector result =
new ForwardJumpCollector(environment, target: collector.target);
- result.enterTry(tryStatementInfo.boxedOnEntry);
+ result.enterTry(variables.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;
+ savedBreaks = builder.state.breakCollectors;
+ savedContinues = builder.state.continueCollectors;
+ savedReturn = builder.state.returnCollector;
- List<JumpCollector> newBreaks = tryBuilder.state.breakCollectors =
+ builder.state.breakCollectors = newBreaks =
savedBreaks.map(interceptJump).toList();
- List<JumpCollector> newContinues = tryBuilder.state.continueCollectors =
+ builder.state.continueCollectors = newContinues =
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();
+ builder.state.returnCollector = newReturn =
+ new ForwardJumpCollector(environment, hasExtraArgument: true)
+ ..enterTry(variables.boxedOnEntry);
+ }
+
+ void leaveTry(IrBuilder builder) {
+ // On exit from the try of try/finally, update the builder's state to
+ // reflect the variables that are no longer boxed and restore the
+ // original, unintercepted break, continue, and return targets.
+ void restoreJump(JumpCollector collector) {
+ collector.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);
- }
+ builder.state.breakCollectors = savedBreaks;
+ builder.state.continueCollectors = savedContinues;
+ builder.state.returnCollector = savedReturn;
+ }
- buildFinallyBlock(catchBuilder);
- if (catchBuilder.isOpen) {
- catchBuilder.add(new ir.Rethrow());
- catchBuilder._current = null;
+ List<ir.Parameter> buildCatch(IrBuilder builder,
+ JumpCollector join) {
+ // The catch block of the try/catch used for try/finally is the finally
+ // code followed by a rethrow.
+ buildFinallyBlock(builder);
+ if (builder.isOpen) {
+ builder.add(new ir.Rethrow());
+ builder._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));
+ return <ir.Parameter>[new ir.Parameter(null), new ir.Parameter(null)];
+ }
+ void leaveTryCatch(IrBuilder builder, JumpCollector join,
+ ir.Expression body) {
// 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];
@@ -1885,10 +1928,13 @@ class IrBuilder {
newReturn.continuation.body = builder._root;
exits.add(newReturn.continuation);
}
- add(new ir.LetCont.many(exits, tryFinallyBuilder._root));
- environment = join.environment;
- buildFinallyBlock(this);
+ builder.add(new ir.LetCont.many(exits, body));
+ builder.environment = join.environment;
+ buildFinallyBlock(builder);
}
+
+ _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry,
+ buildCatch, leaveTryCatch);
}
/// Create a return statement `return value;` or `return;` if [value] is
« no previous file with comments | « no previous file | pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698