Generate correct linenumber for the debugger to see the return value
of suspend function. #KT-20322 Fixed
This commit is contained in:
+9
-32
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isPrimitiveUnboxing
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
@@ -25,8 +24,6 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
@@ -670,7 +667,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
val continuationLabel = LabelNode()
|
||||
val continuationLabelAfterLoadedResult = LabelNode()
|
||||
val suspendElementLineNumber = lineNumber
|
||||
val nextLineNumberNode = suspension.suspensionCallEnd.findNextOrNull { it is LineNumberNode } as? LineNumberNode
|
||||
var nextLineNumberNode = suspension.suspensionCallEnd.findNextOrNull { it is LineNumberNode } as? LineNumberNode
|
||||
with(methodNode.instructions) {
|
||||
// Save state
|
||||
insertBefore(
|
||||
@@ -720,34 +717,18 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
// 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.
|
||||
|
||||
// However, for primitives we generate it separately
|
||||
if (possibleTryCatchBlockStart.next?.isUnboxingSequence() != true) {
|
||||
if (possibleTryCatchBlockStart.next?.opcode?.let {
|
||||
it != Opcodes.ASTORE && it != Opcodes.CHECKCAST && it != Opcodes.INVOKESTATIC &&
|
||||
it != Opcodes.INVOKEVIRTUAL && it != Opcodes.INVOKEINTERFACE
|
||||
} == true
|
||||
) {
|
||||
visitLineNumber(afterSuspensionPointLineNumber, continuationLabelAfterLoadedResult.label)
|
||||
} else {
|
||||
// But keep the linenumber if the result of the call is is used afterwards
|
||||
nextLineNumberNode = null
|
||||
}
|
||||
})
|
||||
|
||||
// In code like val a = suspendReturnsInt()
|
||||
// `a` is coerced from Object to int, and coercion happens before scopeStart's mark:
|
||||
// LL
|
||||
// CHECKCAST java/lang/Number
|
||||
// INVOKEVIRTUAL java/lang/Number.intValue ()I
|
||||
// ISTORE N
|
||||
// LM
|
||||
// /* put lineNumber here */
|
||||
// ...
|
||||
// LOCALVARIABLE name LM LK N
|
||||
if (continuationLabelAfterLoadedResult.label.info.safeAs<AbstractInsnNode>()?.next?.isUnboxingSequence() == true) {
|
||||
// Find next label after unboxing and put linenumber there
|
||||
var current = (continuationLabelAfterLoadedResult.label.info as AbstractInsnNode).next
|
||||
while (current != null && current !is LabelNode) {
|
||||
current = current.next
|
||||
}
|
||||
if (current != null) {
|
||||
insert(current, LineNumberNode(afterSuspensionPointLineNumber, current.cast()))
|
||||
}
|
||||
}
|
||||
|
||||
if (nextLineNumberNode != null) {
|
||||
// Remove the line number instruction as it now covered with line number on continuation label.
|
||||
// If both linenumber are present in bytecode, debugger will trigger line specific events twice.
|
||||
@@ -758,10 +739,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
return continuationLabel
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isUnboxingSequence(): Boolean {
|
||||
return opcode == Opcodes.CHECKCAST && next?.isPrimitiveUnboxing() == true
|
||||
}
|
||||
|
||||
// It's necessary to preserve some sensible invariants like there should be no jump in the middle of try-catch-block
|
||||
// Also it's important that spilled variables are being restored outside of TCB,
|
||||
// otherwise they would be treated as uninitialized within catch-block while they can be used there
|
||||
|
||||
Reference in New Issue
Block a user