[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.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ModuleType
|
import org.jetbrains.kotlin.ir.backend.js.ModuleType
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
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.IrDeclarationWithVisibility
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
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.ir.util.parentAsClass
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
@@ -51,19 +53,30 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) :
|
|||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findModuleExports(module: IrModuleFragment, context: JsGenerationContext): Map<JsName, JsName> {
|
private fun generateExportStatements(
|
||||||
val publicDeclarations = module.files
|
module: IrModuleFragment,
|
||||||
|
context: JsGenerationContext,
|
||||||
|
internalModuleName: JsName
|
||||||
|
): List<JsStatement> {
|
||||||
|
return module.files
|
||||||
.flatMap { it.declarations }
|
.flatMap { it.declarations }
|
||||||
|
.asSequence()
|
||||||
.filterIsInstance<IrDeclarationWithVisibility>()
|
.filterIsInstance<IrDeclarationWithVisibility>()
|
||||||
.filter { it.visibility == Visibilities.PUBLIC }
|
.filter { it.visibility == Visibilities.PUBLIC }
|
||||||
.filter { !it.isEffectivelyExternal() }
|
.filter { !it.isEffectivelyExternal() }
|
||||||
.filterIsInstance<IrSymbolOwner>()
|
.filterIsInstance<IrSymbolOwner>()
|
||||||
|
.map { declaration ->
|
||||||
|
val name = context.getNameForSymbol(declaration.symbol)
|
||||||
|
|
||||||
val publicDeclarationNames = publicDeclarations.map {
|
JsExpressionStatement(
|
||||||
context.getNameForSymbol(it.symbol)
|
if (declaration is IrClass && declaration.isObject) {
|
||||||
}
|
defineProperty(internalModuleName.makeRef(), name.ident, getter = JsNameRef("${name.ident}_getInstance"))
|
||||||
|
} else {
|
||||||
return publicDeclarationNames.associate { it to it }
|
jsAssignment(JsNameRef(name, internalModuleName.makeRef()), name.makeRef())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateModuleInGlobalScope(module: IrModuleFragment): JsProgram {
|
private fun generateModuleInGlobalScope(module: IrModuleFragment): JsProgram {
|
||||||
@@ -149,11 +162,7 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) :
|
|||||||
|
|
||||||
val moduleBody = generateModuleBody(module, rootContext)
|
val moduleBody = generateModuleBody(module, rootContext)
|
||||||
|
|
||||||
val moduleExports = findModuleExports(module, rootContext)
|
val exportStatements = generateExportStatements(module, rootContext, internalModuleName)
|
||||||
|
|
||||||
val exportStatements = moduleExports.map { (from, to) ->
|
|
||||||
JsExpressionStatement(jsAssignment(JsNameRef(from, internalModuleName.makeRef()), to.makeRef()))
|
|
||||||
}
|
|
||||||
|
|
||||||
rootFunction.body.statements += moduleBody
|
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.ir.isStatic
|
||||||
import org.jetbrains.kotlin.backend.common.onlyIf
|
import org.jetbrains.kotlin.backend.common.onlyIf
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
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.JsGenerationContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
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.ir.util.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
||||||
|
|
||||||
@@ -38,6 +41,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
maybeGeneratePrimaryConstructor()
|
maybeGeneratePrimaryConstructor()
|
||||||
val transformer = IrDeclarationToJsTransformer()
|
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) {
|
for (declaration in irClass.declarations) {
|
||||||
when (declaration) {
|
when (declaration) {
|
||||||
is IrConstructor -> {
|
is IrConstructor -> {
|
||||||
@@ -45,6 +52,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
classModel.preDeclarationBlock.statements += generateInheritanceCode()
|
classModel.preDeclarationBlock.statements += generateInheritanceCode()
|
||||||
}
|
}
|
||||||
is IrSimpleFunction -> {
|
is IrSimpleFunction -> {
|
||||||
|
properties.addIfNotNull(declaration.correspondingProperty)
|
||||||
generateMemberFunction(declaration)?.let { classBlock.statements += it }
|
generateMemberFunction(declaration)?.let { classBlock.statements += it }
|
||||||
}
|
}
|
||||||
is IrClass -> {
|
is IrClass -> {
|
||||||
@@ -62,6 +70,30 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() }
|
irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() }
|
||||||
if (irClass.superTypes.any { it.isThrowable() }) {
|
if (irClass.superTypes.any { it.isThrowable() }) {
|
||||||
classBlock.statements += generateThrowableProperties()
|
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
|
context.staticContext.classModels[className] = classModel
|
||||||
return classBlock
|
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 messageGetter = functions.single { it.name == Name.special("<get-message>") }
|
||||||
val causeGetter = functions.single { it.name == Name.special("<get-cause>") }
|
val causeGetter = functions.single { it.name == Name.special("<get-cause>") }
|
||||||
|
|
||||||
val msgProperty = defineProperty(classPrototypeRef, "message") {
|
val msgProperty = defineProperty(classPrototypeRef, "message", getter = buildGetterFunction(messageGetter))
|
||||||
val literal = JsObjectLiteral(true)
|
|
||||||
val function = buildGetterFunction(messageGetter)
|
|
||||||
literal.apply {
|
|
||||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val causeProperty = defineProperty(classPrototypeRef, "cause") {
|
val causeProperty = defineProperty(classPrototypeRef, "cause", getter = buildGetterFunction(causeGetter))
|
||||||
val literal = JsObjectLiteral(true)
|
|
||||||
val function = buildGetterFunction(causeGetter)
|
|
||||||
literal.apply {
|
|
||||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return listOf(msgProperty.makeStmt(), causeProperty.makeStmt())
|
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())
|
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
|
// Partially copied from org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
object JsAstUtils {
|
object JsAstUtils {
|
||||||
private fun deBlockIfPossible(statement: JsStatement): JsStatement {
|
private fun deBlockIfPossible(statement: JsStatement): JsStatement {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1295
|
// EXPECTED_REACHABLE_NODES: 1295
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1297
|
// EXPECTED_REACHABLE_NODES: 1297
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1288
|
// EXPECTED_REACHABLE_NODES: 1288
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1294
|
// EXPECTED_REACHABLE_NODES: 1294
|
||||||
// LANGUAGE_VERSION: 1.2
|
// LANGUAGE_VERSION: 1.2
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|||||||
Reference in New Issue
Block a user