diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 77c732b15e7..a95f6c34438 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -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 { - val publicDeclarations = module.files + private fun generateExportStatements( + module: IrModuleFragment, + context: JsGenerationContext, + internalModuleName: JsName + ): List { + return module.files .flatMap { it.declarations } + .asSequence() .filterIsInstance() .filter { it.visibility == Visibilities.PUBLIC } .filter { !it.isEffectivelyExternal() } .filterIsInstance() + .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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index de14e02e33d..cf82dc0fe70 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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() + 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("") } val causeGetter = functions.single { it.name == Name.special("") } - 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()) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index c3c25952190..d0203c1649d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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 { diff --git a/js/js.translator/testData/box/delegation/jsNamePropertyDelegation.kt b/js/js.translator/testData/box/delegation/jsNamePropertyDelegation.kt index 05598ea0115..a944aea4591 100644 --- a/js/js.translator/testData/box/delegation/jsNamePropertyDelegation.kt +++ b/js/js.translator/testData/box/delegation/jsNamePropertyDelegation.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1295 package foo diff --git a/js/js.translator/testData/box/native/inheritanceFromNativeTrait.kt b/js/js.translator/testData/box/native/inheritanceFromNativeTrait.kt index 4d6eb483092..61864ae6a27 100644 --- a/js/js.translator/testData/box/native/inheritanceFromNativeTrait.kt +++ b/js/js.translator/testData/box/native/inheritanceFromNativeTrait.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1297 package foo diff --git a/js/js.translator/testData/box/native/undefined.kt b/js/js.translator/testData/box/native/undefined.kt index 4be067310b8..4e205d9b8ea 100644 --- a/js/js.translator/testData/box/native/undefined.kt +++ b/js/js.translator/testData/box/native/undefined.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1288 package foo diff --git a/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt b/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt index fa6f8566bbf..9619e1eeb64 100644 --- a/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt +++ b/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1294 // LANGUAGE_VERSION: 1.2 // WITH_RUNTIME