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)
|
||||
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
|
||||
}
|
||||
}
|
||||
+6
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user