From 878452bd2b6558cddba46493adadb1cb2d4e6c86 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Thu, 14 Sep 2023 13:14:15 +0000 Subject: [PATCH] [K/JS] Implement file merging for clashed file during per-file compilation --- .../kotlin/ir/backend/js/JsMapping.kt | 3 - .../kotlin/ir/backend/js/compilerWithIC.kt | 2 +- .../kotlin/ir/backend/js/ic/CacheUpdater.kt | 17 +- .../backend/js/ic/IncrementalCacheArtifact.kt | 14 +- .../kotlin/ir/backend/js/ic/JsPerFileCache.kt | 242 ++++++++++-------- .../kotlin/ir/backend/js/ic/ModuleArtifact.kt | 19 +- .../irToJs/IrModuleToJsTransformer.kt | 74 +++--- .../irToJs/JsIrProgramFragment.kt | 4 +- .../backend/js/transformers/irToJs/Merger.kt | 54 ++++ .../irToJs/ModuleFragmentToExternalName.kt | 19 +- .../transformers/irToJs/PerFileGenerator.kt | 60 +++++ .../serialization/JsIrAstDeserializer.kt | 7 +- .../utils/serialization/JsIrAstSerializer.kt | 11 +- .../codegen/box/sealed/kt54028_cursed.kt | 2 - .../test/frontend/fir/FirFrontendFacade.kt | 25 +- .../jetbrains/kotlin/utils/CollectionUtil.kt | 5 + ...JsFirInvalidationPerFileTestGenerated.java | 6 + ...FirInvalidationPerModuleTestGenerated.java | 6 + ...IrES6InvalidationPerFileTestGenerated.java | 6 + ...ES6InvalidationPerModuleTestGenerated.java | 6 + .../JsIrInvalidationPerFileTestGenerated.java | 6 + ...sIrInvalidationPerModuleTestGenerated.java | 6 + .../js/test/fir/FirJsBoxTestGenerated.java | 6 + .../js/test/fir/FirJsES6BoxTestGenerated.java | 6 + .../js/test/ir/IrBoxJsES6TestGenerated.java | 6 + .../js/test/ir/IrBoxJsTestGenerated.java | 6 + .../box/esModules/export/fileNameClash.kt | 35 +++ .../invalidation/fileNameClash/lib/f1.1.kt | 3 + .../invalidation/fileNameClash/lib/f1.3.kt | 4 + .../invalidation/fileNameClash/lib/f1.kt | 3 + .../invalidation/fileNameClash/lib/f2.1.kt | 5 + .../invalidation/fileNameClash/lib/f2.2.kt | 6 + .../invalidation/fileNameClash/lib/f2.4.kt | 3 + .../fileNameClash/lib/module.info | 22 ++ .../invalidation/fileNameClash/main/m.kt | 3 + .../fileNameClash/main/module.info | 12 + .../invalidation/fileNameClash/project.info | 22 ++ .../kotlin/gradle/Kotlin2JsGradlePluginIT.kt | 21 -- .../build.gradle.kts | 16 -- .../custom/main.txt | 0 .../gradle.properties | 8 - .../src/main/kotlin/base1/Base.kt | 16 -- .../src/main/kotlin/base2/base.kt | 9 - 43 files changed, 529 insertions(+), 277 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/PerFileGenerator.kt create mode 100644 js/js.translator/testData/box/esModules/export/fileNameClash.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.1.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.3.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.1.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.2.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.4.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/lib/module.info create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/main/m.kt create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/main/module.info create mode 100644 js/js.translator/testData/incremental/invalidation/fileNameClash/project.info delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt index d959d7cd8e7..bed8335838b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt @@ -17,9 +17,6 @@ class JsMapping : DefaultMapping() { val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping() val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping>() - // Check if we need concurrency here - val chunkToOriginalFile = WeakHashMap() - val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt index 1e0d4392051..129afd9f39f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compilerWithIC.kt @@ -50,7 +50,7 @@ class JsIrCompilerWithIC( allModules: Collection, dirtyFiles: Collection, mainArguments: List? - ): List<() -> List> { + ): List<() -> JsIrProgramFragments> { val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS) allModules.forEach { 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 65b7eb4c219..364e3318864 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.js.config.JSConfigurationKeys @@ -33,7 +34,7 @@ fun interface JsIrCompilerICInterface { allModules: Collection, dirtyFiles: Collection, mainArguments: List? - ): List<() -> List> + ): List<() -> JsIrProgramFragments> } fun interface JsIrCompilerICInterfaceFactory { @@ -616,7 +617,7 @@ class CacheUpdater( private fun commitCacheAndBuildModuleArtifacts( incrementalCacheArtifacts: Map, moduleNames: Map, - rebuiltFileFragments: KotlinSourceFileMap> + rebuiltFileFragments: KotlinSourceFileMap ): List = stopwatch.measure("Incremental cache - committing artifacts") { incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) -> incrementalCacheArtifact.buildModuleArtifactAndCommitCache( @@ -630,7 +631,7 @@ class CacheUpdater( compilerForIC: JsIrCompilerICInterface, loadedFragments: Map, dirtyFiles: Map> - ): MutableList List>> = + ): MutableList JsIrProgramFragments>> = stopwatch.measure("Processing IR - lowering") { val dirtyFilesForCompiling = mutableListOf() val dirtyFilesForRestoring = mutableListOf>() @@ -737,7 +738,7 @@ class CacheUpdater( private data class FragmentGenerators( val incrementalCacheArtifacts: Map, val moduleNames: Map, - val generators: MutableList List>> + val generators: MutableList JsIrProgramFragments>> ) private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators { @@ -751,9 +752,9 @@ class CacheUpdater( } private fun generateIrFragments( - generators: MutableList List>> - ): KotlinSourceFileMap> = stopwatch.measure("Processing IR - generating program fragments") { - val rebuiltFragments = KotlinSourceFileMutableMap>() + generators: MutableList JsIrProgramFragments>> + ): KotlinSourceFileMap = stopwatch.measure("Processing IR - generating program fragments") { + val rebuiltFragments = KotlinSourceFileMutableMap() while (generators.isNotEmpty()) { val (libFile, srcFile, fragmentGenerator) = generators.removeFirst() rebuiltFragments[libFile, srcFile] = fragmentGenerator() @@ -782,7 +783,7 @@ fun rebuildCacheForDirtyFiles( irFactory: IrFactory, exportedDeclarations: Set, mainArguments: List?, -): Pair>>> { +): Pair>> { val internationService = IrInterningService() val emptyMetadata = object : KotlinSourceFileExports() { override val inverseDependencies = KotlinSourceFileMap>(emptyMap()) 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 a7cc5f58281..c912fdea9a4 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 @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.ir.backend.js.ic -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments import org.jetbrains.kotlin.ir.backend.js.utils.serialization.serializeTo import org.jetbrains.kotlin.konan.file.use import org.jetbrains.kotlin.utils.newHashSetWithExpectedSize @@ -15,7 +15,7 @@ import java.io.File internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) { abstract fun commitMetadata() - fun commitBinaryAst(fragments: List) { + fun commitBinaryAst(fragments: JsIrProgramFragments) { binaryAstFile.parentFile?.mkdirs() BufferedOutputStream(binaryAstFile.outputStream()).use { fragments.serializeTo(it) @@ -30,7 +30,7 @@ internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val srcFile: KotlinSourceFile, binaryAstFile: File, private val metadataFile: File, - private val encodedMetadata: ByteArray + private val encodedMetadata: ByteArray, ) : SourceFileCacheArtifact(srcFile, binaryAstFile) { override fun commitMetadata() { metadataFile.parentFile?.mkdirs() @@ -41,7 +41,7 @@ internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val class RemoveMetadata( srcFile: KotlinSourceFile, binaryAstFile: File, - private val metadataFile: File + private val metadataFile: File, ) : SourceFileCacheArtifact(srcFile, binaryAstFile) { override fun commitMetadata() { metadataFile.delete() @@ -53,13 +53,13 @@ internal class IncrementalCacheArtifact( private val artifactsDir: File, private val forceRebuildJs: Boolean, private val srcCacheActions: List, - private val externalModuleName: String? + private val externalModuleName: String?, ) { fun getSourceFiles() = srcCacheActions.mapTo(newHashSetWithExpectedSize(srcCacheActions.size)) { it.srcFile } fun buildModuleArtifactAndCommitCache( moduleName: String, - rebuiltFileFragments: Map>, + rebuiltFileFragments: Map, ): ModuleArtifact { val fileArtifacts = srcCacheActions.map { srcFileAction -> val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile] @@ -67,7 +67,7 @@ internal class IncrementalCacheArtifact( srcFileAction.commitBinaryAst(rebuiltFileFragment) } srcFileAction.commitMetadata() - SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment ?: emptyList(), srcFileAction.binaryAstFile) + SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment, srcFileAction.binaryAstFile) } return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsPerFileCache.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsPerFileCache.kt index 3a85f75decf..1361ce9b361 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsPerFileCache.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsPerFileCache.kt @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.serialization.cityHash64 import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.* import org.jetbrains.kotlin.protobuf.CodedInputStream import org.jetbrains.kotlin.protobuf.CodedOutputStream -import org.jetbrains.kotlin.utils.addToStdlib.butIf import org.jetbrains.kotlin.utils.addToStdlib.runIf import java.io.File @@ -20,6 +19,48 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult private const val CACHED_EXPORT_FILE_JS = "file.export.js" private const val CACHED_FILE_JS_MAP = "file.js.map" private const val CACHED_FILE_D_TS = "file.d.ts" + + private val moduleFragmentToExternalName = ModuleFragmentToExternalName(emptyMap()) + + private fun JsIrProgramFragment.getMainFragmentExternalName(moduleArtifact: ModuleArtifact) = + moduleFragmentToExternalName.getExternalNameFor(name, packageFqn, moduleArtifact.moduleExternalName) + + private fun JsIrProgramFragment.getExportFragmentExternalName(moduleArtifact: ModuleArtifact) = + moduleFragmentToExternalName.getExternalNameForExporterFile(name, packageFqn, moduleArtifact.moduleExternalName) + + private fun SrcFileArtifact.loadJsIrModuleHeaders(moduleArtifact: ModuleArtifact) = with(loadJsIrFragments()!!) { + LoadedJsIrModuleHeaders( + mainFragment.run { + asIrModuleHeader( + getMainFragmentExternalName(moduleArtifact), + importWithEffectIn = runIf(hasEffect) { moduleArtifact.moduleExternalName } + ) + }, + exportFragment?.run { + asIrModuleHeader( + mainFragment.getExportFragmentExternalName(moduleArtifact), + reexportedIn = moduleArtifact.moduleExternalName + ) + }, + ) + } + + private fun JsIrProgramFragment.asIrModuleHeader( + moduleName: String, + reexportedIn: String? = null, + importWithEffectIn: String? = null, + ): JsIrModuleHeader { + return JsIrModuleHeader( + moduleName = moduleName, + externalModuleName = moduleName, + definitions = definitions, + nameBindings = nameBindings.mapValues { v -> v.value.toString() }, + optionalCrossModuleImports = optionalCrossModuleImports, + reexportedInModuleWithName = reexportedIn, + importedWithEffectInModuleWithName = importWithEffectIn, + associatedModule = JsIrModule(moduleName, moduleName, listOf(this), reexportedIn) + ) + } } sealed class CachedFileInfo(val moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader?) : CacheInfo { @@ -30,6 +71,7 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult if (moduleHeader != null) jsIrHeader = moduleHeader } + abstract fun loadJsIrModule(): JsIrModule sealed class SerializableCachedFileInfo( moduleArtifact: ModuleArtifact, @@ -37,19 +79,60 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult moduleHeader: JsIrModuleHeader? ) : CachedFileInfo(moduleArtifact, moduleHeader) { fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "$filePrefix.$name") } - private val filePrefix by lazy(LazyThreadSafetyMode.NONE) { fileArtifact.srcFilePath.run { "${substringAfterLast('/')}.${cityHash64()}" } } + protected open val filePrefix by lazy(LazyThreadSafetyMode.NONE) { fileArtifact.srcFilePath.run { "${substringAfterLast('/')}.${cityHash64()}" } } + + override fun loadJsIrModule(): JsIrModule { + val fragments = fileArtifact.loadJsIrFragments()!! + val isExportFileCachedInfo = this is ExportFileCachedInfo + return JsIrModule( + jsIrHeader.moduleName, + jsIrHeader.externalModuleName, + listOf(if (isExportFileCachedInfo) fragments.exportFragment!! else fragments.mainFragment), + runIf(isExportFileCachedInfo) { moduleArtifact.moduleSafeName } + ) + } } - class MainFileCachedInfo(moduleArtifact: ModuleArtifact, fileArtifact: SrcFileArtifact, moduleHeader: JsIrModuleHeader? = null) : + open class MainFileCachedInfo(moduleArtifact: ModuleArtifact, fileArtifact: SrcFileArtifact, moduleHeader: JsIrModuleHeader? = null) : SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) { var exportFileCachedInfo: ExportFileCachedInfo? = null val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) } val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) } val sourceMapFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS_MAP) } + + class Merged(private val cachedFileInfos: List) : + MainFileCachedInfo(cachedFileInfos.first().moduleArtifact, cachedFileInfos.first().fileArtifact) { + override val filePrefix by lazy(LazyThreadSafetyMode.NONE) { + val hash = cachedFileInfos.map { it.fileArtifact.srcFilePath }.sorted().joinToString().cityHash64() + fileArtifact.srcFilePath.run { "${substringAfterLast('/')}.$hash.merged" } + } + + override fun loadJsIrModule(): JsIrModule = cachedFileInfos.map { it.loadJsIrModule() }.merge() + + init { + assert(cachedFileInfos.size > 1) { "Merge is unnecessary" } + val isModified = cachedFileInfos.any { it.fileArtifact.isModified() } + val mainAndExportHeaders = when { + isModified -> cachedFileInfos.map { it.fileArtifact.loadJsIrModuleHeaders(moduleArtifact) } + else -> cachedFileInfos.map { LoadedJsIrModuleHeaders(it.jsIrHeader, it.exportFileCachedInfo?.jsIrHeader) } + } + val (mainHeaders, exportHeaders) = mainAndExportHeaders.map(LoadedJsIrModuleHeaders::toPair).unzip() + + jsIrHeader = mainHeaders.merge() + exportFileCachedInfo = exportHeaders.filterNotNull().takeIf { it.isNotEmpty() } + ?.let { + ExportFileCachedInfo.Merged( + filePrefix, + it.merge(), + cachedFileInfos.mapNotNull(MainFileCachedInfo::exportFileCachedInfo) + ) + } + } + } } - class ExportFileCachedInfo( + open class ExportFileCachedInfo( moduleArtifact: ModuleArtifact, fileArtifact: SrcFileArtifact, moduleHeader: JsIrModuleHeader? = null, @@ -57,6 +140,14 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult ) : SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) { val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_EXPORT_FILE_JS) } val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) } + + class Merged( + override val filePrefix: String, + moduleHeader: JsIrModuleHeader, + private val cachedFileInfos: List, + ) : ExportFileCachedInfo(cachedFileInfos.first().moduleArtifact, cachedFileInfos.first().fileArtifact, moduleHeader) { + override fun loadJsIrModule(): JsIrModule = cachedFileInfos.map { it.loadJsIrModule() }.merge() + } } class ModuleProxyFileCachedInfo(moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader? = null) : @@ -66,51 +157,18 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) } private fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "entry.$name") } + + override fun loadJsIrModule(): JsIrModule { + return generateProxyIrModuleWith( + jsIrHeader.externalModuleName, + jsIrHeader.externalModuleName, + jsIrHeader.importedWithEffectInModuleWithName + ) + } } } private val headerToCachedInfo = hashMapOf() - private val moduleFragmentToExternalName = ModuleFragmentToExternalName(emptyMap()) - - private fun JsIrProgramFragment.getMainFragmentExternalName(moduleArtifact: ModuleArtifact) = - moduleFragmentToExternalName.getExternalNameFor(name, packageFqn, moduleArtifact.moduleExternalName) - - private fun JsIrProgramFragment.getExportFragmentExternalName(moduleArtifact: ModuleArtifact) = - moduleFragmentToExternalName.getExternalNameForExporterFile(name, packageFqn, moduleArtifact.moduleExternalName) - - private fun JsIrProgramFragment.asIrModuleHeader( - moduleName: String, - reexportedIn: String? = null, - importWithEffectIn: String? = null, - ): JsIrModuleHeader { - return JsIrModuleHeader( - moduleName = moduleName, - externalModuleName = moduleName, - definitions = definitions, - nameBindings = nameBindings.mapValues { v -> v.value.toString() }, - optionalCrossModuleImports = optionalCrossModuleImports, - reexportedInModuleWithName = reexportedIn, - importedWithEffectInModuleWithName = importWithEffectIn, - associatedModule = null - ) - } - - private fun SrcFileArtifact.loadJsIrModuleHeaders(moduleArtifact: ModuleArtifact) = with(loadJsIrFragments()) { - LoadedJsIrModuleHeaders( - mainFragment.run { - asIrModuleHeader( - getMainFragmentExternalName(moduleArtifact), - importWithEffectIn = runIf(hasEffect) { moduleArtifact.moduleExternalName } - ) - }, - exportFragment?.run { - asIrModuleHeader( - mainFragment.getExportFragmentExternalName(moduleArtifact), - reexportedIn = moduleArtifact.moduleExternalName - ) - }, - ) - } private fun CodedInputStream.loadSingleCachedFileInfo(cachedFileInfo: T): T = cachedFileInfo.also { val moduleName = readString() @@ -142,14 +200,13 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult private fun CachedFileInfo.MainFileCachedInfo.readModuleHeaderCache(f: CodedInputStream.() -> T): T? = moduleHeaderArtifact?.useCodedInputIfExists(f) - private fun ModuleArtifact.fetchFileInfoFor(fileArtifact: SrcFileArtifact): List? { + private fun ModuleArtifact.fetchFileInfoFor(fileArtifact: SrcFileArtifact): CachedFileInfo.MainFileCachedInfo? { val mainFileCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact) return mainFileCachedFileInfo.readModuleHeaderCache { - mainFileCachedFileInfo.run { + mainFileCachedFileInfo.apply { exportFileCachedInfo = fetchFileInfoForExportedPart(this) loadSingleCachedFileInfo(this) - listOfNotNull(exportFileCachedInfo, this) } } } @@ -199,13 +256,13 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult ) } - private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): List { + private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): CachedFileInfo.MainFileCachedInfo { val headers = fileArtifact.loadJsIrModuleHeaders(this) val mainCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact, headers.mainHeader) if (headers.exportHeader != null) { - val tsDeclarationsHash = fileArtifact.loadJsIrFragments().exportFragment?.dts?.raw?.cityHash64() + val tsDeclarationsHash = fileArtifact.loadJsIrFragments()?.exportFragment?.dts?.raw?.cityHash64() val cachedExportFileInfo = mainCachedFileInfo.readModuleHeaderCache { fetchFileInfoForExportedPart(mainCachedFileInfo) } mainCachedFileInfo.exportFileCachedInfo = if (cachedExportFileInfo?.tsDeclarationsHash != tsDeclarationsHash) { CachedFileInfo.ExportFileCachedInfo( @@ -219,7 +276,7 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult } } - return listOfNotNull(mainCachedFileInfo.exportFileCachedInfo, mainCachedFileInfo) + return mainCachedFileInfo } private val CachedFileInfo.cachedFiles: CachedFileArtifacts? @@ -245,59 +302,41 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile) } ?: compilationOutputs - override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule { - if (cacheInfo !is CachedFileInfo.SerializableCachedFileInfo) { - return generateProxyIrModuleWith( - cacheInfo.jsIrHeader.externalModuleName, - cacheInfo.jsIrHeader.externalModuleName, - cacheInfo.jsIrHeader.importedWithEffectInModuleWithName - ) - } - - val fragments = cacheInfo.fileArtifact.loadJsIrFragments() - val isExportFileCachedInfo = cacheInfo is CachedFileInfo.ExportFileCachedInfo - return JsIrModule( - cacheInfo.jsIrHeader.moduleName, - cacheInfo.jsIrHeader.externalModuleName, - listOf(if (isExportFileCachedInfo) fragments.exportFragment!! else fragments.mainFragment), - runIf(isExportFileCachedInfo) { cacheInfo.moduleArtifact.moduleSafeName } - ) - } + override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule = + cacheInfo.loadJsIrModule() override fun loadProgramHeadersFromCache(): List { - var someModuleHasEffect = false val mainModuleArtifact = moduleArtifacts.last() - return moduleArtifacts - .flatMap { moduleArtifact -> - var hasModuleLevelEffect = false - var hasFileWithJsExportedDeclaration = false - moduleArtifact.fileArtifacts - .flatMap { srcFileArtifact -> - val cachedFileInfo = if (srcFileArtifact.isModified()) - moduleArtifact.loadFileInfoFor(srcFileArtifact) - else - moduleArtifact.fetchFileInfoFor(srcFileArtifact) ?: moduleArtifact.loadFileInfoFor(srcFileArtifact) - if (!hasFileWithJsExportedDeclaration && cachedFileInfo.hasExportFile()) { - hasFileWithJsExportedDeclaration = true - } - if (!hasModuleLevelEffect && cachedFileInfo.hasModuleLevelEffect()) { - someModuleHasEffect = true - hasModuleLevelEffect = true - } + val perFileGenerator = object : PerFileGenerator { + override val mainModuleName get() = mainModuleArtifact.moduleExternalName - cachedFileInfo - } - .butIf(hasFileWithJsExportedDeclaration || hasModuleLevelEffect || (someModuleHasEffect && moduleArtifact === mainModuleArtifact)) { list -> - val importWithEffectIn = mainModuleArtifact.moduleExternalName.takeIf { - hasModuleLevelEffect && moduleArtifact !== mainModuleArtifact - } - val fetchedProxyFileInfo = moduleArtifact.fetchModuleProxyFileInfo()?.takeIf { - it.jsIrHeader.importedWithEffectInModuleWithName == importWithEffectIn - } - list.plus(fetchedProxyFileInfo ?: moduleArtifact.generateModuleProxyFileCachedInfo(importWithEffectIn)) - } + override val ModuleArtifact.isMain get() = this === mainModuleArtifact + override val ModuleArtifact.fileList get() = fileArtifacts + + override val CachedFileInfo.artifactName get() = jsIrHeader.externalModuleName + override val CachedFileInfo.hasEffect get() = jsIrHeader.importedWithEffectInModuleWithName != null + override val CachedFileInfo.hasExport get() = this is CachedFileInfo.MainFileCachedInfo && exportFileCachedInfo != null + + override fun SrcFileArtifact.generateArtifact(module: ModuleArtifact) = when { + isModified() -> module.loadFileInfoFor(this) + else -> module.fetchFileInfoFor(this) ?: module.loadFileInfoFor(this) } + + override fun List.merge() = when (size) { + 0 -> error("Can't merge empty list of MainFileCachedInfo") + 1 -> first() + else -> CachedFileInfo.MainFileCachedInfo.Merged(map { it as CachedFileInfo.MainFileCachedInfo }) + } + + override fun ModuleArtifact.generateArtifact(moduleNameForEffects: String?) = + fetchModuleProxyFileInfo()?.takeIf { + it.jsIrHeader.importedWithEffectInModuleWithName == moduleNameForEffects + } ?: generateModuleProxyFileCachedInfo(moduleNameForEffects) + } + + return perFileGenerator.generatePerFileArtifacts(moduleArtifacts) + .flatMap { if (it is CachedFileInfo.MainFileCachedInfo) listOfNotNull(it.exportFileCachedInfo, it) else listOf(it) } .onEach { headerToCachedInfo[it.jsIrHeader] = it } } @@ -319,9 +358,8 @@ class JsPerFileCache(private val moduleArtifacts: List) : JsMult } private data class CachedFileArtifacts(val jsCodeFile: File, val sourceMapFile: File?, val tsDeclarationsFile: File?) - private data class LoadedJsIrModuleHeaders(val mainHeader: JsIrModuleHeader, val exportHeader: JsIrModuleHeader?) + private data class LoadedJsIrModuleHeaders(val mainHeader: JsIrModuleHeader, val exportHeader: JsIrModuleHeader?) { + fun toPair() = mainHeader to exportHeader + } - private fun List.hasExportFile(): Boolean = size > 1 - private fun List.hasModuleLevelEffect(): Boolean = - last().jsIrHeader.importedWithEffectInModuleWithName != null } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ModuleArtifact.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ModuleArtifact.kt index 33d1dc232b1..8c108bf77f2 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ModuleArtifact.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ModuleArtifact.kt @@ -6,22 +6,20 @@ package org.jetbrains.kotlin.ir.backend.js.ic import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrModule -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName import org.jetbrains.kotlin.ir.backend.js.utils.serialization.deserializeJsIrProgramFragment import java.io.File -class SrcFileArtifact(val srcFilePath: String, private val fragments: List, private val astArtifact: File? = null) { - fun loadJsIrFragments(): List { - if (fragments.isNotEmpty()) { +class SrcFileArtifact(val srcFilePath: String, private val fragments: JsIrProgramFragments?, private val astArtifact: File? = null) { + fun loadJsIrFragments(): JsIrProgramFragments? { + if (fragments != null) { return fragments } - return astArtifact?.ifExists { readBytes() }?.let { - deserializeJsIrProgramFragment(it) - } ?: emptyList() + return astArtifact?.ifExists { readBytes() }?.let { deserializeJsIrProgramFragment(it) } } - fun isModified() = fragments.isNotEmpty() + fun isModified() = fragments != null } class ModuleArtifact( @@ -35,7 +33,10 @@ class ModuleArtifact( val moduleExternalName = externalModuleName ?: moduleSafeName fun loadJsIrModule(): JsIrModule { - val fragments = fileArtifacts.sortedBy { it.srcFilePath }.flatMap { it.loadJsIrFragments() } + val fragments = fileArtifacts.sortedBy { it.srcFilePath }.flatMap { + val fragments = it.loadJsIrFragments() + listOfNotNull(fragments?.mainFragment, fragments?.exportFragment) + } return JsIrModule(moduleSafeName, moduleExternalName, fragments) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 9d28c007cae..6552cf70869 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.utils.memoryOptimizedMap +import org.jetbrains.kotlin.utils.putToMultiMap import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import java.io.File @@ -145,7 +146,6 @@ class IrModuleToJsTransformer( val file: IrFile, val exports: List, val tsDeclarations: TypeScriptFragment?, - val generatedFrom: IrFile? = null ) private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List) @@ -208,19 +208,14 @@ class IrModuleToJsTransformer( fun makeIrFragmentsGenerators( dirtyFiles: Collection, allModules: Collection - ): List<() -> List> { - val files = dirtyFiles + backendContext.mapping.chunkToOriginalFile.keys + ): List<() -> JsIrProgramFragments> { val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules) - val exportData = exportModelGenerator.generateExportWithExternals(files) + val exportData = exportModelGenerator.generateExportWithExternals(dirtyFiles) val mode = TranslationMode.fromFlags(production = false, backendContext.granularity, minimizedMemberNames = false) doStaticMembersLowering(allModules) - return exportData - .groupBy { it.generatedFrom ?: it.file } - .map { - { it.value.flatMap { generateProgramFragment(it, mode) } } - } + return exportData.map { { generateProgramFragment(it, mode) } } } private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection): List { @@ -231,7 +226,7 @@ class IrModuleToJsTransformer( val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) { allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) } } - IrFileExports(irFile, allExports, tsDeclarations, context.mapping.chunkToOriginalFile[irFile]) + IrFileExports(irFile, allExports, tsDeclarations) } } @@ -257,7 +252,10 @@ class IrModuleToJsTransformer( JsIrModule( data.fragment.safeName, moduleFragmentToNameMapper.getExternalNameFor(data.fragment), - data.files.flatMap { generateProgramFragment(it, mode) }, + data.files.flatMap { + val fragments = generateProgramFragment(it, mode) + listOfNotNull(fragments.mainFragment, fragments.exportFragment) + }, mainModule.fragment.safeName.takeIf { !isEsModules && data != mainModule } ) } @@ -266,40 +264,40 @@ class IrModuleToJsTransformer( private fun generateJsIrProgramPerFile(exportData: List, mode: TranslationMode): JsIrProgram { val mainModule = exportData.last() - var someModuleHasEffect = false - val modulesPerFile = buildList { - for (module in exportData) { - var hasModuleLevelEffect = false - var hasFileWithJsExportedDeclaration = false + val perFileGenerator = object : PerFileGenerator { + override val mainModuleName get() = mainModule.fragment.safeName - for (fileExports in module.files) { - if (fileExports.file.couldBeSkipped()) continue - val programFragments = generateProgramFragment(fileExports, mode) + override val IrAndExportedDeclarations.isMain get() = this === mainModule + override val IrAndExportedDeclarations.fileList get() = files - fileExports.toJsIrModule(module, programFragments.mainFragment).let { - add(it) - if (it.importedWithEffectInModuleWithName != null) { - someModuleHasEffect = true - hasModuleLevelEffect = true - } - } + override val JsIrModules.artifactName get() = this.mainModule.externalModuleName + override val JsIrModules.hasEffect get() = this.mainModule.importedWithEffectInModuleWithName != null + override val JsIrModules.hasExport get() = this.exportModule != null - programFragments.exportFragment?.let { - add(fileExports.toJsIrModuleForExport(module, it)) - hasFileWithJsExportedDeclaration = true - } + override fun List.merge() = + JsIrModules(map { it.mainModule }.merge(), mapNotNull { it.exportModule }.ifNotEmpty { merge() }) + + override fun IrAndExportedDeclarations.generateArtifact(moduleNameForEffects: String?) = + JsIrModules(toJsIrProxyModule(moduleNameForEffects)) + + override fun IrFileExports.generateArtifact(module: IrAndExportedDeclarations) = takeIf { !file.couldBeSkipped() } + ?.let { generateProgramFragment(it, mode) } + ?.let { + JsIrModules( + toJsIrModule(module, it.mainFragment), + it.exportFragment?.run { toJsIrModuleForExport(module, this) } + ) } - - if (hasFileWithJsExportedDeclaration || hasModuleLevelEffect || (module === mainModule && someModuleHasEffect)) { - add(module.toJsIrProxyModule(mainModule.fragment.safeName.takeIf { module !== mainModule && hasModuleLevelEffect })) - } - } } - return JsIrProgram(modulesPerFile) + return JsIrProgram(perFileGenerator.generatePerFileArtifacts(exportData).flatMap { + listOfNotNull(it.mainModule, it.exportModule) + }) } + private class JsIrModules(val mainModule: JsIrModule, val exportModule: JsIrModule? = null) + private fun IrFileExports.toJsIrModule(module: IrAndExportedDeclarations, programFragment: JsIrProgramFragment): JsIrModule { return JsIrModule( moduleFragmentToNameMapper.getSafeNameFor(file), @@ -350,7 +348,7 @@ class IrModuleToJsTransformer( } } - private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): List { + private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): JsIrProgramFragments { val globalNameScope = NameTable() val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules) val staticContext = JsStaticContext(backendContext, nameGenerator, globalNameScope, mode) @@ -432,7 +430,7 @@ class IrModuleToJsTransformer( optimizeFragmentByJsAst(result) } - return listOfNotNull(result, exportFragment) + return JsIrProgramFragments(result, exportFragment) } private fun Set.computeTag(declaration: IrDeclaration): String? { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIrProgramFragment.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIrProgramFragment.kt index eca6666a8b4..3681cfe6c8c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIrProgramFragment.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIrProgramFragment.kt @@ -12,6 +12,8 @@ import org.jetbrains.kotlin.js.backend.ast.* import java.io.File import org.jetbrains.kotlin.serialization.js.ModuleKind +class JsIrProgramFragments(val mainFragment: JsIrProgramFragment, val exportFragment: JsIrProgramFragment? = null) + class JsIrProgramFragment(val name: String, val packageFqn: String) { val nameBindings = mutableMapOf() val optionalCrossModuleImports = hashSetOf() @@ -303,6 +305,4 @@ fun JsStatement.renameImportedSymbolInternalName(newName: JsName): JsStatement { } } -val List.mainFragment: JsIrProgramFragment get() = first() -val List.exportFragment: JsIrProgramFragment? get() = getOrNull(1) val JsIrProgramFragment.hasEffect: Boolean get() = eagerInitializers.statements.isNotEmpty() \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt index 41237e619b8..b86cafa5546 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt @@ -364,3 +364,57 @@ class Merger( } } } + +fun List.merge(): JsIrModule { + assert(isNotEmpty()) { "Can't merge empty list of modules" } + val firstModule = first() + + return if (size == 1) { + firstModule + } else { + val fragments = mutableListOf() + var reexportedInModuleWithName: String? = null + + for (module in this) { + fragments.addAll(module.fragments) + module.reexportedInModuleWithName?.let { reexportedInModuleWithName = it } + } + + JsIrModule(firstModule.moduleName, firstModule.externalModuleName, fragments, reexportedInModuleWithName) + } +} + +fun List.merge(): JsIrModuleHeader { + assert(isNotEmpty()) { "Can't merge empty list of module headers" } + val firstModule = first() + + return if (size == 1) { + firstModule + } else { + val definitions = mutableSetOf() + val nameBindings = mutableMapOf() + val optionalCrossModuleImports = mutableSetOf() + var reexportedInModuleWithName: String? = null + var importedWithEffectInModuleWithName: String? = null + + for (header in this) { + definitions.addAll(header.definitions) + nameBindings.putAll(header.nameBindings) + optionalCrossModuleImports.addAll(header.optionalCrossModuleImports) + + header.reexportedInModuleWithName?.let { reexportedInModuleWithName = it } + header.importedWithEffectInModuleWithName?.let { importedWithEffectInModuleWithName = it } + } + + JsIrModuleHeader( + firstModule.moduleName, + firstModule.externalModuleName, + definitions, + nameBindings, + optionalCrossModuleImports, + reexportedInModuleWithName, + importedWithEffectInModuleWithName, + null + ) + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt index 49fa25eac3f..acb822d3c25 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt @@ -6,33 +6,16 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import org.jetbrains.kotlin.ir.backend.js.utils.getJsFileName -import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.backend.js.utils.nameWithoutExtension import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.path private const val EXPORTER_FILE_POSTFIX = ".export" class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map) { - private val externalNameToItsFile = hashMapOf() - fun getExternalNameFor(file: IrFile): String { - return getExternalNameFor(file.outputName, file.packageFqName.asString(), file.module.getJsOutputName()).also { - val alreadyReservedBy = externalNameToItsFile.putIfAbsent(it.lowercase(), file) - - if (alreadyReservedBy != null && alreadyReservedBy != file) { - error( - """ - |There are two files in module '${file.module.name}' that have the similar package and file names. - | - Package "${file.packageFqName.asString()}" and path "${file.path}" - | - Package "${alreadyReservedBy.packageFqName.asString()}" and path "${alreadyReservedBy.path}" - |Note, that if the difference is only in letter cases, it also could lead to a clash of the compiled artifacts - """.trimMargin() - ) - } - } + return getExternalNameFor(file.outputName, file.packageFqName.asString(), file.module.getJsOutputName()) } fun getExternalNameFor(fileName: String, packageFqn: String, moduleName: String): String { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/PerFileGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/PerFileGenerator.kt new file mode 100644 index 00000000000..ef518d9011f --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/PerFileGenerator.kt @@ -0,0 +1,60 @@ +/* + * 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.transformers.irToJs + +import org.jetbrains.kotlin.utils.putToMultiMap + +interface PerFileGenerator { + val mainModuleName: String + + val Module.isMain: Boolean + val Module.fileList: Iterable + + val Artifact.artifactName: String + val Artifact.hasEffect: Boolean + val Artifact.hasExport: Boolean + + fun List.merge(): Artifact + fun File.generateArtifact(module: Module): Artifact? + fun Module.generateArtifact(moduleNameForEffects: String?): Artifact + + fun generatePerFileArtifacts(modules: List): List { + var someModuleHasEffect = false + + val nameToModulePerFile = buildMap { + for (module in modules) { + var hasModuleLevelEffect = false + var hasFileWithExportedDeclaration = false + + for (file in module.fileList) { + val generatedArtifact = file.generateArtifact(module) ?: continue + + if (generatedArtifact.hasExport) { + hasFileWithExportedDeclaration = true + } + + if (generatedArtifact.hasEffect) { + hasModuleLevelEffect = true + } + + putToMultiMap(generatedArtifact.artifactName, generatedArtifact) + } + + if (hasModuleLevelEffect) { + someModuleHasEffect = true + } + + if (hasFileWithExportedDeclaration || hasModuleLevelEffect || (module.isMain && someModuleHasEffect)) { + val proxyArtifact = + module.generateArtifact(mainModuleName.takeIf { !module.isMain && hasModuleLevelEffect }) ?: continue + putToMultiMap(proxyArtifact.artifactName, proxyArtifact) + } + } + } + + return nameToModulePerFile.values.map { it.merge() } + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt index 89a9aa034f2..4384f966983 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstDeserializer.kt @@ -8,13 +8,14 @@ package org.jetbrains.kotlin.ir.backend.js.utils.serialization import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.* import java.nio.ByteBuffer import java.util.* -fun deserializeJsIrProgramFragment(input: ByteArray): List { +fun deserializeJsIrProgramFragment(input: ByteArray): JsIrProgramFragments { return JsIrAstDeserializer(input).readFragments() } @@ -76,8 +77,8 @@ private class JsIrAstDeserializer(private val source: ByteArray) { return if (readBoolean()) then() else null } - fun readFragments(): List { - return readList { readFragment() } + fun readFragments(): JsIrProgramFragments { + return JsIrProgramFragments(readFragment(), ifTrue { readFragment() }) } fun readFragment(): JsIrProgramFragment { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt index 99e6dcf4b2d..1fd49906bb9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/serialization/JsIrAstSerializer.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils.serialization import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment +import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.* import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -15,8 +16,8 @@ import java.io.DataOutputStream import java.io.OutputStream import java.util.* -fun List.serializeTo(output: OutputStream) { - JsIrAstSerializer().appendAll(this).saveTo(output) +fun JsIrProgramFragments.serializeTo(output: OutputStream) { + JsIrAstSerializer().append(this).saveTo(output) } private class DataWriter { @@ -89,9 +90,9 @@ private class JsIrAstSerializer { private val fileStack: Deque = ArrayDeque() private val importedNames = mutableSetOf() - fun appendAll(fragments: List): JsIrAstSerializer { - fragmentSerializer.writeInt(fragments.size) - fragments.forEach(::append) + fun append(fragments: JsIrProgramFragments): JsIrAstSerializer { + append(fragments.mainFragment) + fragmentSerializer.ifNotNull(fragments.exportFragment, ::append) return this } diff --git a/compiler/testData/codegen/box/sealed/kt54028_cursed.kt b/compiler/testData/codegen/box/sealed/kt54028_cursed.kt index c308732ba15..316a04c78b2 100644 --- a/compiler/testData/codegen/box/sealed/kt54028_cursed.kt +++ b/compiler/testData/codegen/box/sealed/kt54028_cursed.kt @@ -4,8 +4,6 @@ // TARGET_BACKEND: JVM // TARGET_BACKEND: JVM_IR // TARGET_BACKEND: JS_IR -// K2 JS_IR MUTE_REASON: java.lang.NullPointerException at org.jetbrains.kotlin.fir.backend.Fir2IrClassifierStorage.getIrClassSymbol -// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6 // Test that if we have two different files with the same name in the same package, KT-54028 doesn't reproduce. diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt index b8afa07db7f..f2014967099 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.test.directives.model.singleValue import org.jetbrains.kotlin.test.getAnalyzerServices import org.jetbrains.kotlin.test.model.FrontendFacade import org.jetbrains.kotlin.test.model.FrontendKinds +import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.runners.lightTreeSyntaxDiagnosticsReporterHolder import org.jetbrains.kotlin.test.services.* @@ -282,9 +283,9 @@ open class FirFrontendFacade( val (ktFiles, lightTreeFiles) = when (parser) { FirParser.LightTree -> { - emptyList() to testServices.sourceFileProvider.getKtSourceFilesForSourceFiles(module.files).values + emptyMap() to testServices.sourceFileProvider.getKtSourceFilesForSourceFiles(module.files) } - FirParser.Psi -> testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).values to emptyList() + FirParser.Psi -> testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project) to emptyMap() } val sessionConfigurator: FirSessionConfigurator.() -> Unit = { @@ -304,21 +305,27 @@ open class FirFrontendFacade( sessionConfigurator, predefinedJavaComponents, project, - ktFiles + ktFiles.values ) val firAnalyzerFacade = FirAnalyzerFacade( moduleBasedSession, - ktFiles, - lightTreeFiles, + ktFiles.values, + lightTreeFiles.values, parser, testServices.lightTreeSyntaxDiagnosticsReporterHolder?.reporter, ) val firFiles = firAnalyzerFacade.runResolution() - val filesMap = firFiles.mapNotNull { firFile -> - val testFile = module.files.firstOrNull { it.name == firFile.name } ?: return@mapNotNull null - testFile to firFile - }.toMap() + + val usedFilesMap = when (parser) { + FirParser.LightTree -> lightTreeFiles + FirParser.Psi -> ktFiles + } + + val filesMap = usedFilesMap.keys + .zip(firFiles) + .onEach { assert(it.first.name == it.second.name) } + .toMap() return FirOutputPartForDependsOnModule(module, moduleBasedSession, firAnalyzerFacade, filesMap) } diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/CollectionUtil.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/CollectionUtil.kt index 725bf6ef321..583dd72ef4a 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/CollectionUtil.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/CollectionUtil.kt @@ -75,3 +75,8 @@ fun Collection.atMostOne(): T? { * If there are more such elements, throws an exception. */ inline fun Iterable.atMostOne(predicate: (T) -> Boolean): T? = this.filter(predicate).atMostOne() + +fun MutableMap>.putToMultiMap(key: K, value: V) { + val list = getOrPut(key) { mutableListOf() } + list.add(value) +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerFileTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerFileTestGenerated.java index fe6da03c929..ed55d6f98f6 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerFileTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerFileTestGenerated.java @@ -175,6 +175,12 @@ public class JsFirInvalidationPerFileTestGenerated extends AbstractJsFirInvalida runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerModuleTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerModuleTestGenerated.java index e68a107ae0a..af47fe4d078 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerModuleTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsFirInvalidationPerModuleTestGenerated.java @@ -175,6 +175,12 @@ public class JsFirInvalidationPerModuleTestGenerated extends AbstractJsFirInvali runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerFileTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerFileTestGenerated.java index 8259ff73a51..9de5210334a 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerFileTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerFileTestGenerated.java @@ -175,6 +175,12 @@ public class JsIrES6InvalidationPerFileTestGenerated extends AbstractJsIrES6Inva runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerModuleTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerModuleTestGenerated.java index deb25a7b872..ebda76f7c7c 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerModuleTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationPerModuleTestGenerated.java @@ -175,6 +175,12 @@ public class JsIrES6InvalidationPerModuleTestGenerated extends AbstractJsIrES6In runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerFileTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerFileTestGenerated.java index 95b4a9dd52c..4606fccfee4 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerFileTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerFileTestGenerated.java @@ -175,6 +175,12 @@ public class JsIrInvalidationPerFileTestGenerated extends AbstractJsIrInvalidati runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerModuleTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerModuleTestGenerated.java index f28a9f00fa3..93ccc31e899 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerModuleTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationPerModuleTestGenerated.java @@ -175,6 +175,12 @@ public class JsIrInvalidationPerModuleTestGenerated extends AbstractJsIrInvalida runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); } + @Test + @TestMetadata("fileNameClash") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/"); + } + @Test @TestMetadata("friendDependency") public void testFriendDependency() throws Exception { 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 5085471fa80..48ecea61e85 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 @@ -2349,6 +2349,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest { runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); } + @Test + @TestMetadata("fileNameClash.kt") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/box/esModules/export/fileNameClash.kt"); + } + @Test @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java index 45e60e31a6b..ed0071d167b 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsES6BoxTestGenerated.java @@ -2455,6 +2455,12 @@ public class FirJsES6BoxTestGenerated extends AbstractFirJsES6BoxTest { runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); } + @Test + @TestMetadata("fileNameClash.kt") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/box/esModules/export/fileNameClash.kt"); + } + @Test @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { 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 b7743c3a696..2a957b1a7e8 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 @@ -2455,6 +2455,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test { runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); } + @Test + @TestMetadata("fileNameClash.kt") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/box/esModules/export/fileNameClash.kt"); + } + @Test @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { 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 ae6d026ac01..3745ca4d8bb 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 @@ -2349,6 +2349,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); } + @Test + @TestMetadata("fileNameClash.kt") + public void testFileNameClash() throws Exception { + runTest("js/js.translator/testData/box/esModules/export/fileNameClash.kt"); + } + @Test @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { diff --git a/js/js.translator/testData/box/esModules/export/fileNameClash.kt b/js/js.translator/testData/box/esModules/export/fileNameClash.kt new file mode 100644 index 00000000000..bf691990d44 --- /dev/null +++ b/js/js.translator/testData/box/esModules/export/fileNameClash.kt @@ -0,0 +1,35 @@ +// DONT_TARGET_EXACT_BACKEND: JS +// ES_MODULES + +// MODULE: name_clash +// FILE: test1/lib.kt +package foo + +@JsExport +open class A { + open fun foo(value: T): T = value +} + +// FILE: test2/lib.kt +package foo + +@JsExport +class B: A() { + override fun foo(value: String): String = value +} + +// FILE: entry.mjs +// ENTRY_ES_MODULE +import { A, B } from "./fileNameClash-name_clash_v5.mjs"; + +export function box() { + var a = new A() + var aFoo = a.foo("ok") + if (aFoo != "ok") return "fail 1" + + var b = new B() + var bFoo = b.foo("ok") + if (bFoo != "ok") return "fail 2" + + return "OK" +} diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.1.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.1.kt new file mode 100644 index 00000000000..60b5344042e --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.1.kt @@ -0,0 +1,3 @@ +package foo + +fun test1() = test2() \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.3.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.3.kt new file mode 100644 index 00000000000..d0e0d06c2ff --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.3.kt @@ -0,0 +1,4 @@ +package foo + +@JsExport +fun test1() = test2() \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.kt new file mode 100644 index 00000000000..8e35e76becd --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f1.kt @@ -0,0 +1,3 @@ +package foo + +fun test1() = "OK" \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.1.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.1.kt new file mode 100644 index 00000000000..46d0ad5f1d0 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.1.kt @@ -0,0 +1,5 @@ +@file:JsFileName("f1") + +package foo + +fun test2() = "OK" \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.2.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.2.kt new file mode 100644 index 00000000000..9e80ad5819c --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.2.kt @@ -0,0 +1,6 @@ +@file:JsFileName("f1") + +package foo + +@JsExport +fun test2() = "OK" \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.4.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.4.kt new file mode 100644 index 00000000000..a4a70ccebd3 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/f2.4.kt @@ -0,0 +1,3 @@ +package foo + +fun test2() = "OK" \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/module.info b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/module.info new file mode 100644 index 00000000000..758a23bc85f --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/lib/module.info @@ -0,0 +1,22 @@ +STEP 0: + added file: f1.kt +STEP 1: + modifications: + U : f1.1.kt -> f1.kt + U : f2.1.kt -> f2.kt + modified ir: f1.kt + added file: f2.kt +STEP 2: + modifications: + U : f2.2.kt -> f2.kt + modified ir: f2.kt + updated imports: f1.kt +STEP 3: + modifications: + U : f1.3.kt -> f1.kt + modified ir: f1.kt +STEP 4: + modifications: + U : f2.4.kt -> f2.kt + modified ir: f2.kt + updated imports: f1.kt diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/main/m.kt b/js/js.translator/testData/incremental/invalidation/fileNameClash/main/m.kt new file mode 100644 index 00000000000..6da75e69262 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/main/m.kt @@ -0,0 +1,3 @@ +import foo.test1 + +fun box() = test1() \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/main/module.info b/js/js.translator/testData/incremental/invalidation/fileNameClash/main/module.info new file mode 100644 index 00000000000..d2433280e3e --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/main/module.info @@ -0,0 +1,12 @@ +STEP 0: + dependencies: lib + added file: m.kt +STEP 1: + dependencies: lib +STEP 2: + dependencies: lib +STEP 3: + dependencies: lib + updated imports: m.kt +STEP 4: + dependencies: lib \ No newline at end of file diff --git a/js/js.translator/testData/incremental/invalidation/fileNameClash/project.info b/js/js.translator/testData/incremental/invalidation/fileNameClash/project.info new file mode 100644 index 00000000000..f351ccb5e98 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/fileNameClash/project.info @@ -0,0 +1,22 @@ +MODULES: lib, main + +STEP 0: + libs: lib, main + dirty js modules: main, lib + dirty js files: lib/foo/f1, main/m, main/m.export, main +STEP 1: + libs: lib, main + dirty js modules: lib + dirty js files: lib/foo/f1 +STEP 2: + libs: lib, main + dirty js modules: lib + dirty js files: lib/foo/f1, lib/foo/f1.export, lib +STEP 3: + libs: lib, main + dirty js modules: lib, main + dirty js files: lib/foo/f1, lib/foo/f1.export, main/m +STEP 4: + libs: lib, main + dirty js modules: lib + dirty js files: lib/foo/f1, lib/foo/f2, lib/foo/f1.export diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 41e4b7db696..3bfc778101c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -372,27 +372,6 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() { } } - @DisplayName("per-file with the declarations validation") - @GradleTest - fun testPerFileProjectWithResultFilesClash(gradleVersion: GradleVersion) { - project("kotlin-js-invalid-per-file-project", gradleVersion) { - buildAndFail("compileDevelopmentExecutableKotlinJs") { - assertTasksFailed(":compileDevelopmentExecutableKotlinJs") - assertOutputContains(""" - |There are two files in module '' that have the similar package and file names. - | - Package "com.example" and - """.trimMargin()) - } - buildAndFail("compileProductionExecutableKotlinJs") { - assertTasksFailed(":compileProductionExecutableKotlinJs") - assertOutputContains(""" - |There are two files in module '' that have the similar package and file names. - | - Package "com.example" and path - """.trimMargin()) - } - } - } - @DisplayName("fully qualified names can be used in the sourcemap") @GradleTest fun testKotlinJsSourceMapGenerateFqNames(gradleVersion: GradleVersion) { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts deleted file mode 100644 index 26eb11c7cee..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -plugins { - kotlin("js") -} - -repositories { - mavenLocal() - mavenCentral() -} - -kotlin { - js(IR) { - useEsModules() - binaries.executable() - nodejs() - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties deleted file mode 100644 index abb12f59146..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties +++ /dev/null @@ -1,8 +0,0 @@ -kotlin.tests.individualTaskReports=true -kotlin.js.ir.output.granularity=per-file -kotlin.incremental=false -kotlin.incremental.js=false -kotlin.incremental.js.ir=false -kotlin.incremental.js.klib=false -kotlin.incremental.multiplatform=false -kotlin.incremental.useClasspathSnapshot=false \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt deleted file mode 100644 index 2d3a770d00f..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2010-2019 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 com.example - -@JsExport -fun best(): Int { - return 42 -} - -@JsExport -fun simpleBest(): Int { - return 73 -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt deleted file mode 100644 index d6904f24f79..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2010-2019 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 com.example - -@JsExport -public val foo = 44