From cbe0de61113d80059ef5c227f1b17e5bf1562b67 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Tue, 27 Jul 2021 03:28:21 +0200 Subject: [PATCH] Extend local variable ranges when it is safe. Otherwise, they will not be visible in debugger as soon as they become dead. #KT-47749 Fixed --- .../CoroutineTransformerMethodVisitor.kt | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) 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 c6af4cd084e..0690a25a64d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -1288,8 +1288,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: fun nextLabel(node: AbstractInsnNode?): LabelNode? { var current = node while (current != null) { - if (current is LabelNode) return current - current = current.next + if (current is LabelNode) return current as LabelNode + current = current!!.next } return null } @@ -1302,6 +1302,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: oldLvt += record } method.localVariables.clear() + + val oldLvtNodeToLatestNewLvtNode = mutableMapOf() // Skip `this` for suspend lambda val start = if (isForNamedFunction) 0 else 1 for (variableIndex in start until method.maxLocals) { @@ -1341,33 +1343,41 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: val endLabel = nextLabel(insn.next)?.let { min(lvtRecord.end, it) } ?: lvtRecord.end // startLabel can be null in case of parameters @Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start - val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) - method.localVariables.add(node) + + // Attempt to extend existing local variable node corresponding to the record in + // the original local variable table, if there is no back-edge + val recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord] + var recordExtended = false + if (recordToExtend != null) { + var hasBackEdgeOrStore = false + var current: AbstractInsnNode? = recordToExtend.end + while (current != null && current != endLabel) { + if (current is JumpInsnNode) { + if (method.instructions.indexOf((current as JumpInsnNode).label) < method.instructions.indexOf(current)) { + hasBackEdgeOrStore = true + break + } + } + if (current!!.isStoreOperation() && (current as VarInsnNode).`var` == recordToExtend.index) { + hasBackEdgeOrStore = true + break + } + current = current!!.next + } + if (!hasBackEdgeOrStore) { + recordToExtend.end = endLabel + recordExtended = true + } + } + if (!recordExtended) { + val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) + method.localVariables.add(node) + oldLvtNodeToLatestNewLvtNode[lvtRecord] = node + } } } } - // Merge consequent LVT records, otherwise, atomicfu goes crazy (KT-47749) - val toRemove = arrayListOf() - val sortedLVT = method.localVariables.sortedBy { method.instructions.indexOf(it.start) } - for (i in sortedLVT.indices) { - var endIndex = method.instructions.indexOf(sortedLVT[i].end) - for (j in (i + 1) until sortedLVT.size) { - val startIndex = method.instructions.indexOf(sortedLVT[j].start) - if (endIndex < startIndex) break - if (endIndex != startIndex || - sortedLVT[i].index != sortedLVT[j].index || - sortedLVT[i].name != sortedLVT[j].name || - sortedLVT[i].desc != sortedLVT[j].desc - ) continue - sortedLVT[i].end = sortedLVT[j].end - endIndex = method.instructions.indexOf(sortedLVT[j].end) - toRemove += sortedLVT[j] - } - } - - method.localVariables.removeAll(toRemove) - for (variable in oldLvt) { // $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables // For example, $continuation is used to create async stack trace