JS: cache metadata, grouped by package FQN

This commit is contained in:
Anton Bannykh
2019-04-12 20:54:49 +03:00
parent c1f69ca8d6
commit c3170d1908
13 changed files with 214 additions and 45 deletions
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.metadata.js.JsProtoBuf
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.parentOrNull
import org.jetbrains.kotlin.serialization.deserialization.getClassId import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
import java.io.DataInput import java.io.DataInput
@@ -41,6 +42,7 @@ open class IncrementalJsCache(
private const val TRANSLATION_RESULT_MAP = "translation-result" private const val TRANSLATION_RESULT_MAP = "translation-result"
private const val INLINE_FUNCTIONS = "inline-functions" private const val INLINE_FUNCTIONS = "inline-functions"
private const val HEADER_FILE_NAME = "header.meta" private const val HEADER_FILE_NAME = "header.meta"
private const val PACKAGE_META_FILE = "packages-meta"
fun hasHeaderFile(cachesDir: File) = File(cachesDir, HEADER_FILE_NAME).exists() fun hasHeaderFile(cachesDir: File) = File(cachesDir, HEADER_FILE_NAME).exists()
} }
@@ -49,8 +51,10 @@ open class IncrementalJsCache(
override val dirtyOutputClassesMap = registerMap(DirtyClassesFqNameMap(DIRTY_OUTPUT_CLASSES.storageFile)) override val dirtyOutputClassesMap = registerMap(DirtyClassesFqNameMap(DIRTY_OUTPUT_CLASSES.storageFile))
private val translationResults = registerMap(TranslationResultMap(TRANSLATION_RESULT_MAP.storageFile, pathConverter)) private val translationResults = registerMap(TranslationResultMap(TRANSLATION_RESULT_MAP.storageFile, pathConverter))
private val inlineFunctions = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile, pathConverter)) private val inlineFunctions = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile, pathConverter))
private val packageMetadata = registerMap(PackageMetadataMap(PACKAGE_META_FILE.storageFile))
private val dirtySources = hashSetOf<File>() private val dirtySources = hashSetOf<File>()
private val dirtyPackages = hashSetOf<String>()
private val headerFile: File private val headerFile: File
get() = File(cachesDir, HEADER_FILE_NAME) get() = File(cachesDir, HEADER_FILE_NAME)
@@ -63,6 +67,11 @@ open class IncrementalJsCache(
} }
override fun markDirty(removedAndCompiledSources: Collection<File>) { override fun markDirty(removedAndCompiledSources: Collection<File>) {
removedAndCompiledSources.forEach { sourceFile ->
sourceToClassesMap[sourceFile].forEach {
dirtyPackages += it.parentOrNull()?.asString() ?: ""
}
}
super.markDirty(removedAndCompiledSources) super.markDirty(removedAndCompiledSources)
dirtySources.addAll(removedAndCompiledSources) dirtySources.addAll(removedAndCompiledSources)
} }
@@ -95,6 +104,11 @@ open class IncrementalJsCache(
for ((srcFile, inlineDeclarations) in incrementalResults.inlineFunctions) { for ((srcFile, inlineDeclarations) in incrementalResults.inlineFunctions) {
inlineFunctions.process(srcFile, inlineDeclarations, changesCollector) inlineFunctions.process(srcFile, inlineDeclarations, changesCollector)
} }
for ((packageName, metadata) in incrementalResults.packageMetadata) {
packageMetadata.put(packageName, metadata)
dirtyPackages.remove(packageName)
}
} }
private fun registerOutputForFile(srcFile: File, name: FqName) { private fun registerOutputForFile(srcFile: File, name: FqName) {
@@ -108,18 +122,31 @@ open class IncrementalJsCache(
inlineFunctions.remove(it) inlineFunctions.remove(it)
} }
removeAllFromClassStorage(dirtyOutputClassesMap.getDirtyOutputClasses(), changesCollector) removeAllFromClassStorage(dirtyOutputClassesMap.getDirtyOutputClasses(), changesCollector)
dirtyPackages.forEach {
packageMetadata.remove(it)
}
dirtySources.clear() dirtySources.clear()
dirtyOutputClassesMap.clean() dirtyOutputClassesMap.clean()
dirtyPackages.clear()
} }
fun nonDirtyPackageParts(): Map<File, TranslationResultValue> = fun nonDirtyPackageParts(): Map<File, TranslationResultValue> =
hashMapOf<File, TranslationResultValue>().apply { hashMapOf<File, TranslationResultValue>().apply {
for (file in translationResults.keys()) { for (file in translationResults.keys()) {
if (file !in dirtySources) { if (file !in dirtySources) {
put(file, translationResults[file]!!) put(file, translationResults[file]!!)
} }
} }
} }
fun packageMetadata(): Map<String, ByteArray> = hashMapOf<String, ByteArray>().apply {
for (fqNameString in packageMetadata.keys()) {
if (fqNameString !in dirtyPackages) {
put(fqNameString, packageMetadata[fqNameString]!!)
}
}
}
} }
private object TranslationResultValueExternalizer : DataExternalizer<TranslationResultValue> { private object TranslationResultValueExternalizer : DataExternalizer<TranslationResultValue> {
@@ -154,7 +181,8 @@ private object TranslationResultValueExternalizer : DataExternalizer<Translation
private class TranslationResultMap( private class TranslationResultMap(
storageFile: File, storageFile: File,
private val pathConverter: FileToPathConverter private val pathConverter: FileToPathConverter
) : BasicStringMap<TranslationResultValue>(storageFile, TranslationResultValueExternalizer) { ) :
BasicStringMap<TranslationResultValue>(storageFile, TranslationResultValueExternalizer) {
override fun dumpValue(value: TranslationResultValue): String = override fun dumpValue(value: TranslationResultValue): String =
"Metadata: ${value.metadata.md5()}, Binary AST: ${value.binaryAst.md5()}, InlineData: ${value.inlineData.md5()}" "Metadata: ${value.metadata.md5()}, Binary AST: ${value.binaryAst.md5()}, InlineData: ${value.inlineData.md5()}"
@@ -181,7 +209,7 @@ private class TranslationResultMap(
} }
} }
fun getProtoData(sourceFile: File, metadata: ByteArray): Map<ClassId, ProtoData> { fun getProtoData(sourceFile: File, metadata: ByteArray): Map<ClassId, ProtoData> {
val classes = hashMapOf<ClassId, ProtoData>() val classes = hashMapOf<ClassId, ProtoData>()
val proto = ProtoBuf.PackageFragment.parseFrom(metadata, JsSerializerProtocol.extensionRegistry) val proto = ProtoBuf.PackageFragment.parseFrom(metadata, JsSerializerProtocol.extensionRegistry)
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames) val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
@@ -227,4 +255,35 @@ private class InlineFunctionsMap(
override fun dumpValue(value: Map<String, Long>): String = override fun dumpValue(value: Map<String, Long>): String =
value.dumpMap { java.lang.Long.toHexString(it) } value.dumpMap { java.lang.Long.toHexString(it) }
}
private object ByteArrayExternalizer : DataExternalizer<ByteArray> {
override fun save(output: DataOutput, value: ByteArray) {
output.writeInt(value.size)
output.write(value)
}
override fun read(input: DataInput): ByteArray {
val size = input.readInt()
val array = ByteArray(size)
input.readFully(array)
return array
}
}
private class PackageMetadataMap(storageFile: File) : BasicStringMap<ByteArray>(storageFile, ByteArrayExternalizer) {
fun put(packageName: String, newMetadata: ByteArray) {
storage[packageName] = newMetadata
}
fun remove(packageName: String) {
storage.remove(packageName)
}
fun keys() = storage.keys
operator fun get(packageName: String) = storage[packageName]
override fun dumpValue(value: ByteArray): String = "Package metadata: ${value.md5()}"
} }
@@ -18,4 +18,7 @@ class IncrementalDataProviderFromCache(private val cache: IncrementalJsCache) :
override val metadataVersion: IntArray override val metadataVersion: IntArray
get() = JsMetadataVersion.INSTANCE.toArray() // TODO: store and load correct metadata version get() = JsMetadataVersion.INSTANCE.toArray() // TODO: store and load correct metadata version
override val packageMetadata: Map<String, ByteArray>
get() = cache.packageMetadata()
} }
@@ -65,6 +65,7 @@ import org.jetbrains.kotlin.js.facade.TranslationUnit;
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion; import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.serialization.js.ModuleKind; import org.jetbrains.kotlin.serialization.js.ModuleKind;
@@ -73,6 +74,7 @@ import org.jetbrains.kotlin.utils.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR; import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR;
import static org.jetbrains.kotlin.cli.common.ExitCode.OK; import static org.jetbrains.kotlin.cli.common.ExitCode.OK;
@@ -144,6 +146,15 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
} }
Arrays.sort(allSources); Arrays.sort(allSources);
Set<FqName> dirtyPackages = nonCompiledSources.values().stream().map(KtFile::getPackageFqName).collect(Collectors.toSet());
Map<FqName, byte[]> packageMetadata = new HashMap<>();
for (Map.Entry<String, byte[]> e : incrementalDataProvider.getPackageMetadata().entrySet()) {
FqName name = new FqName(e.getKey());
if (!dirtyPackages.contains(name)) {
packageMetadata.put(name, e.getValue());
}
}
List<TranslationUnit> translationUnits = new ArrayList<>(); List<TranslationUnit> translationUnits = new ArrayList<>();
for (i = 0; i < allSources.length; i++) { for (i = 0; i < allSources.length; i++) {
KtFile nonCompiled = nonCompiledSources.get(allSources[i]); KtFile nonCompiled = nonCompiledSources.get(allSources[i]);
@@ -155,7 +166,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
translationUnits.add(new TranslationUnit.BinaryAst(translatedValue.getBinaryAst(), translatedValue.getInlineData())); translationUnits.add(new TranslationUnit.BinaryAst(translatedValue.getBinaryAst(), translatedValue.getInlineData()));
} }
} }
return translator.translateUnits(reporter, translationUnits, mainCallParameters, jsAnalysisResult); return translator.translateUnits(reporter, translationUnits, mainCallParameters, jsAnalysisResult, packageMetadata);
} }
CollectionsKt.sortBy(allKotlinFiles, ktFile -> VfsUtilCore.virtualToIoFile(ktFile.getVirtualFile())); CollectionsKt.sortBy(allKotlinFiles, ktFile -> VfsUtilCore.virtualToIoFile(ktFile.getVirtualFile()));
@@ -134,6 +134,10 @@ open class CompilerCallbackServicesFacadeServer(
incrementalResultsConsumer!!.processInlineFunctions(functions) incrementalResultsConsumer!!.processInlineFunctions(functions)
} }
override fun incrementalResultsConsumer_processPackageMetadata(packageName: String, metadata: ByteArray) {
incrementalResultsConsumer!!.processPackageMetadata(packageName, metadata)
}
override fun incrementalDataProvider_getHeaderMetadata(): ByteArray = incrementalDataProvider!!.headerMetadata override fun incrementalDataProvider_getHeaderMetadata(): ByteArray = incrementalDataProvider!!.headerMetadata
override fun incrementalDataProvider_getMetadataVersion(): IntArray = incrementalDataProvider!!.metadataVersion override fun incrementalDataProvider_getMetadataVersion(): IntArray = incrementalDataProvider!!.metadataVersion
@@ -142,4 +146,9 @@ open class CompilerCallbackServicesFacadeServer(
incrementalDataProvider!!.compiledPackageParts.entries.map { incrementalDataProvider!!.compiledPackageParts.entries.map {
CompiledPackagePart(it.key.path, it.value.metadata, it.value.binaryAst, it.value.inlineData) CompiledPackagePart(it.key.path, it.value.metadata, it.value.binaryAst, it.value.inlineData)
} }
override fun incrementalDataProvider_getPackageMetadata(): Collection<PackageMetadata> =
incrementalDataProvider!!.packageMetadata.entries.map { (fqName, metadata) ->
PackageMetadata(fqName, metadata)
}
} }
@@ -112,6 +112,9 @@ interface CompilerCallbackServicesFacade : Remote {
@Throws(RemoteException::class) @Throws(RemoteException::class)
fun incrementalResultsConsumer_processInlineFunctions(functions: Collection<JsInlineFunctionHash>) fun incrementalResultsConsumer_processInlineFunctions(functions: Collection<JsInlineFunctionHash>)
@Throws(RemoteException::class)
fun incrementalResultsConsumer_processPackageMetadata(packageName: String, metadata: ByteArray)
// --------------------------------------------------- // ---------------------------------------------------
// IncrementalDataProvider (js) // IncrementalDataProvider (js)
@Throws(RemoteException::class) @Throws(RemoteException::class)
@@ -122,6 +125,9 @@ interface CompilerCallbackServicesFacade : Remote {
@Throws(RemoteException::class) @Throws(RemoteException::class)
fun incrementalDataProvider_getMetadataVersion(): IntArray fun incrementalDataProvider_getMetadataVersion(): IntArray
@Throws(RemoteException::class)
fun incrementalDataProvider_getPackageMetadata(): Collection<PackageMetadata>
} }
class CompiledPackagePart( class CompiledPackagePart(
@@ -129,6 +135,17 @@ class CompiledPackagePart(
val metadata: ByteArray, val binaryAst: ByteArray, val inlineData: ByteArray val metadata: ByteArray, val binaryAst: ByteArray, val inlineData: ByteArray
) : Serializable ) : Serializable
class PackageMetadata(
val packageName: String,
val metadata: ByteArray
) : Serializable {
companion object {
// just a random number, but should never be changed to avoid deserialization problems
private val serialVersionUID: Long = 54021986502349756L
}
}
class RmiFriendlyCompilationCanceledException : Exception(), Serializable { class RmiFriendlyCompilationCanceledException : Exception(), Serializable {
companion object { companion object {
// just a random number, but should never be changed to avoid deserialization problems // just a random number, but should never be changed to avoid deserialization problems
@@ -33,4 +33,14 @@ class RemoteIncrementalDataProvider(val facade: CompilerCallbackServicesFacade,
get() = rpcProfiler.withMeasure(this) { get() = rpcProfiler.withMeasure(this) {
facade.incrementalDataProvider_getMetadataVersion() facade.incrementalDataProvider_getMetadataVersion()
} }
override val packageMetadata: Map<String, ByteArray>
get() = rpcProfiler.withMeasure(this) {
val result = mutableMapOf<String, ByteArray>()
facade.incrementalDataProvider_getPackageMetadata().forEach {
val prev = result.put(it.packageName, it.metadata)
check(prev == null) { "packageMetadata: duplicated entry for package `${it.packageName}`" }
}
result
}
} }
@@ -15,6 +15,7 @@ import java.io.File
class RemoteIncrementalResultsConsumer(val facade: CompilerCallbackServicesFacade, eventManager: EventManager, val rpcProfiler: Profiler) : class RemoteIncrementalResultsConsumer(val facade: CompilerCallbackServicesFacade, eventManager: EventManager, val rpcProfiler: Profiler) :
IncrementalResultsConsumer { IncrementalResultsConsumer {
init { init {
eventManager. eventManager.
onCompilationFinished(this::flush) onCompilationFinished(this::flush)
@@ -42,6 +43,12 @@ class RemoteIncrementalResultsConsumer(val facade: CompilerCallbackServicesFacad
override fun processInlineFunctions(functions: Collection<JsInlineFunctionHash>) = error("Should not be called in Daemon Server") override fun processInlineFunctions(functions: Collection<JsInlineFunctionHash>) = error("Should not be called in Daemon Server")
override fun processPackageMetadata(packageName: String, metadata: ByteArray) {
rpcProfiler.withMeasure(this) {
facade.incrementalResultsConsumer_processPackageMetadata(packageName, metadata)
}
}
fun flush() { fun flush() {
rpcProfiler.withMeasure(this) { rpcProfiler.withMeasure(this) {
facade.incrementalResultsConsumer_processInlineFunctions(deferInlineFuncs.map { facade.incrementalResultsConsumer_processInlineFunctions(deferInlineFuncs.map {
@@ -123,7 +123,7 @@ abstract class AbstractJsLookupTrackerTest : AbstractLookupTrackerTest() {
if (header != null) { if (header != null) {
register( register(
IncrementalDataProvider::class.java, IncrementalDataProvider::class.java,
IncrementalDataProviderImpl(header!!, packageParts, JsMetadataVersion.INSTANCE.toArray()) IncrementalDataProviderImpl(header!!, packageParts, JsMetadataVersion.INSTANCE.toArray(), emptyMap()) // TODO pass correct metadata
) )
} }
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.incremental.js package org.jetbrains.kotlin.incremental.js
import org.jetbrains.kotlin.name.FqName
import java.io.File import java.io.File
// byte arrays are used to simplify passing to different classloaders // byte arrays are used to simplify passing to different classloaders
@@ -27,10 +28,14 @@ interface IncrementalDataProvider {
val compiledPackageParts: Map<File, TranslationResultValue> val compiledPackageParts: Map<File, TranslationResultValue>
val metadataVersion: IntArray val metadataVersion: IntArray
/** gets non-dirty package metadata from previous compilation */
val packageMetadata: Map<String, ByteArray>
} }
class IncrementalDataProviderImpl( class IncrementalDataProviderImpl(
override val headerMetadata: ByteArray, override val headerMetadata: ByteArray,
override val compiledPackageParts: Map<File, TranslationResultValue>, override val compiledPackageParts: Map<File, TranslationResultValue>,
override val metadataVersion: IntArray override val metadataVersion: IntArray,
override val packageMetadata: Map<String, ByteArray>
) : IncrementalDataProvider ) : IncrementalDataProvider
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.incremental.js package org.jetbrains.kotlin.incremental.js
import org.jetbrains.kotlin.name.FqName
import java.io.File import java.io.File
import java.io.Serializable import java.io.Serializable
import java.security.MessageDigest import java.security.MessageDigest
@@ -39,6 +40,8 @@ interface IncrementalResultsConsumer {
* Used in daemon RPC. * Used in daemon RPC.
*/ */
fun processInlineFunctions(functions: Collection<JsInlineFunctionHash>) fun processInlineFunctions(functions: Collection<JsInlineFunctionHash>)
fun processPackageMetadata(packageName: String, metadata: ByteArray)
} }
class JsInlineFunctionHash(val sourceFilePath: String, val fqName: String, val inlineFunctionMd5Hash: Long): Serializable class JsInlineFunctionHash(val sourceFilePath: String, val fqName: String, val inlineFunctionMd5Hash: Long): Serializable
@@ -97,6 +100,14 @@ class IncrementalResultsConsumerImpl : IncrementalResultsConsumer {
val mapForSource = _deferInlineFuncs.getOrPut(sourceFile) { hashMapOf() } val mapForSource = _deferInlineFuncs.getOrPut(sourceFile) { hashMapOf() }
mapForSource[fqName] = FunctionWithSourceInfo(inlineFunction, line, column) mapForSource[fqName] = FunctionWithSourceInfo(inlineFunction, line, column)
} }
private val _packageMetadata = hashMapOf<String, ByteArray>()
val packageMetadata: Map<String, ByteArray>
get() = _packageMetadata
override fun processPackageMetadata(packageName: String, metadata: ByteArray) {
_packageMetadata[packageName] = metadata
}
} }
private fun ByteArray.md5(): Long { private fun ByteArray.md5(): Long {
@@ -75,20 +75,8 @@ object KotlinJavascriptSerializationUtil {
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
metadataVersion: JsMetadataVersion metadataVersion: JsMetadataVersion
): SerializedMetadata { ): SerializedMetadata {
val serializedFragments = HashMap<FqName, ByteArray>() val serializedFragments =
val module = jsDescriptor.data emptyMap<FqName, ByteArray>().missingMetadata(bindingContext, jsDescriptor.data, languageVersionSettings, metadataVersion)
for (fqName in getPackagesFqNames(module).sortedBy { it.asString() }) {
val fragment = serializeDescriptors(
bindingContext, module,
module.getPackage(fqName).memberScope.getContributedDescriptors(),
fqName, languageVersionSettings, metadataVersion
)
if (!fragment.isEmpty()) {
serializedFragments[fqName] = fragment.toByteArray()
}
}
return SerializedMetadata(serializedFragments, jsDescriptor, languageVersionSettings, metadataVersion) return SerializedMetadata(serializedFragments, jsDescriptor, languageVersionSettings, metadataVersion)
} }
@@ -258,9 +246,6 @@ object KotlinJavascriptSerializationUtil {
return filesProto.build() return filesProto.build()
} }
private fun ProtoBuf.PackageFragment.isEmpty(): Boolean =
class_Count == 0 && `package`.let { it.functionCount == 0 && it.propertyCount == 0 && it.typeAliasCount == 0 }
fun serializeHeader( fun serializeHeader(
module: ModuleDescriptor, packageFqName: FqName?, languageVersionSettings: LanguageVersionSettings module: ModuleDescriptor, packageFqName: FqName?, languageVersionSettings: LanguageVersionSettings
): JsProtoBuf.Header { ): JsProtoBuf.Header {
@@ -293,7 +278,7 @@ object KotlinJavascriptSerializationUtil {
return header.build() return header.build()
} }
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> { fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
return mutableSetOf<FqName>().apply { return mutableSetOf<FqName>().apply {
getSubPackagesFqNames(module.getPackage(FqName.ROOT), this) getSubPackagesFqNames(module.getPackage(FqName.ROOT), this)
add(FqName.ROOT) add(FqName.ROOT)
@@ -331,6 +316,34 @@ object KotlinJavascriptSerializationUtil {
} }
} }
fun Map<FqName, ByteArray>.missingMetadata(
bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor,
languageVersionSettings: LanguageVersionSettings,
metadataVersion: JsMetadataVersion
): Map<FqName, ByteArray> {
val serializedFragments = HashMap<FqName, ByteArray>()
for (fqName in KotlinJavascriptSerializationUtil.getPackagesFqNames(moduleDescriptor).sortedBy { it.asString() }) {
if (fqName in this) continue
val fragment = KotlinJavascriptSerializationUtil.serializeDescriptors(
bindingContext, moduleDescriptor,
moduleDescriptor.getPackage(fqName).memberScope.getContributedDescriptors(),
fqName, languageVersionSettings, metadataVersion
)
if (!fragment.isEmpty()) {
serializedFragments[fqName] = fragment.toByteArray()
}
}
return serializedFragments
}
private fun ProtoBuf.PackageFragment.isEmpty(): Boolean =
class_Count == 0 && `package`.let { it.functionCount == 0 && it.propertyCount == 0 && it.typeAliasCount == 0 }
data class KotlinJavaScriptLibraryParts( data class KotlinJavaScriptLibraryParts(
val header: JsProtoBuf.Header, val header: JsProtoBuf.Header,
val body: List<ProtoBuf.PackageFragment>, val body: List<ProtoBuf.PackageFragment>,
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.cli.common.output.writeAllTo
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.daemon.common.PackageMetadata
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderImpl import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderImpl
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
@@ -48,6 +49,7 @@ import org.jetbrains.kotlin.js.test.interop.ScriptEngineV8Lazy
import org.jetbrains.kotlin.js.test.utils.* import org.jetbrains.kotlin.js.test.utils.*
import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.js.util.TextOutputImpl
import org.jetbrains.kotlin.metadata.DebugProtoBuf import org.jetbrains.kotlin.metadata.DebugProtoBuf
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
@@ -395,6 +397,7 @@ abstract class BasicBoxTest(
val sourceFile = File(testFile.fileName) val sourceFile = File(testFile.fileName)
incrementalData.translatedFiles.remove(sourceFile) incrementalData.translatedFiles.remove(sourceFile)
sourceToTranslationUnit[sourceFile] = TranslationUnit.SourceFile(createPsiFile(testFile.fileName)) sourceToTranslationUnit[sourceFile] = TranslationUnit.SourceFile(createPsiFile(testFile.fileName))
incrementalData.packageMetadata.remove(testFile.packageName)
} }
} }
for ((sourceFile, data) in incrementalData.translatedFiles) { for ((sourceFile, data) in incrementalData.translatedFiles) {
@@ -461,7 +464,11 @@ abstract class BasicBoxTest(
private fun removeRecompiledSuffix(text: String): String = text.replace("-recompiled.js", ".js") private fun removeRecompiledSuffix(text: String): String = text.replace("-recompiled.js", ".js")
class IncrementalData(var header: ByteArray? = null, val translatedFiles: MutableMap<File, TranslationResultValue> = hashMapOf()) class IncrementalData(
var header: ByteArray? = null,
val translatedFiles: MutableMap<File, TranslationResultValue> = hashMapOf(),
val packageMetadata: MutableMap<String, ByteArray> = hashMapOf()
)
protected open fun translateFiles( protected open fun translateFiles(
units: List<TranslationUnit>, units: List<TranslationUnit>,
@@ -505,6 +512,8 @@ abstract class BasicBoxTest(
incrementalData.translatedFiles[srcFile] = data incrementalData.translatedFiles[srcFile] = data
} }
incrementalData.packageMetadata += incrementalService.packageMetadata
incrementalData.header = incrementalService.headerMetadata incrementalData.header = incrementalService.headerMetadata
} }
@@ -640,7 +649,7 @@ abstract class BasicBoxTest(
if (header != null) { if (header != null) {
configuration.put( configuration.put(
JSConfigurationKeys.INCREMENTAL_DATA_PROVIDER, JSConfigurationKeys.INCREMENTAL_DATA_PROVIDER,
IncrementalDataProviderImpl(header, incrementalData.translatedFiles, JsMetadataVersion.INSTANCE.toArray()) IncrementalDataProviderImpl(header, incrementalData.translatedFiles, JsMetadataVersion.INSTANCE.toArray(), incrementalData.packageMetadata)
) )
} }
@@ -768,7 +777,12 @@ abstract class BasicBoxTest(
currentModule.sourceMapSourceEmbedding = SourceMapSourceEmbedding.valueOf(match.groupValues[1]) currentModule.sourceMapSourceEmbedding = SourceMapSourceEmbedding.valueOf(match.groupValues[1])
} }
return TestFile(temporaryFile.absolutePath, currentModule, recompile = RECOMPILE_PATTERN.matcher(text).find()) return TestFile(
temporaryFile.absolutePath,
currentModule,
recompile = RECOMPILE_PATTERN.matcher(text).find(),
packageName = ktFile.packageFqName.asString()
)
} }
override fun createModule(name: String, dependencies: List<String>, friends: List<String>) = override fun createModule(name: String, dependencies: List<String>, friends: List<String>) =
@@ -779,7 +793,7 @@ abstract class BasicBoxTest(
} }
} }
private class TestFile(val fileName: String, val module: TestModule, val recompile: Boolean) { private class TestFile(val fileName: String, val module: TestModule, val recompile: Boolean, val packageName: String) {
init { init {
module.files += this module.files += this
} }
@@ -54,6 +54,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf
import org.jetbrains.kotlin.serialization.js.missingMetadata
/** /**
* An entry point of translator. * An entry point of translator.
@@ -140,8 +141,8 @@ class K2JSTranslator @JvmOverloads constructor(
expandIsCalls(translationResult.newFragments) expandIsCalls(translationResult.newFragments)
checkCanceled() checkCanceled()
updatePackageMetadata(translationResult, bindingTrace, moduleDescriptor, packageMetadata) updateMetadataMap(bindingTrace.bindingContext, moduleDescriptor, packageMetadata)
trySaveIncrementalData(translationResult, pathResolver, bindingTrace, moduleDescriptor, packageMetadata) trySaveIncrementalData(translationResult, pathResolver, bindingTrace, moduleDescriptor)
checkCanceled() checkCanceled()
// Global phases // Global phases
@@ -167,11 +168,34 @@ class K2JSTranslator @JvmOverloads constructor(
} }
} }
private fun updateMetadataMap(
bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor,
packageMetadata: MutableMap<FqName, ByteArray>
) {
val additionalMetadata = packageMetadata.missingMetadata(
bindingContext,
moduleDescriptor,
config.configuration.languageVersionSettings,
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION) as? JsMetadataVersion ?: JsMetadataVersion.INSTANCE)
for ((packageName, metadata) in additionalMetadata) {
incrementalResults?.processPackageMetadata(packageName.asString(), metadata)
}
packageMetadata += additionalMetadata
}
private fun checkCanceled() { private fun checkCanceled() {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
} }
private fun serializeScope(bindingContext: BindingContext, moduleDescriptor: ModuleDescriptor, packageName: FqName, scope: Collection<DeclarationDescriptor>): ProtoBuf.PackageFragment { private fun serializeScope(
bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor,
packageName: FqName,
scope: Collection<DeclarationDescriptor>
): ProtoBuf.PackageFragment {
val metadataVersion = config.configuration.get(CommonConfigurationKeys.METADATA_VERSION) val metadataVersion = config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)
return KotlinJavascriptSerializationUtil.serializeDescriptors( return KotlinJavascriptSerializationUtil.serializeDescriptors(
bindingContext, bindingContext,
@@ -183,25 +207,11 @@ class K2JSTranslator @JvmOverloads constructor(
) )
} }
private fun updatePackageMetadata(
translationResult: AstGenerationResult,
bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor,
packageMetadata: MutableMap<FqName, ByteArray>
) {
translationResult.translatedSourceFiles.keys.mapTo(mutableSetOf()) { it.file.packageFqName }.forEach { packageName ->
val memberScope = moduleDescriptor.getPackage(packageName).memberScope.getContributedDescriptors()
val bytes = serializeScope(bindingTrace.bindingContext, moduleDescriptor, packageName, memberScope).toByteArray()
packageMetadata[packageName] = bytes
}
}
private fun trySaveIncrementalData( private fun trySaveIncrementalData(
translationResult: AstGenerationResult, translationResult: AstGenerationResult,
pathResolver: SourceFilePathResolver, pathResolver: SourceFilePathResolver,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor
packageMetadata: Map<FqName, ByteArray>
) { ) {
// TODO Maybe switch validation on for recompile // TODO Maybe switch validation on for recompile
if (incrementalResults == null && !shouldValidateJsAst) return if (incrementalResults == null && !shouldValidateJsAst) return