[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) }
|
declareNewNamespace = { globalNames.declareFreshName(it, it) }
|
||||||
)
|
)
|
||||||
exportedDeclarations.forEach {
|
exportedDeclarations.forEach {
|
||||||
statements += exporter.generateDeclarationExport(it, null)
|
statements += exporter.generateDeclarationExport(
|
||||||
|
it,
|
||||||
|
null,
|
||||||
|
esModules = true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return GeneratedUnit(statements, exportedDeclarations)
|
return GeneratedUnit(statements, exportedDeclarations)
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class ExportedProperty(
|
|||||||
val isProtected: Boolean,
|
val isProtected: Boolean,
|
||||||
val irGetter: IrFunction?,
|
val irGetter: IrFunction?,
|
||||||
val irSetter: IrFunction?,
|
val irSetter: IrFunction?,
|
||||||
|
val exportedObject: ExportedClass? = null,
|
||||||
) : ExportedDeclaration()
|
) : ExportedDeclaration()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+15
-12
@@ -337,6 +337,18 @@ class ExportModelGenerator(
|
|||||||
|
|
||||||
val name = klass.getExportedIdentifier()
|
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) {
|
if (klass.kind == ClassKind.OBJECT) {
|
||||||
var t: ExportedType = ExportedType.InlineInterfaceType(members + nestedClasses)
|
var t: ExportedType = ExportedType.InlineInterfaceType(members + nestedClasses)
|
||||||
if (superType != null)
|
if (superType != null)
|
||||||
@@ -355,21 +367,12 @@ class ExportModelGenerator(
|
|||||||
isAbstract = false,
|
isAbstract = false,
|
||||||
isProtected = klass.visibility == DescriptorVisibilities.PROTECTED,
|
isProtected = klass.visibility == DescriptorVisibilities.PROTECTED,
|
||||||
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
|
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
|
||||||
irSetter = null
|
irSetter = null,
|
||||||
|
exportedObject = exportedClass,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportAsEnumMember(
|
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.prototypeOf
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
|
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.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.emptyScope
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||||
import org.jetbrains.kotlin.ir.util.companionObject
|
import org.jetbrains.kotlin.ir.util.companionObject
|
||||||
@@ -24,10 +25,14 @@ class ExportModelToJsStatements(
|
|||||||
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
||||||
|
|
||||||
fun generateModuleExport(module: ExportedModule, internalModuleName: JsName): List<JsStatement> {
|
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) {
|
return when (declaration) {
|
||||||
is ExportedNamespace -> {
|
is ExportedNamespace -> {
|
||||||
require(namespace != null) { "Only namespaced namespaces are allowed" }
|
require(namespace != null) { "Only namespaced namespaces are allowed" }
|
||||||
@@ -57,20 +62,22 @@ class ExportModelToJsStatements(
|
|||||||
currentRef = newNameSpaceRef
|
currentRef = newNameSpaceRef
|
||||||
currentNamespace = newNamespace
|
currentNamespace = newNamespace
|
||||||
}
|
}
|
||||||
statements + declaration.declarations.flatMap { generateDeclarationExport(it, currentRef) }
|
statements + declaration.declarations.flatMap { generateDeclarationExport(it, currentRef, esModules) }
|
||||||
}
|
}
|
||||||
|
|
||||||
is ExportedFunction -> {
|
is ExportedFunction -> {
|
||||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||||
if (namespace == null) {
|
if (esModules) {
|
||||||
listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||||
} else {
|
} else {
|
||||||
listOf(
|
if (namespace != null) {
|
||||||
jsAssignment(
|
listOf(
|
||||||
jsElementAccess(declaration.name, namespace),
|
jsAssignment(
|
||||||
JsNameRef(name)
|
jsElementAccess(declaration.name, namespace),
|
||||||
).makeStmt()
|
JsNameRef(name)
|
||||||
)
|
).makeStmt()
|
||||||
|
)
|
||||||
|
} else emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,22 +86,33 @@ class ExportModelToJsStatements(
|
|||||||
|
|
||||||
is ExportedProperty -> {
|
is ExportedProperty -> {
|
||||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
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 getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||||
val setter = declaration.irSetter?.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 ErrorDeclaration -> emptyList()
|
||||||
|
|
||||||
is ExportedClass -> {
|
is ExportedClass -> {
|
||||||
if (declaration.isInterface) return emptyList()
|
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 name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||||
val klassExport =
|
val klassExport =
|
||||||
if (namespace == null) {
|
if (esModules) {
|
||||||
JsExport(name, alias = JsName(declaration.name, false))
|
JsExport(name, alias = JsName(declaration.name, false))
|
||||||
} else {
|
} 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
|
// These are only used when exporting secondary constructors annotated with @JsName
|
||||||
@@ -112,9 +130,9 @@ class ExportModelToJsStatements(
|
|||||||
.map { it.generateInnerClassAssignment(declaration) }
|
.map { it.generateInnerClassAssignment(declaration) }
|
||||||
|
|
||||||
val staticsExport = (staticFunctions + staticProperties + declaration.nestedClasses)
|
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
|
// FILE: test.js
|
||||||
function box() {
|
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")
|
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
|
// FILE: test.js
|
||||||
|
|
||||||
function box() {
|
function box() {
|
||||||
@@ -54,5 +67,9 @@ function box() {
|
|||||||
if (nestedObjectExport.MyEnum.B.name != 'B') return 'MyEnum.B failure';
|
if (nestedObjectExport.MyEnum.B.name != 'B') return 'MyEnum.B failure';
|
||||||
if (nestedObjectExport.MyEnum.C.name != 'C') return 'MyEnum.C 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';
|
return 'OK';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user