in compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/jsexport/ExportModelToJsStatements.kt [48:215]
fun generateDeclarationExport(
declaration: ExportedDeclaration,
namespace: JsExpression?,
esModules: Boolean,
parentClass: IrClass? = null,
): List<JsStatement> {
return when (declaration) {
is ExportedNamespace -> {
require(namespace != null) { "Only namespaced namespaces are allowed" }
val namespaceVariableName = JsName(declaration.name, true)
val namespaceRef = jsElementAccess(declaration.name, namespace)
val namespaceDeclaration = JsVars(
JsVars.Variant.Var,
JsVars.JsVar(
namespaceVariableName,
JsAstUtils.or(
namespaceRef,
jsAssignment(
namespaceRef,
JsObjectLiteral()
)
)
)
)
listOf(namespaceDeclaration) + declaration.declarations.flatMap {
generateDeclarationExport(
it,
namespaceVariableName.makeRef(),
esModules
)
}
}
is ExportedFunction -> {
val name = staticContext.getNameForStaticDeclaration(declaration.ir)
when {
namespace != null ->
listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt())
esModules -> {
if (declaration.attributes.contains(ExportedAttribute.DefaultExport)) {
listOf(JsExport(JsExport.Subject.Default(name.makeRef())))
} else {
listOf(JsExport(name.makeRef(), alias = JsName(declaration.name, false)))
}
}
else -> emptyList()
}
}
is ExportedProperty -> {
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
when {
namespace == null -> {
val property = declaration.generateTopLevelGetters()
val exportStatement = if (declaration.attributes.contains(ExportedAttribute.DefaultExport)) {
JsExport(JsExport.Subject.Default(property.name.makeRef()))
} else {
JsExport(property.name.makeRef(), JsName(declaration.name, false))
}
listOf(JsVars(JsVars.Variant.Var, property), exportStatement)
}
declaration.isDefaultImplementation -> {
val property = declaration.generateTopLevelGetters()
listOf(
JsVars(JsVars.Variant.Var, property),
jsAssignment(jsElementAccess(declaration.name, namespace), property.name.makeRef()).makeStmt()
)
}
else -> {
val getter = declaration.irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
val setter = declaration.irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
listOf(
defineProperty(
namespace,
declaration.name,
getter?.makeRef(),
setter?.makeRef(),
staticContext,
enumerable = true
).makeStmt()
)
}
}
}
is ExportedObject -> {
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
val (name, objectClassInitialization) = declaration.getNameAndInitialization()
val newNameSpace = jsElementAccess(Namer.PROTOTYPE_NAME, name.makeRef())
val staticsExport =
declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
val objectExport = when {
es6mode || namespace == null -> generateDeclarationExport(
ExportedProperty(
declaration.name,
isStatic = parentClass?.isObject != true,
irGetter = declaration.irGetter,
).apply { attributes.addAll(declaration.attributes) },
namespace,
esModules,
parentClass
)
else -> listOf(
defineProperty(
namespace,
declaration.name,
staticContext.getNameForStaticDeclaration(
declaration.irGetter
?: irError("Expect to have an object getter in its export model") {
withIrEntry("declaration.ir", declaration.ir)
}
).makeRef(),
null,
staticContext,
enumerable = true
).makeStmt()
)
}
listOfNotNull(objectClassInitialization.takeIf { staticsExport.isNotEmpty() }) + objectExport + staticsExport
}
is ExportedRegularClass -> {
// These are used when @JsStatic is used or exporting secondary constructors annotated with @JsName
val staticMembers = declaration.members
.filter { it is ExportedNamespace || it is ExportedFunction && it.isStatic && !it.ir.isEs6ConstructorReplacement }
.takeIf { !declaration.ir.isInner }.orEmpty()
if (!allowImplementingInterfaces && declaration.isInterface && staticMembers.isEmpty() && declaration.nestedClasses.isEmpty()) {
return emptyList()
}
val (name, classInitialization) = declaration.getNameAndInitialization()
val newNameSpace = when {
namespace != null -> jsElementAccess(declaration.name, namespace)
esModules -> name.makeRef()
else -> prototypeOf(name.makeRef(), staticContext)
}
val klassExport = when {
namespace != null -> jsAssignment(newNameSpace, name.makeRef()).makeStmt()
esModules -> {
if (declaration.attributes.contains(ExportedAttribute.DefaultExport)) {
JsExport(JsExport.Subject.Default(name.makeRef()))
} else {
JsExport(name.makeRef(), JsName(declaration.name, false))
}
}
else -> null
}
val enumEntries = declaration.members.filter { it is ExportedProperty && it.isStatic }
val innerClassesAssignments = declaration.nestedClasses
.filter { it.ir.isInner }
.map { it.generateInnerClassAssignment(name) }
val staticsExport = (staticMembers + enumEntries + declaration.nestedClasses)
.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
listOfNotNull(classInitialization, klassExport) + staticsExport + innerClassesAssignments
}
}
}