JVM: map synthetic line numbers through the SMAP
This is necessary so that IDEA does not ignore the line number.
This commit is contained in:
@@ -278,7 +278,7 @@ class MethodInliner(
|
|||||||
visitInsn(Opcodes.NOP)
|
visitInsn(Opcodes.NOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info)
|
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info, sourceMapper.parent)
|
||||||
addInlineMarker(this, true)
|
addInlineMarker(this, true)
|
||||||
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
||||||
|
|
||||||
|
|||||||
@@ -62,19 +62,22 @@ class SourceMapCopier(val parent: SourceMapper, private val smap: SMAP, val call
|
|||||||
private var lastVisitedRange: RangeMapping? = null
|
private var lastVisitedRange: RangeMapping? = null
|
||||||
|
|
||||||
fun mapLineNumber(lineNumber: Int): Int {
|
fun mapLineNumber(lineNumber: Int): Int {
|
||||||
if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) {
|
|
||||||
return lineNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
val mappedLineNumber = visitedLines.get(lineNumber)
|
val mappedLineNumber = visitedLines.get(lineNumber)
|
||||||
if (mappedLineNumber > 0) {
|
if (mappedLineNumber > 0) {
|
||||||
return mappedLineNumber
|
return mappedLineNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
val range = lastVisitedRange?.takeIf { lineNumber in it } ?: smap.findRange(lineNumber) ?: return -1
|
val newLineNumber = if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) {
|
||||||
val newLineNumber = parent.mapLineNumber(range.mapDestToSource(lineNumber), callSite ?: range.callSite)
|
// This is a compatibility hack for reading bytecode generated by 1.4-M1. Once the bootstrap
|
||||||
|
// compiler is updated to a newer version, it can be removed (as newer bytecode explicitly lists
|
||||||
|
// this range in the SMAP).
|
||||||
|
parent.mapSyntheticLineNumber(lineNumber)
|
||||||
|
} else {
|
||||||
|
val range = lastVisitedRange?.takeIf { lineNumber in it } ?: smap.findRange(lineNumber) ?: return -1
|
||||||
|
lastVisitedRange = range
|
||||||
|
parent.mapLineNumber(range.mapDestToSource(lineNumber), callSite ?: range.callSite)
|
||||||
|
}
|
||||||
visitedLines.put(lineNumber, newLineNumber)
|
visitedLines.put(lineNumber, newLineNumber)
|
||||||
lastVisitedRange = range
|
|
||||||
return newLineNumber
|
return newLineNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,6 +110,11 @@ class SourceMapper(val sourceInfo: SourceInfo?) {
|
|||||||
maxUsedValue = max(maxUsedValue, mappedLineIndex)
|
maxUsedValue = max(maxUsedValue, mappedLineIndex)
|
||||||
return mappedLineIndex
|
return mappedLineIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mapSyntheticLineNumber(id: Int): Int {
|
||||||
|
assert(id in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) { "$id is not a synthetic line number" }
|
||||||
|
return mapLineNumber(SourcePosition(id, "fake.kt", "kotlin/jvm/internal/FakeKt"), null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SMAP(val fileMappings: List<FileMapping>) {
|
class SMAP(val fileMappings: List<FileMapping>) {
|
||||||
|
|||||||
@@ -541,7 +541,7 @@ internal fun isThis0(name: String): Boolean = AsmUtil.CAPTURED_THIS_FIELD == nam
|
|||||||
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||||
private val callLineNumber = codegen.lastLineNumber
|
private val callLineNumber = codegen.lastLineNumber
|
||||||
|
|
||||||
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo) {
|
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo, smap: SourceMapper) {
|
||||||
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
||||||
if (callLineNumber >= 0 && firstLine == callLineNumber) {
|
if (callLineNumber >= 0 && firstLine == callLineNumber) {
|
||||||
// We want the debugger to be able to break both on the inline call itself, plus on each
|
// We want the debugger to be able to break both on the inline call itself, plus on each
|
||||||
@@ -552,7 +552,7 @@ class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
|||||||
// number that is remapped by the SMAP to a line that does not exist.
|
// number that is remapped by the SMAP to a line that does not exist.
|
||||||
val label = Label()
|
val label = Label()
|
||||||
mv.visitLabel(label)
|
mv.visitLabel(label)
|
||||||
mv.visitLineNumber(JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER, label)
|
mv.visitLineNumber(smap.mapSyntheticLineNumber(JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER), label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ fun box(): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FILE: 2.smap
|
// FILE: 2.smap
|
||||||
|
// See KT-23064 for the problem, InlineOnlySmapSkipper for an explanation, and
|
||||||
|
// `stdlibInlineOnlyOneLine.kt` for the case where something should actually happen.
|
||||||
|
|||||||
+14
@@ -14,3 +14,17 @@ fun box(): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FILE: 2.smap
|
// FILE: 2.smap
|
||||||
|
// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation.
|
||||||
|
SMAP
|
||||||
|
2.kt
|
||||||
|
Kotlin
|
||||||
|
*S Kotlin
|
||||||
|
*F
|
||||||
|
+ 1 2.kt
|
||||||
|
_2Kt
|
||||||
|
+ 2 fake.kt
|
||||||
|
kotlin/jvm/internal/FakeKt
|
||||||
|
*L
|
||||||
|
1#1,9:1
|
||||||
|
65100#2:10
|
||||||
|
*E
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
@kotlin.internal.InlineOnly
|
|
||||||
inline fun <T, R> T.myLet(block: (T) -> R) = block(this)
|
|
||||||
|
|
||||||
fun box(): String {
|
|
||||||
val k = "".myLet {
|
|
||||||
it + "K"
|
|
||||||
}
|
|
||||||
return "O".myLet(fun (it: String): String {
|
|
||||||
return it + k
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation.
|
|
||||||
// 0 LINENUMBER 65100
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
@kotlin.internal.InlineOnly
|
|
||||||
inline fun <T, R> T.myLet(block: (T) -> R) = block(this)
|
|
||||||
|
|
||||||
fun box(): String {
|
|
||||||
val k = "".myLet { it + "K" }
|
|
||||||
return "O".myLet(fun (it: String): String { return it + k })
|
|
||||||
}
|
|
||||||
|
|
||||||
// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation.
|
|
||||||
// 2 LINENUMBER 65100
|
|
||||||
@@ -10,6 +10,8 @@ fun box(): String {
|
|||||||
|
|
||||||
// LINENUMBERS
|
// LINENUMBERS
|
||||||
// test.kt:4
|
// test.kt:4
|
||||||
|
// fake.kt:65100
|
||||||
|
// test.kt:4
|
||||||
// test.kt:5
|
// test.kt:5
|
||||||
// test.kt:6
|
// test.kt:6
|
||||||
// test.kt:5
|
// test.kt:5
|
||||||
|
|||||||
@@ -3467,16 +3467,6 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("stdlibInlineOnly.kt")
|
|
||||||
public void testStdlibInlineOnly() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("stdlibInlineOnlyOneLine.kt")
|
|
||||||
public void testStdlibInlineOnlyOneLine() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("tryCatch.kt")
|
@TestMetadata("tryCatch.kt")
|
||||||
public void testTryCatch() throws Exception {
|
public void testTryCatch() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
||||||
|
|||||||
-10
@@ -3552,16 +3552,6 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("stdlibInlineOnly.kt")
|
|
||||||
public void testStdlibInlineOnly() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("stdlibInlineOnlyOneLine.kt")
|
|
||||||
public void testStdlibInlineOnlyOneLine() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("tryCatch.kt")
|
@TestMetadata("tryCatch.kt")
|
||||||
public void testTryCatch() throws Exception {
|
public void testTryCatch() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user