JVM: fix SMAP range extension logic

If `mapLineNumber` was called in non-monotonic order, e.g. N then N+2
then N+1, the first two calls created a range that spans [N; N+2] but
the third call did not reuse it.
This commit is contained in:
pyos
2020-03-25 14:45:37 +01:00
committed by max-kammerer
parent b4d7dfd02c
commit 9d21800d8f
25 changed files with 284 additions and 108 deletions
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
import gnu.trove.TIntIntHashMap import gnu.trove.TIntIntHashMap
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.SourceInfo import org.jetbrains.kotlin.codegen.SourceInfo
import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.END import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.END
import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.FILE_SECTION import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.FILE_SECTION
@@ -169,35 +168,25 @@ object IdenticalSourceMapper : SourceMapper {
} }
} }
class CallSiteMarker(val lineNumber: Int) data class CallSiteMarker(val lineNumber: Int)
open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper { open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
private var maxUsedValue: Int = sourceInfo.linesInFile private var maxUsedValue: Int = sourceInfo.linesInFile
private var lastMappedWithChanges: RawFileMapping? = null private var fileMappings: LinkedHashMap<Pair<String, String>, RawFileMapping> = linkedMapOf()
private var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf()
protected val origin: RawFileMapping
var callSiteMarker: CallSiteMarker? = null var callSiteMarker: CallSiteMarker? = null
set(value) {
lastMappedWithChanges = null
field = value
}
override val resultMappings: List<FileMapping> override val resultMappings: List<FileMapping>
get() = fileMappings.values.map { it.toFileMapping() } get() = fileMappings.values.map { it.toFileMapping() }
init { init {
val name = sourceInfo.source // Explicitly map the file to itself.
val path = sourceInfo.pathOrCleanFQN getOrRegisterNewSource(sourceInfo.source, sourceInfo.pathOrCleanFQN).mapNewInterval(1, 1, sourceInfo.linesInFile)
origin = RawFileMapping(name, path)
origin.initRange(1, sourceInfo.linesInFile)
fileMappings.put(createKey(name, path), origin)
} }
constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>) : this(sourceInfo) { constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>) : this(sourceInfo) {
// The first mapping is already created in the `init` block above.
fileMappings.asSequence().drop(1) fileMappings.asSequence().drop(1)
//default one mapped through sourceInfo
.forEach { fileMapping -> .forEach { fileMapping ->
val newFileMapping = getOrRegisterNewSource(fileMapping.name, fileMapping.path) val newFileMapping = getOrRegisterNewSource(fileMapping.name, fileMapping.path)
fileMapping.lineMappings.forEach { fileMapping.lineMappings.forEach {
@@ -207,10 +196,8 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
} }
} }
private fun createKey(name: String, path: String) = "$name#$path"
private fun getOrRegisterNewSource(name: String, path: String): RawFileMapping { private fun getOrRegisterNewSource(name: String, path: String): RawFileMapping {
return fileMappings.getOrPut(createKey(name, path)) { RawFileMapping(name, path) } return fileMappings.getOrPut(name to path) { RawFileMapping(name, path) }
} }
override fun mapLineNumber(lineNumber: Int): Int { override fun mapLineNumber(lineNumber: Int): Int {
@@ -227,15 +214,9 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
//no source information, so just skip this linenumber //no source information, so just skip this linenumber
return -1 return -1
} }
return createMapping(getOrRegisterNewSource(sourceName, sourcePath), source) val fileMapping = getOrRegisterNewSource(sourceName, sourcePath)
} val mappedLineIndex = fileMapping.mapNewLineNumber(source, maxUsedValue, callSiteMarker)
maxUsedValue = max(maxUsedValue, mappedLineIndex)
private fun createMapping(fileMapping: RawFileMapping, lineNumber: Int): Int {
val mappedLineIndex = fileMapping.mapNewLineNumber(lineNumber, maxUsedValue, lastMappedWithChanges == fileMapping, callSiteMarker)
if (mappedLineIndex > maxUsedValue) {
lastMappedWithChanges = fileMapping
maxUsedValue = mappedLineIndex
}
return mappedLineIndex return mappedLineIndex
} }
} }
@@ -270,8 +251,6 @@ data class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP)
class RawFileMapping(val name: String, val path: String) { class RawFileMapping(val name: String, val path: String) {
private val rangeMappings = arrayListOf<RangeMapping>() private val rangeMappings = arrayListOf<RangeMapping>()
private var lastMappedWithNewIndex = -1000
fun toFileMapping() = fun toFileMapping() =
FileMapping(name, path).apply { FileMapping(name, path).apply {
for (range in rangeMappings) { for (range in rangeMappings) {
@@ -279,39 +258,21 @@ class RawFileMapping(val name: String, val path: String) {
} }
} }
fun initRange(start: Int, end: Int) { fun mapNewLineNumber(source: Int, currentIndex: Int, callSiteMarker: CallSiteMarker?): Int {
assert(rangeMappings.isEmpty()) { "initRange should only be called for empty mapping" } var mapping = rangeMappings.lastOrNull()
rangeMappings.add(RangeMapping(start, start, end - start + 1)) if (mapping != null && mapping.callSiteMarker == callSiteMarker &&
lastMappedWithNewIndex = end (source - mapping.source) in 0 until mapping.range + (if (mapping.maxDest == currentIndex) 10 else 0)
} ) {
// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.
fun mapNewLineNumber(source: Int, currentIndex: Int, isLastMapped: Boolean, callSiteMarker: CallSiteMarker?): Int { mapping.range = max(mapping.range, source - mapping.source + 1)
val dest: Int
val rangeMapping: RangeMapping
if (rangeMappings.isNotEmpty() && isLastMapped && couldFoldInRange(lastMappedWithNewIndex, source)) {
rangeMapping = rangeMappings.last()
rangeMapping.range += source - lastMappedWithNewIndex
dest = rangeMapping.mapSourceToDest(source)
} else { } else {
dest = currentIndex + 1 mapping = mapNewInterval(source, currentIndex + 1, 1, callSiteMarker)
rangeMapping = RangeMapping(source, dest, callSiteMarker = callSiteMarker)
rangeMappings.add(rangeMapping)
} }
return mapping.mapSourceToDest(source)
lastMappedWithNewIndex = source
return dest
} }
fun mapNewInterval(source: Int, dest: Int, range: Int) { fun mapNewInterval(source: Int, dest: Int, range: Int, callSiteMarker: CallSiteMarker? = null): RangeMapping =
val rangeMapping = RangeMapping(source, dest, range) RangeMapping(source, dest, range, callSiteMarker).also { rangeMappings.add(it) }
rangeMappings.add(rangeMapping)
}
private fun couldFoldInRange(first: Int, second: Int): Boolean {
//TODO
val delta = second - first
return delta > 0 && delta <= 10
}
} }
open class FileMapping(val name: String, val path: String) { open class FileMapping(val name: String, val path: String) {
-1
View File
@@ -47,7 +47,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,26:1 1#1,26:1
19#1,6:27
*E *E
// FILE: 2.smap // FILE: 2.smap
@@ -25,7 +25,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,9:1 1#1,9:1
6#1:10
*E *E
SMAP SMAP
@@ -53,7 +53,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,39:1 1#1,39:1
35#1,2:40
*E *E
SMAP SMAP
@@ -55,7 +55,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,39:1 1#1,39:1
35#1,2:40
*E *E
SMAP SMAP
@@ -91,7 +91,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,75:1 1#1,75:1
71#1,2:76
*E *E
SMAP SMAP
@@ -54,7 +54,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,40:1 1#1,40:1
36#1,2:41
*E *E
SMAP SMAP
@@ -56,7 +56,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,40:1 1#1,40:1
36#1,2:41
*E *E
SMAP SMAP
@@ -54,7 +54,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,38:1 1#1,38:1
34#1,2:39
*E *E
SMAP SMAP
@@ -29,7 +29,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,13:1 1#1,13:1
8#1:14
*E *E
SMAP SMAP
@@ -27,7 +27,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,11:1 1#1,11:1
8#1:12
*E *E
SMAP SMAP
@@ -52,7 +52,6 @@ Kotlin
test/_1Kt test/_1Kt
*L *L
1#1,38:1 1#1,38:1
34#1,2:39
*E *E
SMAP SMAP
@@ -36,12 +36,10 @@ test/_1Kt
4#1:19 4#1:19
7#1:21 7#1:21
8#1:23 8#1:23
4#1,6:24 4#1,7:24
10#1:31
4#2:18 4#2:18
4#2:20 4#2:20
4#2:22 4#2:22
4#2:30
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
@@ -51,10 +49,8 @@ _2Kt
8#1:19 8#1:19
13#1:21 13#1:21
13#1:23 13#1:23
13#1,6:24 13#1,7:24
13#1:31
7#1:18 7#1:18
9#1:20 9#1:20
13#1:22 13#1:22
13#1:30
*E *E
@@ -0,0 +1,76 @@
// FILE: 1.kt
package test
inline fun f() {}
inline fun g() {}
inline fun h() {}
inline fun together() {
f() // new range 1.kt:N -> 1.kt:4
h() // new range 1.kt:N+1 -> 1.kt:6 because of different call site
g() // new range 1.kt:N+2 -> 1.kt:5 for the same reason
}
// FILE: 2.kt
import test.*
fun box(): String {
// 1. new range 2.kt:N -> 1.kt:9
// 2. new range 2.kt:N+1 -> 1.kt:4
// 3. extend to 2.kt:N+1..N+7 -> 1.kt:4..10 and use N+7 for 1.kt:10
// 4. use N+2 for 1.kt:5
// 5. extend to 2.kt:N+1..N+8 -> 1.kt:4..11 and use N+8 for 1.kt:11
// 6. use N+3 for 1.kt:6
// 7. extend to 2.kt:N+1..N+9 -> 1.kt:4..12 and use N+9 for 1.kt:12
// steps 4 and 6 *should not* create new ranges
together()
return "OK"
}
// FILE: 1.smap
SMAP
1.kt
Kotlin
*S Kotlin
*F
+ 1 1.kt
test/_1Kt
*L
1#1,14:1
4#1:15
6#1:16
5#1:17
*E
*S KotlinDebug
*F
+ 1 1.kt
test/_1Kt
*L
9#1:15
10#1:16
11#1:17
*E
// FILE: 2.smap
SMAP
2.kt
Kotlin
*S Kotlin
*F
+ 1 2.kt
_2Kt
+ 2 1.kt
test/_1Kt
*L
1#1,17:1
9#2:18
4#2,9:19
*E
*S KotlinDebug
*F
+ 1 2.kt
_2Kt
*L
13#1:18
13#1,9:19
*E
@@ -0,0 +1,100 @@
// FILE: 1.kt
package test
object A { inline fun f() {} }
object B { inline fun g() {} }
object C { inline fun h() {} }
object D {
inline fun together() {
A.f()
C.h()
B.g()
}
}
// FILE: 2.kt
import test.*
object X {
// Unlike `rangeFolding.kt`, the calls in `D.together` refer to different
// classes which are reflected in the SMAP, so they cannot be joined into
// a single range even in `X.foo`; neither can lines corresponding to
// `D.together` because they do not form an uninterrupted range.
fun foo() = D.together()
}
fun box(): String {
X.foo()
return "OK"
}
// FILE: 1.smap
SMAP
1.kt
Kotlin
*S Kotlin
*F
+ 1 1.kt
test/D
+ 2 1.kt
test/A
+ 3 1.kt
test/C
+ 4 1.kt
test/B
*L
1#1,16:1
4#2:17
6#3:18
5#4:19
*E
*S KotlinDebug
*F
+ 1 1.kt
test/D
*L
10#1:17
11#1:18
12#1:19
*E
// FILE: 2.smap
SMAP
2.kt
Kotlin
*S Kotlin
*F
+ 1 2.kt
X
+ 2 1.kt
test/D
+ 3 1.kt
test/A
+ 4 1.kt
test/C
+ 5 1.kt
test/B
*L
1#1,17:1
10#2:18
11#2:20
12#2:22
13#2:24
4#3:19
6#4:21
5#5:23
*E
*S KotlinDebug
*F
+ 1 2.kt
X
*L
9#1:18
9#1:20
9#1:22
9#1:24
9#1:19
9#1:21
9#1:23
*E
@@ -33,14 +33,12 @@ _2Kt
zzz/_1Kt zzz/_1Kt
*L *L
1#1,11:1 1#1,11:1
7#2:12 7#2,3:12
9#2:13
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
6#1:12 6#1,3:12
6#1:13
*E *E
+22 -26
View File
@@ -80,40 +80,36 @@ _2Kt
builders/_1Kt builders/_1Kt
*L *L
1#1,25:1 1#1,25:1
7#1,3:40 7#1,3:36
10#1:45 10#1:41
11#1,2:49 11#1,2:45
13#1:52 13#1:48
15#1:54 15#1:50
19#2:26 19#2:26
7#2,9:27 7#2,9:27
11#2,3:36 19#2:39
8#2:39 7#2:40
19#2:43 15#2:42
7#2:44 11#2,2:43
15#2:46 13#2:47
11#2,2:47 8#2:49
13#2:51
8#2:53
*E *E
*S KotlinDebug *S KotlinDebug
*F *F
+ 1 2.kt + 1 2.kt
_2Kt _2Kt
*L *L
20#1,3:40 20#1,3:36
20#1:45 20#1:41
20#1,2:49 20#1,2:45
20#1:52 20#1:48
20#1:54 20#1:50
9#1:26 9#1:26
9#1,9:27 9#1,9:27
9#1,3:36 20#1:39
9#1:39 20#1:40
20#1:43 20#1:42
20#1:44 20#1,2:43
20#1:46 20#1:47
20#1,2:47 20#1:49
20#1:51
20#1:53
*E *E
@@ -16,4 +16,4 @@ inline fun baz() {
fun nop() {} fun nop() {}
// 2 20 21 3 4 25 26 5 27 6 9 10 11 14 15 17 // 2 20 21 3 4 25 26 5 22 6 9 10 11 14 15 17
@@ -18,4 +18,4 @@ fun fail() : String {
throw AssertionError("fail") throw AssertionError("fail")
} }
// 2 22 3 2 23 5 6 24 5 25 7 10 14 18 // 2 22 3 2 22 5 6 23 5 24 7 10 14 18
@@ -3305,6 +3305,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");
@@ -3305,6 +3305,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");
@@ -3305,6 +3305,16 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");
@@ -3305,6 +3305,16 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");
@@ -2920,6 +2920,16 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");
@@ -2920,6 +2920,16 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt"); runTest("compiler/testData/codegen/boxInline/smap/oneFile.kt");
} }
@TestMetadata("rangeFolding.kt")
public void testRangeFolding() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFolding.kt");
}
@TestMetadata("rangeFoldingInClass.kt")
public void testRangeFoldingInClass() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/rangeFoldingInClass.kt");
}
@TestMetadata("smap.kt") @TestMetadata("smap.kt")
public void testSmap() throws Exception { public void testSmap() throws Exception {
runTest("compiler/testData/codegen/boxInline/smap/smap.kt"); runTest("compiler/testData/codegen/boxInline/smap/smap.kt");