JS: replace suspend inline metadata after inlining

This fixes some issues on coroutine inlining, see tests
This commit is contained in:
Alexey Andreev
2017-11-03 14:35:39 +03:00
parent f8e7861ce6
commit 71b1591044
21 changed files with 344 additions and 60 deletions
@@ -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
}
@@ -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
}
@@ -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) {
@@ -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;
+1
View File
@@ -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) {
@@ -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");
@@ -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())
@@ -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;
}