JS: do not create var for result if statement is 'someVar = inlineCall()'
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
+16
-4
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user