[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. * 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;
}
}
} }