Generate LVT entry for continuation

#KT-25688: Fixed
This commit is contained in:
Ilmir Usmanov
2018-07-24 17:09:43 +03:00
parent dec307799a
commit 82f64b9b14
3 changed files with 49 additions and 3 deletions
@@ -73,6 +73,7 @@ class CoroutineTransformerMethodVisitor(
// First instruction in the method node may change in case of named function
val actualCoroutineStart = methodNode.instructions.first
var startLabelNode: LabelNode? = null
if (isForNamedFunction) {
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
@@ -87,7 +88,7 @@ class CoroutineTransformerMethodVisitor(
}
continuationIndex = methodNode.maxLocals++
prepareMethodNodePreludeForNamedFunction(methodNode)
startLabelNode = prepareMethodNodePreludeForNamedFunction(methodNode)
} else {
ReturnUnitMethodTransformer.cleanUpReturnsUnitMarkers(methodNode, ReturnUnitMethodTransformer.findReturnsUnitMarks(methodNode))
}
@@ -113,9 +114,9 @@ class CoroutineTransformerMethodVisitor(
transformCallAndReturnContinuationLabel(it.index + 1, it.value, methodNode, suspendMarkerVarIndex)
}
val defaultLabel = LabelNode()
methodNode.instructions.apply {
val startLabel = LabelNode()
val defaultLabel = LabelNode()
val tableSwitchLabel = LabelNode()
// tableswitch(this.label)
@@ -152,6 +153,27 @@ class CoroutineTransformerMethodVisitor(
dropSuspensionMarkers(methodNode, suspensionPoints)
methodNode.removeEmptyCatchBlocks()
if (isForNamedFunction) {
addContinuationToLvt(
methodNode,
startLabelNode.sure { "start label has not been initialized during prelude generation" },
defaultLabel
)
}
}
private fun addContinuationToLvt(methodNode: MethodNode, startLabel: LabelNode, endLabel: LabelNode) {
methodNode.localVariables.add(
LocalVariableNode(
"\$continuation",
languageVersionSettings.continuationAsmType().descriptor,
null,
startLabel,
endLabel,
continuationIndex
)
)
}
private fun removeFakeContinuationConstructorCall(methodNode: MethodNode) {
@@ -209,7 +231,7 @@ class CoroutineTransformerMethodVisitor(
)
}
private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode) {
private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode): LabelNode {
val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName)
val continuationArgumentIndex = getLastParameterIndex(methodNode.desc, methodNode.access)
methodNode.instructions.asSequence().filterIsInstance<VarInsnNode>().forEach {
@@ -218,6 +240,8 @@ class CoroutineTransformerMethodVisitor(
it.`var` = continuationIndex
}
val startLabel = LabelNode()
methodNode.instructions.insert(withInstructionAdapter {
val createStateInstance = Label()
val afterCoroutineStateCreated = Label()
@@ -237,6 +261,7 @@ class CoroutineTransformerMethodVisitor(
// `doResume` just before calling the suspend function (see kotlin.coroutines.experimental.jvm.internal.CoroutineImplForNamedFunction).
// So, if it's set we're in continuation.
visitLabel(startLabel.label)
visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex)
instanceOf(objectTypeForState)
ifeq(createStateInstance)
@@ -288,6 +313,7 @@ class CoroutineTransformerMethodVisitor(
visitVarInsn(Opcodes.ASTORE, exceptionIndex)
}
})
return startLabel
}
private fun removeUnreachableSuspensionPointsAndExitPoints(methodNode: MethodNode, suspensionPoints: MutableList<SuspensionPoint>) {
@@ -0,0 +1,15 @@
// LANGUAGE_VERSION: 1.3
suspend fun dummy() {}
suspend fun tailCall() {
dummy()
}
suspend fun stateMachine() {
dummy()
dummy()
}
// for tail-calls there is no need to add continuation to LVT
// 1 LOCALVARIABLE \$continuation Lkotlin/coroutines/Continuation; L.* 2
@@ -1114,6 +1114,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines/debug"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("continuationInLvt.kt")
public void testContinuationInLvt() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/coroutines/debug/continuationInLvt.kt");
}
@TestMetadata("probeCoroutineSuspended.kt")
public void testProbeCoroutineSuspended() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/coroutines/debug/probeCoroutineSuspended.kt");