[K/JS] Rework main function call to support it in per-file

This commit is contained in:
Artem Kobzar
2023-10-04 12:13:20 +00:00
committed by Space Team
parent faae5f9b38
commit eef57f216c
57 changed files with 1091 additions and 172 deletions
@@ -72,6 +72,13 @@ private val K2JSCompilerArguments.granularity: JsGenerationGranularity
else -> JsGenerationGranularity.WHOLE_PROGRAM
}
private val K2JSCompilerArguments.dtsStrategy: TsCompilationStrategy
get() = when {
!this.generateDts -> TsCompilationStrategy.NONE
this.irPerFile -> TsCompilationStrategy.EACH_FILE
else -> TsCompilationStrategy.MERGED
}
private class DisposableZipFileSystemAccessor private constructor(
private val zipAccessor: ZipFileSystemCacheableAccessor
) : Disposable, ZipFileSystemAccessor by zipAccessor {
@@ -100,6 +107,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
) {
private fun lowerIr(): LoweredIr {
return compile(
mainCallArguments,
module,
phaseConfig,
IrFactoryImplForJsIC(WholeWorldStageController()),
@@ -122,7 +130,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
private fun makeJsCodeGenerator(): JsCodeGenerator {
val ir = lowerIr()
val transformer = IrModuleToJsTransformer(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName)
val transformer = IrModuleToJsTransformer(ir.context, ir.moduleFragmentToUniqueName, mainCallArguments != null)
val mode = TranslationMode.fromFlags(arguments.irDce, arguments.granularity, arguments.irMinimizedMemberNames)
return transformer.makeJsCodeGenerator(ir.allModules, mode)
@@ -303,7 +311,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
)
val (outputs, rebuiltModules) = jsExecutableProducer.buildExecutable(arguments.granularity, outJsProgram = false)
outputs.writeAll(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
outputs.writeAll(outputDir, outputName, arguments.dtsStrategy, moduleName, moduleKind)
icCaches.cacheGuard.release()
@@ -391,7 +399,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
outputs.writeAll(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
outputs.writeAll(outputDir, outputName, arguments.dtsStrategy, moduleName, moduleKind)
} catch (e: CompilationException) {
messageCollector.report(
ERROR,
@@ -611,10 +619,10 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
cacheDir = cacheDirectory,
compilerConfiguration = configurationJs,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = mainCallArguments,
compilerInterfaceFactory = { mainModule, cfg ->
JsIrCompilerWithIC(
mainModule,
mainCallArguments,
cfg,
arguments.granularity,
PhaseConfig(jsPhases),
@@ -62,12 +62,13 @@ class JsIrBackendContext(
val additionalExportedDeclarationNames: Set<FqName>,
keep: Set<String>,
override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context
val mainCallArguments: List<String>?,
val dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
val safeExternalBoolean: Boolean = false,
val safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
override val mapping: JsMapping = JsMapping(),
val granularity: JsGenerationGranularity = JsGenerationGranularity.WHOLE_PROGRAM,
val incrementalCacheEnabled: Boolean = false
val incrementalCacheEnabled: Boolean = false,
) : JsCommonBackendContext {
override val scriptMode: Boolean get() = false
@@ -863,6 +863,12 @@ val constEvaluationPhase = makeJsModulePhase(
description = "Evaluate functions that are marked as `IntrinsicConstEvaluation`",
).toModuleLowering()
val mainFunctionCallWrapperLowering = makeJsModulePhase(
::MainFunctionCallWrapperLowering,
name = "MainFunctionCallWrapperLowering",
description = "Generate main function call inside the wrapper-function"
).toModuleLowering()
val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering,
validateIrBeforeLowering,
@@ -968,6 +974,7 @@ val loweringList = listOf<Lowering>(
callsLoweringPhase,
escapedIdentifiersLowering,
implicitlyExportedDeclarationsMarkingLowering,
mainFunctionCallWrapperLowering,
cleanupLoweringPhase,
// Currently broken due to static members lowering making single-open-class
// files non-recognizable as single-class files
@@ -17,6 +17,8 @@ class JsMapping : DefaultMapping() {
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
// Main function wrappers
val mainFunctionToItsWrapper = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
@@ -37,6 +37,7 @@ class LoweredIr(
)
fun compile(
mainCallArguments: List<String>?,
depsDescriptors: ModulesStructure,
phaseConfig: PhaseConfig,
irFactory: IrFactory,
@@ -56,6 +57,7 @@ fun compile(
return compileIr(
moduleFragment,
depsDescriptors.mainModule,
mainCallArguments,
depsDescriptors.compilerConfiguration,
dependencyModules,
moduleToName,
@@ -75,6 +77,7 @@ fun compile(
fun compileIr(
moduleFragment: IrModuleFragment,
mainModule: MainModule,
mainCallArguments: List<String>?,
configuration: CompilerConfiguration,
dependencyModules: List<IrModuleFragment>,
moduleToName: Map<IrModuleFragment, String>,
@@ -109,7 +112,8 @@ fun compileIr(
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
granularity = granularity,
incrementalCacheEnabled = false
incrementalCacheEnabled = false,
mainCallArguments = mainCallArguments
)
// Load declarations referenced during `context` initialization
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
@OptIn(ObsoleteDescriptorBasedAPI::class)
class JsIrCompilerWithIC(
private val mainModule: IrModuleFragment,
private val mainArguments: List<String>?,
configuration: CompilerConfiguration,
granularity: JsGenerationGranularity,
private val phaseConfig: PhaseConfig,
@@ -42,15 +43,12 @@ class JsIrCompilerWithIC(
keep = emptySet(),
configuration = configuration,
granularity = granularity,
incrementalCacheEnabled = true
incrementalCacheEnabled = true,
mainCallArguments = mainArguments
)
}
override fun compile(
allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>,
mainArguments: List<String>?
): List<() -> JsIrProgramFragments> {
override fun compile(allModules: Collection<IrModuleFragment>, dirtyFiles: Collection<IrFile>): List<() -> JsIrProgramFragments> {
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
allModules.forEach {
@@ -64,7 +62,7 @@ class JsIrCompilerWithIC(
lowerPreservingTags(allModules, context, phaseConfig, context.irFactory.stageController as WholeWorldStageController)
val transformer = IrModuleToJsTransformer(context, mainArguments)
val transformer = IrModuleToJsTransformer(context, shouldReferMainFunction = mainArguments != null)
return transformer.makeIrFragmentsGenerators(dirtyFiles, allModules)
}
}
@@ -114,13 +114,9 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackend
dceRuntimeDiagnostic.unreachableDeclarationMethod(context).owner.acceptVoid(declarationsCollector)
}
// TODO: Generate calls to main as IR->IR lowering and reference coroutineEmptyContinuation directly
JsMainFunctionDetector(context).getMainFunctionOrNull(modules.last())?.let { mainFunction ->
add(mainFunction)
if (mainFunction.isLoweredSuspendFunction(context)) {
context.coroutineEmptyContinuation.owner.acceptVoid(declarationsCollector)
}
}
JsMainFunctionDetector(context).getMainFunctionOrNull(modules.last())
?.let { context.mapping.mainFunctionToItsWrapper[it] }
?.let { add(it) }
addIfNotNull(context.intrinsics.void.owner.backingField)
addAll(context.testFunsPerFile.values)
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.common.serialization.cityHash64String
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.*
@@ -30,11 +29,7 @@ import java.nio.file.Files
import java.util.EnumSet
fun interface JsIrCompilerICInterface {
fun compile(
allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>,
mainArguments: List<String>?
): List<() -> JsIrProgramFragments>
fun compile(allModules: Collection<IrModuleFragment>, dirtyFiles: Collection<IrFile>): List<() -> JsIrProgramFragments>
}
fun interface JsIrCompilerICInterfaceFactory {
@@ -62,7 +57,6 @@ class CacheUpdater(
cacheDir: String,
private val compilerConfiguration: CompilerConfiguration,
private val irFactory: () -> IrFactory,
private val mainArguments: List<String>?,
private val compilerInterfaceFactory: JsIrCompilerICInterfaceFactory
) {
private val stopwatch = StopwatchIC()
@@ -646,7 +640,7 @@ class CacheUpdater(
}
}
val fragmentGenerators = compilerForIC.compile(loadedFragments.values, dirtyFilesForCompiling, mainArguments)
val fragmentGenerators = compilerForIC.compile(loadedFragments.values, dirtyFilesForCompiling)
dirtyFilesForRestoring.mapIndexedTo(ArrayList(dirtyFilesForRestoring.size)) { i, libFileAndSrcFile ->
Triple(libFileAndSrcFile.first, libFileAndSrcFile.second, fragmentGenerators[i])
@@ -805,6 +799,7 @@ fun rebuildCacheForDirtyFiles(
val compilerWithIC = JsIrCompilerWithIC(
currentIrModule,
mainArguments,
configuration,
JsGenerationGranularity.PER_MODULE,
PhaseConfig(jsPhases),
@@ -815,7 +810,7 @@ fun rebuildCacheForDirtyFiles(
loadedIr.loadUnboundSymbols()
internationService.clear()
val fragments = compilerWithIC.compile(loadedIr.loadedFragments.values, dirtyIrFiles, mainArguments).memoryOptimizedMap { it() }
val fragments = compilerWithIC.compile(loadedIr.loadedFragments.values, dirtyIrFiles).memoryOptimizedMap { it() }
return currentIrModule to dirtyIrFiles.zip(fragments)
}
@@ -61,9 +61,7 @@ class JsExecutableProducer(
stopwatch.startNext("Loading JS IR modules with updated cross module references")
jsMultiArtifactCache.loadRequiredJsIrModules(crossModuleReferences)
fun CacheInfo?.compileModule(moduleName: String, generateCallToMain: Boolean): CompilationOutputs {
if (this == null) return jsMultiArtifactCache.fetchCompiledJsCodeForNullCacheInfo()
fun CacheInfo.compileModule(moduleName: String, isMainModule: Boolean): CompilationOutputs {
if (jsIrHeader.associatedModule == null) {
stopwatch.startNext("Fetching cached JS code")
val compilationOutputs = jsMultiArtifactCache.fetchCompiledJsCode(this)
@@ -85,7 +83,7 @@ class JsExecutableProducer(
moduleKind = moduleKind,
associatedModule.fragments,
sourceMapsInfo = sourceMapsInfo,
generateCallToMain = generateCallToMain,
generateCallToMain = isMainModule,
crossModuleReferences = crossRef,
outJsProgram = outJsProgram
)
@@ -95,12 +93,13 @@ class JsExecutableProducer(
return jsMultiArtifactCache.commitCompiledJsCode(this, compiledModule)
}
val (cachedMainModule, cachedOtherModules) = jsMultiArtifactCache.getMainModuleAndDependencies(cachedProgram)
val cachedMainModule = cachedProgram.last()
val cachedOtherModules = cachedProgram.dropLast(1)
val mainModuleCompilationOutput = cachedMainModule
.compileModule(mainModuleName, true)
.apply {
dependencies = cachedOtherModules.map {
dependencies += cachedOtherModules.map {
it.jsIrHeader.externalModuleName to it.compileModule(it.jsIrHeader.externalModuleName, false)
}
}
@@ -14,10 +14,8 @@ abstract class JsMultiArtifactCache<T : JsMultiArtifactCache.CacheInfo> {
abstract fun loadProgramHeadersFromCache(): List<T>
abstract fun loadRequiredJsIrModules(crossModuleReferences: Map<JsIrModuleHeader, CrossModuleReferences>)
abstract fun fetchCompiledJsCode(cacheInfo: T): CompilationOutputsCached?
abstract fun commitCompiledJsCode(cacheInfo: T, compilationOutputs: CompilationOutputsBuilt): CompilationOutputs
abstract fun fetchCompiledJsCodeForNullCacheInfo(): CompilationOutputs
abstract fun loadJsIrModule(cacheInfo: T): JsIrModule
abstract fun getMainModuleAndDependencies(cacheInfo: List<T>): Pair<T?, List<T>>
abstract fun commitCompiledJsCode(cacheInfo: T, compilationOutputs: CompilationOutputsBuilt): CompilationOutputs
protected fun File.writeIfNotNull(data: String?) {
if (data != null) {
@@ -30,6 +30,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
private fun SrcFileArtifact.loadJsIrModuleHeaders(moduleArtifact: ModuleArtifact) = with(loadJsIrFragments()!!) {
LoadedJsIrModuleHeaders(
mainFragment.mainFunction,
mainFragment.run {
asIrModuleHeader(
getMainFragmentExternalName(moduleArtifact),
@@ -95,6 +96,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
open class MainFileCachedInfo(moduleArtifact: ModuleArtifact, fileArtifact: SrcFileArtifact, moduleHeader: JsIrModuleHeader? = null) :
SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) {
var mainFunctionTag: String? = null
var exportFileCachedInfo: ExportFileCachedInfo? = null
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) }
@@ -114,13 +116,33 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
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) }
isModified -> cachedFileInfos.asSequence().map { it.fileArtifact.loadJsIrModuleHeaders(moduleArtifact) }
else -> cachedFileInfos.asSequence().map {
LoadedJsIrModuleHeaders(
it.mainFunctionTag,
it.jsIrHeader,
it.exportFileCachedInfo?.jsIrHeader
)
}
}
val mainHeaders = mutableListOf<JsIrModuleHeader>()
val exportHeaders = mutableListOf<JsIrModuleHeader>()
for (loadedIrModuleHeaders in mainAndExportHeaders) {
mainHeaders.add(loadedIrModuleHeaders.mainHeader)
loadedIrModuleHeaders.exportHeader
?.let { exportHeaders.add(it) }
loadedIrModuleHeaders.mainFunctionTag
.takeIf { mainFunctionTag == null }
?.let { mainFunctionTag = it }
}
val (mainHeaders, exportHeaders) = mainAndExportHeaders.map(LoadedJsIrModuleHeaders::toPair).unzip()
jsIrHeader = mainHeaders.merge()
exportFileCachedInfo = exportHeaders.filterNotNull().takeIf { it.isNotEmpty() }
exportFileCachedInfo = exportHeaders
.takeIf { it.isNotEmpty() }
?.let {
ExportFileCachedInfo.Merged(
filePrefix,
@@ -152,6 +174,8 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
class ModuleProxyFileCachedInfo(moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader? = null) :
CachedFileInfo(moduleArtifact, moduleHeader) {
var mainFunctionTag: String? = null
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) }
val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) }
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
@@ -162,6 +186,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
return generateProxyIrModuleWith(
jsIrHeader.externalModuleName,
jsIrHeader.externalModuleName,
mainFunctionTag,
jsIrHeader.importedWithEffectInModuleWithName
)
}
@@ -206,6 +231,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
return mainFileCachedFileInfo.readModuleHeaderCache {
mainFileCachedFileInfo.apply {
exportFileCachedInfo = fetchFileInfoForExportedPart(this)
mainFunctionTag = ifTrue { readString() }
loadSingleCachedFileInfo(this)
}
}
@@ -213,7 +239,10 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
private fun ModuleArtifact.fetchModuleProxyFileInfo(): CachedFileInfo.ModuleProxyFileCachedInfo? {
val mainFileCachedFileInfo = CachedFileInfo.ModuleProxyFileCachedInfo(this)
return mainFileCachedFileInfo.moduleHeaderArtifact?.useCodedInputIfExists { loadSingleCachedFileInfo(mainFileCachedFileInfo) }
return mainFileCachedFileInfo.moduleHeaderArtifact?.useCodedInputIfExists {
mainFileCachedFileInfo.mainFunctionTag = ifTrue { readString() }
loadSingleCachedFileInfo(mainFileCachedFileInfo)
}
}
private fun CodedInputStream.fetchFileInfoForExportedPart(mainCachedFileInfo: CachedFileInfo.MainFileCachedInfo): CachedFileInfo.ExportFileCachedInfo? {
@@ -238,28 +267,38 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
is CachedFileInfo.MainFileCachedInfo -> {
moduleHeaderArtifact?.useCodedOutput {
ifNotNull(exportFileCachedInfo) { commitSingleFileInfo(it) }
ifNotNull(mainFunctionTag) { writeStringNoTag(it) }
commitSingleFileInfo(this@commitFileInfo)
}
}
is CachedFileInfo.ModuleProxyFileCachedInfo -> {
moduleHeaderArtifact?.useCodedOutput {
ifNotNull(mainFunctionTag) { writeStringNoTag(it) }
commitSingleFileInfo(this@commitFileInfo)
}
}
is CachedFileInfo.ExportFileCachedInfo -> {}
}
private fun ModuleArtifact.generateModuleProxyFileCachedInfo(importedWithEffectInModuleWithName: String? = null): CachedFileInfo {
return CachedFileInfo.ModuleProxyFileCachedInfo(
this,
generateProxyIrModuleWith(moduleExternalName, moduleExternalName, importedWithEffectInModuleWithName).makeModuleHeader()
)
private fun ModuleArtifact.generateModuleProxyFileCachedInfo(
mainFunctionTag: String?,
importedWithEffectInModuleWithName: String? = null
): CachedFileInfo {
val moduleHeader = generateProxyIrModuleWith(
moduleExternalName,
moduleExternalName,
mainFunctionTag,
importedWithEffectInModuleWithName
).makeModuleHeader()
return CachedFileInfo.ModuleProxyFileCachedInfo(this, moduleHeader)
.also { it.mainFunctionTag = mainFunctionTag }
}
private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): CachedFileInfo.MainFileCachedInfo {
val headers = fileArtifact.loadJsIrModuleHeaders(this)
val mainCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact, headers.mainHeader)
.apply { mainFunctionTag = headers.mainFunctionTag }
if (headers.exportHeader != null) {
val tsDeclarationsHash = fileArtifact.loadJsIrFragments()?.exportFragment?.dts?.raw?.cityHash64()
@@ -286,10 +325,6 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
is CachedFileInfo.ModuleProxyFileCachedInfo -> jsFileArtifact?.let { CachedFileArtifacts(it, null, dtsFileArtifact) }
}
override fun getMainModuleAndDependencies(cacheInfo: List<CachedFileInfo>) = null to cacheInfo
override fun fetchCompiledJsCodeForNullCacheInfo() = PerFileEntryPointCompilationOutput()
override fun fetchCompiledJsCode(cacheInfo: CachedFileInfo) =
cacheInfo.cachedFiles?.let { (jsCodeFile, sourceMapFile, tsDeclarationsFile) ->
jsCodeFile.ifExists { this }
@@ -302,8 +337,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile)
} ?: compilationOutputs
override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule =
cacheInfo.loadJsIrModule()
override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule = cacheInfo.loadJsIrModule()
override fun loadProgramHeadersFromCache(): List<CachedFileInfo> {
val mainModuleArtifact = moduleArtifacts.last()
@@ -317,6 +351,13 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
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 val CachedFileInfo.packageFqn get() = moduleFragmentToExternalName.excludeFileNameFromExternalName(jsIrHeader.moduleName)
override val CachedFileInfo.mainFunction
get() = when (this) {
is CachedFileInfo.MainFileCachedInfo -> mainFunctionTag
is CachedFileInfo.ModuleProxyFileCachedInfo -> mainFunctionTag
else -> error("Unexpected CachedFileInfo type ${this::class.simpleName}")
}
override fun SrcFileArtifact.generateArtifact(module: ModuleArtifact) = when {
isModified() -> module.loadFileInfoFor(this)
@@ -329,10 +370,10 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
else -> CachedFileInfo.MainFileCachedInfo.Merged(map { it as CachedFileInfo.MainFileCachedInfo })
}
override fun ModuleArtifact.generateArtifact(moduleNameForEffects: String?) =
override fun ModuleArtifact.generateArtifact(mainFunctionTag: String?, moduleNameForEffects: String?) =
fetchModuleProxyFileInfo()?.takeIf {
it.jsIrHeader.importedWithEffectInModuleWithName == moduleNameForEffects
} ?: generateModuleProxyFileCachedInfo(moduleNameForEffects)
it.mainFunction == mainFunctionTag && it.jsIrHeader.importedWithEffectInModuleWithName == moduleNameForEffects
} ?: generateModuleProxyFileCachedInfo(mainFunctionTag, moduleNameForEffects)
}
return perFileGenerator.generatePerFileArtifacts(moduleArtifacts)
@@ -358,8 +399,9 @@ 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?) {
fun toPair() = mainHeader to exportHeader
}
private data class LoadedJsIrModuleHeaders(
val mainFunctionTag: String?,
val mainHeader: JsIrModuleHeader,
val exportHeader: JsIrModuleHeader?
)
}
@@ -57,12 +57,6 @@ class JsPerModuleCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMu
override fun loadJsIrModule(cacheInfo: CachedModuleInfo) = cacheInfo.artifact.loadJsIrModule()
override fun getMainModuleAndDependencies(cacheInfo: List<CachedModuleInfo>) =
cacheInfo.last() to cacheInfo.dropLast(1)
override fun fetchCompiledJsCodeForNullCacheInfo() =
error("Should never happen for per module granularity")
override fun fetchCompiledJsCode(cacheInfo: CachedModuleInfo) = cacheInfo.artifact.artifactsDir?.let { cacheDir ->
val jsCodeFile = File(cacheDir, CACHED_MODULE_JS).ifExists { this }
val sourceMapFile = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { this }
@@ -0,0 +1,86 @@
/*
* 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.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
import org.jetbrains.kotlin.ir.backend.js.utils.isLoweredSuspendFunction
import org.jetbrains.kotlin.ir.backend.js.utils.isStringArrayParameter
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.util.toIrConst
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class MainFunctionCallWrapperLowering(private val context: JsIrBackendContext) : FileLoweringPass {
private val mainFunctionDetector = JsMainFunctionDetector(context)
private var IrSimpleFunction.mainFunctionWrapper by context.mapping.mainFunctionToItsWrapper
private val mainFunctionArgs by lazy(LazyThreadSafetyMode.NONE) {
context.mainCallArguments ?: error("Expect to have main call args at this point")
}
override fun lower(irFile: IrFile) {
if (context.mainCallArguments == null) return
val mainFunction = mainFunctionDetector.getMainFunctionOrNull(irFile) ?: return
val mainFunctionWrapper = context.irFactory.stageController.restrictTo(mainFunction) {
mainFunction.generateWrapperForMainFunction().also {
mainFunction.mainFunctionWrapper = it
}
}
irFile.declarations.add(mainFunctionWrapper)
}
private fun IrSimpleFunction.generateWrapperForMainFunction(): IrSimpleFunction {
val originalFunctionSymbol = symbol
return context.irFactory.createSimpleFunction(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = JsIrBuilder.SYNTHESIZED_DECLARATION,
name = Name.identifier("mainWrapper"),
visibility = visibility,
isInline = false,
isExpect = false,
returnType = returnType,
modality = modality,
symbol = IrSimpleFunctionSymbolImpl(),
isTailrec = false,
isSuspend = false,
isOperator = false,
isInfix = false
).also {
it.parent = parent
it.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply {
statements.add(JsIrBuilder.buildCall(originalFunctionSymbol).apply {
generateMainArguments().forEachIndexed(this::putValueArgument)
})
}
}
}
private fun IrSimpleFunction.generateMainArguments(): List<IrExpression> {
val generateArgv = valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
val generateContinuation = isLoweredSuspendFunction(context)
return listOfNotNull(
runIf(generateArgv) {
JsIrBuilder.buildArray(
mainFunctionArgs.map { it.toIrConst(context.irBuiltIns.stringType) },
valueParameters.first().type,
context.irBuiltIns.stringType
)
},
runIf(generateContinuation) {
JsIrBuilder.buildCall(context.coroutineEmptyContinuation.owner.getter!!.symbol)
}
)
}
}
@@ -20,6 +20,12 @@ val ModuleKind.extension: String
else -> REGULAR_EXTENSION
}
enum class TsCompilationStrategy {
NONE,
MERGED,
EACH_FILE
}
abstract class CompilationOutputs {
var dependencies: Collection<Pair<String, CompilationOutputs>> = emptyList()
@@ -31,7 +37,7 @@ abstract class CompilationOutputs {
fun createWrittenFilesContainer(): MutableSet<File> = LinkedHashSet(2 * (dependencies.size + 1) + 1)
open fun writeAll(outputDir: File, outputName: String, genDTS: Boolean, moduleName: String, moduleKind: ModuleKind): Collection<File> {
open fun writeAll(outputDir: File, outputName: String, dtsStrategy: TsCompilationStrategy, moduleName: String, moduleKind: ModuleKind): Collection<File> {
val writtenFiles = createWrittenFilesContainer()
fun File.writeAsJsFile(out: CompilationOutputs) {
@@ -43,6 +49,12 @@ abstract class CompilationOutputs {
writtenFiles += jsFile
writtenFiles += jsMapFile
out.tsDefinitions.takeIf { dtsStrategy == TsCompilationStrategy.EACH_FILE }?.let {
val tsFile = jsFile.dtsForJsFile
tsFile.writeText(listOf(it).toTypeScript(name, moduleKind))
writtenFiles += tsFile
}
}
dependencies.forEach { (name, content) ->
@@ -52,7 +64,7 @@ abstract class CompilationOutputs {
val outputJsFile = outputDir.resolve("$outputName${moduleKind.extension}")
outputJsFile.writeAsJsFile(this)
if (genDTS) {
if (dtsStrategy == TsCompilationStrategy.MERGED) {
val dtsFile = outputJsFile.dtsForJsFile
dtsFile.writeText(getFullTsDefinition(moduleName, moduleKind))
writtenFiles += dtsFile
@@ -112,40 +124,6 @@ class CompilationOutputsBuilt(
}
}
// The output emulates the main module that has all the dependencies. In per-module we expect that the last processed module is a main module
// and after the compilation we rename it with the provided [outputName] and save all of its dependencies, but with the per-file mode we don't have
// this last "main" module, as a result we need to emulate it with the output. Also, it helps to save .d.ts files file-by-file instead of the generating
// one big main .d.ts file
class PerFileEntryPointCompilationOutput : CompilationOutputs() {
override val tsDefinitions: TypeScriptFragment? = null
override val jsProgram: JsProgram? = null
override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) {}
override fun writeAll(outputDir: File, outputName: String, genDTS: Boolean, moduleName: String, moduleKind: ModuleKind): Collection<File> {
val writtenFiles = createWrittenFilesContainer()
dependencies.forEach { (name, content) ->
val dependencyFile = outputDir.resolve("$name${moduleKind.extension}").also { it.parentFile.mkdirs() }
val jsMapFile = dependencyFile.mapForJsFile
val jsFile = dependencyFile.normalizedAbsoluteFile
val tsFile = jsFile.dtsForJsFile
content.writeJsCode(jsFile, jsMapFile)
writtenFiles += jsFile
writtenFiles += jsMapFile
content.tsDefinitions.takeIf { genDTS }?.let {
tsFile.writeText(listOf(it).toTypeScript(name, moduleKind))
writtenFiles += tsFile
}
}
return writtenFiles.also { deleteNonWrittenFiles(outputDir, it) }
}
}
class CompilationOutputsCached(
private val jsCodeFile: File,
private val sourceMapFile: File?,
@@ -48,12 +48,25 @@ val String.safeModuleName: String
val IrModuleFragment.safeName: String
get() = name.asString().safeModuleName
fun generateProxyIrModuleWith(safeName: String, externalName: String, importedWithEffectInModuleWithName: String? = null) = JsIrModule(
safeName,
externalName,
listOf(JsIrProgramFragment(safeName, "<proxy-file>")),
importedWithEffectInModuleWithName = importedWithEffectInModuleWithName
)
fun generateProxyIrModuleWith(
safeName: String,
externalName: String,
mainFunction: String?,
importedWithEffectInModuleWithName: String? = null
): JsIrModule {
val programFragment = JsIrProgramFragment(safeName, "<proxy-file>").apply {
mainFunction?.let {
this.mainFunction = it
this.nameBindings[it] = JsName("main", true)
}
}
return JsIrModule(
safeName,
externalName,
listOf(programFragment),
importedWithEffectInModuleWithName = importedWithEffectInModuleWithName
)
}
enum class JsGenerationGranularity {
WHOLE_PROGRAM,
@@ -127,8 +140,8 @@ class JsCodeGenerator(
class IrModuleToJsTransformer(
private val backendContext: JsIrBackendContext,
private val mainArguments: List<String>?,
moduleToName: Map<IrModuleFragment, String> = emptyMap(),
private val shouldReferMainFunction: Boolean = false,
private val removeUnusedAssociatedObjects: Boolean = true,
) {
private val shouldGeneratePolyfills = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
@@ -274,12 +287,14 @@ class IrModuleToJsTransformer(
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
override val JsIrModules.packageFqn get() = this.mainModule.fragments.first().packageFqn
override val JsIrModules.mainFunction get() = this.mainModule.fragments.first().mainFunction
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 IrAndExportedDeclarations.generateArtifact(mainFunctionTag: String?, moduleNameForEffects: String?) =
JsIrModules(toJsIrProxyModule(mainFunctionTag, moduleNameForEffects))
override fun IrFileExports.generateArtifact(module: IrAndExportedDeclarations) = takeIf { !file.couldBeSkipped() }
?.let { generateProgramFragment(it, mode) }
@@ -316,10 +331,14 @@ class IrModuleToJsTransformer(
)
}
private fun IrAndExportedDeclarations.toJsIrProxyModule(importedWithEffectInModuleWithName: String? = null): JsIrModule {
private fun IrAndExportedDeclarations.toJsIrProxyModule(
mainFunction: String?,
importedWithEffectInModuleWithName: String? = null
): JsIrModule {
return generateProxyIrModuleWith(
fragment.safeName,
moduleFragmentToNameMapper.getExternalNameFor(fragment),
mainFunction,
importedWithEffectInModuleWithName
)
}
@@ -404,15 +423,6 @@ class IrModuleToJsTransformer(
result.initializers.statements += staticContext.initializerBlock.statements
result.eagerInitializers.statements += staticContext.eagerInitializerBlock.statements
if (mainArguments != null) {
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(fileExports.file)?.let {
val jsName = staticContext.getNameForStaticFunction(it)
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
val generateContinuation = it.isLoweredSuspendFunction(backendContext)
result.mainFunction = JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, staticContext)).makeStmt()
}
}
backendContext.testFunsPerFile[fileExports.file]?.let {
result.testFunInvocation = JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt()
result.suiteFn = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner)
@@ -422,6 +432,12 @@ class IrModuleToJsTransformer(
val definitionSet = fileExports.file.declarations.toSet()
if (shouldReferMainFunction) {
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(fileExports.file)
?.let { backendContext.mapping.mainFunctionToItsWrapper[it] }
?.let { result.mainFunction = definitionSet.computeTag(it) }
}
result.computeAndSaveNameBindings(definitionSet, nameGenerator)
result.computeAndSaveImports(definitionSet, nameGenerator)
result.computeAndSaveDefinitions(definitionSet, fileExports)
@@ -494,25 +510,6 @@ class IrModuleToJsTransformer(
}
}
private fun generateMainArguments(
generateArgv: Boolean,
generateContinuation: Boolean,
staticContext: JsStaticContext,
): List<JsExpression> {
val mainArguments = this.mainArguments!!
val mainArgumentsArray =
if (generateArgv) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
val continuation = if (generateContinuation) {
backendContext.coroutineEmptyContinuation.owner
.let { it.getter!! }
.let { staticContext.getNameForStaticFunction(it) }
.let { JsInvocation(it.makeRef()) }
} else null
return listOfNotNull(mainArgumentsArray, continuation)
}
private fun IrFile.couldBeSkipped(): Boolean = declarations.all { it.origin == JsCodeOutliningLowering.OUTLINED_JS_CODE_ORIGIN }
}
@@ -25,7 +25,7 @@ class JsIrProgramFragment(val name: String, val packageFqn: String) {
val classes = mutableMapOf<JsName, JsIrIcClassModel>()
val initializers = JsCompositeBlock()
val eagerInitializers = JsCompositeBlock()
var mainFunction: JsStatement? = null
var mainFunction: String? = null
var testFunInvocation: JsStatement? = null
var suiteFn: JsName? = null
val definitions = mutableSetOf<String>()
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.serialization.js.ModuleKind
@@ -59,7 +61,6 @@ class Merger(
rename(f.initializers)
rename(f.eagerInitializers)
f.mainFunction?.let { rename(it) }
f.testFunInvocation?.let { rename(it) }
f.suiteFn?.let { f.suiteFn = rename(it) }
}
@@ -247,7 +248,9 @@ class Merger(
moduleBody.endRegion()
}
val callToMain = fragments.sortedBy { it.packageFqn }.firstNotNullOfOrNull { it.mainFunction }
val fragmentWithMainFunction = JsMainFunctionDetector.pickMainFunctionFromCandidates(fragments) {
JsMainFunctionDetector.MainFunctionCandidate(it.packageFqn, it.mainFunction)
}
val exportStatements = declareAndCallJsExporter() + additionalExports + transitiveJsExport()
@@ -267,8 +270,10 @@ class Merger(
statements.addWithComment("block: imports", importStatements)
statements += moduleBody
statements.addWithComment("block: exports", exportStatements)
if (generateCallToMain) {
callToMain?.let { this.statements += it }
if (generateCallToMain && fragmentWithMainFunction != null) {
val mainFunctionTag = fragmentWithMainFunction.mainFunction ?: error("Expect to have main function signature at this point")
val mainFunctionName = fragmentWithMainFunction.nameBindings[mainFunctionTag] ?: error("Expect to have name binding for tag $mainFunctionTag")
statements += JsInvocation(mainFunctionName.makeRef()).makeStmt()
}
this.statements += JsReturn(internalModuleName.makeRef())
}
@@ -42,6 +42,10 @@ class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map<IrModul
return module.getJsOutputName()
}
fun excludeFileNameFromExternalName(externalName: String): String {
return externalName.substringBeforeLast('/')
}
private fun IrModuleFragment.getJsOutputName(): String {
return jsOutputNamesMapping[this] ?: sanitizeName(safeName)
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.putToMultiMap
interface PerFileGenerator<Module, File, Artifact> {
@@ -16,10 +18,12 @@ interface PerFileGenerator<Module, File, Artifact> {
val Artifact.artifactName: String
val Artifact.hasEffect: Boolean
val Artifact.hasExport: Boolean
val Artifact.packageFqn: String
val Artifact.mainFunction: String?
fun List<Artifact>.merge(): Artifact
fun File.generateArtifact(module: Module): Artifact?
fun Module.generateArtifact(moduleNameForEffects: String?): Artifact
fun Module.generateArtifact(mainFunctionTag: String?, moduleNameForEffects: String?): Artifact
fun generatePerFileArtifacts(modules: List<Module>): List<Artifact> {
var someModuleHasEffect = false
@@ -29,8 +33,8 @@ interface PerFileGenerator<Module, File, Artifact> {
var hasModuleLevelEffect = false
var hasFileWithExportedDeclaration = false
for (file in module.fileList) {
val generatedArtifact = file.generateArtifact(module) ?: continue
val artifacts = module.fileList.mapNotNull {
val generatedArtifact = it.generateArtifact(module) ?: return@mapNotNull null
if (generatedArtifact.hasExport) {
hasFileWithExportedDeclaration = true
@@ -41,15 +45,28 @@ interface PerFileGenerator<Module, File, Artifact> {
}
putToMultiMap(generatedArtifact.artifactName, generatedArtifact)
generatedArtifact
}
if (hasModuleLevelEffect) {
someModuleHasEffect = true
}
if (hasFileWithExportedDeclaration || hasModuleLevelEffect || (module.isMain && someModuleHasEffect)) {
val mainFunctionTag = runIf(module.isMain) {
JsMainFunctionDetector
.pickMainFunctionFromCandidates(artifacts) {
JsMainFunctionDetector.MainFunctionCandidate(
it.packageFqn,
it.mainFunction
)
}
?.mainFunction
}
if (mainFunctionTag != null || hasFileWithExportedDeclaration || hasModuleLevelEffect || (module.isMain && someModuleHasEffect)) {
val proxyArtifact =
module.generateArtifact(mainModuleName.takeIf { !module.isMain && hasModuleLevelEffect }) ?: continue
module.generateArtifact(mainFunctionTag, mainModuleName.takeIf { !module.isMain && hasModuleLevelEffect }) ?: continue
putToMultiMap(proxyArtifact.artifactName, proxyArtifact)
}
}
@@ -71,6 +71,17 @@ class JsMainFunctionDetector(val context: JsCommonBackendContext) {
return resultPair?.second
}
class MainFunctionCandidate(val packageFqn: String, val mainFunctionTag: String?)
companion object {
inline fun <T> pickMainFunctionFromCandidates(candidates: List<T>, convertToCandidate: (T) -> MainFunctionCandidate): T? {
return candidates
.map { it to convertToCandidate(it) }
.sortedBy { it.second.packageFqn }
.firstOrNull { it.second.mainFunctionTag != null }
?.first
}
}
}
@@ -104,7 +104,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
readRepeated { classes[nameTable[readInt()]] = readIrIcClassModel() }
ifTrue { testFunInvocation = readStatement() }
ifTrue { mainFunction = readStatement() }
ifTrue { mainFunction = readString() }
ifTrue { dts = TypeScriptFragment(readString()) }
ifTrue { suiteFn = nameTable[readInt()] }
@@ -156,7 +156,7 @@ private class JsIrAstSerializer {
}
ifNotNull(fragment.mainFunction) {
writeStatement(it)
writeString(it)
}
ifNotNull(fragment.dts) {
@@ -17,7 +17,8 @@ class ProjectInfo(
val steps: List<ProjectBuildStep>,
val muted: Boolean,
val moduleKind: ModuleKind,
val ignoredGranularities: Set<JsGenerationGranularity>
val ignoredGranularities: Set<JsGenerationGranularity>,
val callMain: Boolean
) {
class ProjectBuildStep(
@@ -71,6 +72,7 @@ class ModuleInfo(val moduleName: String) {
}
const val PROJECT_INFO_FILE = "project.info"
private const val CALL_MAIN = "CALL_MAIN"
private const val MODULES_LIST = "MODULES"
private const val MODULES_KIND = "MODULE_KIND"
private const val LIBS_LIST = "libs"
@@ -173,6 +175,7 @@ class ProjectInfoParser(infoFile: File) : InfoParser<ProjectInfo>(infoFile) {
val steps = mutableListOf<ProjectInfo.ProjectBuildStep>()
val ignoredGranularities = mutableSetOf<JsGenerationGranularity>()
var muted = false
var callMain = false
var moduleKind = ModuleKind.ES
loop { line ->
@@ -191,6 +194,7 @@ class ProjectInfoParser(infoFile: File) : InfoParser<ProjectInfo>(infoFile) {
when {
op == MODULES_LIST -> libraries += split[1].splitAndTrim()
op == CALL_MAIN && split[1].trim() == "true" -> callMain = true
op == IGNORE_PER_FILE && split[1].trim() == "true" -> ignoredGranularities += JsGenerationGranularity.PER_FILE
op == IGNORE_PER_MODULE && split[1].trim() == "true" -> ignoredGranularities += JsGenerationGranularity.PER_MODULE
op == MODULES_KIND -> moduleKind = split[1].trim()
@@ -222,7 +226,7 @@ class ProjectInfoParser(infoFile: File) : InfoParser<ProjectInfo>(infoFile) {
false
}
return ProjectInfo(entryName, libraries, steps, muted, moduleKind, ignoredGranularities)
return ProjectInfo(entryName, libraries, steps, muted, moduleKind, ignoredGranularities, callMain)
}
}
@@ -605,14 +605,15 @@ class GenerateIrRuntime {
symbolTable,
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
configuration
configuration,
null
)
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker)).generateUnboundSymbolsAsDependencies()
jsPhases.invokeToplevel(phaseConfig, context, listOf(module))
val transformer = IrModuleToJsTransformer(context, null)
val transformer = IrModuleToJsTransformer(context, shouldReferMainFunction = false)
return transformer.generateModule(listOf(module), setOf(TranslationMode.PER_MODULE_DEV), false)
}
@@ -25,10 +25,7 @@ import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
import org.jetbrains.kotlin.ir.backend.js.WholeWorldStageController
import org.jetbrains.kotlin.ir.backend.js.ic.*
import org.jetbrains.kotlin.ir.backend.js.jsPhases
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.extension
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.test.converters.ClassicJsBackendFacade
@@ -44,6 +41,7 @@ import org.jetbrains.kotlin.test.builders.LanguageVersionSettingsBuilder
import org.jetbrains.kotlin.test.util.JUnit4Assertions
import org.jetbrains.kotlin.test.utils.TestDisposable
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.junit.ComparisonFailure
import org.junit.jupiter.api.AfterEach
import java.io.File
@@ -313,11 +311,16 @@ abstract class AbstractInvalidationTest(
)
}
private fun writeJsCode(stepId: Int, mainModuleName: String, jsOutput: CompilationOutputs): List<String> {
private fun writeJsCode(
stepId: Int,
mainModuleName: String,
jsOutput: CompilationOutputs,
dtsStrategy: TsCompilationStrategy
): List<String> {
val compiledJsFiles = jsOutput.writeAll(
jsDir,
mainModuleName,
true,
dtsStrategy,
mainModuleName,
projectInfo.moduleKind
).filter {
@@ -341,6 +344,12 @@ abstract class AbstractInvalidationTest(
fun execute() {
if (granularity in projectInfo.ignoredGranularities) return
val mainArguments = runIf(projectInfo.callMain) { emptyList<String>() }
val dtsStrategy = when (granularity) {
JsGenerationGranularity.PER_FILE -> TsCompilationStrategy.EACH_FILE
else -> TsCompilationStrategy.MERGED
}
for (projStep in projectInfo.steps) {
val testInfo = projStep.order.map { setupTestStep(projStep, it) }
@@ -363,10 +372,10 @@ abstract class AbstractInvalidationTest(
cacheDir = buildDir.resolve("incremental-cache").absolutePath,
compilerConfiguration = configuration,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = null,
compilerInterfaceFactory = { mainModule, cfg ->
JsIrCompilerWithIC(
mainModule,
mainArguments,
cfg,
granularity,
getPhaseConfig(projStep.id),
@@ -390,7 +399,7 @@ abstract class AbstractInvalidationTest(
)
val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(granularity, outJsProgram = true)
val writtenFiles = writeJsCode(projStep.id, mainModuleName, jsOutput)
val writtenFiles = writeJsCode(projStep.id, mainModuleName, jsOutput, dtsStrategy)
verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, dirtyData)
verifyJsCode(projStep.id, mainModuleName, writtenFiles)
@@ -138,10 +138,13 @@ class JsIrBackendFacade(
PhaseConfig(jsPhases)
}
val mainArguments = JsEnvironmentConfigurator.getMainCallParametersForModule(module)
.run { if (shouldBeGenerated()) arguments() else null }
val loweredIr = compileIr(
irModuleFragment.apply { resolveTestPaths() },
MainModule.Klib(inputArtifact.outputFile.absolutePath),
mainArguments,
configuration,
dependencyModules.onEach { it.resolveTestPaths() },
emptyMap(),
@@ -157,12 +160,10 @@ class JsIrBackendFacade(
granularity = granularity,
)
return loweredIr2JsArtifact(module, loweredIr)
return loweredIr2JsArtifact(module, loweredIr, mainArguments != null)
}
private fun loweredIr2JsArtifact(module: TestModule, loweredIr: LoweredIr): BinaryArtifacts.Js {
val mainArguments = JsEnvironmentConfigurator.getMainCallParametersForModule(module)
.run { if (shouldBeGenerated()) arguments() else null }
private fun loweredIr2JsArtifact(module: TestModule, loweredIr: LoweredIr, shouldReferMainFunction: Boolean): BinaryArtifacts.Js {
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in module.directives
val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives
@@ -178,12 +179,12 @@ class JsIrBackendFacade(
val transformer = IrModuleToJsTransformer(
loweredIr.context,
mainArguments,
moduleToName = runIf(isEsModules) {
loweredIr.allModules.associateWith {
"./${getJsArtifactSimpleName(testServices, it.safeName)}_v5".minifyIfNeed()
}
} ?: emptyMap()
} ?: emptyMap(),
shouldReferMainFunction,
)
// If runIrDce then include DCE results
// If perModuleOnly then skip whole program
@@ -290,7 +291,7 @@ class JsIrBackendFacade(
val tmpBuildDir = rootDir.resolve("tmp-build")
// CompilationOutputs keeps the `outputDir` clean by removing all outdated JS and other unknown files.
// To ensure that useful files around `outputFile`, such as irdump, are not removed, use `tmpBuildDir` instead.
val allJsFiles = writeAll(tmpBuildDir, outputFile.nameWithoutExtension, false, moduleId, moduleKind).filter {
val allJsFiles = writeAll(tmpBuildDir, outputFile.nameWithoutExtension, TsCompilationStrategy.NONE, moduleId, moduleKind).filter {
it.extension == "js" || it.extension == "mjs"
}
@@ -343,6 +343,12 @@ public class JsFirInvalidationPerFileTestGenerated extends AbstractJsFirInvalida
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -343,6 +343,12 @@ public class JsFirInvalidationPerModuleTestGenerated extends AbstractJsFirInvali
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -343,6 +343,12 @@ public class JsIrES6InvalidationPerFileTestGenerated extends AbstractJsIrES6Inva
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -343,6 +343,12 @@ public class JsIrES6InvalidationPerModuleTestGenerated extends AbstractJsIrES6In
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -343,6 +343,12 @@ public class JsIrInvalidationPerFileTestGenerated extends AbstractJsIrInvalidati
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -343,6 +343,12 @@ public class JsIrInvalidationPerModuleTestGenerated extends AbstractJsIrInvalida
runTest("js/js.translator/testData/incremental/invalidation/localObjectsLeakThroughInterface/");
}
@Test
@TestMetadata("mainFunction")
public void testMainFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/mainFunction/");
}
@Test
@TestMetadata("mainModuleInvalidation")
public void testMainModuleInvalidation() throws Exception {
@@ -2598,6 +2598,64 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/native")
@TestDataPath("$PROJECT_ROOT")
@@ -2704,6 +2704,64 @@ public class FirJsES6BoxTestGenerated extends AbstractFirJsES6BoxTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/native")
@TestDataPath("$PROJECT_ROOT")
@@ -2704,6 +2704,64 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/native")
@TestDataPath("$PROJECT_ROOT")
@@ -2598,6 +2598,64 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/native")
@TestDataPath("$PROJECT_ROOT")
@@ -0,0 +1,24 @@
// EXPECTED_REACHABLE_NODES: 1281
// ES_MODULES
// CALL_MAIN
var ok: String = "OK"
fun main(args: Array<Int>) {
ok += "Fail Int"
}
fun main(args: Array<in String>) {
ok += "Fail IN"
}
fun main(args: Array<String>): Unit? {
ok += "Fail return Unit?"
return Unit
}
fun Any.main(args: Array<String>) {
ok += "Fail Any.main(...)"
}
fun box() = ok
@@ -0,0 +1,56 @@
// EXPECTED_REACHABLE_NODES: 1281
// ES_MODULES
// CALL_MAIN
// FILE: ok.kt
package ok
var ok: String = "fail"
// FILE: 1.kt
// RECOMPILE
package a.a
import ok.*
fun main(args: Array<String>) {
ok = "fail: b.b"
}
// FILE: 0.kt
package b
import ok.*
fun main(args: Array<String>) {
ok = "fail: b"
}
// FILE: 2.kt
package a
import ok.*
fun main(args: Array<String>) {
ok = "OK"
}
// FILE: 3.kt
package a.b
import ok.*
fun main(args: Array<String>) {
ok = "fail: a.b"
}
// FILE: main.kt
import ok.*
fun box() = ok
+11
View File
@@ -0,0 +1,11 @@
// EXPECTED_REACHABLE_NODES: 1281
// ES_MODULES
// CALL_MAIN
var ok: String = "fail"
fun main() {
ok = "OK"
}
fun box() = ok
+13
View File
@@ -0,0 +1,13 @@
// EXPECTED_REACHABLE_NODES: 1281
// ES_MODULES
// CALL_MAIN
var ok: String = "fail"
fun main(args: Array<String>) {
if (0 != args.size) error("fail")
ok = "OK"
}
fun box() = ok
@@ -0,0 +1,27 @@
// EXPECTED_REACHABLE_NODES: 1296
// ES_MODULES
// CALL_MAIN
import kotlin.coroutines.*
var ok: String = "fail"
var callback: () -> Unit = {}
suspend fun main(args: Array<String>) {
if (0 != args.size) error("Fail")
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
ok = "OK"
}
fun box(): String {
if ("fail" != ok) return "Fail"
callback()
return ok
}
@@ -0,0 +1,25 @@
// EXPECTED_REACHABLE_NODES: 1296
// ES_MODULES
// CALL_MAIN
import kotlin.coroutines.*
var ok: String = "fail"
var callback: () -> Unit = {}
suspend fun main() {
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
ok = "OK"
}
fun box(): String {
if ("fail" != ok) error("Fail")
callback()
return ok
}
@@ -0,0 +1,30 @@
// EXPECTED_REACHABLE_NODES: 1299
// ES_MODULES
// CALL_MAIN
import kotlin.coroutines.*
var callback: () -> Unit = {}
val exception = Exception()
suspend fun main() {
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
throw exception
}
fun box(): String {
try {
callback()
} catch (e: Exception) {
if (e !== exception) return "Fail"
}
return "OK"
}
@@ -0,0 +1,18 @@
// EXPECTED_REACHABLE_NODES: 1281
// ES_MODULES
// CALL_MAIN
var o: String = "fail O"
var k: String = "K"
fun main() {
k = "fail K"
}
fun main(args: Array<String>) {
if (args.size != 0) error("Fail")
o = "O"
}
fun box() = o + k
@@ -0,0 +1,7 @@
package foo.bar
var ok = "Fail"
fun main() {
ok = "OK"
}
@@ -0,0 +1,20 @@
package foo.bar
var ok = "OK"
fun main(args: Array<Int>) {
ok += "Fail Int"
}
fun main(args: Array<in String>) {
ok += "Fail IN"
}
fun main(args: Array<String>): Unit? {
ok += "Fail return Unit?"
return Unit
}
fun Any.main(args: Array<String>) {
ok += "Fail Any.main(...)"
}
@@ -0,0 +1,19 @@
package foo.bar
import kotlin.coroutines.*
var ok: String = "fail"
var callback: () -> Unit = {}
suspend fun main(args: Array<String>) {
if (0 != args.size) error("Fail")
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
ok = "OK"
}
@@ -0,0 +1,11 @@
package foo.bar
var ok = "fail"
fun main(args: Array<String>) {
if (args.size != 0) {
ok = "Fail with args zie"
} else {
ok = "OK"
}
}
@@ -0,0 +1,7 @@
package foo
import foo.bar.ok
fun main() {
ok = "OK2"
}
@@ -0,0 +1,10 @@
var ok = "Fail"
fun main() {
ok = "OK"
}
fun box(stepId: Int): String {
return when (stepId) {
0 -> ok
else -> "Unknown"
}
}
@@ -0,0 +1,13 @@
import foo.bar.ok
fun box(stepId: Int): String {
return when (stepId) {
0 -> ok
1 -> ok
2 -> ok
3 -> ok
4 -> ok
5 -> if (ok != "OK2") "Fail" else "OK"
else -> "Unknown"
}
}
@@ -0,0 +1,18 @@
import foo.bar.ok
import foo.bar.callback
fun box(stepId: Int): String {
return when (stepId) {
0 -> ok
1 -> ok
2 -> ok
3 -> {
if (ok != "fail") return "Fail"
callback()
ok
}
4 -> ok
5 -> if (ok != "OK2") "Fail" else "OK"
else -> "Unknown"
}
}
@@ -0,0 +1,13 @@
import foo.bar.ok
fun box(stepId: Int): String {
return when (stepId) {
0 -> ok
1 -> ok
2 -> ok
3 -> ok
4 -> ok
5 -> if (ok != "OK2") "Fail" else "OK"
else -> "Unknown"
}
}
@@ -0,0 +1,29 @@
STEP 0:
modifications:
U : m.0.kt -> m.kt
added file: m.kt
STEP 1:
modifications:
U : m.1.kt -> m.kt
U : f.1.kt -> f.kt
added file: f.kt
modified ir: m.kt
STEP 2:
modifications:
U : f.2.kt -> f.kt
modified ir: f.kt
STEP 3:
modifications:
U : f.3.kt -> f.kt
U : m.3.kt -> m.kt
modified ir: f.kt, m.kt
STEP 4:
modifications:
U : f.4.kt -> f.kt
U : m.4.kt -> m.kt
modified ir: f.kt, m.kt
STEP 5:
modifications:
U : l.5.kt -> l.kt
added file: l.kt
updated exports: f.kt
@@ -0,0 +1,27 @@
MODULES: main
CALL_MAIN: true
STEP 0:
libs: main
dirty js modules: main
dirty js files: main/m, main/m.export, main
STEP 1:
libs: main
dirty js modules: main
dirty js files: main/foo/bar/f, main/m, main
STEP 2:
libs: main
dirty js modules: main
dirty js files: main/foo/bar/f, main
STEP 3:
libs: main
dirty js modules: main
dirty js files: main/foo/bar/f, main/m, main
STEP 4:
libs: main
dirty js modules: main
dirty js files: main/foo/bar/f, main/m, main
STEP 5:
libs: main
dirty js modules: main
dirty js files: main/foo/l, main/foo/bar/f, main
@@ -356,6 +356,64 @@ public class FirWasmJsTranslatorTestGenerated extends AbstractFirWasmJsTranslato
runTest("js/js.translator/testData/box/esModules/jsName/jsTopLevelClashes.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
}
@Nested
@@ -356,6 +356,64 @@ public class K1WasmJsTranslatorTestGenerated extends AbstractK1WasmJsTranslatorT
runTest("js/js.translator/testData/box/esModules/jsName/jsTopLevelClashes.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/esModules/main")
@TestDataPath("$PROJECT_ROOT")
public class Main {
@Test
public void testAllFilesPresentInMain() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/main"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@Test
@TestMetadata("differentMains.kt")
public void testDifferentMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/differentMains.kt");
}
@Test
@TestMetadata("incremental.kt")
public void testIncremental() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/incremental.kt");
}
@Test
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/noArgs.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/simple.kt");
}
@Test
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMain.kt");
}
@Test
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainNoArgs.kt");
}
@Test
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/suspendMainThrows.kt");
}
@Test
@TestMetadata("twoMains.kt")
public void testTwoMains() throws Exception {
runTest("js/js.translator/testData/box/esModules/main/twoMains.kt");
}
}
}
@Nested