JS: fix serialization of fileId in case of incremental compilation
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.serialization.js
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
sealed class KotlinFileMetadata
|
||||
|
||||
data class KotlinPsiFileMetadata(val ktFile: KtFile) : KotlinFileMetadata()
|
||||
|
||||
data class KotlinDeserializedFileMetadata(
|
||||
val packageFragment: KotlinJavascriptPackageFragment,
|
||||
val fileId: Int
|
||||
) : KotlinFileMetadata()
|
||||
@@ -16,13 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.serialization.js
|
||||
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinFileRegistry {
|
||||
private val fileIdsImpl = mutableMapOf<KtFile, Int>()
|
||||
private val fileIdsImpl = mutableMapOf<KotlinFileMetadata, Int>()
|
||||
|
||||
fun lookup(file: KtFile) = fileIdsImpl.getOrPut(file) { fileIdsImpl.size }
|
||||
fun lookup(file: KotlinFileMetadata) = fileIdsImpl.getOrPut(file) { fileIdsImpl.size }
|
||||
|
||||
val fileIds: Map<KtFile, Int>
|
||||
val fileIds: Map<KotlinFileMetadata, Int>
|
||||
get() = fileIdsImpl
|
||||
}
|
||||
|
||||
+3
-11
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
|
||||
@@ -44,7 +41,7 @@ class KotlinJavascriptPackageFragment(
|
||||
header: JsProtoBuf.Header,
|
||||
configuration: DeserializationConfiguration
|
||||
) : DeserializedPackageFragmentImpl(fqName, storageManager, module, proto, JsContainerSource(fqName, header, configuration)) {
|
||||
private val fileMap: Map<Int, FileHolder> by storageManager.createLazyValue {
|
||||
val fileMap: Map<Int, FileHolder> by storageManager.createLazyValue {
|
||||
this.proto.getExtension(JsProtoBuf.packageFragmentFiles).fileList.withIndex().associate { (index, file) ->
|
||||
(if (file.hasId()) file.id else index) to FileHolder(file.annotationList)
|
||||
}
|
||||
@@ -58,17 +55,12 @@ class KotlinJavascriptPackageFragment(
|
||||
if (DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor::class.java) != this) {
|
||||
throw IllegalArgumentException("Provided descriptor $descriptor does not belong to this package $this")
|
||||
}
|
||||
val fileId = when (descriptor) {
|
||||
is DeserializedClassDescriptor -> descriptor.classProto.getExtension(JsProtoBuf.classContainingFileId)
|
||||
is DeserializedSimpleFunctionDescriptor -> descriptor.proto.getExtension(JsProtoBuf.functionContainingFileId)
|
||||
is DeserializedPropertyDescriptor -> descriptor.proto.getExtension(JsProtoBuf.propertyContainingFileId)
|
||||
else -> null
|
||||
}
|
||||
val fileId = descriptor.extractFileId()
|
||||
|
||||
return fileId?.let { fileMap[it] }?.annotations.orEmpty()
|
||||
}
|
||||
|
||||
private inner class FileHolder(val annotationsProto: List<ProtoBuf.Annotation>) {
|
||||
inner class FileHolder(private val annotationsProto: List<ProtoBuf.Annotation>) {
|
||||
val annotations: List<AnnotationDescriptor> by storageManager.createLazyValue {
|
||||
annotationsProto.map { annotationDeserializer.deserializeAnnotation(it, nameResolver) }
|
||||
}
|
||||
|
||||
+19
-4
@@ -29,6 +29,9 @@ import org.jetbrains.kotlin.serialization.AnnotationSerializer
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.JsMetadataVersion
|
||||
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
|
||||
@@ -130,7 +133,9 @@ object KotlinJavascriptSerializationUtil {
|
||||
val builder = ProtoBuf.PackageFragment.newBuilder()
|
||||
|
||||
// TODO: ModuleDescriptor should be able to return the package only with the contents of that module, without dependencies
|
||||
val skip: (DeclarationDescriptor) -> Boolean = { DescriptorUtils.getContainingModule(it) != module || (it is MemberDescriptor && it.isExpect) }
|
||||
val skip: (DeclarationDescriptor) -> Boolean = {
|
||||
DescriptorUtils.getContainingModule(it) != module || (it is MemberDescriptor && it.isExpect)
|
||||
}
|
||||
|
||||
val fileRegistry = KotlinFileRegistry()
|
||||
val serializerExtension = KotlinJavascriptSerializerExtension(fileRegistry)
|
||||
@@ -183,8 +188,11 @@ object KotlinJavascriptSerializationUtil {
|
||||
if (id != filesProto.fileCount) {
|
||||
fileProto.id = id
|
||||
}
|
||||
for (annotationPsi in file.annotationEntries) {
|
||||
val annotation = bindingContext[BindingContext.ANNOTATION, annotationPsi]!!
|
||||
val annotations = when (file) {
|
||||
is KotlinPsiFileMetadata -> file.ktFile.annotationEntries.map { bindingContext[BindingContext.ANNOTATION, it]!! }
|
||||
is KotlinDeserializedFileMetadata -> file.packageFragment.fileMap[file.fileId]!!.annotations
|
||||
}
|
||||
for (annotation in annotations) {
|
||||
fileProto.addAnnotation(serializer.serializeAnnotation(annotation))
|
||||
}
|
||||
filesProto.addFile(fileProto)
|
||||
@@ -282,4 +290,11 @@ object KotlinJavascriptSerializationUtil {
|
||||
}
|
||||
}
|
||||
|
||||
data class KotlinJavaScriptLibraryParts(val header: JsProtoBuf.Header, val body: List<ProtoBuf.PackageFragment>)
|
||||
data class KotlinJavaScriptLibraryParts(val header: JsProtoBuf.Header, val body: List<ProtoBuf.PackageFragment>)
|
||||
|
||||
internal fun DeclarationDescriptor.extractFileId(): Int? = when (this) {
|
||||
is DeserializedClassDescriptor -> classProto.getExtension(JsProtoBuf.classContainingFileId)
|
||||
is DeserializedSimpleFunctionDescriptor -> proto.getExtension(JsProtoBuf.functionContainingFileId)
|
||||
is DeserializedPropertyDescriptor -> proto.getExtension(JsProtoBuf.propertyContainingFileId)
|
||||
else -> null
|
||||
}
|
||||
+9
-3
@@ -64,11 +64,17 @@ class KotlinJavascriptSerializerExtension(
|
||||
private fun getFileId(descriptor: DeclarationDescriptor): Int? {
|
||||
if (!DescriptorUtils.isTopLevelDeclaration(descriptor) || descriptor !is DeclarationDescriptorWithSource) return null
|
||||
|
||||
val file = descriptor.source.containingFile
|
||||
if (file !is PsiSourceFile) return null
|
||||
val fileId = descriptor.extractFileId()
|
||||
if (fileId != null) {
|
||||
(descriptor.containingDeclaration as? KotlinJavascriptPackageFragment)?.let { packageFragment ->
|
||||
return fileRegistry.lookup(KotlinDeserializedFileMetadata(packageFragment, fileId))
|
||||
}
|
||||
}
|
||||
|
||||
val file = descriptor.source.containingFile as? PsiSourceFile ?: return null
|
||||
|
||||
val psiFile = file.psiFile
|
||||
return (psiFile as? KtFile)?.let { fileRegistry.lookup(it) }
|
||||
return (psiFile as? KtFile)?.let { fileRegistry.lookup(KotlinPsiFileMetadata(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -298,7 +298,8 @@ abstract class BasicBoxTest(
|
||||
val outputFile = File(outputFileName)
|
||||
|
||||
val incrementalData = IncrementalData()
|
||||
translateFiles(psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData)
|
||||
translateFiles(psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData)
|
||||
|
||||
if (module.hasFilesToRecompile) {
|
||||
checkIncrementalCompilation(sourceDirs, module, kotlinFiles, dependencies, friends, multiModule, outputFile,
|
||||
@@ -334,7 +335,7 @@ abstract class BasicBoxTest(
|
||||
.sortedBy { it.canonicalPath }
|
||||
.map { sourceToTranslationUnit[it]!! }
|
||||
|
||||
val recompiledConfig = createConfig(sourceDirs,module, dependencies, friends, multiModule, incrementalData)
|
||||
val recompiledConfig = createConfig(sourceDirs, module, dependencies, friends, multiModule, incrementalData)
|
||||
val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
|
||||
|
||||
translateFiles(translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
@@ -348,6 +349,13 @@ abstract class BasicBoxTest(
|
||||
val recompiledSourceMap = removeRecompiledSuffix(
|
||||
FileUtil.loadFile(File(recompiledOutputFile.parentFile, recompiledOutputFile.name + ".map")))
|
||||
TestCase.assertEquals("Source map file changed after recompilation", originalSourceMap, recompiledSourceMap)
|
||||
|
||||
if (multiModule) {
|
||||
val originalMetadata = FileUtil.loadFile(File(outputFile.parentFile, outputFile.nameWithoutExtension + ".meta.js"))
|
||||
val recompiledMetadata = removeRecompiledSuffix(
|
||||
FileUtil.loadFile(File(recompiledOutputFile.parentFile, recompiledOutputFile.nameWithoutExtension + ".meta.js")))
|
||||
TestCase.assertEquals("Metadata file changed after recompilation", originalMetadata, recompiledMetadata)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeRecompiledSuffix(text: String): String = text.replace("-recompiled.js", ".js")
|
||||
@@ -475,7 +483,8 @@ abstract class BasicBoxTest(
|
||||
private fun createPsiFiles(fileNames: List<String>): List<KtFile> = fileNames.map(this::createPsiFile)
|
||||
|
||||
private fun createConfig(
|
||||
sourceDirs: List<String>,module: TestModule, dependencies: List<String>, friends: List<String>, multiModule: Boolean, incrementalData: IncrementalData?
|
||||
sourceDirs: List<String>,module: TestModule, dependencies: List<String>, friends: List<String>,
|
||||
multiModule: Boolean, incrementalData: IncrementalData?
|
||||
): JsConfig {
|
||||
val configuration = environment.configuration.copy()
|
||||
|
||||
|
||||
@@ -3686,6 +3686,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("jsModule.kt")
|
||||
public void testJsModule() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/incremental/jsModule.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleExport.kt")
|
||||
public void testMultipleExport() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/incremental/multipleExport.kt");
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
define("foo", [], function() {
|
||||
return {
|
||||
"fooF": function() {
|
||||
return "foo";
|
||||
}
|
||||
};
|
||||
});
|
||||
define("bar", [], function() {
|
||||
return {
|
||||
"barF": function() {
|
||||
return "bar";
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1018
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: a.kt
|
||||
// MODULE_KIND: AMD
|
||||
@file:JsModule("foo")
|
||||
|
||||
external fun fooF(): String
|
||||
|
||||
// FILE: b.kt
|
||||
@file:JsModule("bar")
|
||||
|
||||
external fun barF(): String
|
||||
|
||||
// FILE: c.kt
|
||||
// RECOMPILE
|
||||
fun dummyF() = "dummy"
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
// MODULE_KIND: AMD
|
||||
|
||||
fun box(): String {
|
||||
val foo = fooF()
|
||||
if (foo != "foo") return "fail1: $foo"
|
||||
|
||||
val bar = barF()
|
||||
if (bar != "bar") return "fail2: $bar"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user