Fix continuaion retrieval on generated coroutineContext intrinsic code
Use fake continuation instead on real one in JVM BE. Pass continuation parameter to closure generator in JS BE. #KT-22577: Fixed
This commit is contained in:
+18
-4
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
|
|||||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||||
import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
import org.jetbrains.kotlin.codegen.inline.isAfterSuspendMarker
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.isBeforeSuspendMarker
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.isInlineMarker
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||||
@@ -81,6 +78,13 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
|
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||||
|
|
||||||
if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) {
|
if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) {
|
||||||
|
continuationIndex =
|
||||||
|
if (isStatic(methodNode.access))
|
||||||
|
Type.getArgumentTypes(methodNode.desc).size - 1
|
||||||
|
else
|
||||||
|
Type.getArgumentTypes(methodNode.desc).size
|
||||||
|
replaceFakeContinuationsWithRealOnes(methodNode, continuationIndex)
|
||||||
|
|
||||||
dropSuspensionMarkers(methodNode, suspensionPoints)
|
dropSuspensionMarkers(methodNode, suspensionPoints)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -99,6 +103,8 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
// Actual max stack might be increased during the previous phases
|
// Actual max stack might be increased during the previous phases
|
||||||
updateMaxStack(methodNode)
|
updateMaxStack(methodNode)
|
||||||
|
|
||||||
|
replaceFakeContinuationsWithRealOnes(methodNode, continuationIndex)
|
||||||
|
|
||||||
// Remove unreachable suspension points
|
// Remove unreachable suspension points
|
||||||
// If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling)
|
// If we don't do this, then relevant frames will not be analyzed, that is unexpected from point of view of next steps (e.g. variable spilling)
|
||||||
removeUnreachableSuspensionPointsAndExitPoints(methodNode, suspensionPoints)
|
removeUnreachableSuspensionPointsAndExitPoints(methodNode, suspensionPoints)
|
||||||
@@ -153,6 +159,14 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
methodNode.removeEmptyCatchBlocks()
|
methodNode.removeEmptyCatchBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) {
|
||||||
|
val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker)
|
||||||
|
for (fakeContinuation in fakeContinuations) {
|
||||||
|
methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous))
|
||||||
|
methodNode.instructions.set(fakeContinuation, VarInsnNode(Opcodes.ALOAD, continuationIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createInsnForReadingLabel() =
|
private fun createInsnForReadingLabel() =
|
||||||
if (isForNamedFunction)
|
if (isForNamedFunction)
|
||||||
MethodInsnNode(
|
MethodInsnNode(
|
||||||
|
|||||||
+9
-7
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.common.*
|
|||||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationMarker
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.codegen.topLevelClassAsmType
|
import org.jetbrains.kotlin.codegen.topLevelClassAsmType
|
||||||
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
|
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
|
||||||
@@ -336,20 +337,21 @@ fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor):
|
|||||||
Opcodes.ASM5,
|
Opcodes.ASM5,
|
||||||
Opcodes.ACC_STATIC,
|
Opcodes.ACC_STATIC,
|
||||||
"fake",
|
"fake",
|
||||||
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE, CONTINUATION_ASM_TYPE),
|
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE),
|
||||||
null, null
|
null, null
|
||||||
)
|
)
|
||||||
|
|
||||||
node.visitVarInsn(Opcodes.ALOAD, 0)
|
val v = InstructionAdapter(node)
|
||||||
|
|
||||||
node.visitMethodInsn(
|
addFakeContinuationMarker(v)
|
||||||
Opcodes.INVOKEINTERFACE,
|
|
||||||
|
v.invokeinterface(
|
||||||
CONTINUATION_ASM_TYPE.internalName,
|
CONTINUATION_ASM_TYPE.internalName,
|
||||||
GET_CONTEXT_METHOD_NAME,
|
GET_CONTEXT_METHOD_NAME,
|
||||||
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE),
|
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE)
|
||||||
true
|
|
||||||
)
|
)
|
||||||
node.visitInsn(Opcodes.ARETURN)
|
v.areturn(COROUTINE_CONTEXT_ASM_TYPE)
|
||||||
|
|
||||||
node.visitMaxs(1, 1)
|
node.visitMaxs(1, 1)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|||||||
@@ -265,9 +265,6 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
|||||||
addInlineMarker(codegen.v, true)
|
addInlineMarker(codegen.v, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionDescriptor.isBuiltInCoroutineContext())
|
|
||||||
invocationParamBuilder.addNextValueParameter(CONTINUATION_ASM_TYPE, false, continuationValue(), 0)
|
|
||||||
|
|
||||||
val parameters = invocationParamBuilder.buildParameters()
|
val parameters = invocationParamBuilder.buildParameters()
|
||||||
|
|
||||||
val info = RootInliningContext(
|
val info = RootInliningContext(
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ private const val INLINE_MARKER_FINALLY_START = "finallyStart"
|
|||||||
private const val INLINE_MARKER_FINALLY_END = "finallyEnd"
|
private const val INLINE_MARKER_FINALLY_END = "finallyEnd"
|
||||||
private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0
|
private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0
|
||||||
private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1
|
private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1
|
||||||
private const val INLINE_MARKET_RETURNS_UNIT = 2
|
private const val INLINE_MARKER_RETURNS_UNIT = 2
|
||||||
|
private const val INLINE_MARKER_FAKE_CONTINUATION = 3
|
||||||
private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId)
|
private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId)
|
||||||
|
|
||||||
internal fun getMethodNode(
|
internal fun getMethodNode(
|
||||||
@@ -411,7 +412,7 @@ internal fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addReturnsUnitMarker(v: InstructionAdapter) {
|
private fun addReturnsUnitMarker(v: InstructionAdapter) {
|
||||||
v.iconst(INLINE_MARKET_RETURNS_UNIT)
|
v.iconst(INLINE_MARKER_RETURNS_UNIT)
|
||||||
v.visitMethodInsn(
|
v.visitMethodInsn(
|
||||||
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
|
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
|
||||||
"mark",
|
"mark",
|
||||||
@@ -419,9 +420,26 @@ private fun addReturnsUnitMarker(v: InstructionAdapter) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* There are contexts when the continuation does not yet exist, for example, in inline lambdas, which are going to
|
||||||
|
* be inlined into suspendable functions.
|
||||||
|
* In such cases we just generate the marker which is going to be replaced with real continuation on generating state machine.
|
||||||
|
* See [CoroutineTransformerMethodVisitor] for more info.
|
||||||
|
*/
|
||||||
|
internal fun addFakeContinuationMarker(v: InstructionAdapter) {
|
||||||
|
v.iconst(INLINE_MARKER_FAKE_CONTINUATION)
|
||||||
|
v.invokestatic(
|
||||||
|
INLINE_MARKER_CLASS_NAME,
|
||||||
|
"mark",
|
||||||
|
"(I)V", false
|
||||||
|
)
|
||||||
|
v.aconst(null)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun isBeforeSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_BEFORE_SUSPEND_ID)
|
internal fun isBeforeSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_BEFORE_SUSPEND_ID)
|
||||||
internal fun isAfterSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_AFTER_SUSPEND_ID)
|
internal fun isAfterSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_AFTER_SUSPEND_ID)
|
||||||
internal fun isReturnsUnitMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKET_RETURNS_UNIT)
|
internal fun isReturnsUnitMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_RETURNS_UNIT)
|
||||||
|
internal fun isFakeContinuationMarker(insn: AbstractInsnNode) =
|
||||||
|
insn.previous != null && isSuspendMarker(insn.previous, INLINE_MARKER_FAKE_CONTINUATION) && insn.opcode == Opcodes.ACONST_NULL
|
||||||
|
|
||||||
private fun isSuspendMarker(insn: AbstractInsnNode, id: Int) =
|
private fun isSuspendMarker(insn: AbstractInsnNode, id: Int) =
|
||||||
isInlineMarker(insn, "mark") && insn.previous.intConstant == id
|
isInlineMarker(insn, "mark") && insn.previous.intConstant == id
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
import helpers.*
|
||||||
|
import kotlin.coroutines.experimental.*
|
||||||
|
|
||||||
|
inline fun inlinedLambda(block: () -> Unit) {
|
||||||
|
return block()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun useInlined(): Boolean {
|
||||||
|
inlinedLambda { return coroutineContext === EmptyCoroutineContext }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(c: suspend () -> Unit) {
|
||||||
|
c.startCoroutine(EmptyContinuation)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var res = "FAIL 1"
|
||||||
|
builder {
|
||||||
|
if (useInlined())
|
||||||
|
res = "OK"
|
||||||
|
}
|
||||||
|
if (res != "OK") return res
|
||||||
|
res = "FAIL 2"
|
||||||
|
builder {
|
||||||
|
inlinedLambda {
|
||||||
|
res = if (coroutineContext === EmptyCoroutineContext) "OK" else "FAIL 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
Generated
+6
@@ -5679,6 +5679,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("coroutineContextInInlinedLambda.kt")
|
||||||
|
public void testCoroutineContextInInlinedLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("coroutineToString.kt")
|
@TestMetadata("coroutineToString.kt")
|
||||||
public void testCoroutineToString() throws Exception {
|
public void testCoroutineToString() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
||||||
|
|||||||
+6
@@ -5679,6 +5679,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("coroutineContextInInlinedLambda.kt")
|
||||||
|
public void testCoroutineContextInInlinedLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("coroutineToString.kt")
|
@TestMetadata("coroutineToString.kt")
|
||||||
public void testCoroutineToString() throws Exception {
|
public void testCoroutineToString() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
||||||
|
|||||||
+6
@@ -5679,6 +5679,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("coroutineContextInInlinedLambda.kt")
|
||||||
|
public void testCoroutineContextInInlinedLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("coroutineToString.kt")
|
@TestMetadata("coroutineToString.kt")
|
||||||
public void testCoroutineToString() throws Exception {
|
public void testCoroutineToString() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineToString.kt");
|
||||||
|
|||||||
+6
@@ -6285,6 +6285,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("coroutineContextInInlinedLambda.kt")
|
||||||
|
public void testCoroutineContextInInlinedLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("createCoroutineSafe.kt")
|
@TestMetadata("createCoroutineSafe.kt")
|
||||||
public void testCreateCoroutineSafe() throws Exception {
|
public void testCreateCoroutineSafe() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt");
|
||||||
|
|||||||
+5
-4
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo
|
|||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
|
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
||||||
@@ -39,13 +40,13 @@ object CoroutineContextFIF : FunctionIntrinsicFactory {
|
|||||||
|
|
||||||
object Intrinsic : FunctionIntrinsic() {
|
object Intrinsic : FunctionIntrinsic() {
|
||||||
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
|
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
|
||||||
val continuation = context.continuationParameterDescriptor ?: error("coroutineContext called from outside of coroutine")
|
val continuation = TranslationUtils.getEnclosingContinuationParameter(context)
|
||||||
val continuationDescriptor = continuation.type.constructor.declarationDescriptor as? ClassDescriptor ?: error("Continuation is not a class")
|
val continuationDescriptor = continuation.type.constructor.declarationDescriptor as? ClassDescriptor ?: error("Continuation is not a class")
|
||||||
val contContext = DescriptorUtils.getPropertyByName(continuationDescriptor.unsubstitutedMemberScope, Name.identifier("context"))
|
val contContext = DescriptorUtils.getPropertyByName(continuationDescriptor.unsubstitutedMemberScope, Name.identifier("context"))
|
||||||
val res = JsNameRef(
|
val res = JsNameRef(
|
||||||
context.getNameForDescriptor(contContext),
|
context.getNameForDescriptor(contContext),
|
||||||
if (context.declarationDescriptor?.isCoroutineLambda == true) JsAstUtils.stateMachineReceiver()
|
if (context.declarationDescriptor?.isCoroutineLambda == true) JsAstUtils.stateMachineReceiver()
|
||||||
else context.getNameForDescriptor(continuation).makeRef()
|
else TranslationUtils.translateContinuationArgument(context)
|
||||||
)
|
)
|
||||||
res.inlineStrategy = InlineStrategy.NOT_INLINE
|
res.inlineStrategy = InlineStrategy.NOT_INLINE
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ public final class TranslationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static VariableDescriptor getEnclosingContinuationParameter(@NotNull TranslationContext context) {
|
public static VariableDescriptor getEnclosingContinuationParameter(@NotNull TranslationContext context) {
|
||||||
VariableDescriptor result = context.getContinuationParameterDescriptor();
|
VariableDescriptor result = context.getContinuationParameterDescriptor();
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
assert context.getParent() != null;
|
assert context.getParent() != null;
|
||||||
|
|||||||
Reference in New Issue
Block a user