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
This commit is contained in:
+35
-25
@@ -1288,8 +1288,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
|||||||
fun nextLabel(node: AbstractInsnNode?): LabelNode? {
|
fun nextLabel(node: AbstractInsnNode?): LabelNode? {
|
||||||
var current = node
|
var current = node
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
if (current is LabelNode) return current
|
if (current is LabelNode) return current as LabelNode
|
||||||
current = current.next
|
current = current!!.next
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -1302,6 +1302,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
|||||||
oldLvt += record
|
oldLvt += record
|
||||||
}
|
}
|
||||||
method.localVariables.clear()
|
method.localVariables.clear()
|
||||||
|
|
||||||
|
val oldLvtNodeToLatestNewLvtNode = mutableMapOf<LocalVariableNode, LocalVariableNode>()
|
||||||
// Skip `this` for suspend lambda
|
// Skip `this` for suspend lambda
|
||||||
val start = if (isForNamedFunction) 0 else 1
|
val start = if (isForNamedFunction) 0 else 1
|
||||||
for (variableIndex in start until method.maxLocals) {
|
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
|
val endLabel = nextLabel(insn.next)?.let { min(lvtRecord.end, it) } ?: lvtRecord.end
|
||||||
// startLabel can be null in case of parameters
|
// startLabel can be null in case of parameters
|
||||||
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
@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<LocalVariableNode>()
|
|
||||||
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) {
|
for (variable in oldLvt) {
|
||||||
// $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
|
// $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
|
// For example, $continuation is used to create async stack trace
|
||||||
|
|||||||
Reference in New Issue
Block a user