JS: do not create var for result if statement is 'someVar = inlineCall()'

This commit is contained in:
Alexey Tsvetkov
2015-04-17 21:04:30 +03:00
parent fd09d4d837
commit c9b24510bb
4 changed files with 119 additions and 12 deletions
@@ -17,11 +17,15 @@
package org.jetbrains.kotlin.js.inline
import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.util.AstUtil
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.js.inline.clean.removeDefaultInitializers
import org.jetbrains.kotlin.js.inline.context.InliningContext
import org.jetbrains.kotlin.js.inline.context.NamingContext
import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar
import org.jetbrains.kotlin.js.translate.utils.ast.*
import kotlin.platform.platformStatic
class FunctionInlineMutator private (private val call: JsInvocation, private val inliningContext: InliningContext) {
@@ -31,6 +35,7 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
private val body: JsBlock
private var resultExpr: JsExpression? = null
private var breakLabel: JsLabel? = null
private val currentStatement = inliningContext.statementContext.getCurrentNode()
init {
@@ -108,12 +113,6 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
val returnOnTop = ContainerUtil.findInstance(body.getStatements(), javaClass<JsReturn>())
val hasReturnOnTopLevel = returnOnTop != null
if (isResultNeeded) {
val resultName = namingContext.getFreshName(getResultLabel())
namingContext.newVar(resultName, null)
resultExpr = resultName.makeRef()
}
val needBreakLabel = !(returnCount == 1 && hasReturnOnTopLevel)
var breakLabelRef: JsNameRef? = null
@@ -123,10 +122,65 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
breakLabel = JsLabel(breakName)
}
val resultReference = getResultReference()
if (resultReference != null) {
resultExpr = resultReference
}
assert(resultExpr == null || resultExpr is JsNameRef)
replaceReturns(body, resultExpr as? JsNameRef, breakLabelRef)
}
val breakName = namingContext.getFreshName(getBreakLabel())
breakLabel = JsLabel(breakName)
val visitor = ReturnReplacingVisitor(resultExpr as? JsNameRef, breakName.makeRef())
visitor.accept(body)
}
private fun getResultReference(): JsNameRef? {
if (!isResultNeeded) return null
val existingReference = when (currentStatement) {
is JsExpressionStatement -> {
val expression = currentStatement.getExpression() as? JsBinaryOperation
expression?.getResultReference()
}
is JsVars -> currentStatement.getResultReference()
else -> null
}
if (existingReference != null) return existingReference
val resultName = namingContext.getFreshName(getResultLabel())
namingContext.newVar(resultName, null)
return resultName.makeRef()
}
private fun JsBinaryOperation.getResultReference(): JsNameRef? {
if (operator !== JsBinaryOperator.ASG || arg2 !== call) return null
return arg1 as? JsNameRef
}
private fun JsVars.getResultReference(): JsNameRef? {
val vars = getVars()
val variable = vars.first()
// var a = expr1 + call() is ok, but we don't want to reuse 'a' for result,
// as it means to replace every 'return expr2' to 'a = expr1 + expr2'.
// If there is more than one return, expr1 copies are undesirable.
if (variable.initExpression !== call || vars.size() > 1) return null
val varName = variable.getName()
with (inliningContext.statementContext) {
removeMe()
addPrevious(newVar(varName, null))
}
return varName.makeRef()
}
private fun getArguments(): List<JsExpression> {
val arguments = call.getArguments()
if (isCallInvocation(call)) {
@@ -137,8 +191,6 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
}
private fun isResultNeeded(call: JsInvocation): Boolean {
val statementContext = inliningContext.statementContext
val currentStatement = statementContext.getCurrentNode()
return currentStatement !is JsExpressionStatement || call != currentStatement.getExpression()
}
@@ -27,17 +27,29 @@ import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("js/js.translator/testData/inlineNoAdditionalVarsCreated/cases")
@TestMetadata("js/js.translator/testData/inlineSizeReduction/cases")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class InlineNoAdditionalVarsCreatedTestGenerated extends AbstractInlineNoAdditionalVarsCreatedTest {
public class InlineSizeReductionTestGenerated extends AbstractInlineSizeReductionTest {
public void testAllFilesPresentInCases() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/inlineNoAdditionalVarsCreated/cases"), Pattern.compile("^(.+)\\.kt$"), true);
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/inlineSizeReduction/cases"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("simpleReturnFunction.kt")
public void testSimpleReturnFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineNoAdditionalVarsCreated/cases/simpleReturnFunction.kt");
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/simpleReturnFunction.kt");
doTest(fileName);
}
@TestMetadata("valAssignment.kt")
public void testValAssignment() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/valAssignment.kt");
doTest(fileName);
}
@TestMetadata("valDeclaration.kt")
public void testValDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inlineSizeReduction/cases/valDeclaration.kt");
doTest(fileName);
}
}
@@ -0,0 +1,22 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=1
inline fun sum(x: Int, y: Int): Int {
if (x == 0 || y == 0) return 0
return x + y
}
fun test(x: Int, y: Int): Int {
val sum: Int
sum = sum(x, y)
return sum
}
fun box(): String {
assertEquals(3, test(1, 2))
return "OK"
}
@@ -0,0 +1,21 @@
package foo
// CHECK_CONTAINS_NO_CALLS: test
// CHECK_VARS_COUNT: function=test count=1
inline fun sum(x: Int, y: Int): Int {
if (x == 0 || y == 0) return 0
return x + y
}
fun test(x: Int, y: Int): Int {
val sum = sum(x, y)
return sum
}
fun box(): String {
assertEquals(3, test(1, 2))
return "OK"
}