JS: fix translation of safe calls to suspend unit functions
See KT-21317
This commit is contained in:
@@ -20,16 +20,15 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBlock
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsConditional
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.type
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.createCoroutineResult
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -45,6 +44,8 @@ interface CallInfo {
|
||||
val extensionReceiver: JsExpression?
|
||||
|
||||
fun constructSafeCallIfNeeded(result: JsExpression): JsExpression
|
||||
|
||||
fun constructSuspendSafeCallIfNeeded(result: JsStatement): JsStatement
|
||||
}
|
||||
|
||||
abstract class AbstractCallInfo : CallInfo {
|
||||
@@ -165,7 +166,7 @@ private fun TranslationContext.createCallInfo(
|
||||
|
||||
if (dispatchReceiverType != null) {
|
||||
dispatchReceiver = dispatchReceiver?.let {
|
||||
TranslationUtils.coerce(this, it, dispatchReceiverType!!)
|
||||
TranslationUtils.coerce(this, it, dispatchReceiverType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,5 +194,29 @@ private fun TranslationContext.createCallInfo(
|
||||
notNullConditionalForSafeCall
|
||||
}
|
||||
}
|
||||
|
||||
override fun constructSuspendSafeCallIfNeeded(result: JsStatement): JsStatement {
|
||||
return if (notNullConditionalForSafeCall == null) {
|
||||
result
|
||||
}
|
||||
else {
|
||||
val callElement = resolvedCall.call.callElement
|
||||
val coroutineResult = context.createCoroutineResult(resolvedCall)
|
||||
val nullAssignment = JsAstUtils.assignment(coroutineResult, JsNullLiteral()).source(callElement)
|
||||
|
||||
val thenBlock = JsBlock()
|
||||
thenBlock.statements += result
|
||||
val thenContext = context.innerBlock(thenBlock)
|
||||
val lhs = coroutineResult.deepCopy()
|
||||
val rhsOriginal = coroutineResult.deepCopy().apply { type = resolvedCall.getReturnType() }
|
||||
val rhs = TranslationUtils.coerce(thenContext, rhsOriginal, resolvedCall.getReturnType().makeNullable())
|
||||
if (rhs != rhsOriginal) {
|
||||
thenBlock.statements += JsAstUtils.asSyntheticStatement(JsAstUtils.assignment(lhs, rhs).source(callElement))
|
||||
}
|
||||
|
||||
val thenStatement = if (thenBlock.statements.size == 1) thenBlock.statements.first() else thenBlock
|
||||
JsIf(notNullConditionalForSafeCall.testExpression, thenStatement, nullAssignment.makeStmt())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-32
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.*
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
@@ -59,7 +58,8 @@ object CallTranslator {
|
||||
extensionOrDispatchReceiver: JsExpression? = null
|
||||
): JsExpression {
|
||||
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), null)
|
||||
val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
|
||||
val result = variableAccessInfo.constructSafeCallIfNeeded(variableAccessInfo.translateVariableAccess())
|
||||
.source(resolvedCall.call.callElement)
|
||||
result.type = TranslationUtils.getReturnTypeForCoercion(resolvedCall.resultingDescriptor.original)
|
||||
return result
|
||||
}
|
||||
@@ -72,7 +72,8 @@ object CallTranslator {
|
||||
val type = TranslationUtils.getReturnTypeForCoercion(resolvedCall.resultingDescriptor)
|
||||
val coerceValue = TranslationUtils.coerce(context, value, type)
|
||||
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), coerceValue)
|
||||
val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
|
||||
val result = variableAccessInfo.constructSafeCallIfNeeded(variableAccessInfo.translateVariableAccess())
|
||||
.source(resolvedCall.call.callElement)
|
||||
result.type = context.currentModule.builtIns.unitType
|
||||
return result
|
||||
}
|
||||
@@ -108,7 +109,7 @@ private fun translateCall(
|
||||
assert(explicitReceivers.extensionReceiver == null) { "VariableAsFunctionResolvedCall must have one receiver" }
|
||||
val variableCall = resolvedCall.variableCall
|
||||
|
||||
val result = if (variableCall.expectedReceivers()) {
|
||||
return if (variableCall.expectedReceivers()) {
|
||||
val newReceiver = CallTranslator.translateGet(context, variableCall, explicitReceivers.extensionOrDispatchReceiver)
|
||||
translateFunctionCall(context, resolvedCall.functionCall, resolvedCall.variableCall, ExplicitReceivers(newReceiver))
|
||||
} else {
|
||||
@@ -123,8 +124,6 @@ private fun translateCall(
|
||||
ExplicitReceivers(dispatchReceiver, explicitReceivers.extensionOrDispatchReceiver))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
val call = resolvedCall.call
|
||||
@@ -145,7 +144,8 @@ private fun translateFunctionCall(
|
||||
val rangeCheck = RangeCheckTranslator(context).translateAsRangeCheck(resolvedCall, explicitReceivers)
|
||||
if (rangeCheck != null) return rangeCheck
|
||||
|
||||
val callExpression = context.getCallInfo(resolvedCall, explicitReceivers).translateFunctionCall()
|
||||
val callInfo = context.getCallInfo(resolvedCall, explicitReceivers)
|
||||
var callExpression = callInfo.translateFunctionCall()
|
||||
|
||||
if (CallExpressionTranslator.shouldBeInlined(inlineResolvedCall.resultingDescriptor, context)) {
|
||||
setInlineCallMetadata(callExpression, resolvedCall.call.callElement as KtExpression,
|
||||
@@ -154,21 +154,20 @@ private fun translateFunctionCall(
|
||||
|
||||
if (resolvedCall.resultingDescriptor.isSuspend) {
|
||||
if (context.isInStateMachine) {
|
||||
context.currentBlock.statements += JsAstUtils.asSyntheticStatement(callExpression.apply {
|
||||
val statement = callInfo.constructSuspendSafeCallIfNeeded(JsAstUtils.asSyntheticStatement(callExpression.apply {
|
||||
isSuspend = true
|
||||
source = resolvedCall.call.callElement
|
||||
})
|
||||
val coroutineRef = TranslationUtils.translateContinuationArgument(context).apply { source = resolvedCall.call.callElement }
|
||||
return context.defineTemporary(JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply {
|
||||
sideEffects = SideEffectKind.DEPENDS_ON_STATE
|
||||
source = resolvedCall.call.callElement
|
||||
coroutineResult = true
|
||||
})
|
||||
}))
|
||||
context.currentBlock.statements += statement
|
||||
return context.createCoroutineResult(resolvedCall)
|
||||
}
|
||||
else {
|
||||
callExpression.isTailCallSuspend = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
callExpression = callInfo.constructSafeCallIfNeeded(callExpression)
|
||||
}
|
||||
|
||||
callExpression.type = resolvedCall.getReturnType().let { if (resolvedCall.call.isSafeCall()) it.makeNullable() else it }
|
||||
mayBeMarkByRangeMetadata(resolvedCall, callExpression)
|
||||
@@ -254,7 +253,7 @@ abstract class CallCase<in I : CallInfo> {
|
||||
protected open fun I.bothReceivers(): JsExpression = unsupported()
|
||||
|
||||
fun translate(callInfo: I): JsExpression {
|
||||
val result = if (callInfo.dispatchReceiver == null) {
|
||||
return if (callInfo.dispatchReceiver == null) {
|
||||
if (callInfo.extensionReceiver == null)
|
||||
callInfo.noReceivers()
|
||||
else
|
||||
@@ -265,8 +264,6 @@ abstract class CallCase<in I : CallInfo> {
|
||||
} else
|
||||
callInfo.bothReceivers()
|
||||
}
|
||||
|
||||
return callInfo.constructSafeCallIfNeeded(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,20 +276,7 @@ interface DelegateIntrinsic<in I : CallInfo> {
|
||||
fun I.getDescriptor(): CallableDescriptor
|
||||
fun I.getArgs(): List<JsExpression>
|
||||
|
||||
fun intrinsic(callInfo: I): JsExpression? {
|
||||
val result =
|
||||
if (callInfo.canBeApply()) {
|
||||
callInfo.getIntrinsic()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return if (result != null) {
|
||||
callInfo.constructSafeCallIfNeeded(result)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
fun intrinsic(callInfo: I): JsExpression? = if (callInfo.canBeApply()) callInfo.getIntrinsic() else null
|
||||
|
||||
private fun I.getIntrinsic(): JsExpression? {
|
||||
val descriptor = getDescriptor()
|
||||
|
||||
@@ -27,9 +27,7 @@ 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.CoroutineMetadata
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.*
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
|
||||
@@ -41,6 +39,7 @@ 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
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -243,4 +242,15 @@ fun createPrototypeStatements(superName: JsName, name: JsName): List<JsStatement
|
||||
val constructorStatement = JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt()
|
||||
|
||||
return listOf(prototypeStatement, constructorStatement)
|
||||
}
|
||||
|
||||
fun TranslationContext.createCoroutineResult(resolvedCall: ResolvedCall<*>): JsExpression {
|
||||
val callElement = resolvedCall.call.callElement
|
||||
val coroutineRef = TranslationUtils.translateContinuationArgument(this).source(callElement)
|
||||
return JsNameRef("\$\$coroutineResult\$\$", coroutineRef).apply {
|
||||
sideEffects = SideEffectKind.DEPENDS_ON_STATE
|
||||
source = callElement
|
||||
coroutineResult = true
|
||||
synthetic = true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user