From 64465480a3141ffb8ae79ff5a0bd5af3e4899206 Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Fri, 2 Sep 2022 19:08:01 +0200 Subject: [PATCH] [JS] J2K for SourceMap3Builder --- .../kotlin/js/sourceMap/SourceMap3Builder.kt | 332 ++++++++---------- 1 file changed, 139 insertions(+), 193 deletions(-) diff --git a/js/js.sourcemap/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.kt b/js/js.sourcemap/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.kt index 27727280d62..f877981c146 100644 --- a/js/js.sourcemap/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.kt +++ b/js/js.sourcemap/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.kt @@ -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 sources = new TObjectIntHashMap() { - @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 = object : TObjectIntHashMap() { + override fun get(key: SourceKey): Int { + val index = index(key) + return if (index < 0) -1 else _values[index] } - }; + } + private val orderedSources = mutableListOf() + private val orderedSourceContentSuppliers = mutableListOf>() + 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 orderedSources = new ArrayList<>(); - private final List> 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 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 contentSupplier) { - SourceKey key = new SourceKey(source, identityObject); - int sourceIndex = sources.get(key); + private fun getSourceIndex(source: String, fileIdentity: Any?, contentSupplier: Supplier): 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 sourceContent, - int sourceLine, int sourceColumn + override fun addMapping( + source: String, + fileIdentity: Any?, + sourceContent: Supplier, + 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? + ) }