Implement reduction of if statement to ternary conditional expression
This commit is contained in:
@@ -224,6 +224,7 @@ internal class ExpressionDecomposer private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val lazyEval = JsIf(test, thenBlock, elseBlock)
|
val lazyEval = JsIf(test, thenBlock, elseBlock)
|
||||||
|
lazyEval.synthetic = true
|
||||||
addStatement(lazyEval)
|
addStatement(lazyEval)
|
||||||
ctx.replaceMe(tmp.nameRef)
|
ctx.replaceMe(tmp.nameRef)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class FunctionPostProcessor(private val root: JsBlock) {
|
|||||||
hasChanges = hasChanges or TemporaryAssignmentElimination(root).apply()
|
hasChanges = hasChanges or TemporaryAssignmentElimination(root).apply()
|
||||||
hasChanges = hasChanges or RedundantLabelRemoval(root).apply()
|
hasChanges = hasChanges or RedundantLabelRemoval(root).apply()
|
||||||
hasChanges = hasChanges or TemporaryVariableElimination(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 DeadCodeElimination(root).apply()
|
||||||
hasChanges = hasChanges or RedundantVariableDeclarationElimination(root).apply()
|
hasChanges = hasChanges or RedundantVariableDeclarationElimination(root).apply()
|
||||||
} while (hasChanges)
|
} 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
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -71,6 +71,12 @@ public class InlineSizeReductionTestGenerated extends AbstractInlineSizeReductio
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("this.kt")
|
||||||
public void testThis() throws Exception {
|
public void testThis() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/this.kt");
|
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"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user