JVM: make SMAP utilities a little prettier
This commit is contained in:
committed by
Space Cloud
parent
64d4b61628
commit
fc992274bb
+7
-8
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user