[K/JS] Support eager initialization for per-file granularity
This commit is contained in:
+4
@@ -221,6 +221,10 @@ internal fun CrossModuleReferences.crossModuleReferencesHashForIC() = HashCalcul
|
||||
update(exports[tag]!!)
|
||||
}
|
||||
|
||||
updateForEach(importsWithEffect.sortedBy { it.moduleExporter.externalName }) { import ->
|
||||
update(import.moduleExporter.externalName)
|
||||
}
|
||||
|
||||
updateForEach(imports.keys.sorted()) { tag ->
|
||||
val import = imports[tag]!!
|
||||
update(tag)
|
||||
|
||||
+48
-12
@@ -78,7 +78,11 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
private fun JsIrProgramFragment.getExportFragmentExternalName(moduleArtifact: ModuleArtifact) =
|
||||
moduleFragmentToExternalName.getExternalNameForExporterFile(name, packageFqn, moduleArtifact.moduleExternalName)
|
||||
|
||||
private fun JsIrProgramFragment.asIrModuleHeader(moduleName: String, reexportedIn: String? = null): JsIrModuleHeader {
|
||||
private fun JsIrProgramFragment.asIrModuleHeader(
|
||||
moduleName: String,
|
||||
reexportedIn: String? = null,
|
||||
importWithEffectIn: String? = null,
|
||||
): JsIrModuleHeader {
|
||||
return JsIrModuleHeader(
|
||||
moduleName = moduleName,
|
||||
externalModuleName = moduleName,
|
||||
@@ -86,14 +90,25 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
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)) },
|
||||
exportFragment?.run { asIrModuleHeader(mainFragment.getExportFragmentExternalName(moduleArtifact), moduleArtifact.moduleExternalName) },
|
||||
mainFragment.run {
|
||||
asIrModuleHeader(
|
||||
getMainFragmentExternalName(moduleArtifact),
|
||||
importWithEffectIn = runIf(hasEffect) { moduleArtifact.moduleExternalName }
|
||||
)
|
||||
},
|
||||
exportFragment?.run {
|
||||
asIrModuleHeader(
|
||||
mainFragment.getExportFragmentExternalName(moduleArtifact),
|
||||
reexportedIn = moduleArtifact.moduleExternalName
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -108,6 +123,8 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
reexportedIn = cachedFileInfo.moduleArtifact.moduleExternalName
|
||||
}
|
||||
|
||||
|
||||
val importWithEffectIn = ifTrue { readString() }
|
||||
val (definitions, nameBindings, optionalCrossModuleImports) = fetchJsIrModuleHeaderNames()
|
||||
|
||||
it.jsIrHeader = JsIrModuleHeader(
|
||||
@@ -117,6 +134,7 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
reexportedInModuleWithName = reexportedIn,
|
||||
importedWithEffectInModuleWithName = importWithEffectIn,
|
||||
associatedModule = null,
|
||||
)
|
||||
}
|
||||
@@ -149,12 +167,13 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
}
|
||||
}
|
||||
|
||||
private fun CodedOutputStream.commitSingleFileInfo(cachedFileInfo: CachedFileInfo.SerializableCachedFileInfo) {
|
||||
private fun CodedOutputStream.commitSingleFileInfo(cachedFileInfo: CachedFileInfo) {
|
||||
writeStringNoTag(cachedFileInfo.jsIrHeader.externalModuleName)
|
||||
cachedFileInfo.crossFileReferencesHash.toProtoStream(this)
|
||||
if (cachedFileInfo is CachedFileInfo.ExportFileCachedInfo) {
|
||||
ifNotNull(cachedFileInfo.tsDeclarationsHash, ::writeInt64NoTag)
|
||||
}
|
||||
ifNotNull(cachedFileInfo.jsIrHeader.importedWithEffectInModuleWithName) { writeStringNoTag(it) }
|
||||
commitJsIrModuleHeaderNames(cachedFileInfo.jsIrHeader)
|
||||
}
|
||||
|
||||
@@ -167,18 +186,16 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
}
|
||||
is CachedFileInfo.ModuleProxyFileCachedInfo -> {
|
||||
moduleHeaderArtifact?.useCodedOutput {
|
||||
writeStringNoTag(jsIrHeader.externalModuleName)
|
||||
crossFileReferencesHash.toProtoStream(this)
|
||||
commitJsIrModuleHeaderNames(jsIrHeader)
|
||||
commitSingleFileInfo(this@commitFileInfo)
|
||||
}
|
||||
}
|
||||
is CachedFileInfo.ExportFileCachedInfo -> {}
|
||||
}
|
||||
|
||||
private fun ModuleArtifact.generateModuleProxyFileCachedInfo(): CachedFileInfo {
|
||||
private fun ModuleArtifact.generateModuleProxyFileCachedInfo(importedWithEffectInModuleWithName: String? = null): CachedFileInfo {
|
||||
return CachedFileInfo.ModuleProxyFileCachedInfo(
|
||||
this,
|
||||
generateProxyIrModuleWith(moduleExternalName, moduleExternalName).makeModuleHeader()
|
||||
generateProxyIrModuleWith(moduleExternalName, moduleExternalName, importedWithEffectInModuleWithName).makeModuleHeader()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -230,7 +247,11 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
|
||||
override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule {
|
||||
if (cacheInfo !is CachedFileInfo.SerializableCachedFileInfo) {
|
||||
return generateProxyIrModuleWith(cacheInfo.jsIrHeader.externalModuleName, cacheInfo.jsIrHeader.externalModuleName)
|
||||
return generateProxyIrModuleWith(
|
||||
cacheInfo.jsIrHeader.externalModuleName,
|
||||
cacheInfo.jsIrHeader.externalModuleName,
|
||||
cacheInfo.jsIrHeader.importedWithEffectInModuleWithName
|
||||
)
|
||||
}
|
||||
|
||||
val fragments = cacheInfo.fileArtifact.loadJsIrFragments()
|
||||
@@ -244,8 +265,11 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
}
|
||||
|
||||
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 ->
|
||||
@@ -257,11 +281,21 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
if (!hasFileWithJsExportedDeclaration && cachedFileInfo.hasExportFile()) {
|
||||
hasFileWithJsExportedDeclaration = true
|
||||
}
|
||||
if (!hasModuleLevelEffect && cachedFileInfo.hasModuleLevelEffect()) {
|
||||
someModuleHasEffect = true
|
||||
hasModuleLevelEffect = true
|
||||
}
|
||||
|
||||
cachedFileInfo
|
||||
}
|
||||
.butIf(hasFileWithJsExportedDeclaration) {
|
||||
it.plus(moduleArtifact.fetchModuleProxyFileInfo() ?: moduleArtifact.generateModuleProxyFileCachedInfo())
|
||||
.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))
|
||||
}
|
||||
}
|
||||
.onEach { headerToCachedInfo[it.jsIrHeader] = it }
|
||||
@@ -288,4 +322,6 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
private data class LoadedJsIrModuleHeaders(val mainHeader: JsIrModuleHeader, val exportHeader: JsIrModuleHeader?)
|
||||
|
||||
private fun List<JsPerFileCache.CachedFileInfo>.hasExportFile(): Boolean = size > 1
|
||||
private fun List<JsPerFileCache.CachedFileInfo>.hasModuleLevelEffect(): Boolean =
|
||||
last().jsIrHeader.importedWithEffectInModuleWithName != null
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class JsPerModuleCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMu
|
||||
private fun ModuleArtifact.fetchModuleInfo() = File(artifactsDir, JS_MODULE_HEADER).useCodedInputIfExists {
|
||||
val crossModuleReferencesHash = ICHash.fromProtoStream(this)
|
||||
val reexportedInModuleWithName = ifTrue { readString() }
|
||||
val importedWithEffectInModuleWithName = ifTrue { readString() }
|
||||
val (definitions, nameBindings, optionalCrossModuleImports) = fetchJsIrModuleHeaderNames()
|
||||
|
||||
CachedModuleInfo(
|
||||
@@ -38,6 +39,7 @@ class JsPerModuleCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMu
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
reexportedInModuleWithName = reexportedInModuleWithName,
|
||||
importedWithEffectInModuleWithName = importedWithEffectInModuleWithName,
|
||||
associatedModule = null
|
||||
),
|
||||
crossModuleReferencesHash = crossModuleReferencesHash
|
||||
@@ -48,6 +50,7 @@ class JsPerModuleCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMu
|
||||
File(cacheDir, JS_MODULE_HEADER).useCodedOutput {
|
||||
crossModuleReferencesHash.toProtoStream(this)
|
||||
ifNotNull(jsIrHeader.reexportedInModuleWithName) { writeStringNoTag(it) }
|
||||
ifNotNull(jsIrHeader.importedWithEffectInModuleWithName) { writeStringNoTag(it) }
|
||||
commitJsIrModuleHeaderNames(jsIrHeader)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsCodeOutliningLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
@@ -41,8 +42,13 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
|
||||
if (declaration.isExternal) return JsEmpty
|
||||
|
||||
if (declaration.initializer != null) {
|
||||
val eagerInitializationAnnotation = context.staticContext.backendContext.propertyLazyInitialization.eagerInitialization
|
||||
val initializer = declaration.initializer!!.accept(IrElementToJsExpressionTransformer(), context)
|
||||
context.staticContext.initializerBlock.statements += jsAssignment(fieldName.makeRef(), initializer).makeStmt()
|
||||
val initializerBlock = when {
|
||||
declaration.correspondingPropertySymbol?.owner?.hasAnnotation(eagerInitializationAnnotation) == true -> context.staticContext.eagerInitializerBlock
|
||||
else -> context.staticContext.initializerBlock
|
||||
}
|
||||
initializerBlock.statements += jsAssignment(fieldName.makeRef(), initializer).makeStmt()
|
||||
}
|
||||
|
||||
return JsVars(JsVars.JsVar(fieldName))
|
||||
|
||||
+21
-7
@@ -47,10 +47,11 @@ val String.safeModuleName: String
|
||||
val IrModuleFragment.safeName: String
|
||||
get() = name.asString().safeModuleName
|
||||
|
||||
fun generateProxyIrModuleWith(safeName: String, externalName: String) = JsIrModule(
|
||||
fun generateProxyIrModuleWith(safeName: String, externalName: String, importedWithEffectInModuleWithName: String? = null) = JsIrModule(
|
||||
safeName,
|
||||
externalName,
|
||||
listOf(JsIrProgramFragment(safeName, "<proxy-file>"))
|
||||
listOf(JsIrProgramFragment(safeName, "<proxy-file>")),
|
||||
importedWithEffectInModuleWithName = importedWithEffectInModuleWithName
|
||||
)
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
@@ -264,15 +265,25 @@ 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
|
||||
|
||||
for (fileExports in module.files) {
|
||||
if (fileExports.file.couldBeSkipped()) continue
|
||||
val programFragments = generateProgramFragment(fileExports, mode)
|
||||
|
||||
add(fileExports.toJsIrModule(programFragments.mainFragment))
|
||||
fileExports.toJsIrModule(module, programFragments.mainFragment).let {
|
||||
add(it)
|
||||
if (it.importedWithEffectInModuleWithName != null) {
|
||||
someModuleHasEffect = true
|
||||
hasModuleLevelEffect = true
|
||||
}
|
||||
}
|
||||
|
||||
programFragments.exportFragment?.let {
|
||||
add(fileExports.toJsIrModuleForExport(module, it))
|
||||
@@ -280,8 +291,8 @@ class IrModuleToJsTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFileWithJsExportedDeclaration) {
|
||||
add(module.toJsIrProxyModule())
|
||||
if (hasFileWithJsExportedDeclaration || hasModuleLevelEffect || (module === mainModule && someModuleHasEffect)) {
|
||||
add(module.toJsIrProxyModule(mainModule.fragment.safeName.takeIf { module !== mainModule && hasModuleLevelEffect }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,11 +300,12 @@ class IrModuleToJsTransformer(
|
||||
return JsIrProgram(modulesPerFile)
|
||||
}
|
||||
|
||||
private fun IrFileExports.toJsIrModule(programFragment: JsIrProgramFragment): JsIrModule {
|
||||
private fun IrFileExports.toJsIrModule(module: IrAndExportedDeclarations, programFragment: JsIrProgramFragment): JsIrModule {
|
||||
return JsIrModule(
|
||||
moduleFragmentToNameMapper.getSafeNameFor(file),
|
||||
moduleFragmentToNameMapper.getExternalNameFor(file),
|
||||
listOf(programFragment),
|
||||
importedWithEffectInModuleWithName = runIf(programFragment.hasEffect) { module.fragment.safeName }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -306,10 +318,11 @@ class IrModuleToJsTransformer(
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrAndExportedDeclarations.toJsIrProxyModule(): JsIrModule {
|
||||
private fun IrAndExportedDeclarations.toJsIrProxyModule(importedWithEffectInModuleWithName: String? = null): JsIrModule {
|
||||
return generateProxyIrModuleWith(
|
||||
fragment.safeName,
|
||||
moduleFragmentToNameMapper.getExternalNameFor(fragment),
|
||||
importedWithEffectInModuleWithName
|
||||
)
|
||||
}
|
||||
|
||||
@@ -391,6 +404,7 @@ class IrModuleToJsTransformer(
|
||||
}
|
||||
|
||||
result.initializers.statements += staticContext.initializerBlock.statements
|
||||
result.eagerInitializers.statements += staticContext.eagerInitializerBlock.statements
|
||||
|
||||
if (mainArguments != null) {
|
||||
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(fileExports.file)?.let {
|
||||
|
||||
+30
-3
@@ -22,6 +22,7 @@ class JsIrProgramFragment(val name: String, val packageFqn: String) {
|
||||
var dts: TypeScriptFragment? = null
|
||||
val classes = mutableMapOf<JsName, JsIrIcClassModel>()
|
||||
val initializers = JsCompositeBlock()
|
||||
val eagerInitializers = JsCompositeBlock()
|
||||
var mainFunction: JsStatement? = null
|
||||
var testFunInvocation: JsStatement? = null
|
||||
var suiteFn: JsName? = null
|
||||
@@ -34,6 +35,7 @@ class JsIrModule(
|
||||
val externalModuleName: String,
|
||||
val fragments: List<JsIrProgramFragment>,
|
||||
val reexportedInModuleWithName: String? = null,
|
||||
val importedWithEffectInModuleWithName: String? = null,
|
||||
) {
|
||||
fun makeModuleHeader(): JsIrModuleHeader {
|
||||
val nameBindings = mutableMapOf<String, String>()
|
||||
@@ -55,6 +57,7 @@ class JsIrModule(
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
reexportedInModuleWithName = reexportedInModuleWithName.takeIf { hasDeclarationsToReexport },
|
||||
importedWithEffectInModuleWithName = importedWithEffectInModuleWithName,
|
||||
associatedModule = this
|
||||
)
|
||||
}
|
||||
@@ -67,6 +70,7 @@ class JsIrModuleHeader(
|
||||
val nameBindings: Map<String, String>,
|
||||
val optionalCrossModuleImports: Set<String>,
|
||||
val reexportedInModuleWithName: String? = null,
|
||||
val importedWithEffectInModuleWithName: String? = null,
|
||||
var associatedModule: JsIrModule?,
|
||||
) {
|
||||
val externalNames: Set<String> by lazy(LazyThreadSafetyMode.NONE) { nameBindings.keys - definitions }
|
||||
@@ -94,12 +98,14 @@ class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
class CrossModuleDependenciesResolver(private val moduleKind: ModuleKind, private val headers: List<JsIrModuleHeader>) {
|
||||
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
|
||||
val reexportModuleToHeader = headers.groupBy { it.reexportedInModuleWithName }
|
||||
val importedInModuleWithEffect = headers.groupBy { it.importedWithEffectInModuleWithName }
|
||||
val headerToBuilder = headers.associateWith {
|
||||
JsIrModuleCrossModuleReferenceBuilder(
|
||||
moduleKind,
|
||||
it,
|
||||
relativeRequirePath,
|
||||
reexportModuleToHeader[it.moduleName] ?: emptyList(),
|
||||
importedInModuleWithEffect[it.moduleName] ?: emptyList(),
|
||||
)
|
||||
}
|
||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
|
||||
@@ -143,6 +149,7 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
val header: JsIrModuleHeader,
|
||||
val relativeRequirePath: Boolean,
|
||||
val transitiveExportFrom: List<JsIrModuleHeader>,
|
||||
val importWithEffectFrom: List<JsIrModuleHeader>,
|
||||
) {
|
||||
val imports = mutableListOf<CrossModuleRef>()
|
||||
val exports = mutableSetOf<String>()
|
||||
@@ -186,10 +193,14 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
CrossModuleTransitiveExport(import(it).internalName, relativeRequirePath(it) ?: it.externalModuleName)
|
||||
}
|
||||
}
|
||||
|
||||
val importsWithEffect = importWithEffectFrom.map { CrossModuleImportWithEffect(import(it)) }
|
||||
|
||||
return CrossModuleReferences(
|
||||
moduleKind,
|
||||
importedModules.values.toList(),
|
||||
transitiveExport,
|
||||
importsWithEffect,
|
||||
exportNames,
|
||||
resultImports
|
||||
)
|
||||
@@ -220,8 +231,9 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsImportedModule)
|
||||
|
||||
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsImportedModule)
|
||||
class CrossModuleImportWithEffect(val moduleExporter: JsImportedModule)
|
||||
class CrossModuleTransitiveExport(val internalName: JsName, val externalName: String)
|
||||
|
||||
fun CrossModuleTransitiveExport.getRequireEsmName() = "$externalName$ESM_EXTENSION"
|
||||
@@ -230,6 +242,7 @@ class CrossModuleReferences(
|
||||
val moduleKind: ModuleKind,
|
||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
||||
val transitiveExportFrom: List<CrossModuleTransitiveExport>, // the list of modules which provide their exports for transitive export
|
||||
val importsWithEffect: List<CrossModuleImportWithEffect>, // the list of modules which provide their effects for import
|
||||
val exports: Map<String, String>, // tag -> index
|
||||
val imports: Map<String, CrossModuleImport>, // tag -> import statement
|
||||
) {
|
||||
@@ -237,6 +250,8 @@ class CrossModuleReferences(
|
||||
var jsImports = emptyMap<String, JsStatement>() // tag -> import statement
|
||||
private set
|
||||
|
||||
val jsImportsWithEffect: List<JsStatement> = importsWithEffect.map { it.generateCrossModuleImportStatement() }
|
||||
|
||||
fun initJsImportsForModule(module: JsIrModule) {
|
||||
val tagToName = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to it.value }
|
||||
jsImports = imports.entries.associate {
|
||||
@@ -252,6 +267,17 @@ class CrossModuleReferences(
|
||||
}
|
||||
}
|
||||
|
||||
private fun CrossModuleImportWithEffect.generateCrossModuleImportStatement(): JsStatement {
|
||||
return when (moduleKind) {
|
||||
ModuleKind.ES -> generateJsImportStatement()
|
||||
else -> error("Should not appear in non ES-modules compilations")
|
||||
}
|
||||
}
|
||||
|
||||
private fun CrossModuleImportWithEffect.generateJsImportStatement(): JsStatement {
|
||||
return JsImport(moduleExporter.getRequireName(true), JsImport.Target.Effect)
|
||||
}
|
||||
|
||||
private fun CrossModuleImport.generateImportVariableDeclaration(importedAs: JsName): JsStatement {
|
||||
val exportRef = JsNameRef(exportedAs, ReservedJsNames.makeCrossModuleNameRef(moduleExporter.internalName))
|
||||
return JsVars(JsVars.JsVar(importedAs, exportRef))
|
||||
@@ -265,7 +291,7 @@ class CrossModuleReferences(
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun Empty(moduleKind: ModuleKind) = CrossModuleReferences(moduleKind, listOf(), emptyList(), emptyMap(), emptyMap())
|
||||
fun Empty(moduleKind: ModuleKind) = CrossModuleReferences(moduleKind, listOf(), emptyList(), emptyList(), emptyMap(), emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,4 +304,5 @@ fun JsStatement.renameImportedSymbolInternalName(newName: JsName): JsStatement {
|
||||
}
|
||||
|
||||
val List<JsIrProgramFragment>.mainFragment: JsIrProgramFragment get() = first()
|
||||
val List<JsIrProgramFragment>.exportFragment: JsIrProgramFragment? get() = getOrNull(1)
|
||||
val List<JsIrProgramFragment>.exportFragment: JsIrProgramFragment? get() = getOrNull(1)
|
||||
val JsIrProgramFragment.hasEffect: Boolean get() = eagerInitializers.statements.isNotEmpty()
|
||||
+7
-2
@@ -22,6 +22,7 @@ class Merger(
|
||||
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val importStatements = mutableMapOf<String, JsStatement>()
|
||||
private val importStatementsWithEffect = mutableListOf<JsStatement>()
|
||||
private val importedModulesMap = mutableMapOf<JsImportedModuleKey, JsImportedModule>()
|
||||
|
||||
private val additionalExports = mutableListOf<JsStatement>()
|
||||
@@ -57,6 +58,7 @@ 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) }
|
||||
@@ -68,6 +70,8 @@ class Merger(
|
||||
importStatements.putIfAbsent(tag, crossModuleJsImport.renameImportedSymbolInternalName(importName))
|
||||
}
|
||||
|
||||
importStatementsWithEffect.addAll(crossModuleReferences.jsImportsWithEffect)
|
||||
|
||||
if (crossModuleReferences.exports.isNotEmpty()) {
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
|
||||
@@ -213,7 +217,7 @@ class Merger(
|
||||
fragments.forEach {
|
||||
moduleBody += it.declarations.statements
|
||||
classModels += it.classes
|
||||
initializerBlock.statements += it.initializers.statements
|
||||
initializerBlock.statements += it.initializers.statements + it.eagerInitializers.statements
|
||||
polyfillDeclarationBlock.statements += it.polyfills.statements
|
||||
}
|
||||
|
||||
@@ -248,7 +252,7 @@ class Merger(
|
||||
val exportStatements = declareAndCallJsExporter() + additionalExports + transitiveJsExport()
|
||||
|
||||
val importedJsModules = this.importedModulesMap.values.toList() + this.crossModuleReferences.importedModules
|
||||
val importStatements = this.importStatements.values.toList()
|
||||
val importStatements = this.importStatements.values.toList() + this.importStatementsWithEffect.toList()
|
||||
|
||||
val program = JsProgram()
|
||||
|
||||
@@ -342,6 +346,7 @@ class Merger(
|
||||
is JsImport -> JsImport(
|
||||
module,
|
||||
when (target) {
|
||||
is JsImport.Target.Effect -> JsImport.Target.Effect
|
||||
is JsImport.Target.All -> JsImport.Target.All(alias = name.makeRef())
|
||||
is JsImport.Target.Default -> JsImport.Target.Default(name = name.makeRef())
|
||||
is JsImport.Target.Elements -> JsImport.Target.Elements(
|
||||
|
||||
+1
@@ -119,6 +119,7 @@ private fun JsNode.computeScopes(): Scope {
|
||||
|
||||
override fun visitImport(import: JsImport) {
|
||||
when (val target = import.target) {
|
||||
is JsImport.Target.Effect -> {}
|
||||
is JsImport.Target.All -> target.alias.name?.let { currentScope.declaredNames += it }
|
||||
is JsImport.Target.Default -> target.name.name?.let { currentScope.declaredNames += it }
|
||||
is JsImport.Target.Elements -> target.elements.forEach {
|
||||
|
||||
@@ -25,6 +25,7 @@ class JsStaticContext(
|
||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||
|
||||
val initializerBlock = JsCompositeBlock()
|
||||
val eagerInitializerBlock = JsCompositeBlock()
|
||||
|
||||
val isPerFile: Boolean get() = mode.granularity === JsGenerationGranularity.PER_FILE
|
||||
}
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ object ImportType {
|
||||
const val ALL = 0
|
||||
const val ITEMS = 1
|
||||
const val DEFAULT = 2
|
||||
const val EFFECT = 3
|
||||
}
|
||||
|
||||
object ExportType {
|
||||
|
||||
+2
@@ -94,6 +94,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||
|
||||
readRepeated { declarations.statements += readStatement() }
|
||||
readRepeated { initializers.statements += readStatement() }
|
||||
readRepeated { eagerInitializers.statements += readStatement() }
|
||||
readRepeated { exports.statements += readStatement() }
|
||||
readRepeated { polyfills.statements += readStatement() }
|
||||
|
||||
@@ -238,6 +239,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||
JsImport(
|
||||
readString(),
|
||||
when (val type = readByte().toInt()) {
|
||||
ImportType.EFFECT -> JsImport.Target.Effect
|
||||
ImportType.ALL -> JsImport.Target.All(nameTable[readInt()].makeRef())
|
||||
ImportType.DEFAULT -> JsImport.Target.Default(nameTable[readInt()].makeRef())
|
||||
ImportType.ITEMS -> JsImport.Target.Elements(readList {
|
||||
|
||||
+4
@@ -132,6 +132,7 @@ private class JsIrAstSerializer {
|
||||
|
||||
writeCompositeBlock(fragment.declarations)
|
||||
writeCompositeBlock(fragment.initializers)
|
||||
writeCompositeBlock(fragment.eagerInitializers)
|
||||
writeCompositeBlock(fragment.exports)
|
||||
writeCompositeBlock(fragment.polyfills)
|
||||
|
||||
@@ -328,6 +329,9 @@ private class JsIrAstSerializer {
|
||||
writeString(import.module)
|
||||
|
||||
when (val target = import.target) {
|
||||
is JsImport.Target.Effect -> {
|
||||
writeByte(ImportType.EFFECT)
|
||||
}
|
||||
is JsImport.Target.All -> {
|
||||
writeByte(ImportType.ALL)
|
||||
writeInt(internalizeName(target.alias.name!!))
|
||||
|
||||
@@ -1404,7 +1404,10 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
p.print("}");
|
||||
}
|
||||
|
||||
p.print(" from ");
|
||||
if (!(target == JsImport.Target.Effect.INSTANCE)) {
|
||||
p.print(" from ");
|
||||
}
|
||||
|
||||
p.print(javaScriptString(jsImport.getModule()));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class JsImport(
|
||||
get() = (target as Target.Elements).elements
|
||||
|
||||
sealed class Target {
|
||||
object Effect : Target()
|
||||
class Elements(val elements: MutableList<Element>) : Target()
|
||||
class Default(val name: JsNameRef) : Target() {
|
||||
constructor(name: String) : this(JsNameRef(name))
|
||||
@@ -36,6 +37,7 @@ class JsImport(
|
||||
|
||||
override fun acceptChildren(visitor: JsVisitor) {
|
||||
when (target) {
|
||||
is Target.Effect -> {}
|
||||
is Target.All -> visitor.accept(target.alias)
|
||||
is Target.Default -> visitor.accept(target.name)
|
||||
is Target.Elements -> target.elements.forEach {
|
||||
|
||||
+18
@@ -2041,6 +2041,24 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/constructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal3.kt")
|
||||
public void testEagerInitializationGlobal3() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
|
||||
+18
@@ -2147,6 +2147,24 @@ public class FirJsES6BoxTestGenerated extends AbstractFirJsES6BoxTest {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/constructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal3.kt")
|
||||
public void testEagerInitializationGlobal3() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
|
||||
+18
@@ -2147,6 +2147,24 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/constructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal3.kt")
|
||||
public void testEagerInitializationGlobal3() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
|
||||
+18
@@ -2041,6 +2041,24 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/constructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("eagerInitializationGlobal3.kt")
|
||||
public void testEagerInitializationGlobal3() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/crossModuleRefPerFile/eagerInitializationGlobal3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: WASM
|
||||
// ES_MODULES
|
||||
// PROPERTY_LAZY_INITIALIZATION
|
||||
|
||||
// FILE: lib.kt
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
val x = foo()
|
||||
|
||||
private fun foo(): Int {
|
||||
z1 = true
|
||||
return 42
|
||||
}
|
||||
|
||||
// Will be initialized since [x]'s initializer calls a function from the file.
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun box(): String {
|
||||
return return if (z1 && z2) "OK" else "fail"
|
||||
}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: WASM
|
||||
// ES_MODULES
|
||||
// PROPERTY_LAZY_INITIALIZATION
|
||||
|
||||
// FILE: lib.kt
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
val x = run { z1 = true; 42 }
|
||||
|
||||
// Won't be initialized (cause no function from the file will be called during [x] initialization).
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun box(): String {
|
||||
return if (z1 && !z2) "OK" else "fail"
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: WASM
|
||||
// ES_MODULES
|
||||
// PROPERTY_LAZY_INITIALIZATION
|
||||
|
||||
// FILE: lib.kt
|
||||
var z1 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@EagerInitialization
|
||||
val x = run { z1 = !z1; 42 }
|
||||
|
||||
val y = run { 73 }
|
||||
|
||||
fun box(): String {
|
||||
return if (z1) "OK" else "fail"
|
||||
}
|
||||
+11
-3
@@ -1,10 +1,18 @@
|
||||
IGNORE_PER_FILE: true
|
||||
|
||||
MODULES: lib1, main
|
||||
|
||||
STEP 0:
|
||||
libs: lib1, main
|
||||
dirty js modules: lib1, main
|
||||
STEP 1..3:
|
||||
dirty js files: lib1/l1, lib1/l2, main/m, main/m.export, main
|
||||
STEP 1:
|
||||
libs: lib1, main
|
||||
dirty js modules: lib1
|
||||
dirty js files: lib1/l2, lib1, main
|
||||
STEP 2:
|
||||
libs: lib1, main
|
||||
dirty js modules: lib1
|
||||
dirty js files: lib1/l2
|
||||
STEP 3:
|
||||
libs: lib1, main
|
||||
dirty js modules: lib1
|
||||
dirty js files: lib1/l2, main
|
||||
|
||||
Reference in New Issue
Block a user