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