Implement reduction of if statement to ternary conditional expression

This commit is contained in:
Alexey Andreev
2016-03-18 12:03:28 +03:00
parent 33baa1d774
commit e6e3275906
5 changed files with 162 additions and 0 deletions
@@ -224,6 +224,7 @@ internal class ExpressionDecomposer private constructor(
}
val lazyEval = JsIf(test, thenBlock, elseBlock)
lazyEval.synthetic = true
addStatement(lazyEval)
ctx.replaceMe(tmp.nameRef)
}
@@ -25,6 +25,8 @@ class FunctionPostProcessor(private val root: JsBlock) {
hasChanges = hasChanges or TemporaryAssignmentElimination(root).apply()
hasChanges = hasChanges or RedundantLabelRemoval(root).apply()
hasChanges = hasChanges or TemporaryVariableElimination(root).apply()
hasChanges = hasChanges or IfStatementReduction(root).apply()
// TODO: reduce to A || B, A && B if possible
hasChanges = hasChanges or DeadCodeElimination(root).apply()
hasChanges = hasChanges or RedundantVariableDeclarationElimination(root).apply()
} while (hasChanges)
@@ -0,0 +1,107 @@
/*
* 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.inline.clean
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
class IfStatementReduction(private val root: JsStatement) {
private var hasChanges = false
fun apply(): Boolean {
visitor.accept(root)
return hasChanges
}
val visitor = object : JsVisitorWithContextImpl() {
override fun visit(x: JsIf, ctx: JsContext<JsNode>): Boolean {
var thenStatement = x.thenStatement
var elseStatement = x.elseStatement
if (x.synthetic && elseStatement != null) {
thenStatement = extractSingleStatement(thenStatement)
elseStatement = extractSingleStatement(elseStatement)
if (thenStatement is JsExpressionStatement && elseStatement is JsExpressionStatement) {
val thenAssignment = JsAstUtils.decomposeAssignment(thenStatement.expression)
val elseAssignment = JsAstUtils.decomposeAssignment(elseStatement.expression)
if (thenAssignment != null && elseAssignment != null) {
val (thenTarget, thenValue) = thenAssignment
val (elseTarget, elseValue) = elseAssignment
if (lhsEqual(thenTarget, elseTarget)) {
hasChanges = true
val ternary = JsConditional(x.ifExpression, thenValue, elseValue)
val replacement = JsExpressionStatement(JsAstUtils.assignment(thenTarget, ternary))
replacement.synthetic = thenStatement.synthetic && elseStatement.synthetic
ctx.replaceMe(replacement)
accept(replacement)
return false
}
}
}
else if (thenStatement is JsVars && elseStatement is JsVars) {
if (thenStatement.vars.size == 1 && elseStatement.vars.size == 1) {
val thenVar = thenStatement.vars[0]
val elseVar = elseStatement.vars[0]
val thenValue = thenVar.initExpression
val elseValue = elseVar.initExpression
if (thenVar.name == elseVar.name && thenValue != null && elseValue != null) {
hasChanges = true
val ternary = JsConditional(x.ifExpression, thenValue, elseValue)
val replacement = JsAstUtils.newVar(thenVar.name, ternary)
replacement.synthetic = thenStatement.synthetic && elseStatement.synthetic
ctx.replaceMe(replacement)
accept(replacement)
return false
}
}
}
else if (thenStatement is JsReturn && elseStatement is JsReturn) {
val thenValue = thenStatement.expression
val elseValue = elseStatement.expression
if (thenValue != null && elseValue != null) {
hasChanges = true
val ternary = JsConditional(x.ifExpression, thenValue, elseValue)
val replacement = JsReturn(ternary)
accept(replacement)
ctx.replaceMe(replacement)
return false
}
}
}
return super.visit(x, ctx)
}
}
private fun extractSingleStatement(statement: JsStatement): JsStatement {
var result = statement
while (result is JsBlock && result.statements.size == 1) {
result = result.statements[0]
}
return result
}
private fun lhsEqual(a: JsExpression?, b: JsExpression?): Boolean = when {
a == null && b == null -> true
a is JsNameRef && b is JsNameRef -> a.name == b.name && lhsEqual(a.qualifier, b.qualifier)
a is JsArrayAccess && b is JsArrayAccess -> lhsEqual(a.arrayExpression, b.arrayExpression) &&
lhsEqual(a.indexExpression, b.indexExpression)
else -> false
}
}
@@ -71,6 +71,12 @@ public class InlineSizeReductionTestGenerated extends AbstractInlineSizeReductio
doTest(fileName);
}
@TestMetadata("ternaryConditional.kt")
public void testTernaryConditional() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/ternaryConditional.kt");
doTest(fileName);
}
@TestMetadata("this.kt")
public void testThis() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/this.kt");
@@ -0,0 +1,46 @@
package foo
// CHECK_VARS_COUNT: function=test1_za3lpa$ count=0
// CHECK_VARS_COUNT: function=test2_za3lpa$ count=1
// CHECK_VARS_COUNT: function=test3_za3lpa$ count=0
inline fun a(x: Int) = b(x)
fun b(x: Int) = x
fun test1(n: Int) = if (n > 0) a(n + 10) else a(n - 10)
fun test2(n: Int): Int {
var result = if (n > 0) a(n + 10) else a(n - 10)
return result
}
fun test3(n: Int): Int {
Holder.value = if (n > 0) a(n + 10) else a(n - 10)
return Holder.value
}
object Holder {
var value = 0
}
fun box(): String {
var result: Int
result = test1(5)
if (result != 15) return "fail1a: $result"
result = test1(-5)
if (result != -15) return "fail1b: $result"
result = test2(5)
if (result != 15) return "fail2a: $result"
result = test2(-5)
if (result != -15) return "fail2b: $result"
result = test3(5)
if (result != 15) return "fail3a: $result"
result = test3(-5)
if (result != -15) return "fail3b: $result"
return "OK"
}