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