JVM: separate the two kinds of source mappers
* a writing source mapper has `mapLineNumber(line, file, class)` that inserts a new SMAP entry and returns a fake line number from it; * a copying source mapper has `mapLineNumber(line)` that uses an existing SMAP to resolve the line number and call the former method on a different source mapper; * those two types are disjoint.
This commit is contained in:
@@ -13,9 +13,9 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||||
import org.jetbrains.kotlin.codegen.context.*;
|
import org.jetbrains.kotlin.codegen.context.*;
|
||||||
import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper;
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
|
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
|
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.SourceMapper;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||||
@@ -88,7 +88,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
|||||||
private NameGenerator inlineNameGenerator;
|
private NameGenerator inlineNameGenerator;
|
||||||
private boolean jvmAssertFieldGenerated;
|
private boolean jvmAssertFieldGenerated;
|
||||||
|
|
||||||
private DefaultSourceMapper sourceMapper;
|
private SourceMapper sourceMapper;
|
||||||
|
|
||||||
public MemberCodegen(
|
public MemberCodegen(
|
||||||
@NotNull GenerationState state,
|
@NotNull GenerationState state,
|
||||||
@@ -722,10 +722,10 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public DefaultSourceMapper getOrCreateSourceMapper() {
|
public SourceMapper getOrCreateSourceMapper() {
|
||||||
if (sourceMapper == null) {
|
if (sourceMapper == null) {
|
||||||
// note: this is used in InlineCodegen and the element is always physical (KtElement) there
|
// note: this is used in InlineCodegen and the element is always physical (KtElement) there
|
||||||
sourceMapper = new DefaultSourceMapper(SourceInfo.Companion.createInfo((KtElement)element, getClassName()));
|
sourceMapper = new SourceMapper(SourceInfo.Companion.createInfo((KtElement)element, getClassName()));
|
||||||
}
|
}
|
||||||
return sourceMapper;
|
return sourceMapper;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-20
@@ -42,8 +42,7 @@ class AnonymousObjectTransformer(
|
|||||||
private val fieldNames = hashMapOf<String, MutableList<String>>()
|
private val fieldNames = hashMapOf<String, MutableList<String>>()
|
||||||
|
|
||||||
private var constructor: MethodNode? = null
|
private var constructor: MethodNode? = null
|
||||||
private var sourceInfo: String? = null
|
private lateinit var sourceMap: SMAP
|
||||||
private var debugInfo: String? = null
|
|
||||||
private lateinit var sourceMapper: SourceMapper
|
private lateinit var sourceMapper: SourceMapper
|
||||||
private val languageVersionSettings = inliningContext.state.languageVersionSettings
|
private val languageVersionSettings = inliningContext.state.languageVersionSettings
|
||||||
|
|
||||||
@@ -56,6 +55,8 @@ class AnonymousObjectTransformer(
|
|||||||
val methodsToTransform = ArrayList<MethodNode>()
|
val methodsToTransform = ArrayList<MethodNode>()
|
||||||
val metadataReader = ReadKotlinClassHeaderAnnotationVisitor()
|
val metadataReader = ReadKotlinClassHeaderAnnotationVisitor()
|
||||||
lateinit var superClassName: String
|
lateinit var superClassName: String
|
||||||
|
var sourceInfo: String? = null
|
||||||
|
var debugInfo: String? = null
|
||||||
|
|
||||||
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
|
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
|
||||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||||
@@ -113,22 +114,13 @@ class AnonymousObjectTransformer(
|
|||||||
override fun visitEnd() {}
|
override fun visitEnd() {}
|
||||||
}, ClassReader.SKIP_FRAMES)
|
}, ClassReader.SKIP_FRAMES)
|
||||||
|
|
||||||
if (!inliningContext.isInliningLambda) {
|
// When regenerating objects in lambdas, we have to pass the SMAP straight through and keep
|
||||||
sourceMapper = if (debugInfo != null && !debugInfo!!.isEmpty()) {
|
// the original line numbers because SMAPParser does not parse call site markers.
|
||||||
SourceMapper.createFromSmap(SMAPParser.parse(debugInfo!!))
|
val debugInfoToParse = if (inliningContext.isInliningLambda) null else debugInfo
|
||||||
} else {
|
sourceMap = SMAPParser.parseOrCreateDefault(debugInfoToParse, sourceInfo, oldObjectType.internalName, 1, 65535)
|
||||||
//seems we can't do any clever mapping cause we don't know any about original class name
|
// TODO source info should be for the file into which the function is being inlined, else we cannot
|
||||||
IdenticalSourceMapper
|
// generate correct call site markers for lambdas inlined into the object
|
||||||
}
|
sourceMapper = SourceMapper(sourceMap.sourceInfo.takeIf { debugInfoToParse?.isEmpty() == false })
|
||||||
if (sourceInfo != null && !GENERATE_SMAP) {
|
|
||||||
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (sourceInfo != null) {
|
|
||||||
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
|
||||||
}
|
|
||||||
sourceMapper = IdenticalSourceMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
val allCapturedParamBuilder = ParametersBuilder.newBuilder()
|
val allCapturedParamBuilder = ParametersBuilder.newBuilder()
|
||||||
val constructorParamBuilder = ParametersBuilder.newBuilder()
|
val constructorParamBuilder = ParametersBuilder.newBuilder()
|
||||||
@@ -183,7 +175,11 @@ class AnonymousObjectTransformer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classBuilder.visitSMAP(sourceMapper, !state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
if (GENERATE_SMAP && !inliningContext.isInliningLambda) {
|
||||||
|
classBuilder.visitSMAP(sourceMapper, !state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||||
|
} else if (sourceInfo != null) {
|
||||||
|
classBuilder.visitSource(sourceInfo!!, debugInfo)
|
||||||
|
}
|
||||||
|
|
||||||
val visitor = classBuilder.visitor
|
val visitor = classBuilder.visitor
|
||||||
innerClassNodes.forEach { node ->
|
innerClassNodes.forEach { node ->
|
||||||
@@ -296,7 +292,7 @@ class AnonymousObjectTransformer(
|
|||||||
remapper,
|
remapper,
|
||||||
isSameModule,
|
isSameModule,
|
||||||
"Transformer for " + transformationInfo.oldClassName,
|
"Transformer for " + transformationInfo.oldClassName,
|
||||||
sourceMapper,
|
SourceMapCopier(sourceMapper, sourceMap, keepCallSites = inliningContext.isInliningLambda),
|
||||||
InlineCallSiteInfo(
|
InlineCallSiteInfo(
|
||||||
transformationInfo.oldClassName,
|
transformationInfo.oldClassName,
|
||||||
sourceNode.name,
|
sourceNode.name,
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.GENERATE_
|
|||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getLoadStoreArgSize;
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getLoadStoreArgSize;
|
||||||
|
|
||||||
public class InlineAdapter extends InstructionAdapter {
|
public class InlineAdapter extends InstructionAdapter {
|
||||||
private final SourceMapper sourceMapper;
|
private final SourceMapCopier sourceMapper;
|
||||||
private final List<CatchBlock> blocks = new ArrayList<>();
|
private final List<CatchBlock> blocks = new ArrayList<>();
|
||||||
|
|
||||||
private boolean isLambdaInlining = false;
|
private boolean isLambdaInlining = false;
|
||||||
private int nextLocalIndex = 0;
|
private int nextLocalIndex = 0;
|
||||||
private int nextLocalIndexBeforeInline = -1;
|
private int nextLocalIndexBeforeInline = -1;
|
||||||
|
|
||||||
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
|
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapCopier sourceMapper) {
|
||||||
super(Opcodes.API_VERSION, mv);
|
super(Opcodes.API_VERSION, mv);
|
||||||
this.nextLocalIndex = localsSize;
|
this.nextLocalIndex = localsSize;
|
||||||
this.sourceMapper = sourceMapper;
|
this.sourceMapper = sourceMapper;
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
|||||||
val inliner = MethodInliner(
|
val inliner = MethodInliner(
|
||||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||||
"Method inlining " + sourceCompiler.callElementText,
|
"Method inlining " + sourceCompiler.callElementText,
|
||||||
NestedSourceMapper(defaultSourceMapper, nodeAndSmap.classSMAP), info.callSiteInfo,
|
SourceMapCopier(defaultSourceMapper, nodeAndSmap.classSMAP), info.callSiteInfo,
|
||||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||||
!isInlinedToInlineFunInKotlinRuntime()
|
!isInlinedToInlineFunInKotlinRuntime()
|
||||||
) //with captured
|
) //with captured
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ class InlineCodegenForDefaultBody(
|
|||||||
val nodeAndSmap = InlineCodegen.createInlineMethodNode(
|
val nodeAndSmap = InlineCodegen.createInlineMethodNode(
|
||||||
function, methodOwner, jvmSignature, callDefault, null, codegen.typeSystem, state, sourceCompilerForInline
|
function, methodOwner, jvmSignature, callDefault, null, codegen.typeSystem, state, sourceCompilerForInline
|
||||||
)
|
)
|
||||||
val childSourceMapper = NestedSourceMapper(sourceMapper, nodeAndSmap.classSMAP, sameFile = true)
|
val childSourceMapper = SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP, keepCallSites = true)
|
||||||
|
|
||||||
val node = nodeAndSmap.node
|
val node = nodeAndSmap.node
|
||||||
val transformedMethod = MethodNode(
|
val transformedMethod = MethodNode(
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class MethodInliner(
|
|||||||
private val nodeRemapper: FieldRemapper,
|
private val nodeRemapper: FieldRemapper,
|
||||||
private val isSameModule: Boolean,
|
private val isSameModule: Boolean,
|
||||||
private val errorPrefix: String,
|
private val errorPrefix: String,
|
||||||
private val sourceMapper: SourceMapper,
|
private val sourceMapper: SourceMapCopier,
|
||||||
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
||||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
|
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
|
||||||
private val shouldPreprocessApiVersionCalls: Boolean = false
|
private val shouldPreprocessApiVersionCalls: Boolean = false
|
||||||
@@ -288,20 +288,17 @@ class MethodInliner(
|
|||||||
)
|
)
|
||||||
|
|
||||||
setLambdaInlining(true)
|
setLambdaInlining(true)
|
||||||
val lambdaSMAP = info.node.classSMAP
|
|
||||||
|
|
||||||
val childSourceMapper =
|
|
||||||
if (inliningContext.classRegeneration && !inliningContext.isInliningLambda)
|
|
||||||
NestedSourceMapper(sourceMapper, lambdaSMAP)
|
|
||||||
else
|
|
||||||
NestedSourceMapper(sourceMapper.parent!!, lambdaSMAP, sameFile = info !is DefaultLambda)
|
|
||||||
|
|
||||||
|
// TODO we cannot keep call site markers when inlining lambdas into objects because AnonymousObjectTransformer
|
||||||
|
// uses the original's source info, thus KotlinDebug would point to the file with the inline function
|
||||||
|
// instead of the one with the call.
|
||||||
|
val sameFile = info !is DefaultLambda && (!inliningContext.classRegeneration || inliningContext.isInliningLambda)
|
||||||
val inliner = MethodInliner(
|
val inliner = MethodInliner(
|
||||||
info.node.node, lambdaParameters, inliningContext.subInlineLambda(info),
|
info.node.node, lambdaParameters, inliningContext.subInlineLambda(info),
|
||||||
newCapturedRemapper,
|
newCapturedRemapper,
|
||||||
if (info is DefaultLambda) isSameModule else true /*cause all nested objects in same module as lambda*/,
|
if (info is DefaultLambda) isSameModule else true /*cause all nested objects in same module as lambda*/,
|
||||||
"Lambda inlining " + info.lambdaClassType.internalName,
|
"Lambda inlining " + info.lambdaClassType.internalName,
|
||||||
childSourceMapper, inlineCallSiteInfo, null
|
SourceMapCopier(sourceMapper.parent, info.node.classSMAP, sameFile), inlineCallSiteInfo, null
|
||||||
)
|
)
|
||||||
|
|
||||||
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
||||||
|
|||||||
@@ -63,24 +63,15 @@ object SMAPBuilder {
|
|||||||
lineMappings.joinToString("") { it.toSMAP(id) }
|
lineMappings.joinToString("") { it.toSMAP(id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
class NestedSourceMapper(
|
class SourceMapCopier(val parent: SourceMapper, private val smap: SMAP, private val keepCallSites: Boolean = false) {
|
||||||
override val parent: SourceMapper, private val smap: SMAP, private val sameFile: Boolean = false
|
|
||||||
) : DefaultSourceMapper(smap.sourceInfo) {
|
|
||||||
|
|
||||||
private val visitedLines = TIntIntHashMap()
|
private val visitedLines = TIntIntHashMap()
|
||||||
|
|
||||||
private var lastVisitedRange: RangeMapping? = null
|
private var lastVisitedRange: RangeMapping? = null
|
||||||
|
|
||||||
override fun mapLineNumber(lineNumber: Int): Int {
|
fun mapLineNumber(lineNumber: Int): Int {
|
||||||
if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) {
|
if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) {
|
||||||
return lineNumber
|
return lineNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sameFile && lineNumber <= smap.sourceInfo.linesInFile) {
|
|
||||||
// assuming the parent source mapper is for the same file, this line number does not need remapping
|
|
||||||
return lineNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
val mappedLineNumber = visitedLines.get(lineNumber)
|
val mappedLineNumber = visitedLines.get(lineNumber)
|
||||||
if (mappedLineNumber > 0) {
|
if (mappedLineNumber > 0) {
|
||||||
return mappedLineNumber
|
return mappedLineNumber
|
||||||
@@ -88,99 +79,41 @@ class NestedSourceMapper(
|
|||||||
|
|
||||||
val range = lastVisitedRange?.takeIf { lineNumber in it }
|
val range = lastVisitedRange?.takeIf { lineNumber in it }
|
||||||
?: smap.findRange(lineNumber)
|
?: smap.findRange(lineNumber)
|
||||||
?: error("Can't find range to map line $lineNumber in ${sourceInfo.source}: ${sourceInfo.pathOrCleanFQN}")
|
?: error("Can't find range to map line $lineNumber in ${smap.sourceInfo.source}: ${smap.sourceInfo.pathOrCleanFQN}")
|
||||||
val sourceLineNumber = range.mapDestToSource(lineNumber)
|
val sourceLineNumber = range.mapDestToSource(lineNumber)
|
||||||
val newLineNumber = if (sameFile)
|
if (sourceLineNumber < 0) {
|
||||||
parent.mapLineNumber(sourceLineNumber, range.parent.name, range.parent.path, range.callSiteMarker)
|
return -1
|
||||||
else
|
|
||||||
parent.mapLineNumber(sourceLineNumber, range.parent.name, range.parent.path)
|
|
||||||
if (newLineNumber > 0) {
|
|
||||||
visitedLines.put(lineNumber, newLineNumber)
|
|
||||||
}
|
}
|
||||||
|
val callSiteMarker = if (keepCallSites) range.callSiteMarker else parent.callSiteMarker
|
||||||
|
val newLineNumber = parent.mapLineNumber(sourceLineNumber, range.parent.name, range.parent.path, callSiteMarker)
|
||||||
|
visitedLines.put(lineNumber, newLineNumber)
|
||||||
lastVisitedRange = range
|
lastVisitedRange = range
|
||||||
return newLineNumber
|
return newLineNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SourceMapper {
|
|
||||||
val resultMappings: List<FileMapping>
|
|
||||||
val parent: SourceMapper?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
fun mapLineNumber(lineNumber: Int): Int
|
|
||||||
|
|
||||||
fun mapLineNumber(source: Int, sourceName: String, sourcePath: String): Int =
|
|
||||||
mapLineNumber(source, sourceName, sourcePath, null)
|
|
||||||
|
|
||||||
fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun createFromSmap(smap: SMAP): SourceMapper {
|
|
||||||
return DefaultSourceMapper(smap.sourceInfo, smap.fileMappings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object IdenticalSourceMapper : SourceMapper {
|
|
||||||
override val resultMappings: List<FileMapping>
|
|
||||||
get() = emptyList()
|
|
||||||
|
|
||||||
override val parent: SourceMapper?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
override fun mapLineNumber(lineNumber: Int) = lineNumber
|
|
||||||
|
|
||||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int =
|
|
||||||
throw UnsupportedOperationException(
|
|
||||||
"IdenticalSourceMapper#mapLineNumber($source, $sourceName, $sourcePath)\n"
|
|
||||||
+ "This mapper should not encounter a line number out of range of the current file.\n"
|
|
||||||
+ "This indicates that SMAP generation is missed somewhere."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CallSiteMarker(val lineNumber: Int)
|
data class CallSiteMarker(val lineNumber: Int)
|
||||||
|
|
||||||
open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
|
class SourceMapper(val sourceInfo: SourceInfo?) {
|
||||||
private var maxUsedValue: Int = sourceInfo.linesInFile
|
private var maxUsedValue: Int = sourceInfo?.linesInFile ?: 0
|
||||||
private var fileMappings: LinkedHashMap<Pair<String, String>, FileMapping> = linkedMapOf()
|
private var fileMappings: LinkedHashMap<Pair<String, String>, FileMapping> = linkedMapOf()
|
||||||
|
|
||||||
var callSiteMarker: CallSiteMarker? = null
|
var callSiteMarker: CallSiteMarker? = null
|
||||||
|
|
||||||
override val resultMappings: List<FileMapping>
|
val resultMappings: List<FileMapping>
|
||||||
get() = fileMappings.values.toList()
|
get() = fileMappings.values.toList()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Explicitly map the file to itself.
|
sourceInfo?.let {
|
||||||
getOrRegisterNewSource(sourceInfo.source, sourceInfo.pathOrCleanFQN).mapNewInterval(1, 1, sourceInfo.linesInFile)
|
// Explicitly map the file to itself -- we'll probably need a lot of lines from it, so this will produce less ranges.
|
||||||
}
|
getOrRegisterNewSource(it.source, it.pathOrCleanFQN).mapNewInterval(1, 1, it.linesInFile)
|
||||||
|
|
||||||
constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>) : this(sourceInfo) {
|
|
||||||
// The first mapping is already created in the `init` block above.
|
|
||||||
fileMappings.asSequence().drop(1)
|
|
||||||
.forEach { fileMapping ->
|
|
||||||
val newFileMapping = getOrRegisterNewSource(fileMapping.name, fileMapping.path)
|
|
||||||
fileMapping.lineMappings.forEach {
|
|
||||||
newFileMapping.mapNewInterval(it.source, it.dest, it.range)
|
|
||||||
maxUsedValue = max(it.maxDest, maxUsedValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getOrRegisterNewSource(name: String, path: String): FileMapping {
|
|
||||||
return fileMappings.getOrPut(name to path) { FileMapping(name, path) }
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO maybe add assertion that linenumber contained in fileMappings
|
|
||||||
override fun mapLineNumber(lineNumber: Int): Int = lineNumber
|
|
||||||
|
|
||||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String): Int =
|
|
||||||
mapLineNumber(source, sourceName, sourcePath, callSiteMarker)
|
|
||||||
|
|
||||||
override fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int {
|
|
||||||
if (source < 0) {
|
|
||||||
//no source information, so just skip this linenumber
|
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrRegisterNewSource(name: String, path: String): FileMapping =
|
||||||
|
fileMappings.getOrPut(name to path) { FileMapping(name, path) }
|
||||||
|
|
||||||
|
fun mapLineNumber(source: Int, sourceName: String, sourcePath: String, callSiteMarker: CallSiteMarker?): Int {
|
||||||
val fileMapping = getOrRegisterNewSource(sourceName, sourcePath)
|
val fileMapping = getOrRegisterNewSource(sourceName, sourcePath)
|
||||||
val mappedLineIndex = fileMapping.mapNewLineNumber(source, maxUsedValue, callSiteMarker)
|
val mappedLineIndex = fileMapping.mapNewLineNumber(source, maxUsedValue, callSiteMarker)
|
||||||
maxUsedValue = max(maxUsedValue, mappedLineIndex)
|
maxUsedValue = max(maxUsedValue, mappedLineIndex)
|
||||||
@@ -188,13 +121,12 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<FileMapping>) {
|
class SMAP(val fileMappings: List<FileMapping>) {
|
||||||
val sourceInfo: SourceInfo = run {
|
val sourceInfo: SourceInfo
|
||||||
assert(fileMappings.isNotEmpty()) { "File Mappings shouldn't be empty" }
|
get() = fileMappings.firstOrNull()?.toSourceInfo() ?: throw AssertionError("no files mapped")
|
||||||
val defaultFile = fileMappings.first()
|
|
||||||
val defaultRange = defaultFile.lineMappings.first()
|
|
||||||
SourceInfo(defaultFile.name, defaultFile.path, defaultRange.source + defaultRange.range - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// assuming disjoint line mappings (otherwise binary search can't be used anyway)
|
// assuming disjoint line mappings (otherwise binary search can't be used anyway)
|
||||||
private val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest }
|
private val intervals = fileMappings.flatMap { it.lineMappings }.sortedBy { it.dest }
|
||||||
@@ -218,18 +150,19 @@ class FileMapping(val name: String, val path: String) {
|
|||||||
val lineMappings = arrayListOf<RangeMapping>()
|
val lineMappings = arrayListOf<RangeMapping>()
|
||||||
|
|
||||||
fun mapNewLineNumber(source: Int, currentIndex: Int, callSiteMarker: CallSiteMarker?): Int {
|
fun mapNewLineNumber(source: Int, currentIndex: Int, callSiteMarker: CallSiteMarker?): Int {
|
||||||
var mapping = lineMappings.lastOrNull()
|
// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.
|
||||||
if (mapping != null && mapping.callSiteMarker == callSiteMarker &&
|
// TODO some *other* range may already cover `source`; probably too slow to check them all though.
|
||||||
(source - mapping.source) in 0 until mapping.range + (if (mapping.maxDest == currentIndex) 10 else 0)
|
// Maybe keep the list ordered by `source` and use binary search to locate the closest range on the left?
|
||||||
) {
|
val mapping = lineMappings.lastOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSiteMarker) }
|
||||||
// Save some space in the SMAP by reusing (or extending if it's the last one) the existing range.
|
?: lineMappings.firstOrNull()?.takeIf { it.canReuseFor(source, currentIndex, callSiteMarker) }
|
||||||
mapping.range = max(mapping.range, source - mapping.source + 1)
|
?: mapNewInterval(source, currentIndex + 1, 1, callSiteMarker)
|
||||||
} else {
|
mapping.range = max(mapping.range, source - mapping.source + 1)
|
||||||
mapping = mapNewInterval(source, currentIndex + 1, 1, callSiteMarker)
|
|
||||||
}
|
|
||||||
return mapping.mapSourceToDest(source)
|
return mapping.mapSourceToDest(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun RangeMapping.canReuseFor(newSource: Int, globalMaxDest: Int, newCallSite: CallSiteMarker?): Boolean =
|
||||||
|
callSiteMarker == newCallSite && (newSource - source) in 0 until range + (if (maxDest == globalMaxDest) 10 else 0)
|
||||||
|
|
||||||
fun mapNewInterval(source: Int, dest: Int, range: Int, callSiteMarker: CallSiteMarker? = null): RangeMapping =
|
fun mapNewInterval(source: Int, dest: Int, range: Int, callSiteMarker: CallSiteMarker? = null): RangeMapping =
|
||||||
RangeMapping(source, dest, range, callSiteMarker, parent = this).also { lineMappings.add(it) }
|
RangeMapping(source, dest, range, callSiteMarker, parent = this).also { lineMappings.add(it) }
|
||||||
|
|
||||||
|
|||||||
@@ -23,18 +23,12 @@ object SMAPParser {
|
|||||||
if (mappingInfo != null && mappingInfo.isNotEmpty()) {
|
if (mappingInfo != null && mappingInfo.isNotEmpty()) {
|
||||||
return parse(mappingInfo)
|
return parse(mappingInfo)
|
||||||
}
|
}
|
||||||
|
if (source == null || source.isEmpty() || methodStartLine > methodEndLine) {
|
||||||
val mapping =
|
return SMAP(listOf(FileMapping.SKIP))
|
||||||
if (source == null || source.isEmpty() || methodStartLine > methodEndLine)
|
}
|
||||||
FileMapping.SKIP
|
val mapping = FileMapping(source, path).apply {
|
||||||
else
|
mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)
|
||||||
FileMapping(source, path).apply {
|
}
|
||||||
if (methodStartLine <= methodEndLine) {
|
|
||||||
//one to one
|
|
||||||
mapNewInterval(methodStartLine, methodStartLine, methodEndLine - methodStartLine + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SMAP(listOf(mapping))
|
return SMAP(listOf(mapping))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-14
@@ -46,7 +46,7 @@ interface SourceCompilerForInline {
|
|||||||
|
|
||||||
val inlineCallSiteInfo: InlineCallSiteInfo
|
val inlineCallSiteInfo: InlineCallSiteInfo
|
||||||
|
|
||||||
val lazySourceMapper: DefaultSourceMapper
|
val lazySourceMapper: SourceMapper
|
||||||
|
|
||||||
fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode
|
fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode
|
||||||
|
|
||||||
@@ -212,18 +212,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
|||||||
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
|
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSMAPWithDefaultMapping(expression, parentCodegen.orCreateSourceMapper.resultMappings)
|
return SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun createSMAPWithDefaultMapping(
|
|
||||||
declaration: KtExpression,
|
|
||||||
mappings: List<FileMapping>
|
|
||||||
): SMAP {
|
|
||||||
val containingFile = declaration.containingFile
|
|
||||||
CodegenUtil.getLineNumberForElement(containingFile, true) ?: error("Couldn't extract line count in $containingFile")
|
|
||||||
|
|
||||||
return SMAP(mappings)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -310,7 +299,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
|||||||
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||||
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
|
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
|
||||||
)
|
)
|
||||||
createSMAPWithDefaultMapping(inliningFunction, parentCodegen.orCreateSourceMapper.resultMappings)
|
SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||||
} else {
|
} else {
|
||||||
generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
|
generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -148,7 +148,7 @@ abstract class ClassCodegen protected constructor(
|
|||||||
if (writeSourceMap) {
|
if (writeSourceMap) {
|
||||||
visitor.visitSMAP(smap, !context.state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
visitor.visitSMAP(smap, !context.state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||||
} else {
|
} else {
|
||||||
visitor.visitSource(smap.sourceInfo.source, null)
|
visitor.visitSource(smap.sourceInfo!!.source, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.done()
|
visitor.done()
|
||||||
@@ -269,7 +269,7 @@ abstract class ClassCodegen protected constructor(
|
|||||||
|
|
||||||
protected abstract fun bindMethodMetadata(method: IrFunction, signature: Method)
|
protected abstract fun bindMethodMetadata(method: IrFunction, signature: Method)
|
||||||
|
|
||||||
private fun generateMethod(method: IrFunction, classSMAP: DefaultSourceMapper) {
|
private fun generateMethod(method: IrFunction, classSMAP: SourceMapper) {
|
||||||
if (method.isFakeOverride) {
|
if (method.isFakeOverride) {
|
||||||
jvmSignatureClashDetector.trackFakeOverrideMethod(method)
|
jvmSignatureClashDetector.trackFakeOverrideMethod(method)
|
||||||
return
|
return
|
||||||
@@ -281,7 +281,7 @@ abstract class ClassCodegen protected constructor(
|
|||||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||||
)
|
)
|
||||||
val mv = with(node) { visitor.newMethod(method.OtherOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
val mv = with(node) { visitor.newMethod(method.OtherOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||||
val smapCopier = NestedSourceMapper(classSMAP, smap, sameFile = true)
|
val smapCopier = SourceMapCopier(classSMAP, smap, keepCallSites = true)
|
||||||
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
|
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
|
||||||
override fun visitLineNumber(line: Int, start: Label) =
|
override fun visitLineNumber(line: Int, start: Label) =
|
||||||
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
|
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
|
||||||
|
|||||||
+1
-1
@@ -106,7 +106,7 @@ class ExpressionCodegen(
|
|||||||
val mv: InstructionAdapter,
|
val mv: InstructionAdapter,
|
||||||
val classCodegen: ClassCodegen,
|
val classCodegen: ClassCodegen,
|
||||||
val inlinedInto: ExpressionCodegen?,
|
val inlinedInto: ExpressionCodegen?,
|
||||||
val smap: DefaultSourceMapper
|
val smap: SourceMapper
|
||||||
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
) : IrElementVisitor<PromisedValue, BlockInfo>, BaseExpressionCodegen {
|
||||||
|
|
||||||
var finallyDepth = 0
|
var finallyDepth = 0
|
||||||
|
|||||||
+1
-1
@@ -85,7 +85,7 @@ class IrSourceCompilerForInline(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val lazySourceMapper: DefaultSourceMapper
|
override val lazySourceMapper: SourceMapper
|
||||||
get() = codegen.smap.also { codegen.classCodegen.writeSourceMap = true }
|
get() = codegen.smap.also { codegen.classCodegen.writeSourceMap = true }
|
||||||
|
|
||||||
override fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode =
|
override fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode =
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
|
||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper
|
import org.jetbrains.kotlin.codegen.inline.SourceMapper
|
||||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||||
@@ -80,14 +80,14 @@ val IrDeclaration.fileParent: IrFile
|
|||||||
internal val DeclarationDescriptorWithSource.psiElement: PsiElement?
|
internal val DeclarationDescriptorWithSource.psiElement: PsiElement?
|
||||||
get() = (source as? PsiSourceElement)?.psi
|
get() = (source as? PsiSourceElement)?.psi
|
||||||
|
|
||||||
fun JvmBackendContext.getSourceMapper(declaration: IrClass): DefaultSourceMapper {
|
fun JvmBackendContext.getSourceMapper(declaration: IrClass): SourceMapper {
|
||||||
val sourceManager = this.psiSourceManager
|
val sourceManager = this.psiSourceManager
|
||||||
val fileEntry = sourceManager.getFileEntry(declaration.fileParent)
|
val fileEntry = sourceManager.getFileEntry(declaration.fileParent)
|
||||||
// NOTE: apparently inliner requires the source range to cover the
|
// NOTE: apparently inliner requires the source range to cover the
|
||||||
// whole file the class is declared in rather than the class only.
|
// whole file the class is declared in rather than the class only.
|
||||||
// TODO: revise
|
// TODO: revise
|
||||||
val endLineNumber = fileEntry?.getSourceRangeInfo(0, fileEntry.maxOffset)?.endLineNumber ?: 0
|
val endLineNumber = fileEntry?.getSourceRangeInfo(0, fileEntry.maxOffset)?.endLineNumber ?: 0
|
||||||
return DefaultSourceMapper(
|
return SourceMapper(
|
||||||
SourceInfo.createInfoForIr(
|
SourceInfo.createInfoForIr(
|
||||||
endLineNumber + 1,
|
endLineNumber + 1,
|
||||||
typeMapper.mapClass(declaration).internalName,
|
typeMapper.mapClass(declaration).internalName,
|
||||||
|
|||||||
+4
-3
@@ -74,7 +74,8 @@ test/_1Kt
|
|||||||
_2Kt
|
_2Kt
|
||||||
*L
|
*L
|
||||||
1#1,18:1
|
1#1,18:1
|
||||||
14#2,2:19
|
14#2:19
|
||||||
10#2:21
|
10#2:20
|
||||||
7#3:22
|
15#2:22
|
||||||
|
7#3:21
|
||||||
*E
|
*E
|
||||||
+3
-2
@@ -90,6 +90,7 @@ test/_1Kt
|
|||||||
_2Kt
|
_2Kt
|
||||||
*L
|
*L
|
||||||
1#1,19:1
|
1#1,19:1
|
||||||
7#2,3:20
|
7#2,2:20
|
||||||
9#3,2:23
|
9#2:24
|
||||||
|
9#3,2:22
|
||||||
*E
|
*E
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/so
|
|||||||
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:14:13: error: a suspension point at SourceKt$test$2.invokeSuspend(source.kt:17) is inside a critical section
|
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:14:13: error: a suspension point at SourceKt$test$2.invokeSuspend(source.kt:17) is inside a critical section
|
||||||
builder {
|
builder {
|
||||||
^
|
^
|
||||||
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:24:9: error: a suspension point at SourceKt$test$3$invokeSuspend$$inlined$withCrossinline$2.invokeSuspend(source.kt:62) is inside a critical section
|
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:24:9: error: a suspension point at SourceKt$test$3$invokeSuspend$$inlined$withCrossinline$2.invokeSuspend(source.kt:60) is inside a critical section
|
||||||
withCrossinline {
|
withCrossinline {
|
||||||
^
|
^
|
||||||
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:50:34: error: a suspension point at SourceKt$withCrossinline$c$1.invokeSuspend(source.kt:52) is inside a critical section
|
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:50:34: error: a suspension point at SourceKt$withCrossinline$c$1.invokeSuspend(source.kt:52) is inside a critical section
|
||||||
|
|||||||
Reference in New Issue
Block a user