JS: fix incremental generation of source maps
This commit is contained in:
@@ -464,7 +464,9 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
|
||||
rightParen();
|
||||
nestedPush(x.getBody());
|
||||
accept(x.getBody());
|
||||
if (x.getBody() != null) {
|
||||
accept(x.getBody());
|
||||
}
|
||||
nestedPop(x.getBody());
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,7 @@ import java.util.Map;
|
||||
public final class JsProgram extends SourceInfoAwareJsNode {
|
||||
private final JsGlobalBlock globalBlock = new JsGlobalBlock();
|
||||
|
||||
private final TDoubleObjectHashMap<JsDoubleLiteral> doubleLiteralMap = new TDoubleObjectHashMap<JsDoubleLiteral>();
|
||||
private final TIntObjectHashMap<JsIntLiteral> intLiteralMap = new TIntObjectHashMap<JsIntLiteral>();
|
||||
|
||||
private final JsRootScope rootScope;
|
||||
private final Map<String, JsStringLiteral> stringLiteralMap = new THashMap<String, JsStringLiteral>();
|
||||
private final JsObjectScope topScope;
|
||||
|
||||
public JsProgram() {
|
||||
@@ -36,23 +32,11 @@ public final class JsProgram extends SourceInfoAwareJsNode {
|
||||
}
|
||||
|
||||
public JsNumberLiteral getNumberLiteral(double value) {
|
||||
JsDoubleLiteral literal = doubleLiteralMap.get(value);
|
||||
if (literal == null) {
|
||||
literal = new JsDoubleLiteral(value);
|
||||
doubleLiteralMap.put(value, literal);
|
||||
}
|
||||
|
||||
return literal;
|
||||
return new JsDoubleLiteral(value);
|
||||
}
|
||||
|
||||
public JsNumberLiteral getNumberLiteral(int value) {
|
||||
JsIntLiteral literal = intLiteralMap.get(value);
|
||||
if (literal == null) {
|
||||
literal = new JsIntLiteral(value);
|
||||
intLiteralMap.put(value, literal);
|
||||
}
|
||||
|
||||
return literal;
|
||||
return new JsIntLiteral(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,12 +61,7 @@ public final class JsProgram extends SourceInfoAwareJsNode {
|
||||
*/
|
||||
@NotNull
|
||||
public JsStringLiteral getStringLiteral(String value) {
|
||||
JsStringLiteral literal = stringLiteralMap.get(value);
|
||||
if (literal == null) {
|
||||
literal = new JsStringLiteral(value);
|
||||
stringLiteralMap.put(value, literal);
|
||||
}
|
||||
return literal;
|
||||
return new JsStringLiteral(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-15
@@ -31,7 +31,7 @@ class JsAstDeserializer(private val program: JsProgram) {
|
||||
private val stringTable = mutableListOf<String>()
|
||||
private val nameTable = mutableListOf<Name>()
|
||||
private val nameCache = mutableListOf<JsName?>()
|
||||
private val locationStack: Deque<JsLocation> = ArrayDeque()
|
||||
private val fileStack: Deque<String> = ArrayDeque()
|
||||
|
||||
fun deserialize(input: InputStream): JsProgramFragment {
|
||||
return deserialize(Chunk.parseFrom(CodedInputStream.newInstance(input).apply { setRecursionLimit(4096) }))
|
||||
@@ -491,28 +491,27 @@ class JsAstDeserializer(private val program: JsProgram) {
|
||||
}
|
||||
|
||||
private fun <T : JsNode> withLocation(fileId: Int?, location: Location?, action: () -> T): T {
|
||||
val lastLocation = locationStack.peek()
|
||||
val deserializedLocation = if (fileId != null || location != null) {
|
||||
val file = fileId?.let { deserializeString(it) } ?: lastLocation?.file!!
|
||||
val (startLine, startChar) = if (location != null) {
|
||||
Pair(location.startLine, location.startChar)
|
||||
}
|
||||
else {
|
||||
Pair(lastLocation.startLine, lastLocation.startChar)
|
||||
}
|
||||
JsLocation(file, startLine, startChar)
|
||||
val deserializedFile = fileId?.let { deserializeString(it) }
|
||||
val file = deserializedFile ?: fileStack.peek()
|
||||
val deserializedLocation = if (file != null && location != null) {
|
||||
JsLocation(file, location.startLine, location.startChar)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
if (deserializedLocation != null) {
|
||||
locationStack.push(deserializedLocation)
|
||||
val shouldUpdateFile = location != null && deserializedFile != null && deserializedFile != fileStack.peek()
|
||||
if (shouldUpdateFile) {
|
||||
fileStack.push(deserializedFile)
|
||||
}
|
||||
val node = action()
|
||||
if (deserializedLocation != null) {
|
||||
node.source = deserializedLocation
|
||||
locationStack.pop()
|
||||
if (node !is JsLiteral.JsBooleanLiteral && node !is JsLiteral.JsThisRef && node !is JsNullLiteral) {
|
||||
node.source = deserializedLocation
|
||||
}
|
||||
}
|
||||
if (shouldUpdateFile) {
|
||||
fileStack.pop()
|
||||
}
|
||||
|
||||
return node
|
||||
|
||||
@@ -31,7 +31,7 @@ class JsAstSerializer {
|
||||
private val stringTableBuilder = StringTable.newBuilder()
|
||||
private val nameMap = mutableMapOf<JsName, Int>()
|
||||
private val stringMap = mutableMapOf<String, Int>()
|
||||
private val locationStack: Deque<JsLocation> = ArrayDeque()
|
||||
private val fileStack: Deque<String> = ArrayDeque()
|
||||
|
||||
fun serialize(fragment: JsProgramFragment, output: OutputStream) {
|
||||
serialize(fragment).writeTo(output)
|
||||
@@ -561,27 +561,25 @@ class JsAstSerializer {
|
||||
}
|
||||
|
||||
private inline fun withLocation(node: JsNode, fileConsumer: (Int) -> Unit, locationConsumer: (Location) -> Unit, inner: () -> Unit) {
|
||||
val lastLocation = locationStack.peek()
|
||||
val location = extractLocation(node)
|
||||
val locationStackModified = if (lastLocation != location && location != null) {
|
||||
locationStack.push(location)
|
||||
if (lastLocation == null || lastLocation.file != location.file) {
|
||||
var fileChanged = false
|
||||
if (location != null) {
|
||||
val lastFile = fileStack.peek()
|
||||
fileChanged = lastFile != location.file
|
||||
if (fileChanged) {
|
||||
fileConsumer(serialize(location.file))
|
||||
fileStack.push(location.file)
|
||||
}
|
||||
val locationBuilder = Location.newBuilder()
|
||||
locationBuilder.startLine = location.startLine
|
||||
locationBuilder.startChar = location.startChar
|
||||
locationConsumer(locationBuilder.build())
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
|
||||
inner()
|
||||
|
||||
if (locationStackModified) {
|
||||
locationStack.pop()
|
||||
if (fileChanged) {
|
||||
fileStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -270,10 +270,17 @@ abstract class BasicBoxTest(
|
||||
translateFiles(allTranslationUnits, recompiledOutputFile, recompiledConfig)
|
||||
|
||||
val originalOutput = FileUtil.loadFile(outputFile)
|
||||
val recompiledOutput = FileUtil.loadFile(recompiledOutputFile)
|
||||
val recompiledOutput = removeRecompiledSuffix(FileUtil.loadFile(recompiledOutputFile))
|
||||
TestCase.assertEquals("Output file changed after recompilation", originalOutput, recompiledOutput)
|
||||
|
||||
val originalSourceMap = FileUtil.loadFile(File(outputFile.parentFile, outputFile.name + ".map"))
|
||||
val recompiledSourceMap = removeRecompiledSuffix(
|
||||
FileUtil.loadFile(File(recompiledOutputFile.parentFile, recompiledOutputFile.name + ".map")))
|
||||
TestCase.assertEquals("Source map file changed after recompilation", originalSourceMap, recompiledSourceMap)
|
||||
}
|
||||
}protected fun translateFiles(units: List<TranslationUnit>, outputFile: File, config: JsConfig,
|
||||
}
|
||||
|
||||
private fun removeRecompiledSuffix(text: String): String = text.replace("-recompiled.js", ".js")protected fun translateFiles(units: List<TranslationUnit>, outputFile: File, config: JsConfig,
|
||||
outputPrefixFile: File?,
|
||||
outputPostfixFile: File?,
|
||||
mainCallParameters: MainCallParameters) {
|
||||
@@ -362,9 +369,11 @@ abstract class BasicBoxTest(
|
||||
configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.v5)
|
||||
|
||||
configuration.put(JSConfigurationKeys.SOURCE_MAP, generateSourceMap)
|
||||
|
||||
val hasFilesToRecompile = module.hasFilesToRecompile
|
||||
configuration.put(JSConfigurationKeys.META_INFO, multiModule)
|
||||
configuration.put(JSConfigurationKeys.SERIALIZE_FRAGMENTS, hasFilesToRecompile)
|
||||
configuration.put(JSConfigurationKeys.SOURCE_MAP, hasFilesToRecompile)
|
||||
|
||||
if (additionalMetadata != null) {
|
||||
configuration.put(JSConfigurationKeys.FALLBACK_METADATA, additionalMetadata.map { FileUtil.loadFileBytes(it) })
|
||||
@@ -413,9 +422,7 @@ abstract class BasicBoxTest(
|
||||
currentModule.languageVersion = LanguageVersion.fromVersionString(version)
|
||||
}
|
||||
|
||||
return TestFile(temporaryFile.absolutePath, currentModule).apply {
|
||||
recompile = RECOMPILE_PATTERN.matcher(text).find()
|
||||
}
|
||||
return TestFile(temporaryFile.absolutePath, currentModule, recompile = RECOMPILE_PATTERN.matcher(text).find())
|
||||
}
|
||||
|
||||
override fun createModule(name: String, dependencies: List<String>): TestModule? {
|
||||
@@ -427,12 +434,10 @@ abstract class BasicBoxTest(
|
||||
}
|
||||
}
|
||||
|
||||
private class TestFile(val fileName: String, val module: TestModule) {
|
||||
private class TestFile(val fileName: String, val module: TestModule, val recompile: Boolean) {
|
||||
init {
|
||||
module.files += this
|
||||
}
|
||||
|
||||
var recompile = false
|
||||
}
|
||||
|
||||
private class TestModule(
|
||||
|
||||
@@ -20,22 +20,25 @@ import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.PairConsumer;
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsLocation;
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilder;
|
||||
|
||||
class SourceMapBuilderConsumer implements PairConsumer<SourceMapBuilder, Object> {
|
||||
@Override
|
||||
public void consume(SourceMapBuilder builder, Object sourceInfo) {
|
||||
if (!(sourceInfo instanceof PsiElement)) {
|
||||
return;
|
||||
if (sourceInfo instanceof PsiElement) {
|
||||
PsiElement element = (PsiElement) sourceInfo;
|
||||
PsiFile file = element.getContainingFile();
|
||||
int offset = element.getNode().getStartOffset();
|
||||
Document document = file.getViewProvider().getDocument();
|
||||
assert document != null;
|
||||
int line = document.getLineNumber(offset);
|
||||
int column = offset - document.getLineStartOffset(line);
|
||||
builder.addMapping(file.getViewProvider().getVirtualFile().getPath(), line, column);
|
||||
}
|
||||
else if (sourceInfo instanceof JsLocation) {
|
||||
JsLocation location = (JsLocation) sourceInfo;
|
||||
builder.addMapping(location.getFile(), location.getStartLine(), location.getStartChar());
|
||||
}
|
||||
|
||||
PsiElement element = (PsiElement) sourceInfo;
|
||||
PsiFile file = element.getContainingFile();
|
||||
int offset = element.getNode().getStartOffset();
|
||||
Document document = file.getViewProvider().getDocument();
|
||||
assert document != null;
|
||||
int line = document.getLineNumber(offset);
|
||||
int column = offset - document.getLineStartOffset(line);
|
||||
builder.addMapping(file.getViewProvider().getVirtualFile().getPath(), line, column);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user