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
|
package org.jetbrains.kotlin.js.inline
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
|
import com.google.dart.compiler.util.AstUtil
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.js.inline.clean.removeDefaultInitializers
|
import org.jetbrains.kotlin.js.inline.clean.removeDefaultInitializers
|
||||||
import org.jetbrains.kotlin.js.inline.context.InliningContext
|
import org.jetbrains.kotlin.js.inline.context.InliningContext
|
||||||
import org.jetbrains.kotlin.js.inline.context.NamingContext
|
import org.jetbrains.kotlin.js.inline.context.NamingContext
|
||||||
import org.jetbrains.kotlin.js.inline.util.*
|
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
|
import kotlin.platform.platformStatic
|
||||||
|
|
||||||
class FunctionInlineMutator private (private val call: JsInvocation, private val inliningContext: InliningContext) {
|
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 val body: JsBlock
|
||||||
private var resultExpr: JsExpression? = null
|
private var resultExpr: JsExpression? = null
|
||||||
private var breakLabel: JsLabel? = null
|
private var breakLabel: JsLabel? = null
|
||||||
|
private val currentStatement = inliningContext.statementContext.getCurrentNode()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
||||||
@@ -108,12 +113,6 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
|
|||||||
val returnOnTop = ContainerUtil.findInstance(body.getStatements(), javaClass<JsReturn>())
|
val returnOnTop = ContainerUtil.findInstance(body.getStatements(), javaClass<JsReturn>())
|
||||||
val hasReturnOnTopLevel = returnOnTop != null
|
val hasReturnOnTopLevel = returnOnTop != null
|
||||||
|
|
||||||
if (isResultNeeded) {
|
|
||||||
val resultName = namingContext.getFreshName(getResultLabel())
|
|
||||||
namingContext.newVar(resultName, null)
|
|
||||||
resultExpr = resultName.makeRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
val needBreakLabel = !(returnCount == 1 && hasReturnOnTopLevel)
|
val needBreakLabel = !(returnCount == 1 && hasReturnOnTopLevel)
|
||||||
var breakLabelRef: JsNameRef? = null
|
var breakLabelRef: JsNameRef? = null
|
||||||
|
|
||||||
@@ -123,10 +122,65 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
|
|||||||
breakLabel = JsLabel(breakName)
|
breakLabel = JsLabel(breakName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val resultReference = getResultReference()
|
||||||
|
if (resultReference != null) {
|
||||||
|
resultExpr = resultReference
|
||||||
|
}
|
||||||
|
|
||||||
assert(resultExpr == null || resultExpr is JsNameRef)
|
assert(resultExpr == null || resultExpr is JsNameRef)
|
||||||
replaceReturns(body, resultExpr as? JsNameRef, breakLabelRef)
|
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> {
|
private fun getArguments(): List<JsExpression> {
|
||||||
val arguments = call.getArguments()
|
val arguments = call.getArguments()
|
||||||
if (isCallInvocation(call)) {
|
if (isCallInvocation(call)) {
|
||||||
@@ -137,8 +191,6 @@ class FunctionInlineMutator private (private val call: JsInvocation, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isResultNeeded(call: JsInvocation): Boolean {
|
private fun isResultNeeded(call: JsInvocation): Boolean {
|
||||||
val statementContext = inliningContext.statementContext
|
|
||||||
val currentStatement = statementContext.getCurrentNode()
|
|
||||||
return currentStatement !is JsExpressionStatement || call != currentStatement.getExpression()
|
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 */
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@TestMetadata("js/js.translator/testData/inlineNoAdditionalVarsCreated/cases")
|
@TestMetadata("js/js.translator/testData/inlineSizeReduction/cases")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class InlineNoAdditionalVarsCreatedTestGenerated extends AbstractInlineNoAdditionalVarsCreatedTest {
|
public class InlineSizeReductionTestGenerated extends AbstractInlineSizeReductionTest {
|
||||||
public void testAllFilesPresentInCases() throws Exception {
|
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")
|
@TestMetadata("simpleReturnFunction.kt")
|
||||||
public void testSimpleReturnFunction() throws Exception {
|
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);
|
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