[JS IR] Support object declaration export
Fixes KT-39117 and KT-39367
This commit is contained in:
@@ -214,6 +214,11 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue("superTypes")
|
||||
}
|
||||
|
||||
if (declaration.isObject && declaration.isExported(context)) {
|
||||
context.mapping.objectToGetInstanceFunction[declaration]!!
|
||||
.enqueue(declaration, "Exported object getInstance function")
|
||||
}
|
||||
|
||||
declaration.annotations.forEach {
|
||||
val annotationClass = it.symbol.owner.constructedClass
|
||||
if (annotationClass.isAssociatedObjectAnnotatedAnnotation) {
|
||||
|
||||
@@ -14,4 +14,5 @@ object JsLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object JS_CLOSURE_BOX_CLASS : IrStatementOriginImpl("JS_CLOSURE_BOX_CLASS")
|
||||
object JS_CLOSURE_BOX_CLASS_DECLARATION : IrDeclarationOriginImpl("JS_CLOSURE_BOX_CLASS_DECLARATION")
|
||||
object BRIDGE_TO_EXTERNAL_FUNCTION : IrDeclarationOriginImpl("BRIDGE_TO_EXTERNAL_FUNCTION")
|
||||
object OBJECT_GET_INSTANCE_FUNCTION : IrDeclarationOriginImpl("BRIDGE_TO_EXTERNAL_FUNCTION")
|
||||
}
|
||||
|
||||
+10
-2
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
@@ -45,7 +45,8 @@ class ExportedProperty(
|
||||
val isMember: Boolean = false,
|
||||
val isStatic: Boolean = false,
|
||||
val isAbstract: Boolean,
|
||||
val ir: IrProperty
|
||||
val irGetter: IrFunction?,
|
||||
val irSetter: IrFunction?,
|
||||
) : ExportedDeclaration()
|
||||
|
||||
|
||||
@@ -95,6 +96,13 @@ sealed class ExportedType {
|
||||
class TypeParameter(val name: String) : ExportedType()
|
||||
class Nullable(val baseType: ExportedType) : ExportedType()
|
||||
class ErrorType(val comment: String) : ExportedType()
|
||||
class TypeOf(val name: String) : ExportedType()
|
||||
|
||||
class InlineInterfaceType(
|
||||
val members: List<ExportedDeclaration>
|
||||
) : ExportedType()
|
||||
|
||||
class IntersectionType(val lhs: ExportedType, val rhs: ExportedType) : ExportedType()
|
||||
|
||||
fun withNullability(nullable: Boolean) =
|
||||
if (nullable) Nullable(this) else this
|
||||
|
||||
+50
-12
@@ -112,7 +112,8 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
isMember = parentClass != null,
|
||||
isStatic = false,
|
||||
isAbstract = parentClass?.isInterface == false && property.modality == Modality.ABSTRACT,
|
||||
ir = property
|
||||
irGetter = property.getter,
|
||||
irSetter = property.setter
|
||||
)
|
||||
}
|
||||
|
||||
@@ -120,10 +121,10 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
when (klass.kind) {
|
||||
ClassKind.ANNOTATION_CLASS,
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.ENUM_ENTRY,
|
||||
ClassKind.OBJECT ->
|
||||
ClassKind.ENUM_ENTRY ->
|
||||
return Exportability.Prohibited("Class ${klass.fqNameWhenAvailable} with kind: ${klass.kind}")
|
||||
|
||||
ClassKind.OBJECT,
|
||||
ClassKind.CLASS,
|
||||
ClassKind.INTERFACE -> {
|
||||
}
|
||||
@@ -137,7 +138,7 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
private fun exportClass(
|
||||
klass: IrClass
|
||||
): ExportedClass? {
|
||||
): ExportedDeclaration? {
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> return error(exportability.reason)
|
||||
is Exportability.NotNeeded -> return null
|
||||
@@ -160,8 +161,14 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
is IrProperty ->
|
||||
members.addIfNotNull(exportProperty(candidate))
|
||||
|
||||
is IrClass ->
|
||||
nestedClasses.addIfNotNull(exportClass(candidate))
|
||||
is IrClass -> {
|
||||
val ec = exportClass(candidate)
|
||||
if (ec is ExportedClass) {
|
||||
nestedClasses.add(ec)
|
||||
} else {
|
||||
members.addIfNotNull(ec)
|
||||
}
|
||||
}
|
||||
|
||||
is IrField -> {
|
||||
assert(candidate.correspondingPropertySymbol != null) {
|
||||
@@ -188,6 +195,27 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
val name = klass.getExportedIdentifier()
|
||||
|
||||
if (klass.kind == ClassKind.OBJECT) {
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members + nestedClasses)
|
||||
if (superType != null)
|
||||
t = ExportedType.IntersectionType(t, superType)
|
||||
|
||||
for (superInterface in superInterfaces) {
|
||||
t = ExportedType.IntersectionType(t, superInterface)
|
||||
}
|
||||
|
||||
return ExportedProperty(
|
||||
name = name,
|
||||
type = t,
|
||||
mutable = false,
|
||||
isMember = klass.parent is IrClass,
|
||||
isStatic = true,
|
||||
isAbstract = false,
|
||||
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!,
|
||||
irSetter = null
|
||||
)
|
||||
}
|
||||
|
||||
return ExportedClass(
|
||||
name = name,
|
||||
isInterface = klass.isInterface,
|
||||
@@ -254,11 +282,20 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
classifier is IrClassSymbol -> {
|
||||
val klass = classifier.owner
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> ExportedType.ErrorType(exportability.reason)
|
||||
is Exportability.NotNeeded -> error("Not needed classes types cannot be used")
|
||||
else -> ExportedType.ClassType(
|
||||
klass.fqNameWhenAvailable!!.asString(),
|
||||
val name = klass.fqNameWhenAvailable!!.asString()
|
||||
|
||||
when (klass.kind) {
|
||||
ClassKind.ANNOTATION_CLASS,
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.ENUM_ENTRY ->
|
||||
ExportedType.ErrorType("Class $name with kind: ${klass.kind}")
|
||||
|
||||
ClassKind.OBJECT ->
|
||||
ExportedType.TypeOf(name)
|
||||
|
||||
ClassKind.CLASS,
|
||||
ClassKind.INTERFACE -> ExportedType.ClassType(
|
||||
name,
|
||||
type.arguments.map { exportTypeArgument(it) }
|
||||
)
|
||||
}
|
||||
@@ -286,7 +323,8 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
return Exportability.NotNeeded
|
||||
if (function.origin == IrDeclarationOrigin.BRIDGE ||
|
||||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION ||
|
||||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
|
||||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
function.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION
|
||||
) {
|
||||
return Exportability.NotNeeded
|
||||
}
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@ class ExportModelToJsStatements(
|
||||
is ExportedConstructor -> emptyList()
|
||||
|
||||
is ExportedProperty -> {
|
||||
val getter = declaration.ir.getter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.ir.setter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
val getter = declaration.irGetter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.irSetter?.let { JsNameRef(nameTables.getNameForStaticDeclaration(it)) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt())
|
||||
}
|
||||
|
||||
|
||||
+22
-14
@@ -50,7 +50,7 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
|
||||
else -> "function "
|
||||
}
|
||||
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript() }
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
@@ -58,28 +58,28 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
|
||||
else
|
||||
""
|
||||
|
||||
val renderedReturnType = returnType.toTypeScript()
|
||||
val renderedReturnType = returnType.toTypeScript(indent)
|
||||
|
||||
"${prefix}$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
}
|
||||
is ExportedConstructor ->
|
||||
"constructor(${parameters.joinToString(", ") { it.toTypeScript() }});"
|
||||
"constructor(${parameters.joinToString(", ") { it.toTypeScript(indent) }});"
|
||||
|
||||
is ExportedProperty -> {
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "") + (if (!mutable) "readonly " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
prefix + keyword + name + ": " + type.toTypeScript() + ";"
|
||||
prefix + keyword + name + ": " + type.toTypeScript(indent) + ";"
|
||||
}
|
||||
|
||||
is ExportedClass -> {
|
||||
val keyword = if (isInterface) "interface" else "class"
|
||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
||||
|
||||
val superClassClause = superClass?.let { " extends ${it.toTypeScript()}" } ?: ""
|
||||
val superClassClause = superClass?.let { " extends ${it.toTypeScript(indent)}" } ?: ""
|
||||
val superInterfacesClause = if (superInterfaces.isNotEmpty()) {
|
||||
" $superInterfacesKeyword " + superInterfaces.joinToString(", ") { it.toTypeScript() }
|
||||
" $superInterfacesKeyword " + superInterfaces.joinToString(", ") { it.toTypeScript(indent) }
|
||||
} else ""
|
||||
|
||||
val membersString = members.joinToString("") { it.toTypeScript("$indent ") + "\n" }
|
||||
@@ -107,22 +107,30 @@ fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): Strin
|
||||
}
|
||||
}
|
||||
|
||||
fun ExportedParameter.toTypeScript(): String =
|
||||
"$name: ${type.toTypeScript()}"
|
||||
fun ExportedParameter.toTypeScript(indent: String): String =
|
||||
"$name: ${type.toTypeScript(indent)}"
|
||||
|
||||
fun ExportedType.toTypeScript(): String = when (this) {
|
||||
fun ExportedType.toTypeScript(indent: String): String = when (this) {
|
||||
is ExportedType.Primitive -> typescript
|
||||
is ExportedType.Array -> "Array<${elementType.toTypeScript()}>"
|
||||
is ExportedType.Array -> "Array<${elementType.toTypeScript(indent)}>"
|
||||
is ExportedType.Function -> "(" + parameterTypes
|
||||
.withIndex()
|
||||
.joinToString(", ") { (index, type) ->
|
||||
"p$index: ${type.toTypeScript()}"
|
||||
} + ") => " + returnType.toTypeScript()
|
||||
"p$index: ${type.toTypeScript(indent)}"
|
||||
} + ") => " + returnType.toTypeScript(indent)
|
||||
|
||||
is ExportedType.ClassType ->
|
||||
name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript() }}>" else ""
|
||||
name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript(indent) }}>" else ""
|
||||
is ExportedType.TypeOf ->
|
||||
"typeof $name"
|
||||
|
||||
is ExportedType.ErrorType -> "any /*$comment*/"
|
||||
is ExportedType.TypeParameter -> name
|
||||
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript() + ">"
|
||||
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent) + ">"
|
||||
is ExportedType.InlineInterfaceType -> {
|
||||
members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent ") + "\n" }
|
||||
}
|
||||
is ExportedType.IntersectionType -> {
|
||||
lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent)
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -12,8 +12,10 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
@@ -111,6 +113,7 @@ private fun JsCommonBackendContext.getOrCreateGetInstanceFunction(obj: IrClass)
|
||||
jsIrDeclarationBuilder.buildFunction(
|
||||
obj.name.asString() + "_getInstance",
|
||||
returnType = obj.defaultType,
|
||||
parent = obj.parent
|
||||
parent = obj.parent,
|
||||
origin = JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user