[K/JS] Support eager initialization for per-file granularity

This commit is contained in:
Artem Kobzar
2023-09-06 09:27:28 +00:00
committed by Space Team
parent f203681ffa
commit bff433f4e9
22 changed files with 288 additions and 29 deletions
@@ -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)
@@ -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)
}
}
@@ -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))
@@ -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 {
@@ -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()
@@ -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(
@@ -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
}
@@ -37,6 +37,7 @@ object ImportType {
const val ALL = 0
const val ITEMS = 1
const val DEFAULT = 2
const val EFFECT = 3
}
object ExportType {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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