[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.
|
||||
* 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;
|
||||
import gnu.trove.TObjectIntHashMap;
|
||||
import kotlin.io.TextStreamsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.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];
|
||||
class SourceMap3Builder(
|
||||
private val generatedFile: File?,
|
||||
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 {
|
||||
val index = index(key)
|
||||
return if (index < 0) -1 else _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<>();
|
||||
private final List<Supplier<Reader>> orderedSourceContentSuppliers = new ArrayList<>();
|
||||
override fun getOutFile() = File(generatedFile!!.parentFile, "${generatedFile.name}.map")
|
||||
|
||||
private int previousGeneratedColumn = -1;
|
||||
private int previousSourceIndex;
|
||||
private int previousSourceLine;
|
||||
private int previousSourceColumn;
|
||||
private int previousMappingOffset;
|
||||
private int previousPreviousSourceIndex;
|
||||
private int previousPreviousSourceLine;
|
||||
private int previousPreviousSourceColumn;
|
||||
private boolean currentMappingIsEmpty = true;
|
||||
|
||||
public SourceMap3Builder(File generatedFile, TextOutput textOutput, String pathPrefix) {
|
||||
this.generatedFile = generatedFile;
|
||||
this.textOutput = textOutput;
|
||||
this.pathPrefix = pathPrefix;
|
||||
override fun build(): String {
|
||||
val json = JsonObject()
|
||||
json.properties["version"] = JsonNumber(3.0)
|
||||
if (generatedFile != null)
|
||||
json.properties["file"] = JsonString(generatedFile.name)
|
||||
appendSources(json)
|
||||
appendSourcesContent(json)
|
||||
json.properties["names"] = JsonArray()
|
||||
json.properties["mappings"] = JsonString(out.toString())
|
||||
return json.toString()
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutFile() {
|
||||
return new File(generatedFile.getParentFile(), generatedFile.getName() + ".map");
|
||||
private fun appendSources(json: JsonObject) {
|
||||
json.properties["sources"] = JsonArray(
|
||||
orderedSources.mapTo(mutableListOf()) { JsonString(pathPrefix + it) }
|
||||
)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
@SuppressWarnings("unchecked")
|
||||
JsonObject json = new JsonObject();
|
||||
json.getProperties().put("version", new JsonNumber(3));
|
||||
if (generatedFile != null) json.getProperties().put("file", new JsonString(generatedFile.getName()));
|
||||
appendSources(json);
|
||||
appendSourcesContent(json);
|
||||
json.getProperties().put("names", new JsonArray());
|
||||
json.getProperties().put("mappings", new JsonString(out.toString()));
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
private void appendSources(JsonObject json) {
|
||||
JsonArray array = new JsonArray();
|
||||
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);
|
||||
private fun appendSourcesContent(json: JsonObject) {
|
||||
json.properties["sourcesContent"] = JsonArray(
|
||||
orderedSourceContentSuppliers.mapTo(mutableListOf()) {
|
||||
try {
|
||||
it.get().use { reader ->
|
||||
if (reader != null)
|
||||
JsonString(reader.readText())
|
||||
else
|
||||
JsonNull
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
System.err.println("An exception occurred during embedding sources into source map")
|
||||
e.printStackTrace()
|
||||
// can't close the content reader or read from it
|
||||
JsonNull
|
||||
}
|
||||
}
|
||||
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
|
||||
public void newLine() {
|
||||
out.append(';');
|
||||
previousGeneratedColumn = -1;
|
||||
override fun newLine() {
|
||||
out.append(';')
|
||||
previousGeneratedColumn = -1
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipLinesAtBeginning(int count) {
|
||||
out.insert(0, StringUtil.repeatSymbol(';', count));
|
||||
override fun skipLinesAtBeginning(count: Int) {
|
||||
out.insert(0, ";".repeat(count))
|
||||
}
|
||||
|
||||
private int getSourceIndex(String source, Object identityObject, Supplier<Reader> contentSupplier) {
|
||||
SourceKey key = new SourceKey(source, identityObject);
|
||||
int sourceIndex = sources.get(key);
|
||||
private fun getSourceIndex(source: String, fileIdentity: Any?, contentSupplier: Supplier<Reader?>): Int {
|
||||
val key = SourceKey(source, fileIdentity)
|
||||
var sourceIndex = sources[key]
|
||||
if (sourceIndex == -1) {
|
||||
sourceIndex = orderedSources.size();
|
||||
sources.put(key, sourceIndex);
|
||||
orderedSources.add(source);
|
||||
orderedSourceContentSuppliers.add(contentSupplier);
|
||||
sourceIndex = orderedSources.size
|
||||
sources.put(key, sourceIndex)
|
||||
orderedSources.add(source)
|
||||
orderedSourceContentSuppliers.add(contentSupplier)
|
||||
}
|
||||
|
||||
return sourceIndex;
|
||||
return sourceIndex
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMapping(
|
||||
@NotNull String source, @Nullable Object sourceFileIdentity, @NotNull Supplier<Reader> sourceContent,
|
||||
int sourceLine, int sourceColumn
|
||||
override fun addMapping(
|
||||
source: String,
|
||||
fileIdentity: Any?,
|
||||
sourceContent: Supplier<Reader?>,
|
||||
sourceLine: Int,
|
||||
sourceColumn: Int
|
||||
) {
|
||||
source = source.replace(File.separatorChar, '/');
|
||||
int sourceIndex = getSourceIndex(source, sourceFileIdentity, sourceContent);
|
||||
val sourceIndex = getSourceIndex(source.replace(File.separatorChar, '/'), fileIdentity, sourceContent)
|
||||
|
||||
if (!currentMappingIsEmpty && previousSourceIndex == sourceIndex && previousSourceLine == sourceLine &&
|
||||
previousSourceColumn == sourceColumn) {
|
||||
return;
|
||||
if (!currentMappingIsEmpty && previousSourceIndex == sourceIndex && previousSourceLine == sourceLine && previousSourceColumn == sourceColumn) {
|
||||
return
|
||||
}
|
||||
|
||||
startMapping();
|
||||
startMapping()
|
||||
|
||||
Base64VLQ.encode(out, sourceIndex - previousSourceIndex);
|
||||
previousSourceIndex = sourceIndex;
|
||||
Base64VLQ.encode(out, sourceIndex - previousSourceIndex)
|
||||
previousSourceIndex = sourceIndex
|
||||
|
||||
Base64VLQ.encode(out, sourceLine - previousSourceLine);
|
||||
previousSourceLine = sourceLine;
|
||||
Base64VLQ.encode(out, sourceLine - previousSourceLine)
|
||||
previousSourceLine = sourceLine
|
||||
|
||||
Base64VLQ.encode(out, sourceColumn - previousSourceColumn);
|
||||
previousSourceColumn = sourceColumn;
|
||||
Base64VLQ.encode(out, sourceColumn - previousSourceColumn)
|
||||
previousSourceColumn = sourceColumn
|
||||
|
||||
currentMappingIsEmpty = false;
|
||||
currentMappingIsEmpty = false
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEmptyMapping() {
|
||||
override fun addEmptyMapping() {
|
||||
if (!currentMappingIsEmpty) {
|
||||
startMapping();
|
||||
currentMappingIsEmpty = true;
|
||||
startMapping()
|
||||
currentMappingIsEmpty = true
|
||||
}
|
||||
}
|
||||
|
||||
private void startMapping() {
|
||||
boolean newGroupStarted = previousGeneratedColumn == -1;
|
||||
private fun startMapping() {
|
||||
val newGroupStarted = previousGeneratedColumn == -1
|
||||
if (newGroupStarted) {
|
||||
previousGeneratedColumn = 0;
|
||||
previousGeneratedColumn = 0
|
||||
}
|
||||
|
||||
int columnDiff = textOutput.getColumn() - previousGeneratedColumn;
|
||||
val columnDiff = textOutput.column - previousGeneratedColumn
|
||||
if (!newGroupStarted) {
|
||||
out.append(',');
|
||||
out.append(',')
|
||||
}
|
||||
|
||||
if (columnDiff > 0 || newGroupStarted) {
|
||||
Base64VLQ.encode(out, columnDiff);
|
||||
previousGeneratedColumn = textOutput.getColumn();
|
||||
Base64VLQ.encode(out, columnDiff)
|
||||
previousGeneratedColumn = textOutput.column
|
||||
|
||||
previousMappingOffset = out.length();
|
||||
previousPreviousSourceIndex = previousSourceIndex;
|
||||
previousPreviousSourceLine = previousSourceLine;
|
||||
previousPreviousSourceColumn = previousSourceColumn;
|
||||
}
|
||||
else {
|
||||
out.setLength(previousMappingOffset);
|
||||
previousSourceIndex = previousPreviousSourceIndex;
|
||||
previousSourceLine = previousPreviousSourceLine;
|
||||
previousSourceColumn = previousPreviousSourceColumn;
|
||||
previousMappingOffset = out.length
|
||||
previousPreviousSourceIndex = previousSourceIndex
|
||||
previousPreviousSourceLine = previousSourceLine
|
||||
previousPreviousSourceColumn = previousSourceColumn
|
||||
} else {
|
||||
out.setLength(previousMappingOffset)
|
||||
previousSourceIndex = previousPreviousSourceIndex
|
||||
previousSourceLine = previousPreviousSourceLine
|
||||
previousSourceColumn = previousPreviousSourceColumn
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLink() {
|
||||
textOutput.print("\n//# sourceMappingURL=");
|
||||
textOutput.print(generatedFile.getName());
|
||||
textOutput.print(".map\n");
|
||||
override fun addLink() {
|
||||
textOutput.print("\n//# sourceMappingURL=")
|
||||
textOutput.print(generatedFile!!.name)
|
||||
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.
|
||||
private static final int VLQ_BASE_SHIFT = 5;
|
||||
private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT;
|
||||
private const val VLQ_BASE_SHIFT = 5
|
||||
private const val VLQ_BASE = 1 shl VLQ_BASE_SHIFT
|
||||
|
||||
// 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.
|
||||
private static final int VLQ_CONTINUATION_BIT = VLQ_BASE;
|
||||
private const val VLQ_CONTINUATION_BIT = VLQ_BASE
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
private static final char[] BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
@Suppress("SpellCheckingInspection")
|
||||
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) {
|
||||
return value < 0 ? ((-value) << 1) + 1 : value << 1;
|
||||
}
|
||||
|
||||
public static void encode(StringBuilder out, int value) {
|
||||
value = toVLQSigned(value);
|
||||
fun encode(out: StringBuilder, value: Int) {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var value = toVLQSigned(value)
|
||||
do {
|
||||
int digit = value & VLQ_BASE_MASK;
|
||||
value >>>= VLQ_BASE_SHIFT;
|
||||
var digit = value and VLQ_BASE_MASK
|
||||
value = value ushr VLQ_BASE_SHIFT
|
||||
if (value > 0) {
|
||||
digit |= VLQ_CONTINUATION_BIT;
|
||||
digit = digit or VLQ_CONTINUATION_BIT
|
||||
}
|
||||
out.append(BASE64_MAP[digit]);
|
||||
}
|
||||
while (value > 0);
|
||||
out.append(BASE64_MAP[digit])
|
||||
} while (value > 0)
|
||||
}
|
||||
}
|
||||
|
||||
static final class SourceKey {
|
||||
private final String sourcePath;
|
||||
private final Object identityKey;
|
||||
|
||||
SourceKey(String sourcePath, Object identityKey) {
|
||||
this.sourcePath = sourcePath;
|
||||
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;
|
||||
}
|
||||
}
|
||||
private data class SourceKey(
|
||||
private val sourcePath: String,
|
||||
/**
|
||||
* An object to distinguish different files with the same paths
|
||||
*/
|
||||
private val fileIdentity: Any?
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user