diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt index 40b2318ad76..62889fef256 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/ExpressionDecomposer.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.js.inline 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.synthetic import com.intellij.util.SmartList @@ -265,7 +267,13 @@ internal class ExpressionDecomposer private constructor( val callee = qualifier as? JsNameRef 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() callee.qualifier = receiverTmp } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineEvaluationOrderTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineEvaluationOrderTestGenerated.java index ed43e551eb0..21e64fd927e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineEvaluationOrderTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineEvaluationOrderTestGenerated.java @@ -179,6 +179,18 @@ public class InlineEvaluationOrderTestGenerated extends AbstractInlineEvaluation 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") public void testLambdaWithClosure() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/lambdaWithClosure.kt"); @@ -251,6 +263,18 @@ public class InlineEvaluationOrderTestGenerated extends AbstractInlineEvaluation 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") public void testTernaryConditional() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/inlineEvaluationOrder/cases/ternaryConditional.kt"); diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/inlineFunctionAsParameterOfQualifiedCall.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/inlineFunctionAsParameterOfQualifiedCall.kt new file mode 100644 index 00000000000..ba4f2b1ce7b --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/inlineFunctionAsParameterOfQualifiedCall.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/lambdaPropertyExtracted.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/lambdaPropertyExtracted.kt new file mode 100644 index 00000000000..f2c8ad698fa --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/lambdaPropertyExtracted.kt @@ -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 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" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectExtracted.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectExtracted.kt new file mode 100644 index 00000000000..ce188b8fcca --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectExtracted.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectPassedToInlineFunction.kt b/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectPassedToInlineFunction.kt new file mode 100644 index 00000000000..8dfb10bdd1a --- /dev/null +++ b/js/js.translator/testData/inlineEvaluationOrder/cases/propertyWithSideEffectPassedToInlineFunction.kt @@ -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 + get() { + log("a.get") + return arrayOf("a") + } + +val aa: Array + get() { + log("aa.get") + return arrayOf("aa") + } + +fun box(): String { + foo(a[0]) + + assertEquals("a.get;aa.get;", pullLog()) + + return "OK" +} \ No newline at end of file