[JS IR] Possibility to use export for nested inside object
[JS IR] Fix test data after master rebase [JS IR] Use stdlib's listOfNotNull [JS IR] Possibility to use export for nested inside object - Add tests for such cases Merge-request: KT-MR-4883 ^KT-49363 fixed
This commit is contained in:
@@ -241,7 +241,11 @@ class IrToJs(
|
||||
declareNewNamespace = { globalNames.declareFreshName(it, it) }
|
||||
)
|
||||
exportedDeclarations.forEach {
|
||||
statements += exporter.generateDeclarationExport(it, null)
|
||||
statements += exporter.generateDeclarationExport(
|
||||
it,
|
||||
null,
|
||||
esModules = true
|
||||
)
|
||||
}
|
||||
|
||||
return GeneratedUnit(statements, exportedDeclarations)
|
||||
|
||||
@@ -55,6 +55,7 @@ class ExportedProperty(
|
||||
val isProtected: Boolean,
|
||||
val irGetter: IrFunction?,
|
||||
val irSetter: IrFunction?,
|
||||
val exportedObject: ExportedClass? = null,
|
||||
) : ExportedDeclaration()
|
||||
|
||||
|
||||
|
||||
+15
-12
@@ -337,6 +337,18 @@ class ExportModelGenerator(
|
||||
|
||||
val name = klass.getExportedIdentifier()
|
||||
|
||||
val exportedClass = ExportedClass(
|
||||
name = name,
|
||||
isInterface = klass.isInterface,
|
||||
isAbstract = klass.modality == Modality.ABSTRACT,
|
||||
superClass = superType,
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
nestedClasses = nestedClasses,
|
||||
ir = klass
|
||||
)
|
||||
|
||||
if (klass.kind == ClassKind.OBJECT) {
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members + nestedClasses)
|
||||
if (superType != null)
|
||||
@@ -355,21 +367,12 @@ class ExportModelGenerator(
|
||||
isAbstract = false,
|
||||
isProtected = klass.visibility == DescriptorVisibilities.PROTECTED,
|
||||
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
|
||||
irSetter = null
|
||||
irSetter = null,
|
||||
exportedObject = exportedClass,
|
||||
)
|
||||
}
|
||||
|
||||
return ExportedClass(
|
||||
name = name,
|
||||
isInterface = klass.isInterface,
|
||||
isAbstract = klass.modality == Modality.ABSTRACT,
|
||||
superClass = superType,
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
nestedClasses = nestedClasses,
|
||||
ir = klass
|
||||
)
|
||||
return exportedClass
|
||||
}
|
||||
|
||||
private fun exportAsEnumMember(
|
||||
|
||||
+34
-16
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.prototypeOf
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.IrNamer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
@@ -24,10 +25,14 @@ class ExportModelToJsStatements(
|
||||
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
||||
|
||||
fun generateModuleExport(module: ExportedModule, internalModuleName: JsName): List<JsStatement> {
|
||||
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName)) }
|
||||
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName), esModules = false) }
|
||||
}
|
||||
|
||||
fun generateDeclarationExport(declaration: ExportedDeclaration, namespace: JsExpression?): List<JsStatement> {
|
||||
fun generateDeclarationExport(
|
||||
declaration: ExportedDeclaration,
|
||||
namespace: JsExpression?,
|
||||
esModules: Boolean
|
||||
): List<JsStatement> {
|
||||
return when (declaration) {
|
||||
is ExportedNamespace -> {
|
||||
require(namespace != null) { "Only namespaced namespaces are allowed" }
|
||||
@@ -57,20 +62,22 @@ class ExportModelToJsStatements(
|
||||
currentRef = newNameSpaceRef
|
||||
currentNamespace = newNamespace
|
||||
}
|
||||
statements + declaration.declarations.flatMap { generateDeclarationExport(it, currentRef) }
|
||||
statements + declaration.declarations.flatMap { generateDeclarationExport(it, currentRef, esModules) }
|
||||
}
|
||||
|
||||
is ExportedFunction -> {
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
if (namespace == null) {
|
||||
if (esModules) {
|
||||
listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||
} else {
|
||||
listOf(
|
||||
jsAssignment(
|
||||
jsElementAccess(declaration.name, namespace),
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
)
|
||||
if (namespace != null) {
|
||||
listOf(
|
||||
jsAssignment(
|
||||
jsElementAccess(declaration.name, namespace),
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
)
|
||||
} else emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,22 +86,33 @@ class ExportModelToJsStatements(
|
||||
|
||||
is ExportedProperty -> {
|
||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
||||
val underlying: List<JsStatement> = declaration.exportedObject?.let {
|
||||
generateDeclarationExport(it, null, esModules)
|
||||
} ?: emptyList()
|
||||
val getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.irSetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt())
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt()) + underlying
|
||||
}
|
||||
|
||||
is ErrorDeclaration -> emptyList()
|
||||
|
||||
is ExportedClass -> {
|
||||
if (declaration.isInterface) return emptyList()
|
||||
val newNameSpace = jsElementAccess(declaration.name, namespace)
|
||||
val newNameSpace = if (namespace != null)
|
||||
jsElementAccess(declaration.name, namespace)
|
||||
else
|
||||
JsNameRef(Namer.PROTOTYPE_NAME, declaration.name)
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
val klassExport =
|
||||
if (namespace == null) {
|
||||
if (esModules) {
|
||||
JsExport(name, alias = JsName(declaration.name, false))
|
||||
} else {
|
||||
jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
|
||||
if (namespace != null) {
|
||||
jsAssignment(
|
||||
newNameSpace,
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
} else null
|
||||
}
|
||||
|
||||
// These are only used when exporting secondary constructors annotated with @JsName
|
||||
@@ -112,9 +130,9 @@ class ExportModelToJsStatements(
|
||||
.map { it.generateInnerClassAssignment(declaration) }
|
||||
|
||||
val staticsExport = (staticFunctions + staticProperties + declaration.nestedClasses)
|
||||
.flatMap { generateDeclarationExport(it, newNameSpace) }
|
||||
.flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
|
||||
|
||||
listOf(klassExport) + staticsExport + innerClassesAssignments
|
||||
listOfNotNull(klassExport) + staticsExport + innerClassesAssignments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -24,7 +24,25 @@ class B {
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object MyObject {
|
||||
class A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
class B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
class C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.js
|
||||
function box() {
|
||||
return new this["export_nested_class"].B.Foo().bar("K");
|
||||
if (new this["export_nested_class"].B.Foo().bar("K") != "OK") return "fail 1";
|
||||
if (new this["export_nested_class"].MyObject.A().valueA() != "OK") return "fail 2";
|
||||
if (new this["export_nested_class"].MyObject.B().valueB() != "OK") return "fail 3";
|
||||
if (new this["export_nested_class"].MyObject.C().valueC() != "OK") return "fail 4";
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -37,6 +37,19 @@ sealed class MyEnum(val name: String) {
|
||||
object C: MyEnum("C")
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object MyObject {
|
||||
object A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
object B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
object C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.js
|
||||
|
||||
function box() {
|
||||
@@ -54,5 +67,9 @@ function box() {
|
||||
if (nestedObjectExport.MyEnum.B.name != 'B') return 'MyEnum.B failure';
|
||||
if (nestedObjectExport.MyEnum.C.name != 'C') return 'MyEnum.C failure';
|
||||
|
||||
if (nestedObjectExport.MyObject.A.valueA() != "OK") return 'MyObject.A failure';
|
||||
if (nestedObjectExport.MyObject.B.valueB() != "OK") return 'MyObject.B failure';
|
||||
if (nestedObjectExport.MyObject.C.valueC() != "OK") return 'MyObject.C failure';
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user