diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java index fe3ece611af..995f7feab22 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsIf.java @@ -6,50 +6,55 @@ package com.google.dart.compiler.backend.js.ast; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents a JavaScript if statement. */ public final class JsIf extends SourceInfoAwareJsNode implements JsStatement { + @NotNull private JsExpression ifExpression; + + @NotNull private JsStatement thenStatement; + + @Nullable private JsStatement elseStatement; - public JsIf() { - } - - public JsIf(JsExpression ifExpression, JsStatement thenStatement, JsStatement elseStatement) { + public JsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement, @Nullable JsStatement elseStatement) { this.ifExpression = ifExpression; this.thenStatement = thenStatement; this.elseStatement = elseStatement; } - public JsIf(JsExpression ifExpression, JsStatement thenStatement) { - this.ifExpression = ifExpression; - this.thenStatement = thenStatement; + public JsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement) { + this(ifExpression, thenStatement, null); } + @Nullable public JsStatement getElseStatement() { return elseStatement; } + @NotNull public JsExpression getIfExpression() { return ifExpression; } + @NotNull public JsStatement getThenStatement() { return thenStatement; } - public void setElseStatement(JsStatement elseStatement) { + public void setElseStatement(@Nullable JsStatement elseStatement) { this.elseStatement = elseStatement; } - public void setIfExpression(JsExpression ifExpression) { + public void setIfExpression(@NotNull JsExpression ifExpression) { this.ifExpression = ifExpression; } - public void setThenStatement(JsStatement thenStatement) { + public void setThenStatement(@NotNull JsStatement thenStatement) { this.thenStatement = thenStatement; } diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantLabelRemoval.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantLabelRemoval.kt index 4e9fbd4e6b7..96e8ff387e4 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantLabelRemoval.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantLabelRemoval.kt @@ -100,9 +100,13 @@ internal class RedundantLabelRemoval(private val root: JsStatement) { } is JsIf -> { val thenRemoved = perform(statement.thenStatement, name) == null - val elseRemoved = statement.elseStatement?.let { perform(it, name) == null } ?: false + val elseStatement = statement.elseStatement + val elseRemoved = elseStatement?.let { perform(it, name) == null } ?: false when { - thenRemoved && elseRemoved -> null + thenRemoved && (elseRemoved || elseStatement == null) -> { + hasChanges = true + JsAstUtils.asSyntheticStatement(statement.ifExpression) + } elseRemoved -> { hasChanges = true statement.elseStatement = null @@ -110,7 +114,7 @@ internal class RedundantLabelRemoval(private val root: JsStatement) { } thenRemoved -> { hasChanges = true - statement.thenStatement = statement.elseStatement + statement.thenStatement = elseStatement ?: JsEmpty statement.elseStatement = null statement.ifExpression = JsAstUtils.not(statement.ifExpression) statement diff --git a/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java b/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java index d05b6c36814..7c9aeebe178 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java +++ b/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java @@ -630,16 +630,7 @@ public class JsAstMapper { // Create the "if" statement we're mapping to. // - JsIf toIf = new JsIf(); - - // Map the test expression. - // - JsExpression toTestExpr = mapExpression(fromTestExpr); - toIf.setIfExpression(toTestExpr); - - // Map the "then" block. - // - toIf.setThenStatement(mapStatement(fromThenBlock)); + JsIf toIf = new JsIf(mapExpression(fromTestExpr), mapStatement(fromThenBlock)); // Map the "else" block. // diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt index 996e89e537c..10ee773a611 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt @@ -108,6 +108,11 @@ abstract class BasicOptimizerTest(private var basePath: String) { } super.visitIf(x) } + + override fun visitLabel(x: JsLabel) { + x.synthetic = isSyntheticId(x.name.ident) + super.visitLabel(x) + } }.accept(stmt) } } @@ -119,7 +124,6 @@ abstract class BasicOptimizerTest(private var basePath: String) { private fun isSyntheticId(id: String) = id.startsWith("$") - private fun astToString(ast: List): String { val output = TextOutputImpl() val visitor = JsSourceGenerationVisitor(output, null) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/RedundantLabelRemovalTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/RedundantLabelRemovalTest.kt new file mode 100644 index 00000000000..2720593171c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/RedundantLabelRemovalTest.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.test.optimizer + +import org.junit.Test + +class RedundantLabelRemovalTest : BasicOptimizerTest("redundant-label-removal") { + @Test fun emptyIfConditionPreserved() = box() + + @Test fun ifWithEmptyThenAndNoElse() = box() +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 3160caabc18..d0bff44d985 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -285,6 +285,9 @@ public final class ExpressionVisitor extends TranslatorVisitor { return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression); } } + if (thenStatement == null) { + thenStatement = JsEmpty.INSTANCE; + } JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement); return ifStatement.source(expression); } diff --git a/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.optimized.js b/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.optimized.js new file mode 100644 index 00000000000..e24bb774074 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.optimized.js @@ -0,0 +1,17 @@ +var global = ""; + +function id(value) { + global += value + ";"; + return value; +} + +function test(x) { + id(x); + id(x + 1); +} + +function box() { + test(23); + if (global != "23;24;") return "fail"; + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.original.js b/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.original.js new file mode 100644 index 00000000000..273b74296ec --- /dev/null +++ b/js/js.translator/testData/js-optimizer/redundant-label-removal/emptyIfConditionPreserved.original.js @@ -0,0 +1,23 @@ +var global = ""; + +function id(value) { + global += value + ";"; + return value; +} + +function test(x) { + $outer: { + if (id(x) + id(x + 1) > 0) { + break $outer; + } + else { + break $outer; + } + } +} + +function box() { + test(23); + if (global != "23;24;") return "fail"; + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.optimized.js b/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.optimized.js new file mode 100644 index 00000000000..5f4468a8ec2 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.optimized.js @@ -0,0 +1,7 @@ +function test(x) { + return "OK"; +} + +function box() { + return test(23); +} \ No newline at end of file diff --git a/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.original.js b/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.original.js new file mode 100644 index 00000000000..c3f8d9c6a30 --- /dev/null +++ b/js/js.translator/testData/js-optimizer/redundant-label-removal/ifWithEmptyThenAndNoElse.original.js @@ -0,0 +1,12 @@ +function test(x) { + $outer: { + if (x > 10) { + break $outer; + } + } + return "OK"; +} + +function box() { + return test(23); +} \ No newline at end of file diff --git a/js/js.translator/testData/sourcemap/cases/emptyIfInsideInlineLambda.kt b/js/js.translator/testData/sourcemap/cases/emptyIfInsideInlineLambda.kt new file mode 100644 index 00000000000..92498507a70 --- /dev/null +++ b/js/js.translator/testData/sourcemap/cases/emptyIfInsideInlineLambda.kt @@ -0,0 +1,19 @@ +package foo + +fun test1(): String { + run { + if (false) { + } + } + return "O" +} + +fun test2(): String { + 1.let { + if (false) { + } + } + return "K" +} + +fun box() = test1() + test2() \ No newline at end of file