[JS] J2K for SourceMap3Builder
This commit is contained in:
@@ -2,256 +2,202 @@
|
|||||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
package org.jetbrains.kotlin.js.sourceMap
|
||||||
|
|
||||||
package org.jetbrains.kotlin.js.sourceMap;
|
import gnu.trove.TObjectIntHashMap
|
||||||
|
import org.jetbrains.kotlin.js.parser.sourcemaps.*
|
||||||
|
import org.jetbrains.kotlin.js.util.TextOutput
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.Reader
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
class SourceMap3Builder(
|
||||||
import gnu.trove.TObjectIntHashMap;
|
private val generatedFile: File?,
|
||||||
import kotlin.io.TextStreamsKt;
|
private val textOutput: TextOutput,
|
||||||
import org.jetbrains.annotations.NotNull;
|
private val pathPrefix: String
|
||||||
import org.jetbrains.annotations.Nullable;
|
) : SourceMapBuilder {
|
||||||
import org.jetbrains.kotlin.js.parser.sourcemaps.*;
|
private val out = StringBuilder(8192)
|
||||||
import org.jetbrains.kotlin.js.util.TextOutput;
|
private val sources: TObjectIntHashMap<SourceKey> = object : TObjectIntHashMap<SourceKey>() {
|
||||||
|
override fun get(key: SourceKey): Int {
|
||||||
import java.io.File;
|
val index = index(key)
|
||||||
import java.io.IOException;
|
return if (index < 0) -1 else _values[index]
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class SourceMap3Builder implements SourceMapBuilder {
|
|
||||||
private final StringBuilder out = new StringBuilder(8192);
|
|
||||||
private final File generatedFile;
|
|
||||||
private final TextOutput textOutput;
|
|
||||||
private final String pathPrefix;
|
|
||||||
|
|
||||||
private final TObjectIntHashMap<SourceKey> sources = new TObjectIntHashMap<SourceKey>() {
|
|
||||||
@Override
|
|
||||||
public int get(SourceKey key) {
|
|
||||||
int index = index(key);
|
|
||||||
return index < 0 ? -1 : _values[index];
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
private val orderedSources = mutableListOf<String>()
|
||||||
|
private val orderedSourceContentSuppliers = mutableListOf<Supplier<Reader?>>()
|
||||||
|
private var previousGeneratedColumn = -1
|
||||||
|
private var previousSourceIndex = 0
|
||||||
|
private var previousSourceLine = 0
|
||||||
|
private var previousSourceColumn = 0
|
||||||
|
private var previousMappingOffset = 0
|
||||||
|
private var previousPreviousSourceIndex = 0
|
||||||
|
private var previousPreviousSourceLine = 0
|
||||||
|
private var previousPreviousSourceColumn = 0
|
||||||
|
private var currentMappingIsEmpty = true
|
||||||
|
|
||||||
private final List<String> orderedSources = new ArrayList<>();
|
override fun getOutFile() = File(generatedFile!!.parentFile, "${generatedFile.name}.map")
|
||||||
private final List<Supplier<Reader>> orderedSourceContentSuppliers = new ArrayList<>();
|
|
||||||
|
|
||||||
private int previousGeneratedColumn = -1;
|
override fun build(): String {
|
||||||
private int previousSourceIndex;
|
val json = JsonObject()
|
||||||
private int previousSourceLine;
|
json.properties["version"] = JsonNumber(3.0)
|
||||||
private int previousSourceColumn;
|
if (generatedFile != null)
|
||||||
private int previousMappingOffset;
|
json.properties["file"] = JsonString(generatedFile.name)
|
||||||
private int previousPreviousSourceIndex;
|
appendSources(json)
|
||||||
private int previousPreviousSourceLine;
|
appendSourcesContent(json)
|
||||||
private int previousPreviousSourceColumn;
|
json.properties["names"] = JsonArray()
|
||||||
private boolean currentMappingIsEmpty = true;
|
json.properties["mappings"] = JsonString(out.toString())
|
||||||
|
return json.toString()
|
||||||
public SourceMap3Builder(File generatedFile, TextOutput textOutput, String pathPrefix) {
|
|
||||||
this.generatedFile = generatedFile;
|
|
||||||
this.textOutput = textOutput;
|
|
||||||
this.pathPrefix = pathPrefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private fun appendSources(json: JsonObject) {
|
||||||
public File getOutFile() {
|
json.properties["sources"] = JsonArray(
|
||||||
return new File(generatedFile.getParentFile(), generatedFile.getName() + ".map");
|
orderedSources.mapTo(mutableListOf()) { JsonString(pathPrefix + it) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private fun appendSourcesContent(json: JsonObject) {
|
||||||
public String build() {
|
json.properties["sourcesContent"] = JsonArray(
|
||||||
@SuppressWarnings("unchecked")
|
orderedSourceContentSuppliers.mapTo(mutableListOf()) {
|
||||||
JsonObject json = new JsonObject();
|
try {
|
||||||
json.getProperties().put("version", new JsonNumber(3));
|
it.get().use { reader ->
|
||||||
if (generatedFile != null) json.getProperties().put("file", new JsonString(generatedFile.getName()));
|
if (reader != null)
|
||||||
appendSources(json);
|
JsonString(reader.readText())
|
||||||
appendSourcesContent(json);
|
else
|
||||||
json.getProperties().put("names", new JsonArray());
|
JsonNull
|
||||||
json.getProperties().put("mappings", new JsonString(out.toString()));
|
}
|
||||||
return json.toString();
|
} catch (e: IOException) {
|
||||||
}
|
System.err.println("An exception occurred during embedding sources into source map")
|
||||||
|
e.printStackTrace()
|
||||||
private void appendSources(JsonObject json) {
|
// can't close the content reader or read from it
|
||||||
JsonArray array = new JsonArray();
|
JsonNull
|
||||||
for (String source : orderedSources) {
|
}
|
||||||
array.getElements().add(new JsonString(pathPrefix + source));
|
|
||||||
}
|
|
||||||
json.getProperties().put("sources", array);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendSourcesContent(JsonObject json) {
|
|
||||||
JsonArray array = new JsonArray();
|
|
||||||
for (Supplier<Reader> contentSupplier : orderedSourceContentSuppliers) {
|
|
||||||
try (Reader reader = contentSupplier.get()) {
|
|
||||||
array.getElements().add(reader != null ? new JsonString(TextStreamsKt.readText(reader)) : JsonNull.INSTANCE);
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
)
|
||||||
//noinspection UseOfSystemOutOrSystemErr
|
|
||||||
System.err.println("An exception occured during embedding sources into source map");
|
|
||||||
//noinspection CallToPrintStackTrace
|
|
||||||
e.printStackTrace();
|
|
||||||
// can't close the content reader or read from it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json.getProperties().put("sourcesContent", array);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun newLine() {
|
||||||
public void newLine() {
|
out.append(';')
|
||||||
out.append(';');
|
previousGeneratedColumn = -1
|
||||||
previousGeneratedColumn = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun skipLinesAtBeginning(count: Int) {
|
||||||
public void skipLinesAtBeginning(int count) {
|
out.insert(0, ";".repeat(count))
|
||||||
out.insert(0, StringUtil.repeatSymbol(';', count));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSourceIndex(String source, Object identityObject, Supplier<Reader> contentSupplier) {
|
private fun getSourceIndex(source: String, fileIdentity: Any?, contentSupplier: Supplier<Reader?>): Int {
|
||||||
SourceKey key = new SourceKey(source, identityObject);
|
val key = SourceKey(source, fileIdentity)
|
||||||
int sourceIndex = sources.get(key);
|
var sourceIndex = sources[key]
|
||||||
if (sourceIndex == -1) {
|
if (sourceIndex == -1) {
|
||||||
sourceIndex = orderedSources.size();
|
sourceIndex = orderedSources.size
|
||||||
sources.put(key, sourceIndex);
|
sources.put(key, sourceIndex)
|
||||||
orderedSources.add(source);
|
orderedSources.add(source)
|
||||||
orderedSourceContentSuppliers.add(contentSupplier);
|
orderedSourceContentSuppliers.add(contentSupplier)
|
||||||
}
|
}
|
||||||
|
return sourceIndex
|
||||||
return sourceIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun addMapping(
|
||||||
public void addMapping(
|
source: String,
|
||||||
@NotNull String source, @Nullable Object sourceFileIdentity, @NotNull Supplier<Reader> sourceContent,
|
fileIdentity: Any?,
|
||||||
int sourceLine, int sourceColumn
|
sourceContent: Supplier<Reader?>,
|
||||||
|
sourceLine: Int,
|
||||||
|
sourceColumn: Int
|
||||||
) {
|
) {
|
||||||
source = source.replace(File.separatorChar, '/');
|
val sourceIndex = getSourceIndex(source.replace(File.separatorChar, '/'), fileIdentity, sourceContent)
|
||||||
int sourceIndex = getSourceIndex(source, sourceFileIdentity, sourceContent);
|
|
||||||
|
|
||||||
if (!currentMappingIsEmpty && previousSourceIndex == sourceIndex && previousSourceLine == sourceLine &&
|
if (!currentMappingIsEmpty && previousSourceIndex == sourceIndex && previousSourceLine == sourceLine && previousSourceColumn == sourceColumn) {
|
||||||
previousSourceColumn == sourceColumn) {
|
return
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startMapping();
|
startMapping()
|
||||||
|
|
||||||
Base64VLQ.encode(out, sourceIndex - previousSourceIndex);
|
Base64VLQ.encode(out, sourceIndex - previousSourceIndex)
|
||||||
previousSourceIndex = sourceIndex;
|
previousSourceIndex = sourceIndex
|
||||||
|
|
||||||
Base64VLQ.encode(out, sourceLine - previousSourceLine);
|
Base64VLQ.encode(out, sourceLine - previousSourceLine)
|
||||||
previousSourceLine = sourceLine;
|
previousSourceLine = sourceLine
|
||||||
|
|
||||||
Base64VLQ.encode(out, sourceColumn - previousSourceColumn);
|
Base64VLQ.encode(out, sourceColumn - previousSourceColumn)
|
||||||
previousSourceColumn = sourceColumn;
|
previousSourceColumn = sourceColumn
|
||||||
|
|
||||||
currentMappingIsEmpty = false;
|
currentMappingIsEmpty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun addEmptyMapping() {
|
||||||
public void addEmptyMapping() {
|
|
||||||
if (!currentMappingIsEmpty) {
|
if (!currentMappingIsEmpty) {
|
||||||
startMapping();
|
startMapping()
|
||||||
currentMappingIsEmpty = true;
|
currentMappingIsEmpty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startMapping() {
|
private fun startMapping() {
|
||||||
boolean newGroupStarted = previousGeneratedColumn == -1;
|
val newGroupStarted = previousGeneratedColumn == -1
|
||||||
if (newGroupStarted) {
|
if (newGroupStarted) {
|
||||||
previousGeneratedColumn = 0;
|
previousGeneratedColumn = 0
|
||||||
}
|
}
|
||||||
|
val columnDiff = textOutput.column - previousGeneratedColumn
|
||||||
int columnDiff = textOutput.getColumn() - previousGeneratedColumn;
|
|
||||||
if (!newGroupStarted) {
|
if (!newGroupStarted) {
|
||||||
out.append(',');
|
out.append(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnDiff > 0 || newGroupStarted) {
|
if (columnDiff > 0 || newGroupStarted) {
|
||||||
Base64VLQ.encode(out, columnDiff);
|
Base64VLQ.encode(out, columnDiff)
|
||||||
previousGeneratedColumn = textOutput.getColumn();
|
previousGeneratedColumn = textOutput.column
|
||||||
|
|
||||||
previousMappingOffset = out.length();
|
previousMappingOffset = out.length
|
||||||
previousPreviousSourceIndex = previousSourceIndex;
|
previousPreviousSourceIndex = previousSourceIndex
|
||||||
previousPreviousSourceLine = previousSourceLine;
|
previousPreviousSourceLine = previousSourceLine
|
||||||
previousPreviousSourceColumn = previousSourceColumn;
|
previousPreviousSourceColumn = previousSourceColumn
|
||||||
}
|
} else {
|
||||||
else {
|
out.setLength(previousMappingOffset)
|
||||||
out.setLength(previousMappingOffset);
|
previousSourceIndex = previousPreviousSourceIndex
|
||||||
previousSourceIndex = previousPreviousSourceIndex;
|
previousSourceLine = previousPreviousSourceLine
|
||||||
previousSourceLine = previousPreviousSourceLine;
|
previousSourceColumn = previousPreviousSourceColumn
|
||||||
previousSourceColumn = previousPreviousSourceColumn;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun addLink() {
|
||||||
public void addLink() {
|
textOutput.print("\n//# sourceMappingURL=")
|
||||||
textOutput.print("\n//# sourceMappingURL=");
|
textOutput.print(generatedFile!!.name)
|
||||||
textOutput.print(generatedFile.getName());
|
textOutput.print(".map\n")
|
||||||
textOutput.print(".map\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Base64VLQ {
|
private object Base64VLQ {
|
||||||
// A Base64 VLQ digit can represent 5 bits, so it is base-32.
|
// A Base64 VLQ digit can represent 5 bits, so it is base-32.
|
||||||
private static final int VLQ_BASE_SHIFT = 5;
|
private const val VLQ_BASE_SHIFT = 5
|
||||||
private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT;
|
private const val VLQ_BASE = 1 shl VLQ_BASE_SHIFT
|
||||||
|
|
||||||
// A mask of bits for a VLQ digit (11111), 31 decimal.
|
// A mask of bits for a VLQ digit (11111), 31 decimal.
|
||||||
private static final int VLQ_BASE_MASK = VLQ_BASE - 1;
|
private const val VLQ_BASE_MASK = VLQ_BASE - 1
|
||||||
|
|
||||||
// The continuation bit is the 6th bit.
|
// The continuation bit is the 6th bit.
|
||||||
private static final int VLQ_CONTINUATION_BIT = VLQ_BASE;
|
private const val VLQ_CONTINUATION_BIT = VLQ_BASE
|
||||||
|
|
||||||
@SuppressWarnings("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
private static final char[] BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
private val BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray()
|
||||||
|
|
||||||
private Base64VLQ() {
|
private fun toVLQSigned(value: Int) =
|
||||||
}
|
if (value < 0) (-value shl 1) + 1 else value shl 1
|
||||||
|
|
||||||
private static int toVLQSigned(int value) {
|
fun encode(out: StringBuilder, value: Int) {
|
||||||
return value < 0 ? ((-value) << 1) + 1 : value << 1;
|
@Suppress("NAME_SHADOWING")
|
||||||
}
|
var value = toVLQSigned(value)
|
||||||
|
|
||||||
public static void encode(StringBuilder out, int value) {
|
|
||||||
value = toVLQSigned(value);
|
|
||||||
do {
|
do {
|
||||||
int digit = value & VLQ_BASE_MASK;
|
var digit = value and VLQ_BASE_MASK
|
||||||
value >>>= VLQ_BASE_SHIFT;
|
value = value ushr VLQ_BASE_SHIFT
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
digit |= VLQ_CONTINUATION_BIT;
|
digit = digit or VLQ_CONTINUATION_BIT
|
||||||
}
|
}
|
||||||
out.append(BASE64_MAP[digit]);
|
out.append(BASE64_MAP[digit])
|
||||||
}
|
} while (value > 0)
|
||||||
while (value > 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class SourceKey {
|
private data class SourceKey(
|
||||||
private final String sourcePath;
|
private val sourcePath: String,
|
||||||
private final Object identityKey;
|
/**
|
||||||
|
* An object to distinguish different files with the same paths
|
||||||
SourceKey(String sourcePath, Object identityKey) {
|
*/
|
||||||
this.sourcePath = sourcePath;
|
private val fileIdentity: Any?
|
||||||
this.identityKey = identityKey;
|
)
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof SourceKey)) return false;
|
|
||||||
|
|
||||||
SourceKey key = (SourceKey) o;
|
|
||||||
|
|
||||||
if (!sourcePath.equals(key.sourcePath)) return false;
|
|
||||||
if (identityKey != null ? !identityKey.equals(key.identityKey) : key.identityKey != null) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = sourcePath.hashCode();
|
|
||||||
result = 31 * result + (identityKey != null ? identityKey.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user