[K/JS] Implement file merging for clashed file during per-file compilation
This commit is contained in:
@@ -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())
|
||||
|
||||
+7
-7
@@ -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)
|
||||
|
||||
+140
-102
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+36
-38
@@ -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? {
|
||||
|
||||
+2
-2
@@ -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()
|
||||
+54
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-18
@@ -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 {
|
||||
|
||||
+60
@@ -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() }
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -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 {
|
||||
|
||||
+6
-5
@@ -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.
|
||||
|
||||
|
||||
+16
-9
@@ -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)
|
||||
}
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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"
|
||||
+22
@@ -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()
|
||||
+12
@@ -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
|
||||
+22
@@ -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
|
||||
-21
@@ -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) {
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
plugins {
|
||||
kotlin("js")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js(IR) {
|
||||
useEsModules()
|
||||
binaries.executable()
|
||||
nodejs()
|
||||
}
|
||||
}
|
||||
-8
@@ -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
|
||||
-16
@@ -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
|
||||
}
|
||||
-9
@@ -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
|
||||
Reference in New Issue
Block a user