[K/JS] Implement file merging for clashed file during per-file compilation

This commit is contained in:
Artem Kobzar
2023-09-14 13:14:15 +00:00
committed by Space Team
parent 337054ba92
commit 878452bd2b
43 changed files with 529 additions and 277 deletions
@@ -17,9 +17,6 @@ class JsMapping : DefaultMapping() {
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>() val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>() val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
// Check if we need concurrency here
val chunkToOriginalFile = WeakHashMap<IrFile, IrFile>()
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>() val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>() val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>() val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
@@ -50,7 +50,7 @@ class JsIrCompilerWithIC(
allModules: Collection<IrModuleFragment>, allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>, dirtyFiles: Collection<IrFile>,
mainArguments: List<String>? mainArguments: List<String>?
): List<() -> List<JsIrProgramFragment>> { ): List<() -> JsIrProgramFragments> {
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS) val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
allModules.forEach { allModules.forEach {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.* 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.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment 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.declarations.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
@@ -33,7 +34,7 @@ fun interface JsIrCompilerICInterface {
allModules: Collection<IrModuleFragment>, allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>, dirtyFiles: Collection<IrFile>,
mainArguments: List<String>? mainArguments: List<String>?
): List<() -> List<JsIrProgramFragment>> ): List<() -> JsIrProgramFragments>
} }
fun interface JsIrCompilerICInterfaceFactory { fun interface JsIrCompilerICInterfaceFactory {
@@ -616,7 +617,7 @@ class CacheUpdater(
private fun commitCacheAndBuildModuleArtifacts( private fun commitCacheAndBuildModuleArtifacts(
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>, incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
moduleNames: Map<KotlinLibraryFile, String>, moduleNames: Map<KotlinLibraryFile, String>,
rebuiltFileFragments: KotlinSourceFileMap<List<JsIrProgramFragment>> rebuiltFileFragments: KotlinSourceFileMap<JsIrProgramFragments>
): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") { ): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") {
incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) -> incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) ->
incrementalCacheArtifact.buildModuleArtifactAndCommitCache( incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
@@ -630,7 +631,7 @@ class CacheUpdater(
compilerForIC: JsIrCompilerICInterface, compilerForIC: JsIrCompilerICInterface,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>, loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>> dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> = ): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>> =
stopwatch.measure("Processing IR - lowering") { stopwatch.measure("Processing IR - lowering") {
val dirtyFilesForCompiling = mutableListOf<IrFile>() val dirtyFilesForCompiling = mutableListOf<IrFile>()
val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>() val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>()
@@ -737,7 +738,7 @@ class CacheUpdater(
private data class FragmentGenerators( private data class FragmentGenerators(
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>, val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
val moduleNames: Map<KotlinLibraryFile, String>, val moduleNames: Map<KotlinLibraryFile, String>,
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>>
) )
private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators { private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators {
@@ -751,9 +752,9 @@ class CacheUpdater(
} }
private fun generateIrFragments( private fun generateIrFragments(
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>>
): KotlinSourceFileMap<List<JsIrProgramFragment>> = stopwatch.measure("Processing IR - generating program fragments") { ): KotlinSourceFileMap<JsIrProgramFragments> = stopwatch.measure("Processing IR - generating program fragments") {
val rebuiltFragments = KotlinSourceFileMutableMap<List<JsIrProgramFragment>>() val rebuiltFragments = KotlinSourceFileMutableMap<JsIrProgramFragments>()
while (generators.isNotEmpty()) { while (generators.isNotEmpty()) {
val (libFile, srcFile, fragmentGenerator) = generators.removeFirst() val (libFile, srcFile, fragmentGenerator) = generators.removeFirst()
rebuiltFragments[libFile, srcFile] = fragmentGenerator() rebuiltFragments[libFile, srcFile] = fragmentGenerator()
@@ -782,7 +783,7 @@ fun rebuildCacheForDirtyFiles(
irFactory: IrFactory, irFactory: IrFactory,
exportedDeclarations: Set<FqName>, exportedDeclarations: Set<FqName>,
mainArguments: List<String>?, mainArguments: List<String>?,
): Pair<IrModuleFragment, List<Pair<IrFile, List<JsIrProgramFragment>>>> { ): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragments>>> {
val internationService = IrInterningService() val internationService = IrInterningService()
val emptyMetadata = object : KotlinSourceFileExports() { val emptyMetadata = object : KotlinSourceFileExports() {
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap()) override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.ic 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.ir.backend.js.utils.serialization.serializeTo
import org.jetbrains.kotlin.konan.file.use import org.jetbrains.kotlin.konan.file.use
import org.jetbrains.kotlin.utils.newHashSetWithExpectedSize import org.jetbrains.kotlin.utils.newHashSetWithExpectedSize
@@ -15,7 +15,7 @@ import java.io.File
internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) { internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) {
abstract fun commitMetadata() abstract fun commitMetadata()
fun commitBinaryAst(fragments: List<JsIrProgramFragment>) { fun commitBinaryAst(fragments: JsIrProgramFragments) {
binaryAstFile.parentFile?.mkdirs() binaryAstFile.parentFile?.mkdirs()
BufferedOutputStream(binaryAstFile.outputStream()).use { BufferedOutputStream(binaryAstFile.outputStream()).use {
fragments.serializeTo(it) fragments.serializeTo(it)
@@ -30,7 +30,7 @@ internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val
srcFile: KotlinSourceFile, srcFile: KotlinSourceFile,
binaryAstFile: File, binaryAstFile: File,
private val metadataFile: File, private val metadataFile: File,
private val encodedMetadata: ByteArray private val encodedMetadata: ByteArray,
) : SourceFileCacheArtifact(srcFile, binaryAstFile) { ) : SourceFileCacheArtifact(srcFile, binaryAstFile) {
override fun commitMetadata() { override fun commitMetadata() {
metadataFile.parentFile?.mkdirs() metadataFile.parentFile?.mkdirs()
@@ -41,7 +41,7 @@ internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val
class RemoveMetadata( class RemoveMetadata(
srcFile: KotlinSourceFile, srcFile: KotlinSourceFile,
binaryAstFile: File, binaryAstFile: File,
private val metadataFile: File private val metadataFile: File,
) : SourceFileCacheArtifact(srcFile, binaryAstFile) { ) : SourceFileCacheArtifact(srcFile, binaryAstFile) {
override fun commitMetadata() { override fun commitMetadata() {
metadataFile.delete() metadataFile.delete()
@@ -53,13 +53,13 @@ internal class IncrementalCacheArtifact(
private val artifactsDir: File, private val artifactsDir: File,
private val forceRebuildJs: Boolean, private val forceRebuildJs: Boolean,
private val srcCacheActions: List<SourceFileCacheArtifact>, private val srcCacheActions: List<SourceFileCacheArtifact>,
private val externalModuleName: String? private val externalModuleName: String?,
) { ) {
fun getSourceFiles() = srcCacheActions.mapTo(newHashSetWithExpectedSize(srcCacheActions.size)) { it.srcFile } fun getSourceFiles() = srcCacheActions.mapTo(newHashSetWithExpectedSize(srcCacheActions.size)) { it.srcFile }
fun buildModuleArtifactAndCommitCache( fun buildModuleArtifactAndCommitCache(
moduleName: String, moduleName: String,
rebuiltFileFragments: Map<KotlinSourceFile, List<JsIrProgramFragment>>, rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragments>,
): ModuleArtifact { ): ModuleArtifact {
val fileArtifacts = srcCacheActions.map { srcFileAction -> val fileArtifacts = srcCacheActions.map { srcFileAction ->
val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile] val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile]
@@ -67,7 +67,7 @@ internal class IncrementalCacheArtifact(
srcFileAction.commitBinaryAst(rebuiltFileFragment) srcFileAction.commitBinaryAst(rebuiltFileFragment)
} }
srcFileAction.commitMetadata() srcFileAction.commitMetadata()
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment ?: emptyList(), srcFileAction.binaryAstFile) SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment, srcFileAction.binaryAstFile)
} }
return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName) return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName)
@@ -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.ir.backend.js.transformers.irToJs.*
import org.jetbrains.kotlin.protobuf.CodedInputStream import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.protobuf.CodedOutputStream import org.jetbrains.kotlin.protobuf.CodedOutputStream
import org.jetbrains.kotlin.utils.addToStdlib.butIf
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.File import java.io.File
@@ -20,6 +19,48 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
private const val CACHED_EXPORT_FILE_JS = "file.export.js" 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_JS_MAP = "file.js.map"
private const val CACHED_FILE_D_TS = "file.d.ts" 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 { sealed class CachedFileInfo(val moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader?) : CacheInfo {
@@ -30,6 +71,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
if (moduleHeader != null) jsIrHeader = moduleHeader if (moduleHeader != null) jsIrHeader = moduleHeader
} }
abstract fun loadJsIrModule(): JsIrModule
sealed class SerializableCachedFileInfo( sealed class SerializableCachedFileInfo(
moduleArtifact: ModuleArtifact, moduleArtifact: ModuleArtifact,
@@ -37,19 +79,60 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
moduleHeader: JsIrModuleHeader? moduleHeader: JsIrModuleHeader?
) : CachedFileInfo(moduleArtifact, moduleHeader) { ) : CachedFileInfo(moduleArtifact, moduleHeader) {
fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "$filePrefix.$name") } 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) { SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) {
var exportFileCachedInfo: ExportFileCachedInfo? = null var exportFileCachedInfo: ExportFileCachedInfo? = null
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) } val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) }
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) } val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
val sourceMapFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS_MAP) } val sourceMapFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS_MAP) }
class Merged(private val cachedFileInfos: List<MainFileCachedInfo>) :
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, moduleArtifact: ModuleArtifact,
fileArtifact: SrcFileArtifact, fileArtifact: SrcFileArtifact,
moduleHeader: JsIrModuleHeader? = null, moduleHeader: JsIrModuleHeader? = null,
@@ -57,6 +140,14 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
) : SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) { ) : SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) {
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_EXPORT_FILE_JS) } val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_EXPORT_FILE_JS) }
val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) } val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) }
class Merged(
override val filePrefix: String,
moduleHeader: JsIrModuleHeader,
private val cachedFileInfos: List<ExportFileCachedInfo>,
) : ExportFileCachedInfo(cachedFileInfos.first().moduleArtifact, cachedFileInfos.first().fileArtifact, moduleHeader) {
override fun loadJsIrModule(): JsIrModule = cachedFileInfos.map { it.loadJsIrModule() }.merge()
}
} }
class ModuleProxyFileCachedInfo(moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader? = null) : class ModuleProxyFileCachedInfo(moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader? = null) :
@@ -66,51 +157,18 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) } val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
private fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "entry.$name") } 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<JsIrModuleHeader, CachedFileInfo>() private val headerToCachedInfo = hashMapOf<JsIrModuleHeader, CachedFileInfo>()
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 <T : CachedFileInfo> CodedInputStream.loadSingleCachedFileInfo(cachedFileInfo: T): T = cachedFileInfo.also { private fun <T : CachedFileInfo> CodedInputStream.loadSingleCachedFileInfo(cachedFileInfo: T): T = cachedFileInfo.also {
val moduleName = readString() val moduleName = readString()
@@ -142,14 +200,13 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
private fun <T> CachedFileInfo.MainFileCachedInfo.readModuleHeaderCache(f: CodedInputStream.() -> T): T? = private fun <T> CachedFileInfo.MainFileCachedInfo.readModuleHeaderCache(f: CodedInputStream.() -> T): T? =
moduleHeaderArtifact?.useCodedInputIfExists(f) moduleHeaderArtifact?.useCodedInputIfExists(f)
private fun ModuleArtifact.fetchFileInfoFor(fileArtifact: SrcFileArtifact): List<CachedFileInfo>? { private fun ModuleArtifact.fetchFileInfoFor(fileArtifact: SrcFileArtifact): CachedFileInfo.MainFileCachedInfo? {
val mainFileCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact) val mainFileCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact)
return mainFileCachedFileInfo.readModuleHeaderCache { return mainFileCachedFileInfo.readModuleHeaderCache {
mainFileCachedFileInfo.run { mainFileCachedFileInfo.apply {
exportFileCachedInfo = fetchFileInfoForExportedPart(this) exportFileCachedInfo = fetchFileInfoForExportedPart(this)
loadSingleCachedFileInfo(this) loadSingleCachedFileInfo(this)
listOfNotNull(exportFileCachedInfo, this)
} }
} }
} }
@@ -199,13 +256,13 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
) )
} }
private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): List<CachedFileInfo> { private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): CachedFileInfo.MainFileCachedInfo {
val headers = fileArtifact.loadJsIrModuleHeaders(this) val headers = fileArtifact.loadJsIrModuleHeaders(this)
val mainCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact, headers.mainHeader) val mainCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact, headers.mainHeader)
if (headers.exportHeader != null) { 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) } val cachedExportFileInfo = mainCachedFileInfo.readModuleHeaderCache { fetchFileInfoForExportedPart(mainCachedFileInfo) }
mainCachedFileInfo.exportFileCachedInfo = if (cachedExportFileInfo?.tsDeclarationsHash != tsDeclarationsHash) { mainCachedFileInfo.exportFileCachedInfo = if (cachedExportFileInfo?.tsDeclarationsHash != tsDeclarationsHash) {
CachedFileInfo.ExportFileCachedInfo( CachedFileInfo.ExportFileCachedInfo(
@@ -219,7 +276,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
} }
} }
return listOfNotNull(mainCachedFileInfo.exportFileCachedInfo, mainCachedFileInfo) return mainCachedFileInfo
} }
private val CachedFileInfo.cachedFiles: CachedFileArtifacts? private val CachedFileInfo.cachedFiles: CachedFileArtifacts?
@@ -245,59 +302,41 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile) compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile)
} ?: compilationOutputs } ?: compilationOutputs
override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule { override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule =
if (cacheInfo !is CachedFileInfo.SerializableCachedFileInfo) { cacheInfo.loadJsIrModule()
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 loadProgramHeadersFromCache(): List<CachedFileInfo> { override fun loadProgramHeadersFromCache(): List<CachedFileInfo> {
var someModuleHasEffect = false
val mainModuleArtifact = moduleArtifacts.last() 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()) { val perFileGenerator = object : PerFileGenerator<ModuleArtifact, SrcFileArtifact, CachedFileInfo> {
hasFileWithJsExportedDeclaration = true override val mainModuleName get() = mainModuleArtifact.moduleExternalName
}
if (!hasModuleLevelEffect && cachedFileInfo.hasModuleLevelEffect()) {
someModuleHasEffect = true
hasModuleLevelEffect = true
}
cachedFileInfo override val ModuleArtifact.isMain get() = this === mainModuleArtifact
} override val ModuleArtifact.fileList get() = fileArtifacts
.butIf(hasFileWithJsExportedDeclaration || hasModuleLevelEffect || (someModuleHasEffect && moduleArtifact === mainModuleArtifact)) { list ->
val importWithEffectIn = mainModuleArtifact.moduleExternalName.takeIf { override val CachedFileInfo.artifactName get() = jsIrHeader.externalModuleName
hasModuleLevelEffect && moduleArtifact !== mainModuleArtifact override val CachedFileInfo.hasEffect get() = jsIrHeader.importedWithEffectInModuleWithName != null
} override val CachedFileInfo.hasExport get() = this is CachedFileInfo.MainFileCachedInfo && exportFileCachedInfo != null
val fetchedProxyFileInfo = moduleArtifact.fetchModuleProxyFileInfo()?.takeIf {
it.jsIrHeader.importedWithEffectInModuleWithName == importWithEffectIn override fun SrcFileArtifact.generateArtifact(module: ModuleArtifact) = when {
} isModified() -> module.loadFileInfoFor(this)
list.plus(fetchedProxyFileInfo ?: moduleArtifact.generateModuleProxyFileCachedInfo(importWithEffectIn)) else -> module.fetchFileInfoFor(this) ?: module.loadFileInfoFor(this)
}
} }
override fun List<CachedFileInfo>.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 } .onEach { headerToCachedInfo[it.jsIrHeader] = it }
} }
@@ -319,9 +358,8 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
} }
private data class CachedFileArtifacts(val jsCodeFile: File, val sourceMapFile: File?, val tsDeclarationsFile: File?) 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<JsPerFileCache.CachedFileInfo>.hasExportFile(): Boolean = size > 1
private fun List<JsPerFileCache.CachedFileInfo>.hasModuleLevelEffect(): Boolean =
last().jsIrHeader.importedWithEffectInModuleWithName != null
} }
@@ -6,22 +6,20 @@
package org.jetbrains.kotlin.ir.backend.js.ic 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.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.transformers.irToJs.safeModuleName
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.deserializeJsIrProgramFragment import org.jetbrains.kotlin.ir.backend.js.utils.serialization.deserializeJsIrProgramFragment
import java.io.File import java.io.File
class SrcFileArtifact(val srcFilePath: String, private val fragments: List<JsIrProgramFragment>, private val astArtifact: File? = null) { class SrcFileArtifact(val srcFilePath: String, private val fragments: JsIrProgramFragments?, private val astArtifact: File? = null) {
fun loadJsIrFragments(): List<JsIrProgramFragment> { fun loadJsIrFragments(): JsIrProgramFragments? {
if (fragments.isNotEmpty()) { if (fragments != null) {
return fragments return fragments
} }
return astArtifact?.ifExists { readBytes() }?.let { return astArtifact?.ifExists { readBytes() }?.let { deserializeJsIrProgramFragment(it) }
deserializeJsIrProgramFragment(it)
} ?: emptyList()
} }
fun isModified() = fragments.isNotEmpty() fun isModified() = fragments != null
} }
class ModuleArtifact( class ModuleArtifact(
@@ -35,7 +33,10 @@ class ModuleArtifact(
val moduleExternalName = externalModuleName ?: moduleSafeName val moduleExternalName = externalModuleName ?: moduleSafeName
fun loadJsIrModule(): JsIrModule { 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) return JsIrModule(moduleSafeName, moduleExternalName, fragments)
} }
} }
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.js.util.TextOutputImpl
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.memoryOptimizedMap 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.runIf
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File import java.io.File
@@ -145,7 +146,6 @@ class IrModuleToJsTransformer(
val file: IrFile, val file: IrFile,
val exports: List<ExportedDeclaration>, val exports: List<ExportedDeclaration>,
val tsDeclarations: TypeScriptFragment?, val tsDeclarations: TypeScriptFragment?,
val generatedFrom: IrFile? = null
) )
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>) private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>)
@@ -208,19 +208,14 @@ class IrModuleToJsTransformer(
fun makeIrFragmentsGenerators( fun makeIrFragmentsGenerators(
dirtyFiles: Collection<IrFile>, dirtyFiles: Collection<IrFile>,
allModules: Collection<IrModuleFragment> allModules: Collection<IrModuleFragment>
): List<() -> List<JsIrProgramFragment>> { ): List<() -> JsIrProgramFragments> {
val files = dirtyFiles + backendContext.mapping.chunkToOriginalFile.keys
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules) 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) val mode = TranslationMode.fromFlags(production = false, backendContext.granularity, minimizedMemberNames = false)
doStaticMembersLowering(allModules) doStaticMembersLowering(allModules)
return exportData return exportData.map { { generateProgramFragment(it, mode) } }
.groupBy { it.generatedFrom ?: it.file }
.map {
{ it.value.flatMap { generateProgramFragment(it, mode) } }
}
} }
private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> { private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> {
@@ -231,7 +226,7 @@ class IrModuleToJsTransformer(
val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) { val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) {
allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) } allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) }
} }
IrFileExports(irFile, allExports, tsDeclarations, context.mapping.chunkToOriginalFile[irFile]) IrFileExports(irFile, allExports, tsDeclarations)
} }
} }
@@ -257,7 +252,10 @@ class IrModuleToJsTransformer(
JsIrModule( JsIrModule(
data.fragment.safeName, data.fragment.safeName,
moduleFragmentToNameMapper.getExternalNameFor(data.fragment), 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 } mainModule.fragment.safeName.takeIf { !isEsModules && data != mainModule }
) )
} }
@@ -266,40 +264,40 @@ class IrModuleToJsTransformer(
private fun generateJsIrProgramPerFile(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsIrProgram { private fun generateJsIrProgramPerFile(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsIrProgram {
val mainModule = exportData.last() val mainModule = exportData.last()
var someModuleHasEffect = false
val modulesPerFile = buildList { val perFileGenerator = object : PerFileGenerator<IrAndExportedDeclarations, IrFileExports, JsIrModules> {
for (module in exportData) { override val mainModuleName get() = mainModule.fragment.safeName
var hasModuleLevelEffect = false
var hasFileWithJsExportedDeclaration = false
for (fileExports in module.files) { override val IrAndExportedDeclarations.isMain get() = this === mainModule
if (fileExports.file.couldBeSkipped()) continue override val IrAndExportedDeclarations.fileList get() = files
val programFragments = generateProgramFragment(fileExports, mode)
fileExports.toJsIrModule(module, programFragments.mainFragment).let { override val JsIrModules.artifactName get() = this.mainModule.externalModuleName
add(it) override val JsIrModules.hasEffect get() = this.mainModule.importedWithEffectInModuleWithName != null
if (it.importedWithEffectInModuleWithName != null) { override val JsIrModules.hasExport get() = this.exportModule != null
someModuleHasEffect = true
hasModuleLevelEffect = true
}
}
programFragments.exportFragment?.let { override fun List<JsIrModules>.merge() =
add(fileExports.toJsIrModuleForExport(module, it)) JsIrModules(map { it.mainModule }.merge(), mapNotNull { it.exportModule }.ifNotEmpty { merge() })
hasFileWithJsExportedDeclaration = true
} 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 { private fun IrFileExports.toJsIrModule(module: IrAndExportedDeclarations, programFragment: JsIrProgramFragment): JsIrModule {
return JsIrModule( return JsIrModule(
moduleFragmentToNameMapper.getSafeNameFor(file), moduleFragmentToNameMapper.getSafeNameFor(file),
@@ -350,7 +348,7 @@ class IrModuleToJsTransformer(
} }
} }
private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): List<JsIrProgramFragment> { private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): JsIrProgramFragments {
val globalNameScope = NameTable<IrDeclaration>() val globalNameScope = NameTable<IrDeclaration>()
val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules) val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules)
val staticContext = JsStaticContext(backendContext, nameGenerator, globalNameScope, mode) val staticContext = JsStaticContext(backendContext, nameGenerator, globalNameScope, mode)
@@ -432,7 +430,7 @@ class IrModuleToJsTransformer(
optimizeFragmentByJsAst(result) optimizeFragmentByJsAst(result)
} }
return listOfNotNull(result, exportFragment) return JsIrProgramFragments(result, exportFragment)
} }
private fun Set<IrDeclaration>.computeTag(declaration: IrDeclaration): String? { private fun Set<IrDeclaration>.computeTag(declaration: IrDeclaration): String? {
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.js.backend.ast.*
import java.io.File import java.io.File
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
class JsIrProgramFragments(val mainFragment: JsIrProgramFragment, val exportFragment: JsIrProgramFragment? = null)
class JsIrProgramFragment(val name: String, val packageFqn: String) { class JsIrProgramFragment(val name: String, val packageFqn: String) {
val nameBindings = mutableMapOf<String, JsName>() val nameBindings = mutableMapOf<String, JsName>()
val optionalCrossModuleImports = hashSetOf<String>() val optionalCrossModuleImports = hashSetOf<String>()
@@ -303,6 +305,4 @@ fun JsStatement.renameImportedSymbolInternalName(newName: JsName): JsStatement {
} }
} }
val List<JsIrProgramFragment>.mainFragment: JsIrProgramFragment get() = first()
val List<JsIrProgramFragment>.exportFragment: JsIrProgramFragment? get() = getOrNull(1)
val JsIrProgramFragment.hasEffect: Boolean get() = eagerInitializers.statements.isNotEmpty() val JsIrProgramFragment.hasEffect: Boolean get() = eagerInitializers.statements.isNotEmpty()
@@ -364,3 +364,57 @@ class Merger(
} }
} }
} }
fun List<JsIrModule>.merge(): JsIrModule {
assert(isNotEmpty()) { "Can't merge empty list of modules" }
val firstModule = first()
return if (size == 1) {
firstModule
} else {
val fragments = mutableListOf<JsIrProgramFragment>()
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<JsIrModuleHeader>.merge(): JsIrModuleHeader {
assert(isNotEmpty()) { "Can't merge empty list of module headers" }
val firstModule = first()
return if (size == 1) {
firstModule
} else {
val definitions = mutableSetOf<String>()
val nameBindings = mutableMapOf<String, String>()
val optionalCrossModuleImports = mutableSetOf<String>()
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
)
}
}
@@ -6,33 +6,16 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs 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.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.nameWithoutExtension
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.path
private const val EXPORTER_FILE_POSTFIX = ".export" private const val EXPORTER_FILE_POSTFIX = ".export"
class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map<IrModuleFragment, String>) { class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map<IrModuleFragment, String>) {
private val externalNameToItsFile = hashMapOf<String, IrFile>()
fun getExternalNameFor(file: IrFile): String { fun getExternalNameFor(file: IrFile): String {
return getExternalNameFor(file.outputName, file.packageFqName.asString(), file.module.getJsOutputName()).also { return getExternalNameFor(file.outputName, file.packageFqName.asString(), file.module.getJsOutputName())
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()
)
}
}
} }
fun getExternalNameFor(fileName: String, packageFqn: String, moduleName: String): String { fun getExternalNameFor(fileName: String, packageFqn: String, moduleName: String): String {
@@ -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<Module, File, Artifact> {
val mainModuleName: String
val Module.isMain: Boolean
val Module.fileList: Iterable<File>
val Artifact.artifactName: String
val Artifact.hasEffect: Boolean
val Artifact.hasExport: Boolean
fun List<Artifact>.merge(): Artifact
fun File.generateArtifact(module: Module): Artifact?
fun Module.generateArtifact(moduleNameForEffects: String?): Artifact
fun generatePerFileArtifacts(modules: List<Module>): List<Artifact> {
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() }
}
}
@@ -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.export.TypeScriptFragment
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel 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.JsIrProgramFragment
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragments
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.* import org.jetbrains.kotlin.js.backend.ast.metadata.*
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.* import java.util.*
fun deserializeJsIrProgramFragment(input: ByteArray): List<JsIrProgramFragment> { fun deserializeJsIrProgramFragment(input: ByteArray): JsIrProgramFragments {
return JsIrAstDeserializer(input).readFragments() return JsIrAstDeserializer(input).readFragments()
} }
@@ -76,8 +77,8 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
return if (readBoolean()) then() else null return if (readBoolean()) then() else null
} }
fun readFragments(): List<JsIrProgramFragment> { fun readFragments(): JsIrProgramFragments {
return readList { readFragment() } return JsIrProgramFragments(readFragment(), ifTrue { readFragment() })
} }
fun readFragment(): JsIrProgramFragment { fun readFragment(): JsIrProgramFragment {
@@ -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.JsIrIcClassModel
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment 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.*
import org.jetbrains.kotlin.js.backend.ast.metadata.* import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -15,8 +16,8 @@ import java.io.DataOutputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.* import java.util.*
fun List<JsIrProgramFragment>.serializeTo(output: OutputStream) { fun JsIrProgramFragments.serializeTo(output: OutputStream) {
JsIrAstSerializer().appendAll(this).saveTo(output) JsIrAstSerializer().append(this).saveTo(output)
} }
private class DataWriter { private class DataWriter {
@@ -89,9 +90,9 @@ private class JsIrAstSerializer {
private val fileStack: Deque<String> = ArrayDeque() private val fileStack: Deque<String> = ArrayDeque()
private val importedNames = mutableSetOf<JsName>() private val importedNames = mutableSetOf<JsName>()
fun appendAll(fragments: List<JsIrProgramFragment>): JsIrAstSerializer { fun append(fragments: JsIrProgramFragments): JsIrAstSerializer {
fragmentSerializer.writeInt(fragments.size) append(fragments.mainFragment)
fragments.forEach(::append) fragmentSerializer.ifNotNull(fragments.exportFragment, ::append)
return this return this
} }
@@ -4,8 +4,6 @@
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// TARGET_BACKEND: JVM_IR // TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: JS_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. // Test that if we have two different files with the same name in the same package, KT-54028 doesn't reproduce.
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.test.directives.model.singleValue
import org.jetbrains.kotlin.test.getAnalyzerServices import org.jetbrains.kotlin.test.getAnalyzerServices
import org.jetbrains.kotlin.test.model.FrontendFacade import org.jetbrains.kotlin.test.model.FrontendFacade
import org.jetbrains.kotlin.test.model.FrontendKinds 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.model.TestModule
import org.jetbrains.kotlin.test.runners.lightTreeSyntaxDiagnosticsReporterHolder import org.jetbrains.kotlin.test.runners.lightTreeSyntaxDiagnosticsReporterHolder
import org.jetbrains.kotlin.test.services.* import org.jetbrains.kotlin.test.services.*
@@ -282,9 +283,9 @@ open class FirFrontendFacade(
val (ktFiles, lightTreeFiles) = when (parser) { val (ktFiles, lightTreeFiles) = when (parser) {
FirParser.LightTree -> { FirParser.LightTree -> {
emptyList<KtFile>() to testServices.sourceFileProvider.getKtSourceFilesForSourceFiles(module.files).values emptyMap<TestFile, KtFile>() 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 = { val sessionConfigurator: FirSessionConfigurator.() -> Unit = {
@@ -304,21 +305,27 @@ open class FirFrontendFacade(
sessionConfigurator, sessionConfigurator,
predefinedJavaComponents, predefinedJavaComponents,
project, project,
ktFiles ktFiles.values
) )
val firAnalyzerFacade = FirAnalyzerFacade( val firAnalyzerFacade = FirAnalyzerFacade(
moduleBasedSession, moduleBasedSession,
ktFiles, ktFiles.values,
lightTreeFiles, lightTreeFiles.values,
parser, parser,
testServices.lightTreeSyntaxDiagnosticsReporterHolder?.reporter, testServices.lightTreeSyntaxDiagnosticsReporterHolder?.reporter,
) )
val firFiles = firAnalyzerFacade.runResolution() val firFiles = firAnalyzerFacade.runResolution()
val filesMap = firFiles.mapNotNull { firFile ->
val testFile = module.files.firstOrNull { it.name == firFile.name } ?: return@mapNotNull null val usedFilesMap = when (parser) {
testFile to firFile FirParser.LightTree -> lightTreeFiles
}.toMap() FirParser.Psi -> ktFiles
}
val filesMap = usedFilesMap.keys
.zip(firFiles)
.onEach { assert(it.first.name == it.second.name) }
.toMap()
return FirOutputPartForDependsOnModule(module, moduleBasedSession, firAnalyzerFacade, filesMap) return FirOutputPartForDependsOnModule(module, moduleBasedSession, firAnalyzerFacade, filesMap)
} }
@@ -75,3 +75,8 @@ fun <T> Collection<T>.atMostOne(): T? {
* If there are more such elements, throws an exception. * If there are more such elements, throws an exception.
*/ */
inline fun <T> Iterable<T>.atMostOne(predicate: (T) -> Boolean): T? = this.filter(predicate).atMostOne() inline fun <T> Iterable<T>.atMostOne(predicate: (T) -> Boolean): T? = this.filter(predicate).atMostOne()
fun <K, V> MutableMap<K, MutableList<V>>.putToMultiMap(key: K, value: V) {
val list = getOrPut(key) { mutableListOf() }
list.add(value)
}
@@ -175,6 +175,12 @@ public class JsFirInvalidationPerFileTestGenerated extends AbstractJsFirInvalida
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -175,6 +175,12 @@ public class JsFirInvalidationPerModuleTestGenerated extends AbstractJsFirInvali
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -175,6 +175,12 @@ public class JsIrES6InvalidationPerFileTestGenerated extends AbstractJsIrES6Inva
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -175,6 +175,12 @@ public class JsIrES6InvalidationPerModuleTestGenerated extends AbstractJsIrES6In
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -175,6 +175,12 @@ public class JsIrInvalidationPerFileTestGenerated extends AbstractJsIrInvalidati
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -175,6 +175,12 @@ public class JsIrInvalidationPerModuleTestGenerated extends AbstractJsIrInvalida
runTest("js/js.translator/testData/incremental/invalidation/fastPath2/"); 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 @Test
@TestMetadata("friendDependency") @TestMetadata("friendDependency")
public void testFriendDependency() throws Exception { public void testFriendDependency() throws Exception {
@@ -2349,6 +2349,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); 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 @Test
@TestMetadata("nonIndetifierModuleName.kt") @TestMetadata("nonIndetifierModuleName.kt")
public void testNonIndetifierModuleName() throws Exception { public void testNonIndetifierModuleName() throws Exception {
@@ -2455,6 +2455,12 @@ public class FirJsES6BoxTestGenerated extends AbstractFirJsES6BoxTest {
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); 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 @Test
@TestMetadata("nonIndetifierModuleName.kt") @TestMetadata("nonIndetifierModuleName.kt")
public void testNonIndetifierModuleName() throws Exception { public void testNonIndetifierModuleName() throws Exception {
@@ -2455,6 +2455,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); 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 @Test
@TestMetadata("nonIndetifierModuleName.kt") @TestMetadata("nonIndetifierModuleName.kt")
public void testNonIndetifierModuleName() throws Exception { public void testNonIndetifierModuleName() throws Exception {
@@ -2349,6 +2349,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt"); 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 @Test
@TestMetadata("nonIndetifierModuleName.kt") @TestMetadata("nonIndetifierModuleName.kt")
public void testNonIndetifierModuleName() throws Exception { public void testNonIndetifierModuleName() throws Exception {
@@ -0,0 +1,35 @@
// DONT_TARGET_EXACT_BACKEND: JS
// ES_MODULES
// MODULE: name_clash
// FILE: test1/lib.kt
package foo
@JsExport
open class A<T> {
open fun foo(value: T): T = value
}
// FILE: test2/lib.kt
package foo
@JsExport
class B: A<String>() {
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"
}
@@ -0,0 +1,3 @@
package foo
fun test1() = test2()
@@ -0,0 +1,4 @@
package foo
@JsExport
fun test1() = test2()
@@ -0,0 +1,3 @@
package foo
fun test1() = "OK"
@@ -0,0 +1,5 @@
@file:JsFileName("f1")
package foo
fun test2() = "OK"
@@ -0,0 +1,6 @@
@file:JsFileName("f1")
package foo
@JsExport
fun test2() = "OK"
@@ -0,0 +1,3 @@
package foo
fun test2() = "OK"
@@ -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
@@ -0,0 +1,3 @@
import foo.test1
fun box() = test1()
@@ -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
@@ -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
@@ -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 '<kotlin-js-invalid-per-file-project>' that have the similar package and file names.
| - Package "com.example" and
""".trimMargin())
}
buildAndFail("compileProductionExecutableKotlinJs") {
assertTasksFailed(":compileProductionExecutableKotlinJs")
assertOutputContains("""
|There are two files in module '<kotlin-js-invalid-per-file-project>' that have the similar package and file names.
| - Package "com.example" and path
""".trimMargin())
}
}
}
@DisplayName("fully qualified names can be used in the sourcemap") @DisplayName("fully qualified names can be used in the sourcemap")
@GradleTest @GradleTest
fun testKotlinJsSourceMapGenerateFqNames(gradleVersion: GradleVersion) { fun testKotlinJsSourceMapGenerateFqNames(gradleVersion: GradleVersion) {
@@ -1,16 +0,0 @@
plugins {
kotlin("js")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
js(IR) {
useEsModules()
binaries.executable()
nodejs()
}
}
@@ -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
@@ -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
}
@@ -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