Skip to content

Commit f79bc25

Browse files
Implement try/catch in the JS backend.
In the CPS IR, exception handlers initially have two parameters for the exception and the stack trace. In JavaScript: 1. Unwrap the caught value to get the exception that was thrown and use the unwrapped value in place of the exception parameter. 2. Get the stack trace from the (unwrapped) exception that was thrown. 3. Remove the stack trace parameter. BUG= [email protected] Review URL: https://codereview.chromium.org//1083663005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45152 260f80e4-7a28-3924-810f-c04153c831b5
1 parent 75fc25f commit f79bc25

File tree

5 files changed

+116
-15
lines changed

5 files changed

+116
-15
lines changed

pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,6 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
490490
}
491491

492492
visitTryStatement(ast.TryStatement node) {
493-
// Try/catch is not yet implemented in the JS backend.
494-
if (tryStatements == null) {
495-
return giveup(node, 'try/catch in the JS backend');
496-
}
497493
// Multiple catch blocks are not yet implemented.
498494
if (node.catchBlocks.isEmpty ||
499495
node.catchBlocks.nodes.tail == null) {
@@ -2560,6 +2556,17 @@ class JsIrBuilderVisitor extends IrBuilderVisitor {
25602556
return parameters;
25612557
}
25622558

2559+
DartCapturedVariables _analyzeCapturedVariables(ast.Node node) {
2560+
DartCapturedVariables variables = new DartCapturedVariables(elements);
2561+
try {
2562+
variables.analyze(node);
2563+
} catch (e) {
2564+
bailoutMessage = variables.bailoutMessage;
2565+
rethrow;
2566+
}
2567+
return variables;
2568+
}
2569+
25632570
/// Builds the IR for the body of a constructor.
25642571
///
25652572
/// This function is invoked from one or more "factory" constructors built by
@@ -2572,6 +2579,15 @@ class JsIrBuilderVisitor extends IrBuilderVisitor {
25722579
node,
25732580
elements);
25742581

2582+
// We compute variables boxed in mutable variables on entry to each try
2583+
// block, not including variables captured by a closure (which are boxed
2584+
// in the heap). This duplicates some of the work of closure conversion
2585+
// without directly using the results. This duplication is wasteful and
2586+
// error-prone.
2587+
// TODO(kmillikin): We should combine closure conversion and try/catch
2588+
// variable analysis in some way.
2589+
DartCapturedVariables variables = _analyzeCapturedVariables(node);
2590+
tryStatements = variables.tryStatements;
25752591
JsIrBuilder builder = getBuilderFor(body);
25762592

25772593
return withBuilder(builder, () {
@@ -2594,6 +2610,8 @@ class JsIrBuilderVisitor extends IrBuilderVisitor {
25942610
element,
25952611
node,
25962612
elements);
2613+
DartCapturedVariables variables = _analyzeCapturedVariables(node);
2614+
tryStatements = variables.tryStatements;
25972615
IrBuilder builder = getBuilderFor(element);
25982616
return withBuilder(builder, () => _makeFunctionBody(element, node));
25992617
}

pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ abstract class Definition<T extends Definition<T>> extends Node {
3737

3838
bool get hasAtMostOneUse => firstRef == null || firstRef.next == null;
3939
bool get hasExactlyOneUse => firstRef != null && firstRef.next == null;
40+
bool get hasNoUses => firstRef == null;
4041
bool get hasAtLeastOneUse => firstRef != null;
4142
bool get hasMultipleUses => !hasAtMostOneUse;
4243

4344
void substituteFor(Definition<T> other) {
44-
if (other.firstRef == null) return;
45+
if (other.hasNoUses) return;
4546
Reference<T> previous, current = other.firstRef;
4647
do {
4748
current.definition = this;
@@ -51,6 +52,7 @@ abstract class Definition<T extends Definition<T>> extends Node {
5152
previous.next = firstRef;
5253
if (firstRef != null) firstRef.previous = previous;
5354
firstRef = other.firstRef;
55+
other.firstRef = null;
5456
}
5557
}
5658

pkg/compiler/lib/src/js_backend/codegen/codegen.dart

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,13 @@ class CodeGenerator extends tree_ir.StatementVisitor
425425
@override
426426
void visitIf(tree_ir.If node) {
427427
accumulator.add(new js.If(visitExpression(node.condition),
428-
buildBody(node.thenStatement),
429-
buildBody(node.elseStatement)));
428+
buildBodyStatement(node.thenStatement),
429+
buildBodyStatement(node.elseStatement)));
430430
}
431431

432432
@override
433433
void visitLabeledStatement(tree_ir.LabeledStatement node) {
434-
accumulator.add(buildLabeled(() => buildBody(node.body),
434+
accumulator.add(buildLabeled(() => buildBodyStatement(node.body),
435435
node.label,
436436
node.next));
437437
visitStatement(node.next);
@@ -476,20 +476,29 @@ class CodeGenerator extends tree_ir.StatementVisitor
476476
}
477477

478478
/// Builds a nested statement.
479-
js.Statement buildBody(tree_ir.Statement statement) {
479+
js.Statement buildBodyStatement(tree_ir.Statement statement) {
480480
List<js.Statement> savedAccumulator = accumulator;
481-
accumulator = new List<js.Statement>();
481+
accumulator = <js.Statement>[];
482482
visitStatement(statement);
483483
js.Statement result = _bodyAsStatement();
484484
accumulator = savedAccumulator;
485485
return result;
486486
}
487487

488+
js.Block buildBodyBlock(tree_ir.Statement statement) {
489+
List<js.Statement> savedAccumulator = accumulator;
490+
accumulator = <js.Statement>[];
491+
visitStatement(statement);
492+
js.Statement result = new js.Block(accumulator);
493+
accumulator = savedAccumulator;
494+
return result;
495+
}
496+
488497
js.Statement buildWhile(js.Expression condition,
489498
tree_ir.Statement body,
490499
tree_ir.Label label,
491500
tree_ir.Statement fallthroughStatement) {
492-
return buildLabeled(() => new js.While(condition, buildBody(body)),
501+
return buildLabeled(() => new js.While(condition, buildBodyStatement(body)),
493502
label,
494503
fallthroughStatement);
495504
}
@@ -517,8 +526,13 @@ class CodeGenerator extends tree_ir.StatementVisitor
517526

518527
@override
519528
void visitTry(tree_ir.Try node) {
520-
// TODO(kmillikin): implement TryStatement.
521-
return giveup(node);
529+
js.Block tryBlock = buildBodyBlock(node.tryBody);
530+
tree_ir.Variable exceptionVariable = node.catchParameters.first;
531+
js.VariableDeclaration exceptionParameter =
532+
new js.VariableDeclaration(getVariableName(exceptionVariable));
533+
js.Block catchBlock = buildBodyBlock(node.catchBody);
534+
js.Catch catchPart = new js.Catch(exceptionParameter, catchBlock);
535+
accumulator.add(new js.Try(tryBlock, catchPart, null));
522536
}
523537

524538
@override

pkg/compiler/lib/src/js_backend/codegen/glue.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ class Glue {
121121
_backend.namer.globalObjectFor(_backend.interceptorsLibrary));
122122
}
123123

124+
FunctionElement getExceptionUnwrapper() {
125+
return _backend.getExceptionUnwrapper();
126+
}
127+
128+
FunctionElement getTraceFromException() {
129+
return _backend.getTraceFromException();
130+
}
131+
124132
FunctionElement getCreateRuntimeType() {
125133
return _backend.getCreateRuntimeType();
126134
}

pkg/compiler/lib/src/js_backend/codegen/unsugar.dart

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import '../../cps_ir/cps_ir_nodes.dart';
66
import '../../cps_ir/optimizers.dart';
77
import '../../constants/expressions.dart';
88
import '../../constants/values.dart';
9-
import '../../elements/elements.dart' show ClassElement, FieldElement, Element;
9+
import '../../elements/elements.dart'
10+
show ClassElement, FieldElement, FunctionElement, Element;
1011
import '../../js_backend/codegen/glue.dart';
1112
import '../../dart2jslib.dart' show Selector, World;
1213

@@ -15,14 +16,16 @@ import '../../dart2jslib.dart' show Selector, World;
1516
///
1617
/// Performs the following rewrites:
1718
/// - rewrite [IsTrue] in a [Branch] to do boolean conversion.
19+
/// - converts two-parameter exception handlers to one-parameter ones.
1820
class UnsugarVisitor extends RecursiveVisitor {
1921
Glue _glue;
22+
ParentVisitor _parentVisitor = new ParentVisitor();
2023

2124
UnsugarVisitor(this._glue);
2225

2326
void rewrite(FunctionDefinition function) {
2427
// Set all parent pointers.
25-
new ParentVisitor().visit(function);
28+
_parentVisitor.visit(function);
2629
visit(function);
2730
}
2831

@@ -47,6 +50,62 @@ class UnsugarVisitor extends RecursiveVisitor {
4750
let.parent = parent;
4851
}
4952

53+
/// Insert a static call to [function] at the point of [node] with result
54+
/// [result].
55+
///
56+
/// Rewrite [node] to
57+
///
58+
/// let cont continuation(result) = node
59+
/// in invoke function arguments continuation
60+
void insertStaticCall(FunctionElement function, List<Primitive> arguments,
61+
Parameter result,
62+
Expression node) {
63+
InteriorNode parent = node.parent;
64+
Continuation continuation = new Continuation([result]);
65+
continuation.body = node;
66+
_parentVisitor.processContinuation(continuation);
67+
68+
Selector selector = new Selector.fromElement(function);
69+
// TODO(johnniwinther): Come up with an implementation of SourceInformation
70+
// for calls such as this one that don't appear in the original source.
71+
InvokeStatic invoke =
72+
new InvokeStatic(function, selector, continuation, arguments, null);
73+
_parentVisitor.processInvokeStatic(invoke);
74+
75+
LetCont letCont = new LetCont(continuation, invoke);
76+
_parentVisitor.processLetCont(letCont);
77+
78+
parent.body = letCont;
79+
letCont.parent = parent;
80+
}
81+
82+
processLetHandler(LetHandler node) {
83+
// BEFORE: Handlers have two parameters, exception and stack trace.
84+
// AFTER: Handlers have a single parameter, which is unwrapped to get
85+
// the exception and stack trace.
86+
assert(node.handler.parameters.length == 2);
87+
Parameter exceptionParameter = node.handler.parameters.first;
88+
Parameter stackTraceParameter = node.handler.parameters.last;
89+
Expression body = node.handler.body;
90+
if (exceptionParameter.hasAtLeastOneUse ||
91+
stackTraceParameter.hasAtLeastOneUse) {
92+
Parameter exceptionValue = new Parameter(null);
93+
exceptionValue.substituteFor(exceptionParameter);
94+
insertStaticCall(_glue.getExceptionUnwrapper(), [exceptionParameter],
95+
exceptionValue, body);
96+
97+
if (stackTraceParameter.hasAtLeastOneUse) {
98+
Parameter stackTraceValue = new Parameter(null);
99+
stackTraceValue.substituteFor(stackTraceParameter);
100+
insertStaticCall(_glue.getTraceFromException(), [exceptionValue],
101+
stackTraceValue, body);
102+
}
103+
}
104+
105+
assert(stackTraceParameter.hasNoUses);
106+
node.handler.parameters.removeLast();
107+
}
108+
50109
processInvokeMethod(InvokeMethod node) {
51110
Selector selector = node.selector;
52111
// TODO(karlklose): should we rewrite all selectors?

0 commit comments

Comments
 (0)