diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt index 15c079c957f..c29c1388f78 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -118,7 +118,7 @@ class AnonymousObjectTransformer( // save time. The result is effectively the same anyway. val debugInfoToParse = if (inliningContext.isInliningLambda) null else debugInfo sourceMap = SMAPParser.parseOrCreateDefault(debugInfoToParse, sourceInfo, oldObjectType.internalName, 1, 65535) - sourceMapper = SourceMapper(sourceMap.sourceInfo.takeIf { debugInfoToParse?.isEmpty() == false }) + sourceMapper = SourceMapper(sourceMap.fileMappings.firstOrNull { it.name == sourceInfo }?.toSourceInfo()) val allCapturedParamBuilder = ParametersBuilder.newBuilder() val constructorParamBuilder = ParametersBuilder.newBuilder() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index c298456997a..38d12ebb8bd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -614,7 +614,7 @@ abstract class InlineCodegen( callableDescriptor.name.asString() == "arrayOf" -> IntrinsicArrayConstructors.generateArrayOfBody(asmMethod) else -> throw UnsupportedOperationException("Not an array intrinsic: $callableDescriptor") } - return SMAPAndMethodNode(body, SMAP(listOf(FileMapping.SKIP))) + return SMAPAndMethodNode(body, SMAP(listOf())) } assert(callableDescriptor is DescriptorWithContainerSource) { "Not a deserialized function or proper: $callableDescriptor" } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt index b2f58ad81ed..d9884eaf3fd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt @@ -17,13 +17,12 @@ const val KOTLIN_DEBUG_STRATA_NAME = "KotlinDebug" object SMAPBuilder { fun build(fileMappings: List, backwardsCompatibleSyntax: Boolean): String? { - val realMappings = fileMappings.filter { it.lineMappings.isNotEmpty() && it != FileMapping.SKIP } - if (realMappings.isEmpty()) { + if (fileMappings.isEmpty()) { return null } val debugMappings = linkedMapOf, FileMapping>() - for (fileMapping in realMappings) { + for (fileMapping in fileMappings) { for ((_, dest, range, callSite) in fileMapping.lineMappings) { callSite?.let { (line, file, path) -> debugMappings.getOrPut(file to path) { FileMapping(file, path) }.mapNewInterval(line, dest, range) @@ -35,7 +34,7 @@ object SMAPBuilder { // 1. they require *E between strata, which is not correct syntax according to JSR-045; // 2. in KotlinDebug, they use `1#2,3:4` to mean "map lines 4..6 to line 1 of #2", when in reality (and in // the non-debug stratum) this maps lines 4..6 to lines 1..3. The correct syntax is `1#2:4,3`. - val defaultStrata = realMappings.toSMAP(KOTLIN_STRATA_NAME, mapToFirstLine = false) + val defaultStrata = fileMappings.toSMAP(KOTLIN_STRATA_NAME, mapToFirstLine = false) val debugStrata = debugMappings.values.toSMAP(KOTLIN_DEBUG_STRATA_NAME, mapToFirstLine = !backwardsCompatibleSyntax) if (backwardsCompatibleSyntax && defaultStrata.isNotEmpty() && debugStrata.isNotEmpty()) { return "SMAP\n${fileMappings[0].name}\n$KOTLIN_STRATA_NAME\n$defaultStrata${SMAP.END}\n$debugStrata${SMAP.END}\n" @@ -72,14 +71,8 @@ class SourceMapCopier(val parent: SourceMapper, private val smap: SMAP, val call return mappedLineNumber } - val range = lastVisitedRange?.takeIf { lineNumber in it } - ?: smap.findRange(lineNumber) - ?: error("Can't find range to map line $lineNumber in ${smap.sourceInfo.source}: ${smap.sourceInfo.pathOrCleanFQN}") - val inlineSource = range.mapDestToSource(lineNumber) - if (inlineSource.line < 0) { - return -1 - } - val newLineNumber = parent.mapLineNumber(inlineSource, callSite ?: range.callSite) + val range = lastVisitedRange?.takeIf { lineNumber in it } ?: smap.findRange(lineNumber) ?: return -1 + val newLineNumber = parent.mapLineNumber(range.mapDestToSource(lineNumber), callSite ?: range.callSite) visitedLines.put(lineNumber, newLineNumber) lastVisitedRange = range return newLineNumber @@ -113,13 +106,7 @@ class SourceMapper(val sourceInfo: SourceInfo?) { } } -private fun FileMapping.toSourceInfo(): SourceInfo = - SourceInfo(name, path, lineMappings.fold(0) { result, mapping -> max(result, mapping.source + mapping.range - 1) }) - class SMAP(val fileMappings: List) { - val sourceInfo: SourceInfo - get() = fileMappings.firstOrNull()?.toSourceInfo() ?: throw AssertionError("no files mapped") - // assuming disjoint line mappings (otherwise binary search can't be used anyway) private val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest } @@ -141,6 +128,9 @@ data class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) class FileMapping(val name: String, val path: String) { val lineMappings = arrayListOf() + fun toSourceInfo(): SourceInfo = + SourceInfo(name, path, lineMappings.fold(0) { result, mapping -> max(result, mapping.source + mapping.range - 1) }) + fun mapNewLineNumber(source: Int, currentIndex: Int, callSite: SourcePosition?): Int { // Save some space in the SMAP by reusing (or extending if it's the last one) the existing range. // TODO some *other* range may already cover `source`; probably too slow to check them all though. @@ -157,28 +147,20 @@ class FileMapping(val name: String, val path: String) { fun mapNewInterval(source: Int, dest: Int, range: Int, callSite: SourcePosition? = null): RangeMapping = RangeMapping(source, dest, range, callSite, parent = this).also { lineMappings.add(it) } - - companion object { - val SKIP = FileMapping("no-source-info", "no-source-info").apply { - mapNewInterval(-1, -1, 1) - } - } } data class RangeMapping(val source: Int, val dest: Int, var range: Int, val callSite: SourcePosition?, val parent: FileMapping) { - private val skip = source == -1 && dest == -1 - operator fun contains(destLine: Int): Boolean = - skip || (dest <= destLine && destLine < dest + range) + dest <= destLine && destLine < dest + range fun hasMappingForSource(sourceLine: Int): Boolean = - skip || (source <= sourceLine && sourceLine < source + range) + source <= sourceLine && sourceLine < source + range fun mapDestToSource(destLine: Int): SourcePosition = - SourcePosition(if (skip) -1 else source + (destLine - dest), parent.name, parent.path) + SourcePosition(source + (destLine - dest), parent.name, parent.path) fun mapSourceToDest(sourceLine: Int): Int = - if (skip) -1 else dest + (sourceLine - source) + dest + (sourceLine - source) } val RangeMapping.toRange: IntRange diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt index b3023bbaa43..f57270815e7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPParser.kt @@ -24,7 +24,7 @@ object SMAPParser { parseOrNull(mappingInfo)?.let { return it } } if (source == null || source.isEmpty() || methodStartLine > methodEndLine) { - return SMAP(listOf(FileMapping.SKIP)) + return SMAP(listOf()) } val mapping = FileMapping(source, path).apply { mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index cfc3c90b79a..9d467b5d225 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -91,7 +91,7 @@ class FunctionCodegen( val notForInline = irFunction.suspendForInlineToOriginal() val smap = if (!context.state.classBuilderMode.generateBodies || flags.and(Opcodes.ACC_ABSTRACT) != 0 || irFunction.isExternal) { generateAnnotationDefaultValueIfNeeded(methodVisitor) - SMAP(listOf(FileMapping.SKIP)) + SMAP(listOf()) } else if (notForInline != null) { val (originalNode, smap) = classCodegen.generateMethodNode(notForInline) originalNode.accept(MethodBodyVisitor(methodVisitor))