Index: pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
index 6168f8ef69b10cc440c858b960372b696192b527..54e247234d710e64eb456f4a0cbcdc975e40e349 100644 |
--- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
+++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
@@ -46,10 +46,7 @@ class CodeGenerator extends tree_ir.StatementVisitor |
/// Variable names that have already been used. Used to avoid name clashes. |
Set<String> usedVariableNames = new Set<String>(); |
- /// Input to [visitStatement]. Denotes the statement that will execute next |
- /// if the statements produced by [visitStatement] complete normally. |
- /// Set to null if control will fall over the end of the method. |
- tree_ir.Statement fallthrough = null; |
+ final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
@@ -408,12 +405,13 @@ class CodeGenerator extends tree_ir.StatementVisitor |
@override |
void visitContinue(tree_ir.Continue node) { |
- tree_ir.Statement fallthrough = this.fallthrough; |
- if (node.target.binding == fallthrough) { |
+ tree_ir.Statement next = fallthrough.target; |
+ if (node.target.binding == next) { |
// Fall through to continue target |
- } else if (fallthrough is tree_ir.Continue && |
- fallthrough.target == node.target) { |
+ fallthrough.use(); |
+ } else if (next is tree_ir.Continue && next.target == node.target) { |
// Fall through to equivalent continue |
+ fallthrough.use(); |
} else { |
usedLabels.add(node.target); |
accumulator.add(new js.Continue(node.target.name)); |
@@ -421,6 +419,21 @@ class CodeGenerator extends tree_ir.StatementVisitor |
} |
@override |
+ void visitBreak(tree_ir.Break node) { |
+ tree_ir.Statement next = fallthrough.target; |
+ if (node.target.binding.next == next) { |
+ // Fall through to break target |
+ fallthrough.use(); |
+ } else if (next is tree_ir.Break && next.target == node.target) { |
+ // Fall through to equivalent break |
+ fallthrough.use(); |
+ } else { |
+ usedLabels.add(node.target); |
+ accumulator.add(new js.Break(node.target.name)); |
+ } |
+ } |
+ |
+ @override |
void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
accumulator.add(new js.ExpressionStatement( |
visitExpression(node.expression))); |
@@ -429,9 +442,19 @@ class CodeGenerator extends tree_ir.StatementVisitor |
@override |
void visitIf(tree_ir.If node) { |
- accumulator.add(new js.If(visitExpression(node.condition), |
- buildBodyStatement(node.thenStatement), |
- buildBodyStatement(node.elseStatement))); |
+ js.Expression condition = visitExpression(node.condition); |
+ int usesBefore = fallthrough.useCount; |
+ js.Statement thenBody = buildBodyStatement(node.thenStatement); |
+ bool thenHasFallthrough = (fallthrough.useCount > usesBefore); |
+ if (thenHasFallthrough) { |
+ js.Statement elseBody = buildBodyStatement(node.elseStatement); |
+ accumulator.add(new js.If(condition, thenBody, elseBody)); |
+ } else { |
+ // The 'then' body cannot complete normally, so emit a short 'if' |
+ // and put the 'else' body after it. |
+ accumulator.add(new js.If.noElse(condition, thenBody)); |
+ visitStatement(node.elseStatement); |
+ } |
} |
@override |
@@ -443,32 +466,17 @@ class CodeGenerator extends tree_ir.StatementVisitor |
} |
js.Statement buildLabeled(js.Statement buildBody(), |
- tree_ir.Label label, |
- tree_ir.Statement fallthroughStatement) { |
- tree_ir.Statement savedFallthrough = fallthrough; |
- fallthrough = fallthroughStatement; |
+ tree_ir.Label label, |
+ tree_ir.Statement fallthroughStatement) { |
+ fallthrough.push(fallthroughStatement); |
js.Statement result = buildBody(); |
if (usedLabels.remove(label)) { |
result = new js.LabeledStatement(label.name, result); |
} |
- fallthrough = savedFallthrough; |
+ fallthrough.pop(); |
return result; |
} |
- @override |
- void visitBreak(tree_ir.Break node) { |
- tree_ir.Statement fallthrough = this.fallthrough; |
- if (node.target.binding.next == fallthrough) { |
- // Fall through to break target |
- } else if (fallthrough is tree_ir.Break && |
- fallthrough.target == node.target) { |
- // Fall through to equivalent break |
- } else { |
- usedLabels.add(node.target); |
- accumulator.add(new js.Break(node.target.name)); |
- } |
- } |
- |
/// Returns the current [accumulator] wrapped in a block if neccessary. |
js.Statement _bodyAsStatement() { |
if (accumulator.length == 0) { |
@@ -524,9 +532,19 @@ class CodeGenerator extends tree_ir.StatementVisitor |
buildWhile(new js.LiteralBool(true), node.body, node.label, node)); |
} |
+ bool isNull(tree_ir.Expression node) { |
+ return node is tree_ir.Constant && node.value.isNull; |
+ } |
+ |
@override |
void visitReturn(tree_ir.Return node) { |
- accumulator.add(new js.Return(visitExpression(node.value))); |
+ if (isNull(node.value) && fallthrough.target == null) { |
+ // Do nothing. Implicitly return JS undefined by falling over the end. |
+ registry.registerCompileTimeConstant(new NullConstantValue()); |
+ fallthrough.use(); |
+ } else { |
+ accumulator.add(new js.Return(visitExpression(node.value))); |
+ } |
} |
@override |