JS: when both clauses of if become empty during optimization, remove if entirely. Make condition and then clause of JsIf non-nullable. Fix #KT-13912

This commit is contained in:
Alexey Andreev
2016-09-27 16:34:50 +03:00
committed by Alexey Andreev
parent b5358122e2
commit d2fdc7ffc0
11 changed files with 134 additions and 24 deletions
@@ -6,50 +6,55 @@ package com.google.dart.compiler.backend.js.ast;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Represents a JavaScript if statement. * Represents a JavaScript if statement.
*/ */
public final class JsIf extends SourceInfoAwareJsNode implements JsStatement { public final class JsIf extends SourceInfoAwareJsNode implements JsStatement {
@NotNull
private JsExpression ifExpression; private JsExpression ifExpression;
@NotNull
private JsStatement thenStatement; private JsStatement thenStatement;
@Nullable
private JsStatement elseStatement; private JsStatement elseStatement;
public JsIf() { public JsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement, @Nullable JsStatement elseStatement) {
}
public JsIf(JsExpression ifExpression, JsStatement thenStatement, JsStatement elseStatement) {
this.ifExpression = ifExpression; this.ifExpression = ifExpression;
this.thenStatement = thenStatement; this.thenStatement = thenStatement;
this.elseStatement = elseStatement; this.elseStatement = elseStatement;
} }
public JsIf(JsExpression ifExpression, JsStatement thenStatement) { public JsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement) {
this.ifExpression = ifExpression; this(ifExpression, thenStatement, null);
this.thenStatement = thenStatement;
} }
@Nullable
public JsStatement getElseStatement() { public JsStatement getElseStatement() {
return elseStatement; return elseStatement;
} }
@NotNull
public JsExpression getIfExpression() { public JsExpression getIfExpression() {
return ifExpression; return ifExpression;
} }
@NotNull
public JsStatement getThenStatement() { public JsStatement getThenStatement() {
return thenStatement; return thenStatement;
} }
public void setElseStatement(JsStatement elseStatement) { public void setElseStatement(@Nullable JsStatement elseStatement) {
this.elseStatement = elseStatement; this.elseStatement = elseStatement;
} }
public void setIfExpression(JsExpression ifExpression) { public void setIfExpression(@NotNull JsExpression ifExpression) {
this.ifExpression = ifExpression; this.ifExpression = ifExpression;
} }
public void setThenStatement(JsStatement thenStatement) { public void setThenStatement(@NotNull JsStatement thenStatement) {
this.thenStatement = thenStatement; this.thenStatement = thenStatement;
} }
@@ -100,9 +100,13 @@ internal class RedundantLabelRemoval(private val root: JsStatement) {
} }
is JsIf -> { is JsIf -> {
val thenRemoved = perform(statement.thenStatement, name) == null 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 { when {
thenRemoved && elseRemoved -> null thenRemoved && (elseRemoved || elseStatement == null) -> {
hasChanges = true
JsAstUtils.asSyntheticStatement(statement.ifExpression)
}
elseRemoved -> { elseRemoved -> {
hasChanges = true hasChanges = true
statement.elseStatement = null statement.elseStatement = null
@@ -110,7 +114,7 @@ internal class RedundantLabelRemoval(private val root: JsStatement) {
} }
thenRemoved -> { thenRemoved -> {
hasChanges = true hasChanges = true
statement.thenStatement = statement.elseStatement statement.thenStatement = elseStatement ?: JsEmpty
statement.elseStatement = null statement.elseStatement = null
statement.ifExpression = JsAstUtils.not(statement.ifExpression) statement.ifExpression = JsAstUtils.not(statement.ifExpression)
statement statement
@@ -630,16 +630,7 @@ public class JsAstMapper {
// Create the "if" statement we're mapping to. // Create the "if" statement we're mapping to.
// //
JsIf toIf = new JsIf(); JsIf toIf = new JsIf(mapExpression(fromTestExpr), mapStatement(fromThenBlock));
// Map the test expression.
//
JsExpression toTestExpr = mapExpression(fromTestExpr);
toIf.setIfExpression(toTestExpr);
// Map the "then" block.
//
toIf.setThenStatement(mapStatement(fromThenBlock));
// Map the "else" block. // Map the "else" block.
// //
@@ -108,6 +108,11 @@ abstract class BasicOptimizerTest(private var basePath: String) {
} }
super.visitIf(x) super.visitIf(x)
} }
override fun visitLabel(x: JsLabel) {
x.synthetic = isSyntheticId(x.name.ident)
super.visitLabel(x)
}
}.accept(stmt) }.accept(stmt)
} }
} }
@@ -119,7 +124,6 @@ abstract class BasicOptimizerTest(private var basePath: String) {
private fun isSyntheticId(id: String) = id.startsWith("$") private fun isSyntheticId(id: String) = id.startsWith("$")
private fun astToString(ast: List<JsStatement>): String { private fun astToString(ast: List<JsStatement>): String {
val output = TextOutputImpl() val output = TextOutputImpl()
val visitor = JsSourceGenerationVisitor(output, null) val visitor = JsSourceGenerationVisitor(output, null)
@@ -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()
}
@@ -285,6 +285,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression); return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression);
} }
} }
if (thenStatement == null) {
thenStatement = JsEmpty.INSTANCE;
}
JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement); JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
return ifStatement.source(expression); return ifStatement.source(expression);
} }
@@ -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";
}
@@ -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";
}
@@ -0,0 +1,7 @@
function test(x) {
return "OK";
}
function box() {
return test(23);
}
@@ -0,0 +1,12 @@
function test(x) {
$outer: {
if (x > 10) {
break $outer;
}
}
return "OK";
}
function box() {
return test(23);
}
@@ -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()