JS: disable suspend function tail-call optimization based on FE data

Partially fixes KT-21026
This commit is contained in:
Alexey Andreev
2017-10-31 17:36:03 +03:00
parent 6ab87ad66a
commit 26843509c7
7 changed files with 17 additions and 42 deletions
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
@@ -189,15 +189,21 @@ class CoroutineFunctionTransformer(private val function: JsFunction, name: Strin
): List<JsStatement> {
val indexOfGlobalCatch = blocks.indexOf(context.globalCatchBlock)
val stateRef = JsNameRef(context.metadata.stateName, JsThisRef())
val exceptionStateRef = JsNameRef(context.metadata.exceptionStateName, JsThisRef())
val isFromGlobalCatch = JsAstUtils.equality(stateRef, JsIntLiteral(indexOfGlobalCatch))
val catch = JsCatch(functionWithBody.scope, "e")
val continueWithException = JsBlock(
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.metadata.exceptionStateName, JsThisRef())).makeStmt(),
JsAstUtils.assignment(stateRef.deepCopy(), exceptionStateRef.deepCopy()).makeStmt(),
JsAstUtils.assignment(JsNameRef(context.metadata.exceptionName, JsThisRef()),
catch.parameter.name.makeRef()).makeStmt()
)
catch.body = JsBlock(JsIf(isFromGlobalCatch, JsThrow(catch.parameter.name.makeRef()), continueWithException))
val adjustExceptionState = JsAstUtils.assignment(exceptionStateRef.deepCopy(), stateRef.deepCopy()).makeStmt()
catch.body = JsBlock(JsIf(
isFromGlobalCatch,
JsBlock(adjustExceptionState, JsThrow(catch.parameter.name.makeRef())),
continueWithException
))
val throwResultRef = JsNameRef(context.metadata.exceptionName, JsThisRef())
context.globalCatchBlock.statements += JsThrow(throwResultRef)
@@ -6862,13 +6862,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("inlineWithStateMachine.kt")
public void testInlineWithStateMachine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("inlineWithoutStateMachine.kt")
@@ -153,17 +153,12 @@ private fun translateFunctionCall(
}
if (resolvedCall.resultingDescriptor.isSuspend) {
if (context.isInStateMachine) {
val statement = callInfo.constructSuspendSafeCallIfNeeded(JsAstUtils.asSyntheticStatement(callExpression.apply {
isSuspend = true
source = resolvedCall.call.callElement
}))
context.currentBlock.statements += statement
return context.createCoroutineResult(resolvedCall)
}
else {
callExpression.isTailCallSuspend = true
}
val statement = callInfo.constructSuspendSafeCallIfNeeded(JsAstUtils.asSyntheticStatement(callExpression.apply {
isSuspend = true
source = resolvedCall.call.callElement
}))
context.currentBlock.statements += statement
return context.createCoroutineResult(resolvedCall)
}
else {
callExpression = callInfo.constructSafeCallIfNeeded(callExpression)
@@ -200,9 +195,6 @@ private val untilFqName = FqName("kotlin.ranges.until")
fun ResolvedCall<out CallableDescriptor>.getReturnType(): KotlinType = TranslationUtils.getReturnTypeForCoercion(resultingDescriptor)
private val TranslationContext.isInStateMachine
get() = (declarationDescriptor as? FunctionDescriptor)?.requiresStateMachineTransformation(this) == true
fun computeExplicitReceiversForInvoke(
context: TranslationContext,
resolvedCall: ResolvedCall<out FunctionDescriptor>,
@@ -121,9 +121,7 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor<Unit>() {
val innerContext = context.newDeclaration(descriptor).translateAndAliasParameters(descriptor, function.parameters)
if (descriptor.isSuspend) {
if (descriptor.requiresStateMachineTransformation(context)) {
function.fillCoroutineMetadata(context, descriptor, hasController = false)
}
function.fillCoroutineMetadata(context, descriptor, hasController = false)
}
if (!descriptor.isOverridable) {
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.translateFunctionBody
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.requiresStateMachineTransformation
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -76,12 +75,7 @@ fun TranslationContext.translateAndAliasParameters(
if (continuationDescriptor != null) {
val jsParameter = JsParameter(getNameForDescriptor(continuationDescriptor))
targetList += jsParameter
aliases[continuationDescriptor] = if (!descriptor.requiresStateMachineTransformation(this)) {
JsAstUtils.pureFqn(jsParameter.name, null)
}
else {
JsAstUtils.stateMachineReceiver()
}
aliases[continuationDescriptor] = JsAstUtils.stateMachineReceiver()
}
return this.innerContextWithDescriptorsAliased(aliases)
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
@@ -37,7 +36,6 @@ import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue
@@ -163,17 +161,11 @@ fun TranslationContext.addAccessorsToPrototype(
addDeclarationStatement(defineProperty.makeStmt())
}
fun FunctionDescriptor.requiresStateMachineTransformation(context: TranslationContext): Boolean =
this is AnonymousFunctionDescriptor ||
context.bindingContext()[BindingContext.CONTAINS_NON_TAIL_SUSPEND_CALLS, this] == true
fun JsFunction.fillCoroutineMetadata(
context: TranslationContext,
descriptor: FunctionDescriptor,
hasController: Boolean
) {
if (!descriptor.requiresStateMachineTransformation(context)) return
val suspendPropertyDescriptor = context.currentModule.getPackage(COROUTINES_INTRINSICS_PACKAGE_FQ_NAME)
.memberScope
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND).first()