[JS] Support the 'names' field in sourcemaps
This commit is contained in:
committed by
Space Team
parent
227864c6ec
commit
1d0025c3cf
+4
-4
@@ -522,17 +522,17 @@ object JsAstUtils {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T : JsNode> T.withSource(node: IrElement, context: JsGenerationContext): T {
|
||||
addSourceInfoIfNeed(node, context)
|
||||
internal fun <T : JsNode> T.withSource(node: IrElement, context: JsGenerationContext, originalName: String? = null): T {
|
||||
addSourceInfoIfNeed(node, context, originalName)
|
||||
return this
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun <T : JsNode> T.addSourceInfoIfNeed(node: IrElement, context: JsGenerationContext) {
|
||||
private inline fun <T : JsNode> T.addSourceInfoIfNeed(node: IrElement, context: JsGenerationContext, originalName: String?) {
|
||||
|
||||
val sourceMapsInfo = context.staticContext.backendContext.sourceMapsInfo ?: return
|
||||
|
||||
val location = context.getLocationForIrElement(node) ?: return
|
||||
val location = context.getLocationForIrElement(node, originalName) ?: return
|
||||
|
||||
val isNodeFromCurrentModule = context.currentFile.module.descriptor == context.staticContext.backendContext.module
|
||||
|
||||
|
||||
+2
-2
@@ -87,9 +87,9 @@ class JsGenerationContext(
|
||||
|
||||
fun checkIfHasAssociatedJsCode(symbol: IrFunctionSymbol): Boolean = staticContext.backendContext.getJsCodeForFunction(symbol) != null
|
||||
|
||||
fun getLocationForIrElement(irElement: IrElement): JsLocation? {
|
||||
fun getLocationForIrElement(irElement: IrElement, originalName: String? = null): JsLocation? {
|
||||
return locationCache.getOrPut(irElement.startOffset) {
|
||||
irElement.getSourceInfo(currentFile.fileEntry) ?: return null
|
||||
}
|
||||
}.copy(name = originalName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ package org.jetbrains.kotlin.js.backend.ast
|
||||
|
||||
import java.io.Reader
|
||||
|
||||
data class JsLocation(
|
||||
data class JsLocation @JvmOverloads constructor(
|
||||
override val file: String,
|
||||
override val startLine: Int,
|
||||
override val startChar: Int
|
||||
override val startChar: Int,
|
||||
override val name: String? = null
|
||||
) : JsLocationWithSource {
|
||||
override val fileIdentity: Any?
|
||||
get() = null
|
||||
@@ -36,6 +37,11 @@ interface JsLocationWithSource {
|
||||
val startLine: Int
|
||||
val startChar: Int
|
||||
|
||||
/**
|
||||
* The original name of the entity in the source code that this JS node was generated from.
|
||||
*/
|
||||
val name: String?
|
||||
|
||||
/**
|
||||
* An object to distinguish different files with the same paths
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,8 @@ class SourceMap(val sourceContentResolver: (String) -> Reader?) {
|
||||
for ((index, group) in groups.withIndex()) {
|
||||
writer.print("${index + 1}:")
|
||||
for (segment in group.segments) {
|
||||
writer.print(" ${segment.generatedColumnNumber + 1}:${segment.sourceLineNumber + 1},${segment.sourceColumnNumber + 1}")
|
||||
val nameIfPresent = if (segment.name != null) "(${segment.name})" else ""
|
||||
writer.print(" ${segment.generatedColumnNumber + 1}:${segment.sourceLineNumber + 1},${segment.sourceColumnNumber + 1}$nameIfPresent")
|
||||
}
|
||||
writer.println()
|
||||
}
|
||||
@@ -42,8 +43,9 @@ class SourceMap(val sourceContentResolver: (String) -> Reader?) {
|
||||
val generatedLine = generatedLines[index]
|
||||
val segmentsByColumn = group.segments.map { it.generatedColumnNumber to it }.toMap()
|
||||
for (i in generatedLine.indices) {
|
||||
segmentsByColumn[i]?.let { (_, sourceFile, sourceLine, sourceColumn) ->
|
||||
writer.print("<$sourceFile:${sourceLine + 1}:${sourceColumn + 1}>")
|
||||
segmentsByColumn[i]?.let { (_, sourceFile, sourceLine, sourceColumn, name) ->
|
||||
val nameIfPresent = if (name != null) "($name)" else ""
|
||||
writer.print("<$sourceFile:${sourceLine + 1}:${sourceColumn + 1}$nameIfPresent>")
|
||||
}
|
||||
writer.print(generatedLine[i])
|
||||
}
|
||||
@@ -96,7 +98,8 @@ data class SourceMapSegment(
|
||||
val generatedColumnNumber: Int,
|
||||
val sourceFileName: String?,
|
||||
val sourceLineNumber: Int,
|
||||
val sourceColumnNumber: Int
|
||||
val sourceColumnNumber: Int,
|
||||
val name: String?,
|
||||
)
|
||||
|
||||
class SourceMapGroup {
|
||||
|
||||
+3
-3
@@ -70,10 +70,10 @@ class SourceMapLocationRemapper(private val sourceMap: SourceMap, private val so
|
||||
val segment = findCorrespondingSegment(node)
|
||||
val sourceFileName = segment?.sourceFileName
|
||||
node.source = if (sourceFileName != null) {
|
||||
val location = JsLocation(sourceMapPathMapper(sourceFileName), segment.sourceLineNumber, segment.sourceColumnNumber)
|
||||
val location =
|
||||
JsLocation(sourceMapPathMapper(sourceFileName), segment.sourceLineNumber, segment.sourceColumnNumber, segment.name)
|
||||
JsLocationWithEmbeddedSource(location, null) { sourceMap.sourceContentResolver(segment.sourceFileName) }
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,17 @@ object SourceMapParser {
|
||||
}
|
||||
}
|
||||
|
||||
val names = jsonObject.properties["names"].let {
|
||||
if (it != null) {
|
||||
val namesProperty = it as? JsonArray ?: return SourceMapError("'names' property is not of array type")
|
||||
namesProperty.elements.map {
|
||||
(it as? JsonString ?: return SourceMapError("'names' array must contain strings")).value
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
val sourcePathToContent = sources.zip(sourcesContent).associate { it }
|
||||
|
||||
val mappings = jsonObject.properties["mappings"] ?: return SourceMapError("'mappings' property not found")
|
||||
@@ -95,6 +106,7 @@ object SourceMapParser {
|
||||
var sourceLine = 0
|
||||
var sourceColumn = 0
|
||||
var sourceIndex = 0
|
||||
var nameIndex = 0
|
||||
val stream = MappingStream(mappings.value)
|
||||
val sourceMap = SourceMap { sourcePathToContent[it]?.let { StringReader(it) } }
|
||||
var currentGroup = SourceMapGroup().also { sourceMap.groups += it }
|
||||
@@ -113,17 +125,20 @@ object SourceMapParser {
|
||||
sourceIndex += stream.readInt() ?: return stream.createError("VLQ-encoded source index expected")
|
||||
sourceLine += stream.readInt() ?: return stream.createError("VLQ-encoded source line expected")
|
||||
sourceColumn += stream.readInt() ?: return stream.createError("VLQ-encoded source column expected")
|
||||
if (stream.isEncodedInt) {
|
||||
stream.readInt() ?: return stream.createError("VLQ-encoded name index expected")
|
||||
}
|
||||
val name = if (stream.isEncodedInt) {
|
||||
nameIndex += stream.readInt() ?: return stream.createError("VLQ-encoded name index expected")
|
||||
if (nameIndex !in names.indices) {
|
||||
return stream.createError("Name index $nameIndex is out of bounds ${names.indices}")
|
||||
}
|
||||
names[nameIndex]
|
||||
} else null
|
||||
|
||||
if (sourceIndex !in sources.indices) {
|
||||
return stream.createError("Source index $sourceIndex is out of bounds ${sources.indices}")
|
||||
}
|
||||
currentGroup.segments += SourceMapSegment(jsColumn, sourceRoot + sources[sourceIndex], sourceLine, sourceColumn)
|
||||
}
|
||||
else {
|
||||
currentGroup.segments += SourceMapSegment(jsColumn, null, -1, -1)
|
||||
currentGroup.segments += SourceMapSegment(jsColumn, sourceRoot + sources[sourceIndex], sourceLine, sourceColumn, name)
|
||||
} else {
|
||||
currentGroup.segments += SourceMapSegment(jsColumn, null, -1, -1, null)
|
||||
}
|
||||
|
||||
when {
|
||||
|
||||
@@ -17,15 +17,25 @@ class SourceMap3Builder(
|
||||
private val textOutput: TextOutput,
|
||||
private val pathPrefix: String
|
||||
) : SourceMapBuilder {
|
||||
private val out = StringBuilder(8192)
|
||||
private val sources: TObjectIntHashMap<SourceKey> = object : TObjectIntHashMap<SourceKey>() {
|
||||
override fun get(key: SourceKey): Int {
|
||||
|
||||
private class ObjectIntHashMap<T> : TObjectIntHashMap<T>() {
|
||||
override fun get(key: T): Int {
|
||||
val index = index(key)
|
||||
return if (index < 0) -1 else _values[index]
|
||||
}
|
||||
}
|
||||
|
||||
private val out = StringBuilder(8192)
|
||||
|
||||
private val sources = ObjectIntHashMap<SourceKey>()
|
||||
private val orderedSources = mutableListOf<String>()
|
||||
private val orderedSourceContentSuppliers = mutableListOf<Supplier<Reader?>>()
|
||||
|
||||
private val names = ObjectIntHashMap<String>()
|
||||
private val orderedNames = mutableListOf<String>()
|
||||
private var previousNameIndex = 0
|
||||
private var previousPreviousNameIndex = 0
|
||||
|
||||
private var previousGeneratedColumn = -1
|
||||
private var previousSourceIndex = 0
|
||||
private var previousSourceLine = 0
|
||||
@@ -45,7 +55,9 @@ class SourceMap3Builder(
|
||||
json.properties["file"] = JsonString(generatedFile.name)
|
||||
appendSources(json)
|
||||
appendSourcesContent(json)
|
||||
json.properties["names"] = JsonArray()
|
||||
json.properties["names"] = JsonArray(
|
||||
orderedNames.mapTo(mutableListOf()) { JsonString(it) }
|
||||
)
|
||||
json.properties["mappings"] = JsonString(out.toString())
|
||||
return json.toString()
|
||||
}
|
||||
@@ -97,16 +109,33 @@ class SourceMap3Builder(
|
||||
return sourceIndex
|
||||
}
|
||||
|
||||
private fun getNameIndex(name: String): Int {
|
||||
var nameIndex = names[name]
|
||||
if (nameIndex == -1) {
|
||||
nameIndex = orderedNames.size
|
||||
names.put(name, nameIndex)
|
||||
orderedNames.add(name)
|
||||
}
|
||||
return nameIndex
|
||||
}
|
||||
|
||||
override fun addMapping(
|
||||
source: String,
|
||||
fileIdentity: Any?,
|
||||
sourceContent: Supplier<Reader?>,
|
||||
sourceLine: Int,
|
||||
sourceColumn: Int
|
||||
sourceColumn: Int,
|
||||
name: String?,
|
||||
) {
|
||||
val sourceIndex = getSourceIndex(source.replace(File.separatorChar, '/'), fileIdentity, sourceContent)
|
||||
|
||||
if (!currentMappingIsEmpty && previousSourceIndex == sourceIndex && previousSourceLine == sourceLine && previousSourceColumn == sourceColumn) {
|
||||
val nameIndex = name?.let(this::getNameIndex) ?: -1
|
||||
|
||||
if (!currentMappingIsEmpty &&
|
||||
previousSourceIndex == sourceIndex &&
|
||||
previousSourceLine == sourceLine &&
|
||||
previousSourceColumn == sourceColumn
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,6 +150,11 @@ class SourceMap3Builder(
|
||||
Base64VLQ.encode(out, sourceColumn - previousSourceColumn)
|
||||
previousSourceColumn = sourceColumn
|
||||
|
||||
if (nameIndex >= 0) {
|
||||
Base64VLQ.encode(out, nameIndex - previousNameIndex)
|
||||
previousNameIndex = nameIndex
|
||||
}
|
||||
|
||||
currentMappingIsEmpty = false
|
||||
}
|
||||
|
||||
@@ -148,11 +182,13 @@ class SourceMap3Builder(
|
||||
previousPreviousSourceIndex = previousSourceIndex
|
||||
previousPreviousSourceLine = previousSourceLine
|
||||
previousPreviousSourceColumn = previousSourceColumn
|
||||
previousPreviousNameIndex = previousNameIndex
|
||||
} else {
|
||||
out.setLength(previousMappingOffset)
|
||||
previousSourceIndex = previousPreviousSourceIndex
|
||||
previousSourceLine = previousPreviousSourceLine
|
||||
previousSourceColumn = previousPreviousSourceColumn
|
||||
previousNameIndex = previousPreviousNameIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class SourceMapBuilderConsumer(
|
||||
} else {
|
||||
{ null }
|
||||
}
|
||||
mappingConsumer.addMapping(sourceFilePath, null, contentSupplier, startLine, startChar)
|
||||
mappingConsumer.addMapping(sourceFilePath, null, contentSupplier, startLine, startChar, null)
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException("IO error occurred generating source maps", e)
|
||||
}
|
||||
@@ -85,7 +85,8 @@ class SourceMapBuilderConsumer(
|
||||
sourceInfo.fileIdentity,
|
||||
contentSupplier,
|
||||
sourceInfo.startLine,
|
||||
sourceInfo.startChar
|
||||
sourceInfo.startChar,
|
||||
sourceInfo.name
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public interface SourceMapMappingConsumer {
|
||||
|
||||
void addMapping(
|
||||
@NotNull String source, @Nullable Object fileIdentity, @NotNull Supplier<Reader> sourceSupplier,
|
||||
int sourceLine, int sourceColumn
|
||||
int sourceLine, int sourceColumn, @Nullable String name
|
||||
);
|
||||
|
||||
void addEmptyMapping();
|
||||
|
||||
Reference in New Issue
Block a user