[JS IR BE] Generate and export JS properties
This commit is contained in:
+21
-12
@@ -10,10 +10,12 @@ import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ModuleType
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isObject
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
@@ -51,19 +53,30 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) :
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun findModuleExports(module: IrModuleFragment, context: JsGenerationContext): Map<JsName, JsName> {
|
||||
val publicDeclarations = module.files
|
||||
private fun generateExportStatements(
|
||||
module: IrModuleFragment,
|
||||
context: JsGenerationContext,
|
||||
internalModuleName: JsName
|
||||
): List<JsStatement> {
|
||||
return module.files
|
||||
.flatMap { it.declarations }
|
||||
.asSequence()
|
||||
.filterIsInstance<IrDeclarationWithVisibility>()
|
||||
.filter { it.visibility == Visibilities.PUBLIC }
|
||||
.filter { !it.isEffectivelyExternal() }
|
||||
.filterIsInstance<IrSymbolOwner>()
|
||||
.map { declaration ->
|
||||
val name = context.getNameForSymbol(declaration.symbol)
|
||||
|
||||
val publicDeclarationNames = publicDeclarations.map {
|
||||
context.getNameForSymbol(it.symbol)
|
||||
}
|
||||
|
||||
return publicDeclarationNames.associate { it to it }
|
||||
JsExpressionStatement(
|
||||
if (declaration is IrClass && declaration.isObject) {
|
||||
defineProperty(internalModuleName.makeRef(), name.ident, getter = JsNameRef("${name.ident}_getInstance"))
|
||||
} else {
|
||||
jsAssignment(JsNameRef(name, internalModuleName.makeRef()), name.makeRef())
|
||||
}
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun generateModuleInGlobalScope(module: IrModuleFragment): JsProgram {
|
||||
@@ -149,11 +162,7 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) :
|
||||
|
||||
val moduleBody = generateModuleBody(module, rootContext)
|
||||
|
||||
val moduleExports = findModuleExports(module, rootContext)
|
||||
|
||||
val exportStatements = moduleExports.map { (from, to) ->
|
||||
JsExpressionStatement(jsAssignment(JsNameRef(from, internalModuleName.makeRef()), to.makeRef()))
|
||||
}
|
||||
val exportStatements = generateExportStatements(module, rootContext, internalModuleName)
|
||||
|
||||
rootFunction.body.statements += moduleBody
|
||||
|
||||
|
||||
+34
-14
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.backend.common.ir.isStatic
|
||||
import org.jetbrains.kotlin.backend.common.onlyIf
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
@@ -20,6 +22,7 @@ import org.jetbrains.kotlin.ir.types.isAny
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
||||
|
||||
@@ -38,6 +41,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
maybeGeneratePrimaryConstructor()
|
||||
val transformer = IrDeclarationToJsTransformer()
|
||||
|
||||
// Properties might be lowered out of classes
|
||||
// We'll use IrSimpleFunction::correspondingProperty to collect them into set
|
||||
val properties = mutableSetOf<IrProperty>()
|
||||
|
||||
for (declaration in irClass.declarations) {
|
||||
when (declaration) {
|
||||
is IrConstructor -> {
|
||||
@@ -45,6 +52,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
classModel.preDeclarationBlock.statements += generateInheritanceCode()
|
||||
}
|
||||
is IrSimpleFunction -> {
|
||||
properties.addIfNotNull(declaration.correspondingProperty)
|
||||
generateMemberFunction(declaration)?.let { classBlock.statements += it }
|
||||
}
|
||||
is IrClass -> {
|
||||
@@ -62,6 +70,30 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() }
|
||||
if (irClass.superTypes.any { it.isThrowable() }) {
|
||||
classBlock.statements += generateThrowableProperties()
|
||||
} else if (!irClass.defaultType.isThrowable()) {
|
||||
// TODO: Test export properties of throwable subtype
|
||||
if (!irClass.isInterface && !irClass.isEnumClass && !irClass.isEnumEntry) {
|
||||
for (property in properties) {
|
||||
|
||||
if (property.getter?.extensionReceiverParameter != null || property.setter?.extensionReceiverParameter != null)
|
||||
continue
|
||||
|
||||
if (property.visibility != Visibilities.PUBLIC)
|
||||
continue
|
||||
|
||||
if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE)
|
||||
continue
|
||||
|
||||
classBlock.statements += JsExpressionStatement(
|
||||
defineProperty(
|
||||
classPrototypeRef,
|
||||
context.getNameForDeclaration(property).ident,
|
||||
getter = property.getter?.let { context.getNameForDeclaration(it) }?.let { JsNameRef(it, classPrototypeRef) },
|
||||
setter = property.setter?.let { context.getNameForDeclaration(it) }?.let { JsNameRef(it, classPrototypeRef) }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
context.staticContext.classModels[className] = classModel
|
||||
return classBlock
|
||||
@@ -73,21 +105,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
val messageGetter = functions.single { it.name == Name.special("<get-message>") }
|
||||
val causeGetter = functions.single { it.name == Name.special("<get-cause>") }
|
||||
|
||||
val msgProperty = defineProperty(classPrototypeRef, "message") {
|
||||
val literal = JsObjectLiteral(true)
|
||||
val function = buildGetterFunction(messageGetter)
|
||||
literal.apply {
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function)
|
||||
}
|
||||
}
|
||||
val msgProperty = defineProperty(classPrototypeRef, "message", getter = buildGetterFunction(messageGetter))
|
||||
|
||||
val causeProperty = defineProperty(classPrototypeRef, "cause") {
|
||||
val literal = JsObjectLiteral(true)
|
||||
val function = buildGetterFunction(causeGetter)
|
||||
literal.apply {
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function)
|
||||
}
|
||||
}
|
||||
val causeProperty = defineProperty(classPrototypeRef, "cause", getter = buildGetterFunction(causeGetter))
|
||||
|
||||
return listOf(msgProperty.makeStmt(), causeProperty.makeStmt())
|
||||
}
|
||||
|
||||
+13
@@ -88,6 +88,19 @@ fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpressi
|
||||
return JsInvocation(objectDefineProperty, receiver, JsStringLiteral(name), value())
|
||||
}
|
||||
|
||||
|
||||
fun defineProperty(receiver: JsExpression, name: String, getter: JsExpression?, setter: JsExpression? = null) =
|
||||
defineProperty(receiver, name) {
|
||||
val literal = JsObjectLiteral(true)
|
||||
literal.apply {
|
||||
if (getter != null)
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), getter)
|
||||
if (setter != null)
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), setter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Partially copied from org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
object JsAstUtils {
|
||||
private fun deBlockIfPossible(statement: JsStatement): JsStatement {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1295
|
||||
package foo
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1297
|
||||
package foo
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1288
|
||||
package foo
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1294
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
// WITH_RUNTIME
|
||||
|
||||
Reference in New Issue
Block a user