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 31d030ff622970a9e12718834346d86cb264e5ff..2a4480e135dbdf4d73467d417e313f8e611b956d 100644 |
--- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
+++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart |
@@ -48,6 +48,12 @@ class CodeGenerator extends tree_ir.StatementVisitor |
final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
+ /// Stacks whose top element is the current target of an unlabeled break |
+ /// or continue. For continues, this is the loop node itself. |
+ final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); |
+ final tree_ir.FallthroughStack shortContinue = |
+ new tree_ir.FallthroughStack(); |
+ |
Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
List<js.Statement> accumulator = new List<js.Statement>(); |
@@ -390,27 +396,36 @@ class CodeGenerator extends tree_ir.StatementVisitor |
@override |
void visitContinue(tree_ir.Continue node) { |
tree_ir.Statement next = fallthrough.target; |
- if (node.target.binding == next) { |
- // Fall through to continue target |
- fallthrough.use(); |
- } else if (next is tree_ir.Continue && next.target == node.target) { |
- // Fall through to equivalent continue |
+ if (node.target.binding == next || |
+ next is tree_ir.Continue && node.target == next.target) { |
+ // Fall through to continue target or to equivalent continue. |
fallthrough.use(); |
+ } else if (node.target.binding == shortContinue.target) { |
+ // The target is the immediately enclosing loop. |
+ shortContinue.use(); |
+ accumulator.add(new js.Continue(null)); |
} else { |
usedLabels.add(node.target); |
accumulator.add(new js.Continue(node.target.name)); |
} |
} |
+ /// True if [other] is the target of [node] or is a [Break] with the same |
+ /// target. This means jumping to [other] is equivalent to executing [node]. |
+ bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) { |
+ return node.target.binding.next == other || |
+ other is tree_ir.Break && node.target == other.target; |
+ } |
+ |
@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 |
+ if (isEffectiveBreakTarget(node, fallthrough.target)) { |
+ // Fall through to break target or to equivalent break. |
fallthrough.use(); |
+ } else if (isEffectiveBreakTarget(node, shortBreak.target)) { |
+ // Unlabeled break to the break target or to an equivalent break. |
+ shortBreak.use(); |
+ accumulator.add(new js.Break(null)); |
} else { |
usedLabels.add(node.target); |
accumulator.add(new js.Break(node.target.name)); |
@@ -443,22 +458,20 @@ class CodeGenerator extends tree_ir.StatementVisitor |
@override |
void visitLabeledStatement(tree_ir.LabeledStatement node) { |
- accumulator.add(buildLabeled(() => buildBodyStatement(node.body), |
- node.label, |
- node.next)); |
+ fallthrough.push(node.next); |
+ js.Statement body = buildBodyStatement(node.body); |
+ fallthrough.pop(); |
+ accumulator.add(insertLabel(node.label, body)); |
visitStatement(node.next); |
} |
- js.Statement buildLabeled(js.Statement buildBody(), |
- tree_ir.Label label, |
- tree_ir.Statement fallthroughStatement) { |
- fallthrough.push(fallthroughStatement); |
- js.Statement result = buildBody(); |
+ /// Wraps a node in a labeled statement unless the label is unused. |
+ js.Statement insertLabel(tree_ir.Label label, js.Statement node) { |
if (usedLabels.remove(label)) { |
- result = new js.LabeledStatement(label.name, result); |
+ return new js.LabeledStatement(label.name, node); |
+ } else { |
+ return node; |
} |
- fallthrough.pop(); |
- return result; |
} |
/// Returns the current [accumulator] wrapped in a block if neccessary. |
@@ -491,29 +504,36 @@ class CodeGenerator extends tree_ir.StatementVisitor |
return result; |
} |
- js.Statement buildWhile(js.Expression condition, |
- tree_ir.Statement body, |
- tree_ir.Label label, |
- tree_ir.Statement fallthroughStatement) { |
- return buildLabeled(() => new js.While(condition, buildBodyStatement(body)), |
- label, |
- fallthroughStatement); |
- } |
- |
@override |
void visitWhileCondition(tree_ir.WhileCondition node) { |
- accumulator.add( |
- buildWhile(visitExpression(node.condition), |
- node.body, |
- node.label, |
- node)); |
+ js.Expression condition = visitExpression(node.condition); |
+ shortBreak.push(node.next); |
+ shortContinue.push(node); |
+ fallthrough.push(node); |
+ js.Statement jsBody = buildBodyStatement(node.body); |
+ fallthrough.pop(); |
+ shortContinue.pop(); |
+ shortBreak.pop(); |
+ accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); |
visitStatement(node.next); |
} |
@override |
void visitWhileTrue(tree_ir.WhileTrue node) { |
- accumulator.add( |
- buildWhile(new js.LiteralBool(true), node.body, node.label, node)); |
+ js.Expression condition = new js.LiteralBool(true); |
+ // A short break in the while will jump to the current fallthrough target. |
+ shortBreak.push(fallthrough.target); |
+ shortContinue.push(node); |
+ fallthrough.push(node); |
+ js.Statement jsBody = buildBodyStatement(node.body); |
+ fallthrough.pop(); |
+ shortContinue.pop(); |
+ if (shortBreak.useCount > 0) { |
+ // Short breaks use the current fallthrough target. |
+ fallthrough.use(); |
+ } |
+ shortBreak.pop(); |
+ accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); |
} |
bool isNull(tree_ir.Expression node) { |