diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt index 5e932a2ddd3..505755314ad 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.serialization.cityHash64 import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.* @@ -241,18 +240,15 @@ class CacheUpdater( } private fun KotlinSourceFileMutableMap.getExportedSignaturesAndAddMetadata( - jsIrLinker: JsIrLinker, - irModule: IrModuleFragment, + symbolProviders: List, libFile: KotlinLibraryFile, dirtySrcFiles: Set ): Map { val idSignatureToFile = hashMapOf() - val moduleDeserializer = jsIrLinker.moduleDeserializer(irModule.descriptor) val incrementalCache = getLibIncrementalCache(libFile) - for (fileDeserializer in moduleDeserializer.fileDeserializers()) { - val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() - val maybeImportedSignatures = HashSet(reachableSignatures.keys) - val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols) + for (fileSymbolProvider in symbolProviders) { + val maybeImportedSignatures = fileSymbolProvider.getReachableSignatures().toHashSet() + val implementedSymbols = fileSymbolProvider.getImplementedSymbols() for ((signature, symbol) in implementedSymbols) { var symbolCanBeExported = maybeImportedSignatures.remove(signature) resolveFakeOverrideFunction(symbol)?.let { resolvedSignature -> @@ -262,11 +258,11 @@ class CacheUpdater( symbolCanBeExported = true } if (symbolCanBeExported) { - idSignatureToFile[signature] = IdSignatureSource(libFile, fileDeserializer.file, symbol) + idSignatureToFile[signature] = IdSignatureSource(libFile, fileSymbolProvider.irFile, symbol) } } - val libSrcFile = KotlinSourceFile(fileDeserializer.file) + val libSrcFile = KotlinSourceFile(fileSymbolProvider.irFile) if (libSrcFile in dirtySrcFiles) { val metadata = incrementalCache.fetchSourceFileFullMetadata(libSrcFile) this[libFile, libSrcFile] = DirtyFileMetadata(maybeImportedSignatures, metadata.directDependencies) @@ -295,16 +291,16 @@ class CacheUpdater( } fun rebuildDirtySourceMetadata( - jsIrLinker: JsIrLinker, - loadedFragments: Map, + loadedIr: LoadedJsIr, dirtySrcFiles: KotlinSourceFileMap, ): KotlinSourceFileMap { val idSignatureToFile = hashMapOf() val updatedMetadata = KotlinSourceFileMutableMap() - for ((lib, irModule) in loadedFragments) { + for (lib in loadedIr.loadedFragments.keys) { val libDirtySrcFiles = dirtySrcFiles[lib]?.keys ?: emptySet() - idSignatureToFile += updatedMetadata.getExportedSignaturesAndAddMetadata(jsIrLinker, irModule, lib, libDirtySrcFiles) + val symbolProviders = loadedIr.getSignatureProvidersForLib(lib) + idSignatureToFile += updatedMetadata.getExportedSignaturesAndAddMetadata(symbolProviders, lib, libDirtySrcFiles) } signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile) @@ -488,20 +484,17 @@ class CacheUpdater( } fun updateStdlibIntrinsicDependencies( - linker: JsIrLinker, + loadedIr: LoadedJsIr, mainModule: IrModuleFragment, - loadedFragments: Map, dirtyFiles: Map> ) { - val stdlibDescriptor = mainModule.descriptor.builtIns.builtInsModule - val (stdlibFile, stdlibIr) = loadedFragments.entries.find { - it.value.descriptor === stdlibDescriptor - } ?: notFoundIcError("stdlib loaded fragment") - + val (stdlibFile, _) = findStdlib(mainModule, loadedIr.loadedFragments) val stdlibDirtyFiles = dirtyFiles[stdlibFile] ?: return + val stdlibSymbolProviders = loadedIr.getSignatureProvidersForLib(stdlibFile) + val updatedMetadata = KotlinSourceFileMutableMap() - val idSignatureToFile = updatedMetadata.getExportedSignaturesAndAddMetadata(linker, stdlibIr, stdlibFile, stdlibDirtyFiles) + val idSignatureToFile = updatedMetadata.getExportedSignaturesAndAddMetadata(stdlibSymbolProviders, stdlibFile, stdlibDirtyFiles) signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile) @@ -550,10 +543,7 @@ class CacheUpdater( } } - fun buildAndCommitCacheArtifacts( - jsIrLinker: JsIrLinker, - loadedFragments: Map - ): Map { + fun buildAndCommitCacheArtifacts(loadedIr: LoadedJsIr): Map { removedIncrementalCaches.forEach { if (!it.cacheDir.deleteRecursively()) { icError("can not delete cache directory ${it.cacheDir.absolutePath}") @@ -562,13 +552,21 @@ class CacheUpdater( return libraryDependencies.keys.associate { library -> val libFile = KotlinLibraryFile(library) val incrementalCache = getLibIncrementalCache(libFile) - val libFragment = loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile) - val moduleDeserializer = jsIrLinker.moduleDeserializer(libFragment.descriptor) + val providers = loadedIr.getSignatureProvidersForLib(libFile) + val signatureToIndexMapping = providers.associate { KotlinSourceFile(it.irFile) to it.getSignatureToIndexMapping() } - val signatureToIndexMapping = moduleDeserializer.fileDeserializers().associate { - KotlinSourceFile(it.file) to it.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() + val cacheArtifact = incrementalCache.buildIncrementalCacheArtifact(signatureToIndexMapping) + + val libFragment = loadedIr.loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile) + val sourceFilesFromCache = cacheArtifact.getSourceFiles() + for (irFile in libFragment.files) { + if (KotlinSourceFile(irFile) !in sourceFilesFromCache) { + // IC doesn't support cases when extra IrFiles (which don't exist in klib) are added into IrModuleFragment + icError("file ${irFile.fileEntry.name} is absent in incremental cache and klib", libFile) + } } - libFile to incrementalCache.buildIncrementalCacheArtifact(signatureToIndexMapping) + + libFile to cacheArtifact } } } @@ -642,7 +640,7 @@ class CacheUpdater( while (true) { stopwatch.startNext("Dependencies ($iterations) - updating a dependency graph") - val dirtyMetadata = updater.rebuildDirtySourceMetadata(loadedIr.linker, loadedIr.loadedFragments, lastDirtyFiles) + val dirtyMetadata = updater.rebuildDirtySourceMetadata(loadedIr, lastDirtyFiles) stopwatch.startNext("Dependencies ($iterations) - collecting files with updated exports and imports") val filesWithModifiedExportsOrImports = updater.collectFilesWithModifiedExportsAndImports(dirtyMetadata) @@ -672,15 +670,15 @@ class CacheUpdater( val compilerForIC = compilerInterfaceFactory.createCompilerForIC(mainModuleFragment, compilerConfiguration) // Load declarations referenced during `context` initialization - loadedIr.linker.loadUnboundSymbols() + loadedIr.loadUnboundSymbols() val dirtyFiles = dirtyFileExports.entries.associateTo(HashMap(dirtyFileExports.size)) { it.key to HashSet(it.value.keys) } stopwatch.startNext("Processing IR - updating intrinsics and builtins dependencies") - updater.updateStdlibIntrinsicDependencies(loadedIr.linker, mainModuleFragment, loadedIr.loadedFragments, dirtyFiles) + updater.updateStdlibIntrinsicDependencies(loadedIr, mainModuleFragment, dirtyFiles) stopwatch.startNext("Incremental cache - building artifacts") - val incrementalCachesArtifacts = updater.buildAndCommitCacheArtifacts(loadedIr.linker, loadedIr.loadedFragments) + val incrementalCachesArtifacts = updater.buildAndCommitCacheArtifacts(loadedIr) stopwatch.stop() return IrForDirtyFilesAndCompiler(incrementalCachesArtifacts, loadedIr.loadedFragments, dirtyFiles, compilerForIC) @@ -746,9 +744,9 @@ fun rebuildCacheForDirtyFiles( val modifiedFiles = mapOf(libFile to dirtySrcFiles.associateWith { emptyMetadata }) val jsIrLoader = JsIrLinkerLoader(configuration, dependencyGraph, emptyList(), irFactory) - val (jsIrLinker, irModules) = jsIrLoader.loadIr(KotlinSourceFileMap(modifiedFiles), true) + val loadedIr = jsIrLoader.loadIr(KotlinSourceFileMap(modifiedFiles), true) - val currentIrModule = irModules[libFile] ?: notFoundIcError("loaded fragment", libFile) + val currentIrModule = loadedIr.loadedFragments[libFile] ?: notFoundIcError("loaded fragment", libFile) val dirtyIrFiles = dirtyFiles?.let { val files = it.toSet() currentIrModule.files.filter { irFile -> irFile.fileEntry.name in files } @@ -764,9 +762,9 @@ fun rebuildCacheForDirtyFiles( ) // Load declarations referenced during `context` initialization - jsIrLinker.loadUnboundSymbols() + loadedIr.loadUnboundSymbols() - val fragments = compilerWithIC.compile(irModules.values, dirtyIrFiles, mainArguments).map { it() } + val fragments = compilerWithIC.compile(loadedIr.loadedFragments.values, dirtyIrFiles, mainArguments).map { it() } return currentIrModule to dirtyIrFiles.zip(fragments) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt index 4e60e2e9424..b8cb667b7b0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.ir.backend.js.ic +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.protobuf.CodedInputStream import org.jetbrains.kotlin.protobuf.CodedOutputStream import java.io.File @@ -47,6 +48,17 @@ internal inline fun buildMapUntil(to: Int, builderAction: MutableMap(to).apply { repeat(to) { builderAction(it) } } } +internal fun findStdlib( + mainFragment: IrModuleFragment, + allFragments: Map +): Pair { + val stdlibDescriptor = mainFragment.descriptor.builtIns.builtInsModule + val (stdlibFile, stdlibIr) = allFragments.entries.find { + it.value.descriptor === stdlibDescriptor + } ?: notFoundIcError("stdlib fragment") + return stdlibFile to stdlibIr +} + internal class StopwatchIC { private var lapStart: Long = 0 private var lapDescription: String? = null diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureSource.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureSource.kt index eae2c372591..d57305c3e76 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureSource.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureSource.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.ir.backend.js.ic +import org.jetbrains.kotlin.backend.common.serialization.IrFileDeserializer import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrSymbol @@ -57,7 +58,7 @@ internal fun resolveFakeOverrideFunction(symbol: IrSymbol): IdSignature? { } } -internal fun collectImplementedSymbol(deserializedSymbols: Map): Map { +private fun collectImplementedSymbol(deserializedSymbols: Map): Map { return HashMap(deserializedSymbols.size).apply { for ((signature, symbol) in deserializedSymbols) { put(signature, symbol) @@ -98,3 +99,39 @@ internal fun collectImplementedSymbol(deserializedSymbols: Map + abstract fun getReachableSignatures(): Set + abstract fun getImplementedSymbols(): Map + + class DeserializedFromKlib(private val fileDeserializer: IrFileDeserializer) : FileSignatureProvider(fileDeserializer.file) { + override fun getSignatureToIndexMapping(): Map { + return fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() + } + + override fun getReachableSignatures(): Set { + return getSignatureToIndexMapping().keys + } + + override fun getImplementedSymbols(): Map { + return collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols) + } + } + + class GeneratedFunctionTypeInterface(file: IrFile) : FileSignatureProvider(file) { + private val allSignatures = run { + val topLevelSymbols = buildMap { + for (declaration in irFile.declarations) { + val signature = declaration.symbol.signature ?: continue + put(signature, declaration.symbol) + } + } + collectImplementedSymbol(topLevelSymbols) + } + + override fun getSignatureToIndexMapping(): Map = emptyMap() + override fun getReachableSignatures(): Set = allSignatures.keys + override fun getImplementedSymbols(): Map = allSignatures + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt index 76fbb16f6cd..801a6fdfb5c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt @@ -98,7 +98,7 @@ internal class IncrementalCache(private val library: KotlinLibraryHeader, val ca } val fileArtifacts = klibSrcFiles.map { srcFile -> - commitSourceFileMetadata(srcFile.getCacheFile(BINARY_AST_SUFFIX), srcFile, signatureToIndexMapping[srcFile] ?: emptyMap()) + commitSourceFileMetadata(srcFile, signatureToIndexMapping[srcFile] ?: emptyMap()) } return IncrementalCacheArtifact(cacheDir, removedSrcFiles.isNotEmpty(), fileArtifacts, library.jsOutputName) } @@ -200,10 +200,10 @@ internal class IncrementalCache(private val library: KotlinLibraryHeader, val ca } private fun commitSourceFileMetadata( - binaryAstFile: File, srcFile: KotlinSourceFile, signatureToIndexMapping: Map ): SourceFileCacheArtifact { + val binaryAstFile = srcFile.getCacheFile(BINARY_AST_SUFFIX) val headerCacheFile = srcFile.getCacheFile(METADATA_SUFFIX) val sourceFileMetadata = kotlinLibrarySourceFileMetadata[srcFile] ?: return SourceFileCacheArtifact.DoNotChangeMetadata(srcFile, binaryAstFile) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCacheArtifact.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCacheArtifact.kt index 58a88baee36..dfb0a96ce6d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCacheArtifact.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCacheArtifact.kt @@ -54,6 +54,8 @@ internal class IncrementalCacheArtifact( private val srcCacheActions: List, private val externalModuleName: String? ) { + fun getSourceFiles() = srcCacheActions.mapTo(HashSet(srcCacheActions.size)) { it.srcFile } + fun buildModuleArtifactAndCommitCache( moduleName: String, rebuiltFileFragments: Map, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt index 42d191f69bb..6282214b26c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.ir.backend.js.ic import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy +import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionInterface +import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor import org.jetbrains.kotlin.config.CompilerConfiguration @@ -14,11 +16,13 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI +import org.jetbrains.kotlin.ir.backend.js.FunctionTypeInterfacePackages import org.jetbrains.kotlin.ir.backend.js.JsFactories import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc import org.jetbrains.kotlin.ir.declarations.IrFactory import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.irMessageLogger @@ -30,10 +34,43 @@ import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl import org.jetbrains.kotlin.storage.LockBasedStorageManager -internal fun JsIrLinker.loadUnboundSymbols() { - ExternalDependenciesGenerator(symbolTable, listOf(this)).generateUnboundSymbolsAsDependencies() - postProcess() - checkNoUnboundSymbols(symbolTable, "at the end of IR linkage process") +internal data class LoadedJsIr( + val loadedFragments: Map, + private val linker: JsIrLinker, + private val functionTypeInterfacePackages: FunctionTypeInterfacePackages +) { + private val signatureProvidersImpl = hashMapOf>() + + private fun collectSignatureProviders(irModule: IrModuleFragment): List { + val moduleDeserializer = linker.moduleDeserializer(irModule.descriptor) + val deserializers = moduleDeserializer.fileDeserializers() + val providers = ArrayList(deserializers.size) + + for (fileDeserializer in deserializers) { + val irFile = fileDeserializer.file + if (functionTypeInterfacePackages.isFunctionTypeInterfacePackageFile(irFile)) { + providers += FileSignatureProvider.GeneratedFunctionTypeInterface(irFile) + } else { + providers += FileSignatureProvider.DeserializedFromKlib(fileDeserializer) + } + } + + return providers + } + + fun getSignatureProvidersForLib(lib: KotlinLibraryFile): List { + return signatureProvidersImpl.getOrPut(lib) { + val irFragment = loadedFragments[lib] ?: notFoundIcError("loaded fragment", lib) + collectSignatureProviders(irFragment) + } + } + + fun loadUnboundSymbols() { + signatureProvidersImpl.clear() + ExternalDependenciesGenerator(linker.symbolTable, listOf(linker)).generateUnboundSymbolsAsDependencies() + linker.postProcess() + linker.checkNoUnboundSymbols(linker.symbolTable, "at the end of IR linkage process") + } } internal class JsIrLinkerLoader( @@ -45,14 +82,34 @@ internal class JsIrLinkerLoader( private val mainLibrary = dependencyGraph.keys.lastOrNull() ?: notFoundIcError("main library") @OptIn(ObsoleteDescriptorBasedAPI::class) - private fun createLinker(loadedModules: Map): JsIrLinker { + private class LinkerContext( + val symbolTable: SymbolTable, + val typeTranslator: TypeTranslatorImpl, + val irBuiltIns: IrBuiltInsOverDescriptors, + val linker: JsIrLinker + ) { + val functionTypeInterfacePackages = FunctionTypeInterfacePackages() + + fun loadFunctionInterfacesIntoStdlib(stdlibModule: IrModuleFragment) { + irBuiltIns.functionFactory = IrDescriptorBasedFunctionFactory( + irBuiltIns, + symbolTable, + typeTranslator, + functionTypeInterfacePackages.makePackageAccessor(stdlibModule), + true + ) + } + } + + @OptIn(ObsoleteDescriptorBasedAPI::class) + private fun createLinker(loadedModules: Map): LinkerContext { val signaturer = IdSignatureDescriptor(JsManglerDesc) val symbolTable = SymbolTable(signaturer, irFactory) val moduleDescriptor = loadedModules.keys.last() val typeTranslator = TypeTranslatorImpl(symbolTable, compilerConfiguration.languageVersionSettings, moduleDescriptor) val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable) val partialLinkageEnabled = compilerConfiguration[JSConfigurationKeys.PARTIAL_LINKAGE] ?: false - return JsIrLinker( + val linker = JsIrLinker( currentModule = null, messageLogger = compilerConfiguration.irMessageLogger, builtIns = irBuiltIns, @@ -61,6 +118,7 @@ internal class JsIrLinkerLoader( translationPluginContext = null, friendModules = mapOf(mainLibrary.uniqueName to mainModuleFriends.map { it.uniqueName }) ) + return LinkerContext(symbolTable, typeTranslator, irBuiltIns, linker) } private fun loadModules(): Map { @@ -97,11 +155,9 @@ internal class JsIrLinkerLoader( .toMap() } - data class LoadedJsIr(val linker: JsIrLinker, val loadedFragments: Map) - fun loadIr(modifiedFiles: KotlinSourceFileMap, loadAllIr: Boolean = false): LoadedJsIr { val loadedModules = loadModules() - val jsIrLinker = createLinker(loadedModules) + val linkerContext = createLinker(loadedModules) val irModules = loadedModules.entries.associate { (descriptor, module) -> val libraryFile = KotlinLibraryFile(module) @@ -111,7 +167,7 @@ internal class JsIrLinkerLoader( else -> DeserializationStrategy.EXPLICITLY_EXPORTED } val modified = modifiedFiles[libraryFile] ?: emptyMap() - libraryFile to jsIrLinker.deserializeIrModuleHeader(descriptor, module, { + libraryFile to linkerContext.linker.deserializeIrModuleHeader(descriptor, module, { when (KotlinSourceFile(it)) { in modified -> modifiedStrategy else -> DeserializationStrategy.WITH_INLINE_BODIES @@ -119,15 +175,22 @@ internal class JsIrLinkerLoader( }) } - jsIrLinker.init(null, emptyList()) + val mainLibraryFile = KotlinLibraryFile(mainLibrary) + val mainFragment = irModules[mainLibraryFile] ?: notFoundIcError("main module fragment", mainLibraryFile) + val (_, stdlibFragment) = findStdlib(mainFragment, irModules) + linkerContext.loadFunctionInterfacesIntoStdlib(stdlibFragment) + + linkerContext.linker.init(null, emptyList()) if (!loadAllIr) { for ((loadingLibFile, loadingSrcFiles) in modifiedFiles) { val loadingIrModule = irModules[loadingLibFile] ?: notFoundIcError("loading fragment", loadingLibFile) - val moduleDeserializer = jsIrLinker.moduleDeserializer(loadingIrModule.descriptor) + val moduleDeserializer = linkerContext.linker.moduleDeserializer(loadingIrModule.descriptor) for (loadingSrcFileSignatures in loadingSrcFiles.values) { for (loadingSignature in loadingSrcFileSignatures.getExportedSignatures()) { - if (loadingSignature in moduleDeserializer) { + if (checkIsFunctionInterface(loadingSignature)) { + moduleDeserializer.tryDeserializeIrSymbol(loadingSignature, BinarySymbolData.SymbolKind.CLASS_SYMBOL) + } else if (loadingSignature in moduleDeserializer) { moduleDeserializer.addModuleReachableTopLevel(loadingSignature) } } @@ -135,7 +198,8 @@ internal class JsIrLinkerLoader( } } - jsIrLinker.loadUnboundSymbols() - return LoadedJsIr(jsIrLinker, irModules) + val loadedIr = LoadedJsIr(irModules, linkerContext.linker, linkerContext.functionTypeInterfacePackages) + loadedIr.loadUnboundSymbols() + return loadedIr } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt index 5f5959bbf22..85cc7867ede 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt @@ -107,22 +107,21 @@ internal object KotlinSourceFileMetadataNotExist : KotlinSourceFileMetadata() { override val directDependencies = KotlinSourceFileMap>(emptyMap()) } -internal class DirtyFileExports( - override val inverseDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap() -) : KotlinSourceFileExports() { - override fun getExportedSignatures(): Set = allExportedSignatures - +internal class DirtyFileExports : KotlinSourceFileExports() { val allExportedSignatures = hashSetOf() + + override val inverseDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap() + + override fun getExportedSignatures(): Set = allExportedSignatures } internal class DirtyFileMetadata( val maybeImportedSignatures: Collection, - - val oldDirectDependencies: KotlinSourceFileMap<*>, - - override val inverseDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap(), - override val directDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap(), + val oldDirectDependencies: KotlinSourceFileMap<*> ) : KotlinSourceFileMetadata() { + override val inverseDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap() + override val directDependencies: KotlinSourceFileMutableMap> = KotlinSourceFileMutableMap() + fun addInverseDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) = when (val signatures = inverseDependencies[lib, src]) { null -> inverseDependencies[lib, src] = hashSetOf(signature) diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FunctionalInterfacesUtils.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FunctionalInterfacesUtils.kt index 509aacb5ad4..3e33e5f6373 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FunctionalInterfacesUtils.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/FunctionalInterfacesUtils.kt @@ -10,12 +10,14 @@ import java.util.regex.Pattern internal val functionPattern = Pattern.compile("^K?(Suspend)?Function\\d+$") -internal val functionalPackages = listOf("kotlin", "kotlin.coroutines", "kotlin.reflect") +internal val functionTypeInterfacePackages = listOf("kotlin", "kotlin.coroutines", "kotlin.reflect") + +fun checkIsFunctionTypeInterfacePackageFqName(fqName: String) = fqName in functionTypeInterfacePackages fun checkIsFunctionInterface(idSig: IdSignature?): Boolean { val publicSig = idSig?.asPublic() return publicSig != null && - publicSig.packageFqName in functionalPackages && + checkIsFunctionTypeInterfacePackageFqName(publicSig.packageFqName) && publicSig.declarationFqName.isNotEmpty() && functionPattern.matcher(publicSig.firstNameSegment).find() } diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/FunctionTypeInterfacePackages.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/FunctionTypeInterfacePackages.kt new file mode 100644 index 00000000000..a52d25873ac --- /dev/null +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/FunctionTypeInterfacePackages.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js + +import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionTypeInterfacePackageFqName +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment + +class FunctionTypeInterfacePackages { + companion object { + private const val FUNCTION_TYPE_INTERFACE_DIR = "function-type-interface" + } + + private val functionTypeInterfacePackageFiles = hashSetOf() + + // Function type interfaces are not declared in a standard library, and they are generated on flight during a klib deserialization. + // The accessor allows finding a package for storing the generated function type interfaces. + // The optimization reduces the size of a standard library klib. + // Here are some numbers: + // 255 interfaces in one package increase the size of uncompressed klib up to 3.5+MB; + // 512 interfaces in one package increase the size of uncompressed klib up to 14.5+MB! + // We need 3 packages. + fun makePackageAccessor(stdlibModule: IrModuleFragment) = { packageFragmentDescriptor: PackageFragmentDescriptor -> + val packageFqName = packageFragmentDescriptor.fqName.toString() + check(checkIsFunctionTypeInterfacePackageFqName(packageFqName)) { "unexpected function type interface package $packageFqName" } + + val fileWithRequiredPackage = "${packageFqName.replace('.', '-')}-package.kt" + val packageFile = stdlibModule.files.singleOrNull { + // Do not check by name "$FUNCTION_TYPE_INTERFACE_DIR/$fileWithRequiredPackage" because the path separator depends on OS + it.fileEntry.name.endsWith(fileWithRequiredPackage) && it.fileEntry.name.contains(FUNCTION_TYPE_INTERFACE_DIR) + } ?: error("can not find a functional interface file for $packageFqName package") + + check(packageFragmentDescriptor.fqName == packageFile.fqName) { + "unexpected package in file ${packageFile.fileEntry.name}; expected $packageFqName, got ${packageFile.fqName}" + } + + functionTypeInterfacePackageFiles += packageFile + packageFile + } + + fun isFunctionTypeInterfacePackageFile(file: IrFile) = file in functionTypeInterfacePackageFiles +} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index 6b5236b4706..6620da2b4b2 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider @@ -38,7 +37,6 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.* import org.jetbrains.kotlin.ir.declarations.IrFactory import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory import org.jetbrains.kotlin.ir.linkage.IrDeserializer import org.jetbrains.kotlin.ir.symbols.IrSymbol @@ -71,6 +69,7 @@ import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.util.DummyLogger import org.jetbrains.kotlin.util.Logger import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.addToStdlib.ifTrue import java.io.File val KotlinLibrary.moduleName: String @@ -184,14 +183,6 @@ fun deserializeDependencies( } } -fun getFunctionFactoryCallback(stdlibModule: IrModuleFragment) = { packageFragmentDescriptor: PackageFragmentDescriptor -> - IrFileImpl( - NaiveSourceBasedFileEntryImpl("${packageFragmentDescriptor.fqName}-[K][Suspend]Functions"), - packageFragmentDescriptor, - stdlibModule - ).also { stdlibModule.files += it } -} - fun loadIr( depsDescriptors: ModulesStructure, irFactory: IrFactory, @@ -287,7 +278,9 @@ fun getIrModuleInfoForKlib( irBuiltIns, symbolTable, typeTranslator, - if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null, + loadFunctionInterfacesIntoStdlib.ifTrue { + FunctionTypeInterfacePackages().makePackageAccessor(deserializedModuleFragments.first()) + }, true ) @@ -345,7 +338,9 @@ fun getIrModuleInfoForSourceFiles( irBuiltIns, symbolTable, psi2IrContext.typeTranslator, - if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null, + loadFunctionInterfacesIntoStdlib.ifTrue { + FunctionTypeInterfacePackages().makePackageAccessor(deserializedModuleFragments.first()) + }, true ) diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java index 6a1471a049a..00dabe0b904 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java @@ -170,6 +170,11 @@ public class JsIrES6InvalidationTestGenerated extends AbstractJsIrES6Invalidatio runTest("js/js.translator/testData/incremental/invalidation/functionSignature/"); } + @TestMetadata("functionalInterface") + public void testFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/functionalInterface/"); + } + @TestMetadata("genericFunctions") public void testGenericFunctions() throws Exception { runTest("js/js.translator/testData/incremental/invalidation/genericFunctions/"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java index 28768a05d56..16568406423 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java @@ -170,6 +170,11 @@ public class JsIrInvalidationTestGenerated extends AbstractJsIrInvalidationTest runTest("js/js.translator/testData/incremental/invalidation/functionSignature/"); } + @TestMetadata("functionalInterface") + public void testFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/functionalInterface/"); + } + @TestMetadata("genericFunctions") public void testGenericFunctions() throws Exception { runTest("js/js.translator/testData/incremental/invalidation/genericFunctions/"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index 487574432c1..93cd8793d4d 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -861,6 +861,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { public void testSuspendFunctionIsAs() throws Exception { runTest("js/js.translator/testData/box/coroutines/suspendFunctionIsAs.kt"); } + + @Test + @TestMetadata("suspendFunctionalInterface.kt") + public void testSuspendFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java index 700a8349503..1ded5fc71b1 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java @@ -925,6 +925,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest { public void testSuspendFunctionIsAs() throws Exception { runTest("js/js.translator/testData/box/coroutines/suspendFunctionIsAs.kt"); } + + @Test + @TestMetadata("suspendFunctionalInterface.kt") + public void testSuspendFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java index 1739dac1caa..cf492125cdf 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java @@ -925,6 +925,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test { public void testSuspendFunctionIsAs() throws Exception { runTest("js/js.translator/testData/box/coroutines/suspendFunctionIsAs.kt"); } + + @Test + @TestMetadata("suspendFunctionalInterface.kt") + public void testSuspendFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 05adb8f8874..c0df910ec4c 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -925,6 +925,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { public void testSuspendFunctionIsAs() throws Exception { runTest("js/js.translator/testData/box/coroutines/suspendFunctionIsAs.kt"); } + + @Test + @TestMetadata("suspendFunctionalInterface.kt") + public void testSuspendFunctionalInterface() throws Exception { + runTest("js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt"); + } } @Nested diff --git a/js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt b/js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt new file mode 100644 index 00000000000..4bea14eb0d0 --- /dev/null +++ b/js/js.translator/testData/box/coroutines/suspendFunctionalInterface.kt @@ -0,0 +1,30 @@ +// IGNORE_BACKEND: JS + +inline fun handle(s: T): String { + return "${T::class}" +} + +fun check(got: String, expected: String): String? { + if (got != expected) { + return "Failed; expected $expected, got $got" + } + return null +} + +fun box(): String { + val s0: suspend () -> Unit = {} + check(handle(s0), "class SuspendFunction0")?.let { return it } + + val s1: suspend (String) -> Unit = {} + check(handle(s1), "class SuspendFunction1")?.let { return it } + + val s7: suspend (Any, Any, Any, Any, Any, Any, Any) -> Unit = { _, _, _, _, _, _, _ -> } + check(handle(s7), "class SuspendFunction7")?.let { return it } + + val s15: suspend (Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any) -> Unit + = { _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ -> } + check(handle(s15), "class SuspendFunction15")?.let { return it } + + return "OK" + +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.0.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.0.kt new file mode 100644 index 00000000000..5bb7f3ca689 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.0.kt @@ -0,0 +1,3 @@ +inline fun getTypeName(s: T): String { + return "${T::class}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.11.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.11.kt new file mode 100644 index 00000000000..f178d3cdef9 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.11.kt @@ -0,0 +1,3 @@ +inline fun getTypeName(s: T): String { + return "_" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.9.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.9.kt new file mode 100644 index 00000000000..aa702c289c4 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/l1.9.kt @@ -0,0 +1,3 @@ +inline fun getTypeName(s: T): String { + return "_${T::class}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/module.info b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/module.info new file mode 100644 index 00000000000..e96f99344a5 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib1/module.info @@ -0,0 +1,17 @@ +STEP 0: + modifications: + U : l1.0.kt -> l1.kt + added file: l1.kt +STEP 1..8: +STEP 9: + modifications: + U : l1.9.kt -> l1.kt + modified ir: l1.kt +STEP 10: +STEP 11: + modifications: + U : l1.11.kt -> l1.kt + modified ir: l1.kt +STEP 12: + updated exports: l1.kt +STEP 13..15: diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.0.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.0.kt new file mode 100644 index 00000000000..3df26a2fe60 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.0.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend () -> Unit = {} + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.1.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.1.kt new file mode 100644 index 00000000000..bff655fcb93 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.1.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend (Any) -> Unit = { _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.10.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.10.kt new file mode 100644 index 00000000000..b7c78cd98d8 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.10.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: (Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.12.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.12.kt new file mode 100644 index 00000000000..2fc50104a9e --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.12.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: (Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _, _ -> } + return "${s::class}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.13.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.13.kt new file mode 100644 index 00000000000..1bd8bda66ad --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.13.kt @@ -0,0 +1,5 @@ +fun getString(): String { + val s1: (Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _, _ -> } + val s2: (Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _, _ -> } + return "${s1::class.hashCode() == s2::class.hashCode()}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.14.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.14.kt new file mode 100644 index 00000000000..6ac8d88ab8c --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.14.kt @@ -0,0 +1,5 @@ +fun getString(): String { + val s1: (Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _, _ -> } + val s2: (Int, Int, Int, Int) -> Unit = { _, _, _, _ -> } + return "${s1::class == s2::class}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.15.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.15.kt new file mode 100644 index 00000000000..eb1ebdfc5de --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.15.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend (Any, Any, Any, Any, Any) -> Unit = { _, _, _, _, _ -> } + return "${s::class}" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.2.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.2.kt new file mode 100644 index 00000000000..0463dff650b --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.2.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend (Any, Any, Any, Any, Any, Any, Any) -> Unit = { _, _, _, _, _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.3.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.3.kt new file mode 100644 index 00000000000..c56c34c953d --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.3.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: (Any, Any, Any) -> Unit = { _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.4.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.4.kt new file mode 100644 index 00000000000..ea4210cdbef --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.4.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: (Any, Any, Any, Any, Any, Any) -> Unit = { _, _, _, _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.6.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.6.kt new file mode 100644 index 00000000000..1ee4e1dd26c --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.6.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend (Any, Any, Any, Any, Any, Any) -> Unit = { _, _, _, _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.7.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.7.kt new file mode 100644 index 00000000000..134241fd2a8 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.7.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: Any = 123 + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.8.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.8.kt new file mode 100644 index 00000000000..541feff9507 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2a.8.kt @@ -0,0 +1,4 @@ +fun getString(): String { + val s: suspend (Int, Int, Int, Int, Int, Int, Int, Int) -> Unit = { _, _, _, _, _, _, _, _ -> } + return getTypeName(s) +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.0.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.0.kt new file mode 100644 index 00000000000..a38a7a5d0cb --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.0.kt @@ -0,0 +1,3 @@ +fun doTest(): String { + return getString() +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.5.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.5.kt new file mode 100644 index 00000000000..9b5453d0cce --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/l2b.5.kt @@ -0,0 +1,3 @@ +fun doTest(): String { + return getString() + "_" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/module.info b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/module.info new file mode 100644 index 00000000000..40e22cc0df4 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/lib2/module.info @@ -0,0 +1,77 @@ +STEP 0: + dependencies: lib1 + modifications: + U : l2a.0.kt -> l2a.kt + U : l2b.0.kt -> l2b.kt + added file: l2a.kt, l2b.kt +STEP 1: + dependencies: lib1 + modifications: + U : l2a.1.kt -> l2a.kt + modified ir: l2a.kt +STEP 2: + dependencies: lib1 + modifications: + U : l2a.2.kt -> l2a.kt + modified ir: l2a.kt +STEP 3: + dependencies: lib1 + modifications: + U : l2a.3.kt -> l2a.kt + modified ir: l2a.kt +STEP 4: + dependencies: lib1 + modifications: + U : l2a.4.kt -> l2a.kt + modified ir: l2a.kt +STEP 5: + dependencies: lib1 + modifications: + U : l2b.5.kt -> l2b.kt + modified ir: l2b.kt +STEP 6: + dependencies: lib1 + modifications: + U : l2a.6.kt -> l2a.kt + modified ir: l2a.kt +STEP 7: + dependencies: lib1 + modifications: + U : l2a.7.kt -> l2a.kt + modified ir: l2a.kt +STEP 8: + dependencies: lib1 + modifications: + U : l2a.8.kt -> l2a.kt + modified ir: l2a.kt +STEP 9: + dependencies: lib1 + updated imports: l2a.kt +STEP 10: + dependencies: lib1 + modifications: + U : l2a.10.kt -> l2a.kt + modified ir: l2a.kt +STEP 11: + dependencies: lib1 + updated imports: l2a.kt +STEP 12: + dependencies: lib1 + modifications: + U : l2a.12.kt -> l2a.kt + modified ir: l2a.kt +STEP 13: + dependencies: lib1 + modifications: + U : l2a.13.kt -> l2a.kt + modified ir: l2a.kt +STEP 14: + dependencies: lib1 + modifications: + U : l2a.14.kt -> l2a.kt + modified ir: l2a.kt +STEP 15: + dependencies: lib1 + modifications: + U : l2a.15.kt -> l2a.kt + modified ir: l2a.kt diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/main/m.kt b/js/js.translator/testData/incremental/invalidation/functionalInterface/main/m.kt new file mode 100644 index 00000000000..76dd80834ad --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/main/m.kt @@ -0,0 +1,26 @@ +fun box(stepId: Int): String { + val s = doTest() + when (stepId) { + 0 -> if (s == "class SuspendFunction0") return "OK" + 1 -> if (s == "class SuspendFunction1") return "OK" + 2 -> if (s == "class SuspendFunction7") return "OK" + 3 -> if (s == "class Function3") return "OK" + 4 -> if (s == "class Function6") return "OK" + 5 -> if (s == "class Function6_") return "OK" + 6 -> if (s == "class SuspendFunction6_") return "OK" + 7 -> if (s == "class Any_") return "OK" + 8 -> if (s == "class SuspendFunction8_") return "OK" + 9 -> if (s == "_class SuspendFunction8_") return "OK" + 10 -> if (s == "_class Function9_") return "OK" + 11 -> if (s == "__") return "OK" + 12 -> if (s == "class Function9_") return "OK" + 13 -> if (s == "true_") return "OK" + 14 -> if (s == "false_") return "OK" + // TODO: I would expect, that it should be SuspendFunction5_, + // but it seems a feature of Kotlin/JS runtime. + // In Kotlin/JVM it also gives different strings. + 15 -> if (s == "class Function6_") return "OK" + else -> return "Unknown" + } + return "Fail; got $s" +} diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/main/module.info b/js/js.translator/testData/incremental/invalidation/functionalInterface/main/module.info new file mode 100644 index 00000000000..69cf88630b0 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/main/module.info @@ -0,0 +1,5 @@ +STEP 0: + dependencies: lib1, lib2 + added file: m.kt +STEP 1..15: + dependencies: lib1, lib2 diff --git a/js/js.translator/testData/incremental/invalidation/functionalInterface/project.info b/js/js.translator/testData/incremental/invalidation/functionalInterface/project.info new file mode 100644 index 00000000000..b336590ba6b --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/functionalInterface/project.info @@ -0,0 +1,20 @@ +MODULES: lib1, lib2, main + +STEP 0: + libs: lib1, lib2, main + dirty js: lib1, lib2, main +STEP 1..8: + libs: lib1, lib2, main + dirty js: lib2 +STEP 9: + libs: lib1, lib2, main + dirty js: lib1, lib2 +STEP 10: + libs: lib1, lib2, main + dirty js: lib2 +STEP 11..12: + libs: lib1, lib2, main + dirty js: lib1, lib2 +STEP 13..15: + libs: lib1, lib2, main + dirty js: lib2 diff --git a/libraries/stdlib/js-ir/src/function-type-interface/kotlin-coroutines-package.kt b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-coroutines-package.kt new file mode 100644 index 00000000000..9bfec66c836 --- /dev/null +++ b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-coroutines-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.coroutines + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt diff --git a/libraries/stdlib/js-ir/src/function-type-interface/kotlin-package.kt b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-package.kt new file mode 100644 index 00000000000..aa8d4287ca2 --- /dev/null +++ b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt diff --git a/libraries/stdlib/js-ir/src/function-type-interface/kotlin-reflect-package.kt b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-reflect-package.kt new file mode 100644 index 00000000000..7e3eca8202b --- /dev/null +++ b/libraries/stdlib/js-ir/src/function-type-interface/kotlin-reflect-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.reflect + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt diff --git a/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-coroutines-package.kt b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-coroutines-package.kt new file mode 100644 index 00000000000..9bfec66c836 --- /dev/null +++ b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-coroutines-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.coroutines + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt diff --git a/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-package.kt b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-package.kt new file mode 100644 index 00000000000..aa8d4287ca2 --- /dev/null +++ b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt diff --git a/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-reflect-package.kt b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-reflect-package.kt new file mode 100644 index 00000000000..7e3eca8202b --- /dev/null +++ b/libraries/stdlib/wasm/src/kotlin/function-type-interface/kotlin-reflect-package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.reflect + +// Do not remove this file. +// During klib deserialization the compiler uses this file as a place for declaring function type interfaces. +// See FunctionTypeInterfacePackages.kt