diff --git a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
index d89f07ec27f..f1358a9e924 100644
--- a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
+++ b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
@@ -227,6 +227,10 @@ public final class DebugJsAstProtoBuf {
* COROUTINE_RECEIVER = 8;
*/
COROUTINE_RECEIVER(7, 8),
+ /**
+ * SET_COROUTINE_RESULT = 9;
+ */
+ SET_COROUTINE_RESULT(8, 9),
;
/**
@@ -261,6 +265,10 @@ public final class DebugJsAstProtoBuf {
* COROUTINE_RECEIVER = 8;
*/
public static final int COROUTINE_RECEIVER_VALUE = 8;
+ /**
+ * SET_COROUTINE_RESULT = 9;
+ */
+ public static final int SET_COROUTINE_RESULT_VALUE = 9;
public final int getNumber() { return value; }
@@ -275,6 +283,7 @@ public final class DebugJsAstProtoBuf {
case 6: return COROUTINE_RESULT;
case 7: return COROUTINE_CONTROLLER;
case 8: return COROUTINE_RECEIVER;
+ case 9: return SET_COROUTINE_RESULT;
default: return null;
}
}
@@ -49553,12 +49562,13 @@ public final class DebugJsAstProtoBuf {
"\013SideEffects\022\021\n\rAFFECTS_STATE\020\001\022\024\n\020DEPEN" +
"DS_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016InlineStrateg" +
"y\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PLACE\020\001\022\016\n\nNOT_" +
- "INLINE\020\002*\275\001\n\017SpecialFunction\022\032\n\026DEFINE_I" +
+ "INLINE\020\002*\327\001\n\017SpecialFunction\022\032\n\026DEFINE_I" +
"NLINE_FUNCTION\020\001\022\021\n\rWRAP_FUNCTION\020\002\022\021\n\rT" +
"O_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHAR\020\004\022\020\n\014SUSPEN" +
"D_CALL\020\005\022\024\n\020COROUTINE_RESULT\020\006\022\030\n\024COROUT" +
"INE_CONTROLLER\020\007\022\026\n\022COROUTINE_RECEIVER\020\010",
- "B\024B\022DebugJsAstProtoBuf"
+ "\022\030\n\024SET_COROUTINE_RESULT\020\tB\024B\022DebugJsAst" +
+ "ProtoBuf"
};
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
diff --git a/compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt b/compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt
new file mode 100644
index 00000000000..07b6ed72e8b
--- /dev/null
+++ b/compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt
@@ -0,0 +1,50 @@
+// WITH_RUNTIME
+// WITH_COROUTINES
+import helpers.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
+
+var stopped = false
+var log = ""
+var postponed: () -> Unit = { }
+
+suspend fun delay(): Unit = suspendCoroutine { c ->
+ log += "1"
+ postponed = {
+ log += "3"
+ c.resume(Unit)
+ }
+ COROUTINE_SUSPENDED
+}
+
+suspend inline fun foo(x: String): String {
+ delay()
+ return x
+}
+
+suspend inline fun bar(x: String) = foo(x)
+
+suspend fun baz(x: String) = bar(x)
+
+fun act(c: suspend () -> Unit) {
+ stopped = false
+
+ c.startCoroutine(handleResultContinuation {
+ stopped = true
+ })
+
+ while (!stopped) {
+ log += "2"
+ postponed()
+ }
+}
+
+fun box(): String {
+ var result = ""
+ act {
+ result = baz("OK")
+ }
+
+ if (log != "123") return "fail: $log"
+ return result
+}
\ No newline at end of file
diff --git a/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt b/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt
new file mode 100644
index 00000000000..0b1cfebcd66
--- /dev/null
+++ b/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt
@@ -0,0 +1,75 @@
+// IGNORE_BACKEND: NATIVE
+// WITH_COROUTINES
+// WITH_RUNTIME
+
+// MODULE: lib(support)
+// FILE: lib.kt
+
+import helpers.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
+
+var continuation: () -> Unit = { }
+var log = ""
+var finished = false
+
+suspend fun foo(v: T): T = suspendCoroutineOrReturn { x ->
+ continuation = {
+ x.resume(v)
+ }
+ log += "foo($v);"
+ COROUTINE_SUSPENDED
+}
+
+inline suspend fun boo(v: String): String {
+ foo("!$v")
+ log += "boo($v);"
+ return foo(v)
+}
+
+inline suspend fun bar(v: String): String {
+ val x = boo(v)
+ log += "bar($x);"
+ return x
+}
+
+fun builder(c: suspend () -> Unit) {
+ c.startCoroutine(handleResultContinuation {
+ continuation = { }
+ finished = true
+ })
+}
+
+// MODULE: main(lib)
+// FILE: main.kt
+
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
+
+suspend fun baz() {
+ val a = bar("A")
+ log += "$a;"
+ log += "between bar;"
+ val b = bar("B")
+ log += "$b;"
+}
+
+val expectedString =
+ "foo(!A);@;boo(A);foo(A);@;bar(A);A;" +
+ "between bar;" +
+ "foo(!B);@;boo(B);foo(B);@;bar(B);B;"
+
+fun box(): String {
+ builder {
+ baz()
+ }
+
+ while (!finished) {
+ log += "@;"
+ continuation()
+ }
+
+ if (log != expectedString) return "fail: $log"
+
+ return "OK"
+}
diff --git a/compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt b/compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt
new file mode 100644
index 00000000000..edb8ccb72ae
--- /dev/null
+++ b/compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt
@@ -0,0 +1,29 @@
+// IGNORE_BACKEND: NATIVE
+// WITH_RUNTIME
+// WITH_COROUTINES
+// MODULE: lib
+// FILE: lib.kt
+suspend inline fun foo(v: String): String = v
+
+suspend inline fun bar(): String = foo("O")
+
+// MODULE: main(lib, support)
+// FILE: main.kt
+import helpers.*
+import kotlin.coroutines.experimental.*
+import kotlin.coroutines.experimental.intrinsics.*
+
+fun builder(c: suspend () -> Unit) {
+ c.startCoroutine(EmptyContinuation)
+}
+
+fun box(): String {
+ var result = ""
+
+ builder {
+ result = bar()
+ result += foo("K")
+ }
+
+ return result
+}
diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java
index 1124c391897..17e7815e58d 100644
--- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java
+++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java
@@ -5513,6 +5513,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
+ @TestMetadata("indirectInlineUsedAsNonInline.kt")
+ public void testIndirectInlineUsedAsNonInline() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("inlineFunInGenericClass.kt")
public void testInlineFunInGenericClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlineFunInGenericClass.kt");
@@ -6133,6 +6139,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
+ @TestMetadata("inlineMultiModuleWithInnerInlining.kt")
+ public void testInlineMultiModuleWithInnerInlining() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inlineTailCall.kt")
+ public void testInlineTailCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/simple.kt");
diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java
index e879650d311..1db20d31e53 100644
--- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java
+++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java
@@ -5513,6 +5513,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
+ @TestMetadata("indirectInlineUsedAsNonInline.kt")
+ public void testIndirectInlineUsedAsNonInline() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("inlineFunInGenericClass.kt")
public void testInlineFunInGenericClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlineFunInGenericClass.kt");
@@ -6133,6 +6139,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
+ @TestMetadata("inlineMultiModuleWithInnerInlining.kt")
+ public void testInlineMultiModuleWithInnerInlining() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inlineTailCall.kt")
+ public void testInlineTailCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/simple.kt");
diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
index c614b0b3c9c..a7d1e64913a 100644
--- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
+++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
@@ -5513,6 +5513,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
+ @TestMetadata("indirectInlineUsedAsNonInline.kt")
+ public void testIndirectInlineUsedAsNonInline() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("inlineFunInGenericClass.kt")
public void testInlineFunInGenericClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlineFunInGenericClass.kt");
@@ -6133,6 +6139,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
+ @TestMetadata("inlineMultiModuleWithInnerInlining.kt")
+ public void testInlineMultiModuleWithInnerInlining() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inlineTailCall.kt")
+ public void testInlineTailCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/simple.kt");
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
index 39e5cb6e558..ce547fc1977 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
@@ -115,6 +115,8 @@ var JsNameRef.coroutineReceiver by MetadataProperty(default = false)
var JsFunction.forceStateMachine by MetadataProperty(default = false)
+var JsFunction.isInlineableCoroutineBody by MetadataProperty(default = false)
+
var JsName.imported by MetadataProperty(default = false)
var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default = null)
@@ -156,7 +158,8 @@ enum class SpecialFunction(val suggestedName: String) {
SUSPEND_CALL("suspendCall"),
COROUTINE_RESULT("coroutineResult"),
COROUTINE_CONTROLLER("coroutineController"),
- COROUTINE_RECEIVER("coroutineReceiver")
+ COROUTINE_RECEIVER("coroutineReceiver"),
+ SET_COROUTINE_RESULT("setCoroutineResult")
}
enum class BoxingKind {
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineTransformer.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineTransformer.kt
index de2050a5f4f..3087ee17d14 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineTransformer.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/coroutine/CoroutineTransformer.kt
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.js.coroutine
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
+import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineableCoroutineBody
+import org.jetbrains.kotlin.js.translate.declaration.transformCoroutineMetadataToSpecialFunctions
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
@@ -57,6 +59,14 @@ class CoroutineTransformer : JsVisitorWithContextImpl() {
return super.visit(x, ctx)
}
+ override fun visit(x: JsFunction, ctx: JsContext<*>): Boolean {
+ if (x.isInlineableCoroutineBody) {
+ x.body = transformCoroutineMetadataToSpecialFunctions(x.body)
+ return false
+ }
+ return super.visit(x, ctx)
+ }
+
override fun visit(x: JsVars.JsVar, ctx: JsContext<*>): Boolean {
val initExpression = x.initExpression
if (initExpression != null) {
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 4db8056c901..4b46d3e0979 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
@@ -63,7 +63,9 @@ internal class ExpressionDecomposer private constructor(
val decomposer = with (statement) {
val extractable = match(canBeExtractedByInliner)
val containsExtractable = withParentsOfNodes(extractable)
- val nodesWithSideEffect = match { it !is JsLiteral.JsValueLiteral }
+ val nodesWithSideEffect = match {
+ !(it is JsLiteral.JsValueLiteral || (it is JsExpression && it.sideEffects == SideEffectKind.PURE))
+ }
val containsNodeWithSideEffect = withParentsOfNodes(nodesWithSideEffect)
ExpressionDecomposer(containsExtractable, containsNodeWithSideEffect)
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
index 941a55f41f3..23b85483141 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
@@ -233,7 +233,8 @@ class FunctionReader(
}
val position = info.offsetToSourceMapping[offset]
- val functionExpr = parseFunction(source, info.filePath, position, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram())) ?:
+ val jsScope = JsRootScope(JsProgram())
+ val functionExpr = parseFunction(source, info.filePath, position, offset, ThrowExceptionOnErrorReporter, jsScope) ?:
return null
functionExpr.fixForwardNameReferences()
val (function, wrapper) = if (isWrapped) {
@@ -261,12 +262,7 @@ class FunctionReader(
wrapperStatements?.forEach { replaceExternalNames(it, replacements, allDefinedNames) }
function.markInlineArguments(descriptor)
markDefaultParams(function)
-
- for (externalName in (collectReferencedNames(function) - allDefinedNames)) {
- info.specialFunctions[externalName.ident]?.let {
- externalName.specialFunction = it
- }
- }
+ markSpecialFunctions(function, allDefinedNames, info, jsScope)
val namesWithoutSizeEffects = wrapperStatements.orEmpty().asSequence()
.flatMap { collectDefinedNames(it).asSequence() }
@@ -289,6 +285,33 @@ class FunctionReader(
return FunctionWithWrapper(function, wrapper)
}
+ private fun markSpecialFunctions(function: JsFunction, allDefinedNames: Set, info: ModuleInfo, scope: JsScope) {
+ for (externalName in (collectReferencedNames(function) - allDefinedNames)) {
+ info.specialFunctions[externalName.ident]?.let {
+ externalName.specialFunction = it
+ }
+ }
+
+ function.body.accept(object : RecursiveJsVisitor() {
+ override fun visitNameRef(nameRef: JsNameRef) {
+ super.visitNameRef(nameRef)
+ markQualifiedSpecialFunction(nameRef)
+ }
+
+ private fun markQualifiedSpecialFunction(nameRef: JsNameRef) {
+ val qualifier = nameRef.qualifier as? JsNameRef ?: return
+ if (qualifier.ident != info.kotlinVariable || qualifier.qualifier != null) return
+ if (nameRef.name?.specialFunction != null) return
+
+ val specialFunction = specialFunctionsByName[nameRef.ident] ?: return
+ if (nameRef.name == null) {
+ nameRef.name = scope.declareName(nameRef.ident)
+ }
+ nameRef.name!!.specialFunction = specialFunction
+ }
+ })
+ }
+
private fun markDefaultParams(function: JsFunction) {
val paramsByNames = function.parameters.associate { it.name to it }
for (ifStatement in function.body.statements) {
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/ReturnReplacingVisitor.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/ReturnReplacingVisitor.kt
index b34e7fe59b8..e3e248b90b0 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/ReturnReplacingVisitor.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/ReturnReplacingVisitor.kt
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.js.inline.util.rewriters
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
+import org.jetbrains.kotlin.js.coroutine.isStateMachineResult
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
@@ -43,12 +44,8 @@ class ReturnReplacingVisitor(
ctx.removeMe()
- val returnExpression = x.expression
val returnReplacement = getReturnReplacement(x.expression)
if (returnReplacement != null) {
- if (returnExpression != null && returnExpression.isTailCallSuspend) {
- returnReplacement.isSuspend = true
- }
if (returnReplacement.source == null) {
returnReplacement.source = x.source
}
@@ -74,7 +71,7 @@ class ReturnReplacingVisitor(
}
private fun processCoroutineResult(expression: JsExpression?): JsExpression? {
- if (!isSuspend) return expression
+ if (!isSuspend || expression.isStateMachineResult()) return expression
val lhs = JsNameRef("\$\$coroutineResult\$\$", JsAstUtils.stateMachineReceiver()).apply { coroutineResult = true }
return JsAstUtils.assignment(lhs, expression ?: Namer.getUndefinedExpression())
}
diff --git a/js/js.libraries/src/js/markerFunctions.js b/js/js.libraries/src/js/markerFunctions.js
index 05aef8b00ce..15c2fc30754 100644
--- a/js/js.libraries/src/js/markerFunctions.js
+++ b/js/js.libraries/src/js/markerFunctions.js
@@ -75,6 +75,9 @@ Kotlin.coroutineController = function(qualifier) {
Kotlin.coroutineReceiver = function(qualifier) {
};
+Kotlin.setCoroutineResult = function(value, qualifier) {
+};
+
Kotlin.getFunctionById = function(id, defaultValue) {
return function() {
return defaultValue;
diff --git a/js/js.serializer/src/js-ast.proto b/js/js.serializer/src/js-ast.proto
index eca42fe7395..f1b8e723446 100644
--- a/js/js.serializer/src/js-ast.proto
+++ b/js/js.serializer/src/js-ast.proto
@@ -440,6 +440,7 @@ enum SpecialFunction {
COROUTINE_RESULT = 6;
COROUTINE_CONTROLLER = 7;
COROUTINE_RECEIVER = 8;
+ SET_COROUTINE_RESULT = 9;
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
index 06d5549fa20..b146778dd38 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
@@ -527,6 +527,7 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable SpecialFunction.COROUTINE_RESULT
JsAstProtoBuf.SpecialFunction.COROUTINE_CONTROLLER -> SpecialFunction.COROUTINE_CONTROLLER
JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER -> SpecialFunction.COROUTINE_RECEIVER
+ JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT -> SpecialFunction.SET_COROUTINE_RESULT
}
private fun withLocation(fileId: Int?, location: Location?, action: () -> T): T {
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
index ff4995aa0e7..ea82a1beca8 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
@@ -175,6 +175,10 @@ public final class JsAstProtoBuf {
* COROUTINE_RECEIVER = 8;
*/
COROUTINE_RECEIVER(7, 8),
+ /**
+ * SET_COROUTINE_RESULT = 9;
+ */
+ SET_COROUTINE_RESULT(8, 9),
;
/**
@@ -209,6 +213,10 @@ public final class JsAstProtoBuf {
* COROUTINE_RECEIVER = 8;
*/
public static final int COROUTINE_RECEIVER_VALUE = 8;
+ /**
+ * SET_COROUTINE_RESULT = 9;
+ */
+ public static final int SET_COROUTINE_RESULT_VALUE = 9;
public final int getNumber() { return value; }
@@ -223,6 +231,7 @@ public final class JsAstProtoBuf {
case 6: return COROUTINE_RESULT;
case 7: return COROUTINE_CONTROLLER;
case 8: return COROUTINE_RECEIVER;
+ case 9: return SET_COROUTINE_RESULT;
default: return null;
}
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
index 08e3a0775cb..efd792785bd 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
@@ -563,6 +563,7 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
SpecialFunction.COROUTINE_RESULT -> JsAstProtoBuf.SpecialFunction.COROUTINE_RESULT
SpecialFunction.COROUTINE_CONTROLLER -> JsAstProtoBuf.SpecialFunction.COROUTINE_CONTROLLER
SpecialFunction.COROUTINE_RECEIVER -> JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER
+ SpecialFunction.SET_COROUTINE_RESULT -> JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT
}
private fun serialize(name: JsName): Int = nameMap.getOrPut(name) {
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java
index 371b623fb9c..5958ea4e180 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java
@@ -6083,6 +6083,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
+ @TestMetadata("indirectInlineUsedAsNonInline.kt")
+ public void testIndirectInlineUsedAsNonInline() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/indirectInlineUsedAsNonInline.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("inlineFunInGenericClass.kt")
public void testInlineFunInGenericClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlineFunInGenericClass.kt");
@@ -6685,6 +6691,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
+ @TestMetadata("inlineMultiModuleWithInnerInlining.kt")
+ public void testInlineMultiModuleWithInnerInlining() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithInnerInlining.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inlineTailCall.kt")
+ public void testInlineTailCall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineTailCall.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/simple.kt");
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
index 8275f367347..247c8767d53 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsFunction
import org.jetbrains.kotlin.js.backend.ast.JsName
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
+import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineableCoroutineBody
import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.*
@@ -100,9 +101,10 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() {
if (descriptor.isSuspend && descriptor.isInline && descriptor.shouldBeExported(context.config) && functionAndContext != null) {
val innerContext = functionAndContext.second
- val inlineFunction = transformCoroutineMetadataToSpecialFunctions(context, functionAndContext.first.deepCopy() as JsFunction)
+ val inlineFunction = functionAndContext.first.deepCopy() as JsFunction
inlineFunction.name = null
inlineFunction.coroutineMetadata = null
+ inlineFunction.isInlineableCoroutineBody = true
val metadata = InlineMetadata.compose(inlineFunction, descriptor, innerContext)
val functionWithMetadata = metadata.functionWithMetadata(context, descriptor.source.getPsi())
context.addDeclarationStatement(functionWithMetadata.makeStmt())
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/inlineCoroutineUtil.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/inlineCoroutineUtil.kt
index 7c14f3749ff..cbcc5c6becf 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/inlineCoroutineUtil.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/inlineCoroutineUtil.kt
@@ -14,31 +14,16 @@
* limitations under the License.
*/
-/*
- * Copyright 2010-2017 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package org.jetbrains.kotlin.js.translate.declaration
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
-import org.jetbrains.kotlin.js.translate.context.TranslationContext
-import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
+import org.jetbrains.kotlin.js.translate.context.Namer
+import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment
+import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.name
-fun transformCoroutineMetadataToSpecialFunctions(context: TranslationContext, node: T): T {
+fun transformCoroutineMetadataToSpecialFunctions(node: T): T {
val visitor = object : JsVisitorWithContextImpl() {
override fun endVisit(x: JsNameRef, ctx: JsContext) {
val specialFunction = when {
@@ -49,7 +34,11 @@ fun transformCoroutineMetadataToSpecialFunctions(context: Translati
}
if (specialFunction != null) {
val arguments = listOfNotNull(x.qualifier).toTypedArray()
- ctx.replaceMe(TranslationUtils.invokeSpecialFunction(context, specialFunction, *arguments).source(x.source))
+ ctx.replaceMe(JsInvocation(specialFunction.ref(), *arguments).apply {
+ synthetic = x.synthetic
+ sideEffects = x.sideEffects
+ source = x.source
+ })
}
else {
super.endVisit(x, ctx)
@@ -59,13 +48,29 @@ fun transformCoroutineMetadataToSpecialFunctions(context: Translati
override fun endVisit(x: JsExpression, ctx: JsContext) {
if (x.isSuspend) {
x.isSuspend = false
- ctx.replaceMe(TranslationUtils.invokeSpecialFunction(context, SpecialFunction.SUSPEND_CALL, x).source(x.source))
+ ctx.replaceMe(JsInvocation(SpecialFunction.SUSPEND_CALL.ref(), x).source(x.source))
}
}
+
+ override fun visit(x: JsBinaryOperation, ctx: JsContext): Boolean {
+ val lhs = x.arg1
+ if (lhs is JsNameRef && lhs.coroutineResult) {
+ val arguments = listOf(accept(x.arg2)) + listOfNotNull(lhs.qualifier?.let { accept(it) })
+ ctx.replaceMe(JsInvocation(SpecialFunction.SET_COROUTINE_RESULT.ref(), arguments).apply {
+ synthetic = x.synthetic
+ sideEffects = x.sideEffects
+ source = x.source
+ })
+ return false
+ }
+ return super.visit(x, ctx)
+ }
}
return visitor.accept(node)
}
+private fun SpecialFunction.ref() = pureFqn(suggestedName, Namer.kotlinObject())
+
fun transformSpecialFunctionsToCoroutineMetadata(node: T): T {
val visitor = object : JsVisitorWithContextImpl() {
override fun endVisit(x: JsInvocation, ctx: JsContext) {
@@ -91,9 +96,20 @@ fun transformSpecialFunctionsToCoroutineMetadata(node: T): T {
isSuspend = true
}
}
+ SpecialFunction.SET_COROUTINE_RESULT -> {
+ val lhs = JsNameRef("\$result\$", x.arguments.getOrNull(1)).apply {
+ coroutineResult = true
+ }
+ assignment(lhs, x.arguments[0])
+ }
else -> null
}
- replacement?.let { ctx.replaceMe(it) }
+ replacement?.let {
+ it.source = x.source
+ it.sideEffects = x.sideEffects
+ it.synthetic = x.synthetic
+ ctx.replaceMe(it)
+ }
}
}
}
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java
index a534164a134..336a7d1d0c4 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java
@@ -454,26 +454,6 @@ public final class JsAstUtils {
return new JsObjectLiteral(Collections.singletonList(new JsPropertyInitializer(label, value)));
}
- public static JsExpression replaceRootReference(@NotNull JsNameRef fullQualifier, @NotNull JsExpression newQualifier) {
- if (fullQualifier.getQualifier() == null) {
- assert Namer.getRootPackageName().equals(fullQualifier.getIdent()) : "Expected root package, but: " + fullQualifier.getIdent();
- return newQualifier;
- }
-
- fullQualifier = fullQualifier.deepCopy();
- JsNameRef qualifier = fullQualifier;
- while (true) {
- JsExpression parent = qualifier.getQualifier();
- assert parent instanceof JsNameRef : "unexpected qualifier: " + parent + ", original: " + fullQualifier;
- if (((JsNameRef) parent).getQualifier() == null) {
- assert Namer.getRootPackageName().equals(((JsNameRef) parent).getIdent());
- qualifier.setQualifier(newQualifier);
- return fullQualifier;
- }
- qualifier = (JsNameRef) parent;
- }
- }
-
@NotNull
public static List flattenStatement(@NotNull JsStatement statement) {
if (statement instanceof JsBlock) {
@@ -527,7 +507,7 @@ public final class JsAstUtils {
@NotNull
public static JsExpression stateMachineReceiver() {
- JsNameRef result = new JsNameRef("$this$");
+ JsNameRef result = pureFqn("$this$", null);
MetadataProperties.setCoroutineReceiver(result, true);
return result;
}