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