JVM: make SMAP utilities a little prettier

This commit is contained in:
Sonya Valchuk
2024-03-08 11:10:55 +00:00
committed by Space Cloud
parent 64d4b61628
commit fc992274bb
4 changed files with 38 additions and 42 deletions
@@ -54,7 +54,7 @@ class AnonymousObjectTransformer(
val methodsToTransform = ArrayList<MethodNode>()
val metadataReader = ReadKotlinClassHeaderAnnotationVisitor()
lateinit var superClassName: String
var sourceInfo: String? = null
var debugFileName: String? = null
var debugInfo: String? = null
var debugMetadataAnnotation: AnnotationNode? = null
@@ -120,7 +120,7 @@ class AnonymousObjectTransformer(
}
override fun visitSource(source: String, debug: String?) {
sourceInfo = source
debugFileName = source
debugInfo = debug
}
@@ -133,10 +133,9 @@ class AnonymousObjectTransformer(
// When regenerating objects in inline lambdas, keep the old SMAP and don't remap the line numbers to
// save time. The result is effectively the same anyway.
val debugInfoToParse = if (inliningContext.isInliningLambda) null else debugInfo
val (firstLine, lastLine) = (methodsToTransform + listOfNotNull(constructor)).lineNumberRange()
sourceMap = SMAPParser.parseOrCreateDefault(debugInfoToParse, sourceInfo, oldObjectType.internalName, firstLine, lastLine)
sourceMapper = SourceMapper(sourceMap.fileMappings.firstOrNull { it.name == sourceInfo }?.toSourceInfo())
sourceMap = debugInfo.takeIf { !inliningContext.isInliningLambda }?.let(SMAPParser::parseOrNull)
?: SMAP.identityMapping(debugFileName, oldObjectType.internalName, methodsToTransform + listOfNotNull(constructor))
sourceMapper = SourceMapper(debugFileName, sourceMap)
val allCapturedParamBuilder = ParametersBuilder.newBuilder()
val constructorParamBuilder = ParametersBuilder.newBuilder()
@@ -201,8 +200,8 @@ class AnonymousObjectTransformer(
if (GENERATE_SMAP && !inliningContext.isInliningLambda) {
classBuilder.visitSMAP(sourceMapper, !state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
} else if (sourceInfo != null) {
classBuilder.visitSource(sourceInfo!!, debugInfo)
} else if (debugFileName != null) {
classBuilder.visitSource(debugFileName!!, debugInfo)
}
innerClassNodes.forEach { node ->
@@ -7,9 +7,12 @@ package org.jetbrains.kotlin.codegen.inline
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap
import org.jetbrains.kotlin.codegen.SourceInfo
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.util.*
import kotlin.math.max
import kotlin.math.min
const val KOTLIN_STRATA_NAME = "Kotlin"
const val KOTLIN_DEBUG_STRATA_NAME = "KotlinDebug"
@@ -78,6 +81,8 @@ class SourceMapCopier(val parent: SourceMapper, private val smap: SMAP, val call
data class SourcePosition(val line: Int, val file: String, val path: String)
class SourceMapper(val sourceInfo: SourceInfo?) {
constructor(name: String?, original: SMAP) : this(original.fileMappings.firstOrNull { it.name == name }?.toSourceInfo())
private var maxUsedValue: Int = sourceInfo?.linesInFile ?: 0
private var fileMappings: LinkedHashMap<Pair<String, String>, FileMapping> = linkedMapOf()
@@ -134,6 +139,24 @@ class SMAP(val fileMappings: List<FileMapping>) {
const val LINE_SECTION = "*L"
const val STRATA_SECTION = "*S"
const val END = "*E"
// Create a mapping that simply maps a range of a file to itself, which is equivalent to having no mapping at all.
// The contract is: if `smap` is the return value of this method, then `SourceMapCopier(SourceMapper(name, smap), smap)`
// will not change any line numbers in any of the methods passed as an argument.
fun identityMapping(name: String?, path: String, methods: Collection<MethodNode>): SMAP {
if (name.isNullOrEmpty()) return SMAP(emptyList())
var start = 0
var end = 0
for (node in methods) {
for (insn in node.instructions.asSequence()) {
if (insn !is LineNumberNode) continue
start = min(start, insn.line)
end = max(end, insn.line + 1)
}
}
if (start >= end) return SMAP(emptyList())
return SMAP(listOf(FileMapping(name, path).apply { mapNewInterval(start, start, end - start) }))
}
}
}
@@ -17,23 +17,11 @@
package org.jetbrains.kotlin.codegen.inline
object SMAPParser {
/*null smap means that there is no any debug info in file (e.g. sourceName)*/
@JvmStatic
fun parseOrCreateDefault(mappingInfo: String?, source: String?, path: String, methodStartLine: Int, methodEndLine: Int): SMAP {
if (mappingInfo != null && mappingInfo.isNotEmpty()) {
parseOrNull(mappingInfo)?.let { return it }
}
if (source == null || source.isEmpty() || methodStartLine > methodEndLine) {
return SMAP(listOf())
}
val mapping = FileMapping(source, path).apply {
mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)
}
return SMAP(listOf(mapping))
}
fun parseOrNull(mappingInfo: String): SMAP? =
parseStratum(mappingInfo, KOTLIN_STRATA_NAME, parseStratum(mappingInfo, KOTLIN_DEBUG_STRATA_NAME, null))
if (mappingInfo.isNotEmpty())
parseStratum(mappingInfo, KOTLIN_STRATA_NAME, parseStratum(mappingInfo, KOTLIN_DEBUG_STRATA_NAME, null))
else
null
private class SMAPTokenizer(private val text: String, private val headerString: String) : Iterator<String> {
@@ -50,7 +50,6 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.math.max
import kotlin.math.min
const val GENERATE_SMAP = true
const val NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function"
@@ -104,29 +103,16 @@ internal inline fun getMethodNode(classData: ByteArray, classType: Type, crossin
}
}, ClassReader.SKIP_FRAMES or if (GENERATE_SMAP) 0 else ClassReader.SKIP_DEBUG)
return node?.let{
val (first, last) = listOfNotNull(it).lineNumberRange()
SMAPAndMethodNode(it, SMAPParser.parseOrCreateDefault(sourceMap, sourceFile, classType.internalName, first, last))
return node?.let {
val parsedSourceMap = sourceMap?.let(SMAPParser::parseOrNull)
?: SMAP.identityMapping(sourceFile, classType.internalName, listOfNotNull(it))
SMAPAndMethodNode(it, parsedSourceMap)
}
}
internal fun getMethodNode(classData: ByteArray, classType: Type, method: Method): SMAPAndMethodNode? =
getMethodNode(classData, classType) { it == method }
internal fun Collection<MethodNode>.lineNumberRange(): Pair<Int, Int> {
var minLine = Int.MAX_VALUE
var maxLine = Int.MIN_VALUE
for (node in this) {
for (insn in node.instructions.asSequence()) {
if (insn is LineNumberNode) {
minLine = min(minLine, insn.line)
maxLine = max(maxLine, insn.line)
}
}
}
return minLine to maxLine
}
internal fun findVirtualFile(state: GenerationState, classId: ClassId): VirtualFile? {
return VirtualFileFinder.getInstance(state.project, state.module).findVirtualFileWithHeader(classId)
}