[JS] J2K for SourceMap3Builder

This commit is contained in:
Sergej Jaskiewicz
2022-09-02 19:08:01 +02:00
committed by Space
parent 660e8ff4da
commit 64465480a3
@@ -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?
)
}