[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 esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
// Check if we need concurrency here
val chunkToOriginalFile = WeakHashMap<IrFile, IrFile>()
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
@@ -50,7 +50,7 @@ class JsIrCompilerWithIC(
allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>,
mainArguments: List<String>?
): List<() -> List<JsIrProgramFragment>> {
): List<() -> JsIrProgramFragments> {
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
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.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<IrModuleFragment>,
dirtyFiles: Collection<IrFile>,
mainArguments: List<String>?
): List<() -> List<JsIrProgramFragment>>
): List<() -> JsIrProgramFragments>
}
fun interface JsIrCompilerICInterfaceFactory {
@@ -616,7 +617,7 @@ class CacheUpdater(
private fun commitCacheAndBuildModuleArtifacts(
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
moduleNames: Map<KotlinLibraryFile, String>,
rebuiltFileFragments: KotlinSourceFileMap<List<JsIrProgramFragment>>
rebuiltFileFragments: KotlinSourceFileMap<JsIrProgramFragments>
): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") {
incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) ->
incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
@@ -630,7 +631,7 @@ class CacheUpdater(
compilerForIC: JsIrCompilerICInterface,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> =
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>> =
stopwatch.measure("Processing IR - lowering") {
val dirtyFilesForCompiling = mutableListOf<IrFile>()
val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>()
@@ -737,7 +738,7 @@ class CacheUpdater(
private data class FragmentGenerators(
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
val moduleNames: Map<KotlinLibraryFile, String>,
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>>
)
private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators {
@@ -751,9 +752,9 @@ class CacheUpdater(
}
private fun generateIrFragments(
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
): KotlinSourceFileMap<List<JsIrProgramFragment>> = stopwatch.measure("Processing IR - generating program fragments") {
val rebuiltFragments = KotlinSourceFileMutableMap<List<JsIrProgramFragment>>()
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragments>>
): KotlinSourceFileMap<JsIrProgramFragments> = stopwatch.measure("Processing IR - generating program fragments") {
val rebuiltFragments = KotlinSourceFileMutableMap<JsIrProgramFragments>()
while (generators.isNotEmpty()) {
val (libFile, srcFile, fragmentGenerator) = generators.removeFirst()
rebuiltFragments[libFile, srcFile] = fragmentGenerator()
@@ -782,7 +783,7 @@ fun rebuildCacheForDirtyFiles(
irFactory: IrFactory,
exportedDeclarations: Set<FqName>,
mainArguments: List<String>?,
): Pair<IrModuleFragment, List<Pair<IrFile, List<JsIrProgramFragment>>>> {
): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragments>>> {
val internationService = IrInterningService()
val emptyMetadata = object : KotlinSourceFileExports() {
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
@@ -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<JsIrProgramFragment>) {
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<SourceFileCacheArtifact>,
private val externalModuleName: String?
private val externalModuleName: String?,
) {
fun getSourceFiles() = srcCacheActions.mapTo(newHashSetWithExpectedSize(srcCacheActions.size)) { it.srcFile }
fun buildModuleArtifactAndCommitCache(
moduleName: String,
rebuiltFileFragments: Map<KotlinSourceFile, List<JsIrProgramFragment>>,
rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragments>,
): 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)
@@ -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<ModuleArtifact>) : 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<ModuleArtifact>) : 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<ModuleArtifact>) : 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>) :
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<ModuleArtifact>) : 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>,
) : 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<ModuleArtifact>) : 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<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 {
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? =
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)
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<ModuleArtifact>) : JsMult
)
}
private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): List<CachedFileInfo> {
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<ModuleArtifact>) : JsMult
}
}
return listOfNotNull(mainCachedFileInfo.exportFileCachedInfo, mainCachedFileInfo)
return mainCachedFileInfo
}
private val CachedFileInfo.cachedFiles: CachedFileArtifacts?
@@ -245,59 +302,41 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : 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<CachedFileInfo> {
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<ModuleArtifact, SrcFileArtifact, CachedFileInfo> {
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<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 }
}
@@ -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 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
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<JsIrProgramFragment>, private val astArtifact: File? = null) {
fun loadJsIrFragments(): List<JsIrProgramFragment> {
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)
}
}
@@ -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<ExportedDeclaration>,
val tsDeclarations: TypeScriptFragment?,
val generatedFrom: IrFile? = null
)
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>)
@@ -208,19 +208,14 @@ class IrModuleToJsTransformer(
fun makeIrFragmentsGenerators(
dirtyFiles: Collection<IrFile>,
allModules: Collection<IrModuleFragment>
): List<() -> List<JsIrProgramFragment>> {
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<IrFile>): List<IrFileExports> {
@@ -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<IrAndExportedDeclarations>, 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<IrAndExportedDeclarations, IrFileExports, JsIrModules> {
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<JsIrModules>.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<JsIrProgramFragment> {
private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): JsIrProgramFragments {
val globalNameScope = NameTable<IrDeclaration>()
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<IrDeclaration>.computeTag(declaration: IrDeclaration): String? {
@@ -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<String, JsName>()
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()
@@ -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
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<IrModuleFragment, String>) {
private val externalNameToItsFile = hashMapOf<String, IrFile>()
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 {
@@ -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.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<JsIrProgramFragment> {
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<JsIrProgramFragment> {
return readList { readFragment() }
fun readFragments(): JsIrProgramFragments {
return JsIrProgramFragments(readFragment(), ifTrue { readFragment() })
}
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.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<JsIrProgramFragment>.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<String> = ArrayDeque()
private val importedNames = mutableSetOf<JsName>()
fun appendAll(fragments: List<JsIrProgramFragment>): JsIrAstSerializer {
fragmentSerializer.writeInt(fragments.size)
fragments.forEach(::append)
fun append(fragments: JsIrProgramFragments): JsIrAstSerializer {
append(fragments.mainFragment)
fragmentSerializer.ifNotNull(fragments.exportFragment, ::append)
return this
}
@@ -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.
@@ -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<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 = {
@@ -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)
}
@@ -75,3 +75,8 @@ fun <T> Collection<T>.atMostOne(): T? {
* If there are more such elements, throws an exception.
*/
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/");
}
@Test
@TestMetadata("fileNameClash")
public void testFileNameClash() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fileNameClash/");
}
@Test
@TestMetadata("friendDependency")
public void testFriendDependency() throws Exception {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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")
@GradleTest
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