JS: replace suspend inline metadata after inlining
This fixes some issues on coroutine inlining, see tests
This commit is contained in:
+12
-2
@@ -227,6 +227,10 @@ public final class DebugJsAstProtoBuf {
|
||||
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||
*/
|
||||
COROUTINE_RECEIVER(7, 8),
|
||||
/**
|
||||
* <code>SET_COROUTINE_RESULT = 9;</code>
|
||||
*/
|
||||
SET_COROUTINE_RESULT(8, 9),
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -261,6 +265,10 @@ public final class DebugJsAstProtoBuf {
|
||||
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||
*/
|
||||
public static final int COROUTINE_RECEIVER_VALUE = 8;
|
||||
/**
|
||||
* <code>SET_COROUTINE_RESULT = 9;</code>
|
||||
*/
|
||||
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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
+75
@@ -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 <T> 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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+18
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<JsName>, 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) {
|
||||
|
||||
+2
-5
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -440,6 +440,7 @@ enum SpecialFunction {
|
||||
COROUTINE_RESULT = 6;
|
||||
COROUTINE_CONTROLLER = 7;
|
||||
COROUTINE_RECEIVER = 8;
|
||||
SET_COROUTINE_RESULT = 9;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -527,6 +527,7 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable<Fi
|
||||
JsAstProtoBuf.SpecialFunction.COROUTINE_RESULT -> 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 <T : JsNode> withLocation(fileId: Int?, location: Location?, action: () -> T): T {
|
||||
|
||||
@@ -175,6 +175,10 @@ public final class JsAstProtoBuf {
|
||||
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||
*/
|
||||
COROUTINE_RECEIVER(7, 8),
|
||||
/**
|
||||
* <code>SET_COROUTINE_RESULT = 9;</code>
|
||||
*/
|
||||
SET_COROUTINE_RESULT(8, 9),
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -209,6 +213,10 @@ public final class JsAstProtoBuf {
|
||||
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||
*/
|
||||
public static final int COROUTINE_RECEIVER_VALUE = 8;
|
||||
/**
|
||||
* <code>SET_COROUTINE_RESULT = 9;</code>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+18
@@ -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");
|
||||
|
||||
+3
-1
@@ -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<Unit>() {
|
||||
|
||||
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())
|
||||
|
||||
+38
-22
@@ -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 <T : JsNode> transformCoroutineMetadataToSpecialFunctions(context: TranslationContext, node: T): T {
|
||||
fun <T : JsNode> transformCoroutineMetadataToSpecialFunctions(node: T): T {
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsExpression>) {
|
||||
val specialFunction = when {
|
||||
@@ -49,7 +34,11 @@ fun <T : JsNode> 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 <T : JsNode> transformCoroutineMetadataToSpecialFunctions(context: Translati
|
||||
override fun endVisit(x: JsExpression, ctx: JsContext<in JsExpression>) {
|
||||
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<in JsExpression>): 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 <T : JsNode> transformSpecialFunctionsToCoroutineMetadata(node: T): T {
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun endVisit(x: JsInvocation, ctx: JsContext<in JsExpression>) {
|
||||
@@ -91,9 +96,20 @@ fun <T : JsNode> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<JsStatement> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user