Old SourceMappers replaced with new ones

This commit is contained in:
Michael Bogdanov
2016-04-20 12:16:54 +03:00
parent 68f5e4d10e
commit bb5c24eae5
5 changed files with 81 additions and 284 deletions
@@ -24,8 +24,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.inline2.*;
import org.jetbrains.kotlin.codegen.inline2.DefaultSourceMapper;
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.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
@@ -27,9 +27,6 @@ import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment; import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
import org.jetbrains.kotlin.codegen.*; import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.inline2.*;
import org.jetbrains.kotlin.codegen.inline2.DefaultSourceMapper;
import org.jetbrains.kotlin.codegen.inline2.NestedSourceMapper;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
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;
@@ -22,9 +22,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.ClosureCodegen; import org.jetbrains.kotlin.codegen.ClosureCodegen;
import org.jetbrains.kotlin.codegen.StackValue; import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.kotlin.codegen.inline2.*;
import org.jetbrains.kotlin.codegen.inline2.InlineLambdaSourceMapper;
import org.jetbrains.kotlin.codegen.inline2.NestedSourceMapper;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer; import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
@@ -19,7 +19,6 @@ 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.ClassBuilder
import org.jetbrains.kotlin.codegen.SourceInfo import org.jetbrains.kotlin.codegen.SourceInfo
import org.jetbrains.kotlin.codegen.inline2.CallSiteMarker
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.MethodVisitor
import java.util.* import java.util.*
@@ -86,58 +85,56 @@ class SMAPBuilder(
} }
open class NestedSourceMapper( open class NestedSourceMapper(
parent: SourceMapper, val ranges: List<RangeMapping>, sourceInfo: SourceInfo override val parent: SourceMapper, val ranges: List<RangeMapping>, sourceInfo: SourceInfo
) : DefaultSourceMapper(sourceInfo, parent) { ) : DefaultSourceMapper(sourceInfo) {
val visitedLines = TIntIntHashMap()
var lastVisitedRange: RangeMapping? = null
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) { override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
val mappedLineNumber = visitedLines.get(lineNumber)
if (mappedLineNumber > 0) {
iv.visitLineNumber(mappedLineNumber, start)
} else {
val rangeForMapping = if (lastVisitedRange?.contains(lineNumber) ?: false) lastVisitedRange!! else findMappingIfExists(lineNumber)!!
val sourceLineNumber = rangeForMapping.mapDestToSource(lineNumber)
val visitLineNumber = parent.visitLineNumber(iv, start, sourceLineNumber, rangeForMapping.parent!!.name, rangeForMapping.parent!!.path)
if (visitLineNumber > 0) {
visitedLines.put(lineNumber, visitLineNumber)
}
lastVisitedRange = rangeForMapping
}
}
fun findMappingIfExists(lineNumber: Int): RangeMapping? {
val index = ranges.binarySearch(RangeMapping(lineNumber, lineNumber, 1), Comparator { val index = ranges.binarySearch(RangeMapping(lineNumber, lineNumber, 1), Comparator {
value, key -> value, key ->
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key) if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
}) })
if (index < 0) { return if (index < 0) null else ranges[index];
parent!!.visitSource(sourceInfo.source, sourceInfo.pathOrCleanFQN)
parent!!.visitLineNumber(iv, lineNumber, start)
}
else {
val rangeMapping = ranges[index]
parent!!.visitSource(rangeMapping.parent!!.name, rangeMapping.parent!!.path)
parent!!.visitLineNumber(iv, rangeMapping.mapDestToSource(lineNumber), start)
}
} }
} }
open class InlineLambdaSourceMapper( open class InlineLambdaSourceMapper(
parent: SourceMapper, smap: SMAPAndMethodNode parent: SourceMapper, smap: SMAPAndMethodNode
) : NestedSourceMapper(parent, smap.ranges, smap.classSMAP.sourceInfo) { ) : NestedSourceMapper(parent, smap.ranges, smap.classSMAP.sourceInfo) {
override fun visitSource(name: String, path: String) {
super.visitSource(name, path) init {
if (isOriginalVisited()) { assert(smap.ranges.isNotEmpty()) {
parent!!.visitOrigin() "Mapping ranges should be presented in inline lambda: ${smap.node}"
} }
} }
override fun visitOrigin() {
super.visitOrigin()
parent!!.visitOrigin()
}
private fun isOriginalVisited(): Boolean {
return lastVisited == origin
}
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) { override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
val index = ranges.binarySearch(RangeMapping(lineNumber, lineNumber, 1), Comparator { if (ranges.firstOrNull()?.contains(lineNumber) ?: false) {
value, key -> //don't remap origin lambda line numbers
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key) iv.visitLineNumber(lineNumber, start)
}) }
if (index >= 0) { else {
val mapping = ranges[index].parent!! super.visitLineNumber(iv, lineNumber, start)
if (mapping.path == origin.path && mapping.name == origin.name) {
parent!!.visitOrigin()
parent!!.visitLineNumber(iv, lineNumber, start)
return
}
} }
super.visitLineNumber(iv, lineNumber, start)
} }
} }
@@ -173,7 +170,7 @@ interface SourceMapper {
fun createFromSmap(smap: SMAP): SourceMapper { fun createFromSmap(smap: SMAP): SourceMapper {
val maxUsedIndex = smap.fileMappings.maxBy { it.lineMappings.maxBy { it.maxDest }!!.maxDest }!!.lineMappings.maxBy { it.maxDest }!!.maxDest val maxUsedIndex = smap.fileMappings.maxBy { it.lineMappings.maxBy { it.maxDest }!!.maxDest }!!.lineMappings.maxBy { it.maxDest }!!.maxDest
val sourceMapper = org.jetbrains.kotlin.codegen.inline2.DefaultSourceMapper(smap.sourceInfo, maxUsedIndex) val sourceMapper = DefaultSourceMapper(smap.sourceInfo, maxUsedIndex)
smap.fileMappings.asSequence() smap.fileMappings.asSequence()
//default one mapped through sourceInfo //default one mapped through sourceInfo
.filterNot { it == smap.default } .filterNot { it == smap.default }
@@ -205,8 +202,19 @@ object IdenticalSourceMapper : SourceMapper {
} }
} }
open class DefaultSourceMapper(val sourceInfo: SourceInfo, override val parent: SourceMapper?): SourceMapper { class CallSiteMarker(val lineNumber: Int)
protected var maxUsedValue: Int = sourceInfo.linesInFile
open class DefaultSourceMapper @JvmOverloads constructor(
val sourceInfo: SourceInfo,
protected var maxUsedValue: Int = sourceInfo.linesInFile
) : SourceMapper {
var callSiteMarker: CallSiteMarker? = null;
set(value) {
lastMappedWithChanges = null
field = value
}
var lastVisited: RawFileMapping? = null var lastVisited: RawFileMapping? = null
private var lastMappedWithChanges: RawFileMapping? = null private var lastMappedWithChanges: RawFileMapping? = null
private var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf() private var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf()
@@ -216,7 +224,7 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo, override val parent:
val name = sourceInfo.source val name = sourceInfo.source
val path = sourceInfo.pathOrCleanFQN val path = sourceInfo.pathOrCleanFQN
origin = RawFileMapping(name, path) origin = RawFileMapping(name, path)
origin.initRange(1, maxUsedValue) origin.initRange(1, sourceInfo.linesInFile)
fileMappings.put(createKey(name, path), origin) fileMappings.put(createKey(name, path), origin)
lastVisited = origin lastVisited = origin
} }
@@ -239,13 +247,28 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo, override val parent:
//no source information, so just skip this linenumber //no source information, so just skip this linenumber
return return
} }
val mappedLineIndex = createMapping(lineNumber) //TODO add assertion that mapping exists
//val sourceLineNumber = createMapping(lineNumberToMap)
val sourceLineNumber = lineNumber
assert(lineNumber == sourceLineNumber)
iv.visitLineNumber(lineNumber, start)
}
override fun visitLineNumber(iv: MethodVisitor, start: Label, source: Int, sourceName: String, sourcePath: String): Int {
if (source < 0) {
//no source information, so just skip this linenumber
return -1
}
visitSource(sourceName, sourcePath)
val mappedLineIndex = createMapping(source)
iv.visitLineNumber(mappedLineIndex, start) iv.visitLineNumber(mappedLineIndex, start)
return mappedLineIndex
} }
protected fun createMapping(lineNumber: Int): Int { protected fun createMapping(lineNumber: Int): Int {
val fileMapping = lastVisited!! val fileMapping = lastVisited!!
val mappedLineIndex = fileMapping.mapLine(lineNumber, maxUsedValue, lastMappedWithChanges == lastVisited) val mappedLineIndex = fileMapping.mapNewLineNumber(lineNumber, maxUsedValue, lastMappedWithChanges == lastVisited, callSiteMarker)
if (mappedLineIndex > maxUsedValue) { if (mappedLineIndex > maxUsedValue) {
lastMappedWithChanges = fileMapping lastMappedWithChanges = fileMapping
maxUsedValue = mappedLineIndex maxUsedValue = mappedLineIndex
@@ -277,8 +300,8 @@ class SMAP(val fileMappings: List<FileMapping>) {
} }
} }
class RawFileMapping(val name: String, val path: String) { class RawFileMapping(val name: String, val path: String) {
private val lineMappings = TIntIntHashMap()
private val rangeMappings = arrayListOf<RangeMapping>() private val rangeMappings = arrayListOf<RangeMapping>()
private var lastMappedWithNewIndex = -1000 private var lastMappedWithNewIndex = -1000
@@ -291,43 +314,32 @@ class RawFileMapping(val name: String, val path: String) {
} }
fun initRange(start: Int, end: Int) { fun initRange(start: Int, end: Int) {
assert(lineMappings.isEmpty) { "initRange should only be called for empty mapping" } assert(rangeMappings.isEmpty()) { "initRange should only be called for empty mapping" }
for (index in start..end) {
lineMappings.put(index, index)
}
rangeMappings.add(RangeMapping(start, start, end - start + 1)) rangeMappings.add(RangeMapping(start, start, end - start + 1))
lastMappedWithNewIndex = end lastMappedWithNewIndex = end
} }
fun mapLine(source: Int, currentIndex: Int, isLastMapped: Boolean): Int { fun mapNewLineNumber(source: Int, currentIndex: Int, isLastMapped: Boolean, callSiteMarker: CallSiteMarker?): Int {
var dest = lineMappings[source] val dest: Int
if (dest == 0) { // line numbers are 1-based, so 0 is ok to indicate missing value val rangeMapping: RangeMapping
val rangeMapping: RangeMapping if (rangeMappings.isNotEmpty() && isLastMapped && couldFoldInRange(lastMappedWithNewIndex, source)) {
if (rangeMappings.isNotEmpty() && isLastMapped && couldFoldInRange(lastMappedWithNewIndex, source)) { rangeMapping = rangeMappings.last()
rangeMapping = rangeMappings.last() rangeMapping.range += source - lastMappedWithNewIndex
rangeMapping.range += source - lastMappedWithNewIndex dest = rangeMapping.mapSourceToDest(source)
dest = lineMappings[lastMappedWithNewIndex] + source - lastMappedWithNewIndex }
} else {
else { dest = currentIndex + 1
dest = currentIndex + 1 rangeMapping = RangeMapping(source, dest, callSiteMarker = callSiteMarker)
rangeMapping = RangeMapping(source, dest) rangeMappings.add(rangeMapping)
rangeMappings.add(rangeMapping)
}
lineMappings.put(source, dest)
lastMappedWithNewIndex = source
} }
lastMappedWithNewIndex = source
return dest return dest
} }
fun mapNewInterval(source: Int, dest: Int, range: Int) { fun mapNewInterval(source: Int, dest: Int, range: Int) {
val rangeMapping = RangeMapping(source, dest, range) val rangeMapping = RangeMapping(source, dest, range)
rangeMappings.add(rangeMapping) rangeMappings.add(rangeMapping)
(source..(source + range - 1)).forEach {
lineMappings.put(source, dest)
}
} }
private fun couldFoldInRange(first: Int, second: Int): Boolean { private fun couldFoldInRange(first: Int, second: Int): Boolean {
@@ -1,207 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.inline2
import gnu.trove.TIntIntHashMap
import org.jetbrains.kotlin.codegen.SourceInfo
import org.jetbrains.kotlin.codegen.inline.FileMapping
import org.jetbrains.kotlin.codegen.inline.RangeMapping
import org.jetbrains.kotlin.codegen.inline.SMAPAndMethodNode
import org.jetbrains.kotlin.codegen.inline.SourceMapper
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.MethodVisitor
import java.util.*
class CallSiteMarker(val lineNumber: Int)
open class NestedSourceMapper(
override val parent: SourceMapper, val ranges: List<RangeMapping>, sourceInfo: SourceInfo
) : DefaultSourceMapper(sourceInfo) {
val visitedLines = TIntIntHashMap()
var lastVisitedRange: RangeMapping? = null
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
val mappedLineNumber = visitedLines.get(lineNumber)
if (mappedLineNumber > 0) {
iv.visitLineNumber(mappedLineNumber, start)
} else {
val rangeForMapping = if (lastVisitedRange?.contains(lineNumber) ?: false) lastVisitedRange!! else findMappingIfExists(lineNumber)!!
val sourceLineNumber = rangeForMapping.mapDestToSource(lineNumber)
val visitLineNumber = parent.visitLineNumber(iv, start, sourceLineNumber, rangeForMapping.parent!!.name, rangeForMapping.parent!!.path)
if (visitLineNumber > 0) {
visitedLines.put(lineNumber, visitLineNumber)
}
lastVisitedRange = rangeForMapping
}
}
fun findMappingIfExists(lineNumber: Int): RangeMapping? {
val index = ranges.binarySearch(RangeMapping(lineNumber, lineNumber, 1), Comparator {
value, key ->
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
})
return if (index < 0) null else ranges[index];
}
}
open class InlineLambdaSourceMapper(
parent: SourceMapper, smap: SMAPAndMethodNode
) : NestedSourceMapper(parent, smap.ranges, smap.classSMAP.sourceInfo) {
init {
assert(smap.ranges.isNotEmpty()) {
"Mapping ranges should be presented in inline lambda: ${smap.node}"
}
}
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
if (ranges.firstOrNull()?.contains(lineNumber) ?: false) {
//don't remap origin lambda line numbers
iv.visitLineNumber(lineNumber, start)
}
else {
super.visitLineNumber(iv, lineNumber, start)
}
}
}
open class DefaultSourceMapper @JvmOverloads constructor(
val sourceInfo: SourceInfo,
protected var maxUsedValue: Int = sourceInfo.linesInFile
) : SourceMapper {
var callSiteMarker: CallSiteMarker? = null;
set(value) {
lastMappedWithChanges = null
field = value
}
var lastVisited: RawFileMapping? = null
private var lastMappedWithChanges: RawFileMapping? = null
private var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf()
protected val origin: RawFileMapping
init {
val name = sourceInfo.source
val path = sourceInfo.pathOrCleanFQN
origin = RawFileMapping(name, path)
origin.initRange(1, sourceInfo.linesInFile)
fileMappings.put(createKey(name, path), origin)
lastVisited = origin
}
private fun createKey(name: String, path: String) = "$name#$path"
override val resultMappings: List<FileMapping>
get() = fileMappings.values.map { it.toFileMapping() }
override fun visitSource(name: String, path: String) {
lastVisited = fileMappings.getOrPut(createKey(name, path)) { RawFileMapping(name, path) }
}
override fun visitOrigin() {
lastVisited = origin
}
override fun visitLineNumber(iv: MethodVisitor, lineNumber: Int, start: Label) {
if (lineNumber < 0) {
//no source information, so just skip this linenumber
return
}
//TODO add assertion that mapping exists
//val sourceLineNumber = createMapping(lineNumberToMap)
val sourceLineNumber = lineNumber
assert(lineNumber == sourceLineNumber)
iv.visitLineNumber(lineNumber, start)
}
override fun visitLineNumber(iv: MethodVisitor, start: Label, source: Int, sourceName: String, sourcePath: String): Int {
if (source < 0) {
//no source information, so just skip this linenumber
return -1
}
visitSource(sourceName, sourcePath)
val mappedLineIndex = createMapping(source)
iv.visitLineNumber(mappedLineIndex, start)
return mappedLineIndex
}
protected fun createMapping(lineNumber: Int): Int {
val fileMapping = lastVisited!!
val mappedLineIndex = fileMapping.mapNewLineNumber(lineNumber, maxUsedValue, lastMappedWithChanges == lastVisited, callSiteMarker)
if (mappedLineIndex > maxUsedValue) {
lastMappedWithChanges = fileMapping
maxUsedValue = mappedLineIndex
}
return mappedLineIndex
}
}
class RawFileMapping(val name: String, val path: String) {
private val rangeMappings = arrayListOf<RangeMapping>()
private var lastMappedWithNewIndex = -1000
fun toFileMapping() =
FileMapping(name, path).apply {
for (range in rangeMappings) {
addRangeMapping(range)
}
}
fun initRange(start: Int, end: Int) {
assert(rangeMappings.isEmpty()) { "initRange should only be called for empty mapping" }
rangeMappings.add(RangeMapping(start, start, end - start + 1))
lastMappedWithNewIndex = end
}
fun mapNewLineNumber(source: Int, currentIndex: Int, isLastMapped: Boolean, callSiteMarker: CallSiteMarker?): Int {
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 {
dest = currentIndex + 1
rangeMapping = RangeMapping(source, dest, callSiteMarker = callSiteMarker)
rangeMappings.add(rangeMapping)
}
lastMappedWithNewIndex = source
return dest
}
fun mapNewInterval(source: Int, dest: Int, range: Int) {
val rangeMapping = RangeMapping(source, dest, range)
rangeMappings.add(rangeMapping)
}
private fun couldFoldInRange(first: Int, second: Int): Boolean {
//TODO
val delta = second - first
return delta > 0 && delta <= 10
}
}