From 361d6dfca01e833a09f38cd56bd242c59fa06b84 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 5 Sep 2017 16:23:22 +0300 Subject: [PATCH] JS: rewrite source map generator to use specialized JSON writer Fixes problems with string escaping. See KT-20005 --- .../testData/cli/js/sourceMapCharEscape.args | 7 ++ .../testData/cli/js/sourceMapCharEscape.kt | 1 + .../testData/cli/js/sourceMapCharEscape.out | 1 + .../testData/cli/js/sourceMapCharEscape.test | 2 + .../kotlin/cli/CliTestGenerated.java | 6 ++ js/js.translator/js.translator.iml | 1 + .../js/sourceMap/SourceMap3Builder.java | 69 ++++++++----------- 7 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 compiler/testData/cli/js/sourceMapCharEscape.args create mode 100644 compiler/testData/cli/js/sourceMapCharEscape.kt create mode 100644 compiler/testData/cli/js/sourceMapCharEscape.out create mode 100644 compiler/testData/cli/js/sourceMapCharEscape.test diff --git a/compiler/testData/cli/js/sourceMapCharEscape.args b/compiler/testData/cli/js/sourceMapCharEscape.args new file mode 100644 index 00000000000..171ae477352 --- /dev/null +++ b/compiler/testData/cli/js/sourceMapCharEscape.args @@ -0,0 +1,7 @@ +$TESTDATA_DIR$/sourceMapCharEscape.kt +-no-stdlib +-source-map +-source-map-embed-sources +always +-output +$TEMP_DIR$/out.js diff --git a/compiler/testData/cli/js/sourceMapCharEscape.kt b/compiler/testData/cli/js/sourceMapCharEscape.kt new file mode 100644 index 00000000000..aba87cc3237 --- /dev/null +++ b/compiler/testData/cli/js/sourceMapCharEscape.kt @@ -0,0 +1 @@ +fun foo() = "©∑\n" diff --git a/compiler/testData/cli/js/sourceMapCharEscape.out b/compiler/testData/cli/js/sourceMapCharEscape.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/js/sourceMapCharEscape.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/js/sourceMapCharEscape.test b/compiler/testData/cli/js/sourceMapCharEscape.test new file mode 100644 index 00000000000..19f6cbe5320 --- /dev/null +++ b/compiler/testData/cli/js/sourceMapCharEscape.test @@ -0,0 +1,2 @@ +// EXISTS: out.js +// CONTAINS: out.js.map, \"©∑\\n\"\n diff --git a/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java index 650a1db7883..448383b6b4b 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -545,6 +545,12 @@ public class CliTestGenerated extends AbstractCliTest { doJsTest(fileName); } + @TestMetadata("sourceMapCharEscape.args") + public void testSourceMapCharEscape() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapCharEscape.args"); + doJsTest(fileName); + } + @TestMetadata("sourceMapDuplicateRelativePaths.args") public void testSourceMapDuplicateRelativePaths() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapDuplicateRelativePaths.args"); diff --git a/js/js.translator/js.translator.iml b/js/js.translator/js.translator.iml index e522b8393b3..602eee31925 100644 --- a/js/js.translator/js.translator.iml +++ b/js/js.translator/js.translator.iml @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.java b/js/js.translator/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.java index 72408b57f7a..43db1fe0f15 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/sourceMap/SourceMap3Builder.java @@ -21,11 +21,13 @@ import gnu.trove.TObjectIntHashMap; import kotlin.io.TextStreamsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor; import org.jetbrains.kotlin.js.util.TextOutput; +import org.json.JSONWriter; import java.io.File; +import java.io.IOException; import java.io.Reader; +import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -70,55 +72,44 @@ public class SourceMap3Builder implements SourceMapBuilder { @Override public String build() { - StringBuilder sb = new StringBuilder(out.length() + (128 * orderedSources.size())); - sb.append("{\"version\":3,\"file\":\"").append(generatedFile.getName()).append('"').append(','); + try { + StringWriter stringWriter = new StringWriter(); + JSONWriter writer = new JSONWriter(stringWriter); + writer.object(); + writer.key("version").value(3); + writer.key("file").value(generatedFile.getName()); - appendSources(sb); - sb.append(","); - appendSourcesContent(sb); + appendSources(writer); + appendSourcesContent(writer); - sb.append(",\"names\":["); - sb.append("],\"mappings\":\""); - sb.append(out); - sb.append("\"}"); - return sb.toString(); + writer.key("names").array().endArray(); + writer.key("mappings").value(out.toString()); + + writer.endObject(); + stringWriter.close(); + + return stringWriter.toString(); + } + catch (IOException e) { + throw new AssertionError("This exception should have not been thrown from StringWriter", e); + } } - private void appendSources(StringBuilder sb) { - boolean isNotFirst = false; - sb.append('"').append("sources").append("\":["); + private void appendSources(JSONWriter writer) { + writer.key("sources").array(); for (String source : orderedSources) { - if (isNotFirst) { - sb.append(','); - } - else { - isNotFirst = true; - } - sb.append(JsToStringGenerationVisitor.javaScriptString(pathPrefix + source, true)); + writer.value(pathPrefix + source); } - sb.append(']'); + writer.endArray(); } - private void appendSourcesContent(StringBuilder sb) { - boolean isNotFirst = false; - sb.append('"').append("sourcesContent").append("\":["); + private void appendSourcesContent(JSONWriter writer) { + writer.key("sourcesContent").array(); for (Supplier contentSupplier : orderedSourceContentSuppliers) { - if (isNotFirst) { - sb.append(','); - } - else { - isNotFirst = true; - } - Reader reader = contentSupplier.get(); - if (reader != null) { - sb.append(JsToStringGenerationVisitor.javaScriptString(TextStreamsKt.readText(reader), true)); - } - else { - sb.append("null"); - } + writer.value(reader != null ? TextStreamsKt.readText(reader) : null); } - sb.append(']'); + writer.endArray(); } @Override