| 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) {
|
|
|