From 49baba017a7a03960cf9d72ef0916b6de771b3f9 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 7 Jun 2017 20:37:05 +0300 Subject: [PATCH] Allow to stop in suspend funciton on enter and before suspended return This commit is needed to implement "step over" action over suspended call. Debugger have to understand when function is going to be suspended and be able to set a breakpoint on function re-enter. #KT-18453 In Progress --- .../codegen/coroutines/CoroutineCodegen.kt | 1 + .../CoroutineTransformerMethodVisitor.kt | 26 ++++++++++++++++--- .../SuspendFunctionGenerationStrategy.kt | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index 25043ec231e..128212c8172 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -310,6 +310,7 @@ class CoroutineCodegenForLambda private constructor( return CoroutineTransformerMethodVisitor( mv, access, name, desc, null, null, obtainClassBuilderForCoroutineState = { v }, + element = element, containingClassInternalName = v.thisName, isForNamedFunction = false ) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index a19306caa8e..1055ca9aef0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.coroutines import com.intellij.util.containers.Stack +import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.StackValue @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.utils.sure @@ -50,6 +52,7 @@ class CoroutineTransformerMethodVisitor( private val containingClassInternalName: String, obtainClassBuilderForCoroutineState: () -> ClassBuilder, private val isForNamedFunction: Boolean, + private val element: KtElement, // It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls private val needDispatchReceiver: Boolean = false, // May differ from containingClassInternalName in case of DefaultImpls @@ -59,8 +62,8 @@ class CoroutineTransformerMethodVisitor( private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState) private val continuationIndex = if (isForNamedFunction) getLastParameterIndex(desc, access) else 0 - var dataIndex = if (isForNamedFunction) -1 else 1 - var exceptionIndex = if (isForNamedFunction) -1 else 2 + private var dataIndex = if (isForNamedFunction) -1 else 1 + private var exceptionIndex = if (isForNamedFunction) -1 else 2 override fun performTransformations(methodNode: MethodNode) { val suspensionPoints = collectSuspensionPoints(methodNode) @@ -107,10 +110,16 @@ class CoroutineTransformerMethodVisitor( val startLabel = LabelNode() val defaultLabel = LabelNode() val firstToInsertBefore = actualCoroutineStart + val tableSwitchLabel = LabelNode() + val lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0 + // tableswitch(this.label) insertBefore(firstToInsertBefore, insnListOf( *withInstructionAdapter { loadCoroutineSuspendedMarker() }.toArray(), + tableSwitchLabel, + // Allow debugger to stop on enter into suspend function + LineNumberNode(lineNumber, tableSwitchLabel), VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex), VarInsnNode(Opcodes.ALOAD, continuationIndex), createInsnForReadingLabel(), @@ -463,6 +472,8 @@ class CoroutineTransformerMethodVisitor( ): LabelNode { val continuationLabel = LabelNode() val continuationLabelAfterLoadedResult = LabelNode() + val suspendElementLineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0 + val nextLineNumberNode = suspension.suspensionCallEnd.findNextOrNull { it is LineNumberNode } as? LineNumberNode with(methodNode.instructions) { // Save state insertBefore(suspension.suspensionCallBegin, @@ -479,6 +490,10 @@ class CoroutineTransformerMethodVisitor( ifacmpne(continuationLabelAfterLoadedResult.label) // Exit + val returnLabel = LabelNode() + visitLabel(returnLabel.label) + // Special line number to stop in debugger before suspend return + visitLineNumber(suspendElementLineNumber, returnLabel.label) load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE) areturn(AsmTypes.OBJECT_TYPE) // Mark place for continuation @@ -503,6 +518,11 @@ class CoroutineTransformerMethodVisitor( load(dataIndex, AsmTypes.OBJECT_TYPE) visitLabel(continuationLabelAfterLoadedResult.label) + + // Extend next instruction linenumber. Can't use line number of suspension point here because both non-suspended execution + // and re-entering after suspension passes this label. + val afterSuspensionPointLineNumber = nextLineNumberNode?.line ?: suspendElementLineNumber + visitLineNumber(afterSuspensionPointLineNumber, continuationLabelAfterLoadedResult.label) }) } @@ -720,4 +740,4 @@ private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): } private val SAFE_OPCODES = - ((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet() + ((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet() \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt index a8bd64150f9..83c5786ac46 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt @@ -57,6 +57,7 @@ class SuspendFunctionGenerationStrategy( return CoroutineTransformerMethodVisitor( mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState, isForNamedFunction = true, + element = declaration, needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null, internalNameForDispatchReceiver = containingClassInternalNameOrNull() ).also {