JS: optimize JSON parser
This commit is contained in:
+1
-1
@@ -205,7 +205,7 @@ private fun kjsmToString(kjsmFile: File): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun sourceMapFileToString(sourceMapFile: File, generatedJsFile: File): String {
|
private fun sourceMapFileToString(sourceMapFile: File, generatedJsFile: File): String {
|
||||||
val sourceMapParseResult = SourceMapParser.parse(StringReader(sourceMapFile.readText()))
|
val sourceMapParseResult = SourceMapParser.parse(sourceMapFile.readText())
|
||||||
return when (sourceMapParseResult) {
|
return when (sourceMapParseResult) {
|
||||||
is SourceMapSuccess -> {
|
is SourceMapSuccess -> {
|
||||||
val bytesOut = ByteArrayOutputStream()
|
val bytesOut = ByteArrayOutputStream()
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class K2JSDce : CLITool<K2JSDceArguments>() {
|
|||||||
|
|
||||||
private fun mapSourcePaths(inputFile: File, targetFile: File): Boolean {
|
private fun mapSourcePaths(inputFile: File, targetFile: File): Boolean {
|
||||||
val json = try {
|
val json = try {
|
||||||
InputStreamReader(FileInputStream(inputFile), "UTF-8").use { parseJson(it) }
|
parseJson(inputFile)
|
||||||
} catch (e: JsonSyntaxException) {
|
} catch (e: JsonSyntaxException) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ private fun mergeStdlibParts(outputFile: File, wrapperFile: File, baseDir: File,
|
|||||||
|
|
||||||
val sourceMapFile = File(file.parent, file.name + ".map")
|
val sourceMapFile = File(file.parent, file.name + ".map")
|
||||||
if (sourceMapFile.exists()) {
|
if (sourceMapFile.exists()) {
|
||||||
val sourceMapParse = sourceMapFile.reader().use { SourceMapParser.parse(it) }
|
val sourceMapParse = SourceMapParser.parse(sourceMapFile)
|
||||||
when (sourceMapParse) {
|
when (sourceMapParse) {
|
||||||
is SourceMapError -> {
|
is SourceMapError -> {
|
||||||
System.err.println("Error parsing source map file $sourceMapFile: ${sourceMapParse.message}")
|
System.err.println("Error parsing source map file $sourceMapFile: ${sourceMapParse.message}")
|
||||||
@@ -82,7 +82,7 @@ private fun mergeStdlibParts(outputFile: File, wrapperFile: File, baseDir: File,
|
|||||||
|
|
||||||
outputFile.writeText(programText + "\n//# sourceMappingURL=${sourceMapFile.name}\n")
|
outputFile.writeText(programText + "\n//# sourceMappingURL=${sourceMapFile.name}\n")
|
||||||
|
|
||||||
val sourceMapJson = StringReader(sourceMapContent).use { parseJson(it) }
|
val sourceMapJson = parseJson(sourceMapContent)
|
||||||
val sources = (sourceMapJson as JsonObject).properties["sources"] as JsonArray
|
val sources = (sourceMapJson as JsonObject).properties["sources"] as JsonArray
|
||||||
|
|
||||||
sourceMapJson.properties["sourcesContent"] = JsonArray(*sources.elements.map { sourcePath ->
|
sourceMapJson.properties["sourcesContent"] = JsonArray(*sources.elements.map { sourcePath ->
|
||||||
|
|||||||
@@ -89,8 +89,7 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit
|
|||||||
return@map block
|
return@map block
|
||||||
}
|
}
|
||||||
val sourceMapParse = file.sourceMapResource
|
val sourceMapParse = file.sourceMapResource
|
||||||
?.let { InputStreamReader(it.reader(), "UTF-8") }
|
?.let { SourceMapParser.parse(InputStreamReader(it.reader(), "UTF-8").readText()) }
|
||||||
?.use { SourceMapParser.parse(it) }
|
|
||||||
when (sourceMapParse) {
|
when (sourceMapParse) {
|
||||||
is SourceMapError -> {
|
is SourceMapError -> {
|
||||||
logConsumer(
|
logConsumer(
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class FunctionReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sourceMap = sourceMapContent?.let {
|
val sourceMap = sourceMapContent?.let {
|
||||||
val sourceMapResult = SourceMapParser.parse(StringReader(it))
|
val sourceMapResult = SourceMapParser.parse(it)
|
||||||
when (sourceMapResult) {
|
when (sourceMapResult) {
|
||||||
is SourceMapSuccess -> sourceMapResult.value
|
is SourceMapSuccess -> sourceMapResult.value
|
||||||
is SourceMapError -> {
|
is SourceMapError -> {
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.parser.sourcemaps
|
package org.jetbrains.kotlin.js.parser.sourcemaps
|
||||||
|
|
||||||
import java.io.Reader
|
import java.io.*
|
||||||
import java.io.StringReader
|
|
||||||
import java.io.StringWriter
|
|
||||||
import java.io.Writer
|
|
||||||
|
|
||||||
sealed class JsonNode {
|
sealed class JsonNode {
|
||||||
abstract fun write(writer: Writer)
|
abstract fun write(writer: Writer)
|
||||||
@@ -120,8 +117,7 @@ data class JsonNumber(val value: Double) : JsonNode() {
|
|||||||
override fun write(writer: Writer) {
|
override fun write(writer: Writer) {
|
||||||
if (value.toLong().toDouble() == value) {
|
if (value.toLong().toDouble() == value) {
|
||||||
writer.append(value.toLong().toString())
|
writer.append(value.toLong().toString())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
writer.append(value.toString())
|
writer.append(value.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,14 +126,15 @@ data class JsonNumber(val value: Double) : JsonNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class JsonSyntaxException(val offset: Int, val line: Int, val column: Int, val text: String) :
|
class JsonSyntaxException(val offset: Int, val line: Int, val column: Int, val text: String) :
|
||||||
RuntimeException("JSON syntax error at ${line + 1}, ${column + 1}: $text")
|
RuntimeException("JSON syntax error at ${line + 1}, ${column + 1}: $text")
|
||||||
|
|
||||||
fun parseJson(reader: Reader): JsonNode = JsonParser(reader).parse()
|
fun parseJson(file: File): JsonNode = parseJson(file.readText(Charsets.UTF_8))
|
||||||
|
|
||||||
fun parseJson(text: String): JsonNode = parseJson(StringReader(text))
|
fun parseJson(text: String): JsonNode = JsonParser(text).parse()
|
||||||
|
|
||||||
private class JsonParser(val reader: Reader) {
|
private class JsonParser(val content: String) {
|
||||||
private var charCode = reader.read()
|
private var index = 0
|
||||||
|
private var charCode = content.getOrNull(index++)?.toInt() ?: -1
|
||||||
private var offset = 0
|
private var offset = 0
|
||||||
private var line = 0
|
private var line = 0
|
||||||
private var col = 0
|
private var col = 0
|
||||||
@@ -165,13 +162,27 @@ private class JsonParser(val reader: Reader) {
|
|||||||
'['.toInt() -> parseArray()
|
'['.toInt() -> parseArray()
|
||||||
'{'.toInt() -> parseObject()
|
'{'.toInt() -> parseObject()
|
||||||
'"'.toInt() -> JsonString(parseString())
|
'"'.toInt() -> JsonString(parseString())
|
||||||
'n'.toInt() -> { expectString("null"); JsonNull }
|
'n'.toInt() -> {
|
||||||
'f'.toInt() -> { expectString("false"); JsonBoolean.FALSE }
|
expectString("null")
|
||||||
't'.toInt() -> { expectString("true"); JsonBoolean.TRUE }
|
JsonNull
|
||||||
in '0'.toInt()..'9'.toInt() -> JsonNumber(parseNumber())
|
}
|
||||||
'-'.toInt() -> { advance(); JsonNumber(-parseNumber())
|
'f'.toInt() -> {
|
||||||
|
expectString("false")
|
||||||
|
JsonBoolean.FALSE
|
||||||
|
}
|
||||||
|
't'.toInt() -> {
|
||||||
|
expectString("true")
|
||||||
|
JsonBoolean.TRUE
|
||||||
|
}
|
||||||
|
'-'.toInt() -> {
|
||||||
|
advance()
|
||||||
|
JsonNumber(-parseNumber())
|
||||||
|
}
|
||||||
|
else -> if (charCode in '0'.toInt()..'9'.toInt()) {
|
||||||
|
JsonNumber(parseNumber())
|
||||||
|
} else {
|
||||||
|
error("Unexpected char")
|
||||||
}
|
}
|
||||||
else -> error("Unexpected char")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +194,7 @@ private class JsonParser(val reader: Reader) {
|
|||||||
if (charCode == ']'.toInt()) {
|
if (charCode == ']'.toInt()) {
|
||||||
advance()
|
advance()
|
||||||
break
|
break
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (result.elements.isNotEmpty()) {
|
if (result.elements.isNotEmpty()) {
|
||||||
expectCharAndAdvance(',')
|
expectCharAndAdvance(',')
|
||||||
}
|
}
|
||||||
@@ -202,8 +212,7 @@ private class JsonParser(val reader: Reader) {
|
|||||||
if (charCode == '}'.toInt()) {
|
if (charCode == '}'.toInt()) {
|
||||||
advance()
|
advance()
|
||||||
break
|
break
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (result.properties.isNotEmpty()) {
|
if (result.properties.isNotEmpty()) {
|
||||||
expectCharAndAdvance(',')
|
expectCharAndAdvance(',')
|
||||||
}
|
}
|
||||||
@@ -356,7 +365,7 @@ private class JsonParser(val reader: Reader) {
|
|||||||
wasCR = false
|
wasCR = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
charCode = reader.read()
|
charCode = content.getOrNull(index++)?.toInt() ?: -1
|
||||||
offset++
|
offset++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,20 +16,29 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.parser.sourcemaps
|
package org.jetbrains.kotlin.js.parser.sourcemaps
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Reader
|
|
||||||
import java.io.StringReader
|
import java.io.StringReader
|
||||||
|
|
||||||
object SourceMapParser {
|
object SourceMapParser {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun parse(reader: Reader): SourceMapParseResult {
|
fun parse(file: File): SourceMapParseResult {
|
||||||
|
return parse(file.readText(Charsets.UTF_8))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun parse(content: String): SourceMapParseResult {
|
||||||
val jsonObject = try {
|
val jsonObject = try {
|
||||||
parseJson(reader)
|
parseJson(content)
|
||||||
}
|
}
|
||||||
catch (e: JsonSyntaxException) {
|
catch (e: JsonSyntaxException) {
|
||||||
return SourceMapError(e.message ?: "parse error")
|
return SourceMapError(e.message ?: "parse error")
|
||||||
}
|
}
|
||||||
|
return parse(jsonObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun parse(jsonObject: JsonNode): SourceMapParseResult {
|
||||||
if (jsonObject !is JsonObject) return SourceMapError("Top-level object expected")
|
if (jsonObject !is JsonObject) return SourceMapError("Top-level object expected")
|
||||||
|
|
||||||
val version = jsonObject.properties["version"] ?: return SourceMapError("Version not defined")
|
val version = jsonObject.properties["version"] ?: return SourceMapError("Version not defined")
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ abstract class BasicBoxTest(
|
|||||||
val recompiledSourceMap = removeRecompiledSuffix(
|
val recompiledSourceMap = removeRecompiledSuffix(
|
||||||
FileUtil.loadFile(File(recompiledOutputFile.parentFile, recompiledOutputFile.name + ".map")))
|
FileUtil.loadFile(File(recompiledOutputFile.parentFile, recompiledOutputFile.name + ".map")))
|
||||||
if (originalSourceMap != recompiledSourceMap) {
|
if (originalSourceMap != recompiledSourceMap) {
|
||||||
val originalSourceMapParse = SourceMapParser.parse(StringReader(originalSourceMap))
|
val originalSourceMapParse = SourceMapParser.parse(originalSourceMap)
|
||||||
val recompiledSourceMapParse = SourceMapParser.parse(StringReader(recompiledSourceMap))
|
val recompiledSourceMapParse = SourceMapParser.parse(recompiledSourceMap)
|
||||||
if (originalSourceMapParse is SourceMapSuccess && recompiledSourceMapParse is SourceMapSuccess) {
|
if (originalSourceMapParse is SourceMapSuccess && recompiledSourceMapParse is SourceMapSuccess) {
|
||||||
assertEquals("Source map file changed after recompilation",
|
assertEquals("Source map file changed after recompilation",
|
||||||
originalSourceMapParse.toDebugString(),
|
originalSourceMapParse.toDebugString(),
|
||||||
@@ -570,7 +570,7 @@ abstract class BasicBoxTest(
|
|||||||
val parsedProgram = JsProgram()
|
val parsedProgram = JsProgram()
|
||||||
parsedProgram.globalBlock.statements += parse(code, ThrowExceptionOnErrorReporter, parsedProgram.scope, outputFile.path).orEmpty()
|
parsedProgram.globalBlock.statements += parse(code, ThrowExceptionOnErrorReporter, parsedProgram.scope, outputFile.path).orEmpty()
|
||||||
removeLocationFromBlocks(parsedProgram)
|
removeLocationFromBlocks(parsedProgram)
|
||||||
val sourceMapParseResult = SourceMapParser.parse(StringReader(generatedSourceMap))
|
val sourceMapParseResult = SourceMapParser.parse(generatedSourceMap)
|
||||||
val sourceMap = when (sourceMapParseResult) {
|
val sourceMap = when (sourceMapParseResult) {
|
||||||
is SourceMapSuccess -> sourceMapParseResult.value
|
is SourceMapSuccess -> sourceMapParseResult.value
|
||||||
is SourceMapError -> error("Could not parse source map: ${sourceMapParseResult.message}")
|
is SourceMapError -> error("Could not parse source map: ${sourceMapParseResult.message}")
|
||||||
|
|||||||
Reference in New Issue
Block a user