JS/Inlining: in function call like fn() extract fn to a local variable, since fn can be access to property with side effects. Add tests to prove that evaluation order became proper in certain cases. Fix KT-11711, KT-7674, KT-7043
This commit is contained in:
@@ -17,6 +17,8 @@
|
|||||||
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.backend.js.ast.metadata.HasMetadata
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
@@ -265,7 +267,13 @@ internal class ExpressionDecomposer private constructor(
|
|||||||
val callee = qualifier as? JsNameRef
|
val callee = qualifier as? JsNameRef
|
||||||
val receiver = callee?.qualifier
|
val receiver = callee?.qualifier
|
||||||
|
|
||||||
if (callee != null && receiver != null && receiver in containsNodeWithSideEffect) {
|
// Qualifier might be a reference to lambda property. See KT-7674
|
||||||
|
// An exception here is `fn.call()`, which are marked as side effect free. Further recognition of such
|
||||||
|
// case in inliner might be quite difficult, so never extract such call (and other calls marked this way).
|
||||||
|
if (qualifier in containsNodeWithSideEffect && (qualifier as? HasMetadata)?.sideEffects ?: true) {
|
||||||
|
qualifier = qualifier.extractToTemporary()
|
||||||
|
}
|
||||||
|
else if (callee != null && receiver != null && receiver in containsNodeWithSideEffect) {
|
||||||
val receiverTmp = receiver.extractToTemporary()
|
val receiverTmp = receiver.extractToTemporary()
|
||||||
callee.qualifier = receiverTmp
|
callee.qualifier = receiverTmp
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -179,6 +179,18 @@ public class InlineEvaluationOrderTestGenerated extends AbstractInlineEvaluation
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineFunctionAsParameterOfQualifiedCall.kt")
|
||||||
|
public void testInlineFunctionAsParameterOfQualifiedCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/inlineFunctionAsParameterOfQualifiedCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaPropertyExtracted.kt")
|
||||||
|
public void testLambdaPropertyExtracted() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/lambdaPropertyExtracted.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("lambdaWithClosure.kt")
|
@TestMetadata("lambdaWithClosure.kt")
|
||||||
public void testLambdaWithClosure() throws Exception {
|
public void testLambdaWithClosure() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/lambdaWithClosure.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/lambdaWithClosure.kt");
|
||||||
@@ -251,6 +263,18 @@ public class InlineEvaluationOrderTestGenerated extends AbstractInlineEvaluation
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyWithSideEffectExtracted.kt")
|
||||||
|
public void testPropertyWithSideEffectExtracted() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectExtracted.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyWithSideEffectPassedToInlineFunction.kt")
|
||||||
|
public void testPropertyWithSideEffectPassedToInlineFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectPassedToInlineFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ternaryConditional.kt")
|
@TestMetadata("ternaryConditional.kt")
|
||||||
public void testTernaryConditional() throws Exception {
|
public void testTernaryConditional() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/ternaryConditional.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/ternaryConditional.kt");
|
||||||
|
|||||||
Vendored
+101
@@ -0,0 +1,101 @@
|
|||||||
|
// See KT-11711
|
||||||
|
package foo
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
val b: B
|
||||||
|
}
|
||||||
|
|
||||||
|
interface B {
|
||||||
|
fun c(a: Any?)
|
||||||
|
}
|
||||||
|
|
||||||
|
val a: A
|
||||||
|
get() {
|
||||||
|
log("a.get")
|
||||||
|
return object : A {
|
||||||
|
override val b: B
|
||||||
|
get() {
|
||||||
|
log("b.get")
|
||||||
|
return object : B {
|
||||||
|
override fun c(a: Any?) {
|
||||||
|
log("c()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val g: Any?
|
||||||
|
get() {
|
||||||
|
log("g.get")
|
||||||
|
return "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun foo(): Any? {
|
||||||
|
log("foo()")
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun bar(): Any? {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun baz(): Any? {
|
||||||
|
return log("baz()");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun boo(a: Any?): Any? {
|
||||||
|
return log("boo()");
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
log("--1--")
|
||||||
|
a.b.c(g)
|
||||||
|
|
||||||
|
log("--2--")
|
||||||
|
|
||||||
|
a.b.c(foo())
|
||||||
|
|
||||||
|
log("--3--")
|
||||||
|
|
||||||
|
a.b.c(bar())
|
||||||
|
|
||||||
|
log("--4--")
|
||||||
|
|
||||||
|
a.b.c(baz())
|
||||||
|
|
||||||
|
log("--5--")
|
||||||
|
|
||||||
|
a.b.c(boo(g))
|
||||||
|
|
||||||
|
assertEquals("""--1--
|
||||||
|
a.get
|
||||||
|
b.get
|
||||||
|
g.get
|
||||||
|
c()
|
||||||
|
--2--
|
||||||
|
a.get
|
||||||
|
b.get
|
||||||
|
foo()
|
||||||
|
g.get
|
||||||
|
c()
|
||||||
|
--3--
|
||||||
|
a.get
|
||||||
|
b.get
|
||||||
|
g.get
|
||||||
|
c()
|
||||||
|
--4--
|
||||||
|
a.get
|
||||||
|
b.get
|
||||||
|
baz()
|
||||||
|
c()
|
||||||
|
--5--
|
||||||
|
a.get
|
||||||
|
b.get
|
||||||
|
g.get
|
||||||
|
boo()
|
||||||
|
c()
|
||||||
|
""", pullLog().replace(';', '\n'))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// See KT-7674
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class A(val a: Int) {
|
||||||
|
val plus: (Int)->Int
|
||||||
|
get() {
|
||||||
|
log("get plus fun")
|
||||||
|
return {
|
||||||
|
log("do plus")
|
||||||
|
a + it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T : Any> id(x: T): T {
|
||||||
|
log(x.toString())
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(3, A(id(1)).plus(id(2)))
|
||||||
|
assertEquals("1;get plus fun;2;do plus;", pullLog())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// Looks similar to KT-7674
|
||||||
|
package foo
|
||||||
|
|
||||||
|
inline fun bar(): Int {
|
||||||
|
log("bar")
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
val x: Int
|
||||||
|
get() {
|
||||||
|
log("x")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(12, x + bar() + x)
|
||||||
|
assertEquals("x;bar;x;", pullLog())
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// See KT-7043, KT-11711
|
||||||
|
package foo
|
||||||
|
|
||||||
|
inline fun foo(b: Any) {
|
||||||
|
val t = aa[0]
|
||||||
|
val a = b
|
||||||
|
}
|
||||||
|
|
||||||
|
val a: Array<String>
|
||||||
|
get() {
|
||||||
|
log("a.get")
|
||||||
|
return arrayOf("a")
|
||||||
|
}
|
||||||
|
|
||||||
|
val aa: Array<String>
|
||||||
|
get() {
|
||||||
|
log("aa.get")
|
||||||
|
return arrayOf("aa")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
foo(a[0])
|
||||||
|
|
||||||
|
assertEquals("a.get;aa.get;", pullLog())
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user