diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt index 5f1c5457614..a1e8161d8c7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.keysToMap class ExportModelGenerator( val context: JsIrBackendContext, @@ -58,7 +59,13 @@ class ExportModelGenerator( return when (candidate) { is IrSimpleFunction -> exportFunction(candidate) is IrProperty -> exportProperty(candidate) - is IrClass -> exportClass(candidate) + is IrClass -> { + if (candidate.isEnumClass) { + exportEnumClass(candidate) + } else { + exportClass(candidate) + } + } is IrField -> null else -> error("Can't export declaration $candidate") } @@ -135,37 +142,38 @@ class ExportModelGenerator( ) } - private fun exportEnumEntry(field: IrField, enumEntries: List): ExportedProperty { + private fun exportEnumEntry(field: IrField, enumEntries: Map): ExportedProperty { val irEnumEntry = context.mapping.fieldToEnumEntry[field] ?: error("Unable to find enum entry for ${field.fqNameWhenAvailable}") val parentClass = field.parent as IrClass val name = irEnumEntry.getExportedIdentifier() - val ordinal = enumEntries.indexOf(irEnumEntry) + val ordinal = enumEntries.getValue(irEnumEntry) - val nameProperty = ExportedProperty( + fun fakeProperty(name: String, type: ExportedType) = + ExportedProperty( + name = name, + type = type, + mutable = false, + isMember = true, + isStatic = false, + isAbstract = false, + isProtected = false, + irGetter = null, + irSetter = null, + ) + + val nameProperty = fakeProperty( name = "name", type = ExportedType.LiteralType.StringLiteralType(name), - mutable = false, - isMember = true, - isStatic = false, - isAbstract = false, - isProtected = false, - irGetter = null, - irSetter = null, ) - val ordinalProperty = ExportedProperty( + + val ordinalProperty = fakeProperty( name = "ordinal", type = ExportedType.LiteralType.NumberLiteralType(ordinal), - mutable = false, - isMember = true, - isStatic = false, - isAbstract = false, - isProtected = false, - irGetter = null, - irSetter = null, ) + val type = ExportedType.InlineInterfaceType( listOf(nameProperty, ordinalProperty) ) @@ -213,25 +221,61 @@ class ExportModelGenerator( } } + val (members, nestedClasses) = exportClassDeclarations(klass) + + return exportClass( + klass, + members, + nestedClasses + ) + } + + private fun exportEnumClass( + klass: IrClass + ): ExportedDeclaration? { + when (val exportability = classExportability(klass)) { + is Exportability.Prohibited -> error(exportability.reason) + is Exportability.NotNeeded -> return null + Exportability.Allowed -> { + } + } + + val enumEntries = klass + .declarations + .filterIsInstance() + .mapNotNull { context.mapping.fieldToEnumEntry[it] } + + val enumEntriesToOrdinal: Map = + enumEntries + .keysToMap(enumEntries::indexOf) + + val (members, nestedClasses) = exportClassDeclarations(klass) { candidate -> + val enumExportedMember = exportAsEnumMember(candidate, enumEntriesToOrdinal) + enumExportedMember + } + + return exportClass( + klass, + members, + nestedClasses + ) + } + + private fun exportClassDeclarations( + klass: IrClass, + specialProcessing: (IrDeclarationWithName) -> ExportedDeclaration? = { null } + ): ExportedClassDeclarationsInfo { val members = mutableListOf() val nestedClasses = mutableListOf() - val enumEntries = if (klass.isEnumClass) { - klass - .declarations - .filterIsInstance() - .mapNotNull { context.mapping.fieldToEnumEntry[it] } - } else null for (declaration in klass.declarations) { val candidate = getExportCandidate(declaration) ?: continue if (!shouldDeclarationBeExported(candidate, context)) continue - if (enumEntries != null) { - val enumExportedMember = exportAsEnumMember(candidate, enumEntries) - if (enumExportedMember != null) { - members.add(enumExportedMember) - continue - } + val processingResult = specialProcessing(candidate) + if (processingResult != null) { + members.add(processingResult) + continue } when (candidate) { @@ -256,8 +300,8 @@ class ExportModelGenerator( is IrField -> { assert( candidate.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE || - candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS - || candidate.correspondingPropertySymbol != null + candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS || + candidate.correspondingPropertySymbol != null ) { "Unexpected field without property ${candidate.fqNameWhenAvailable}" } @@ -267,6 +311,17 @@ class ExportModelGenerator( } } + return ExportedClassDeclarationsInfo( + members, + nestedClasses + ) + } + + private fun exportClass( + klass: IrClass, + members: List, + nestedClasses: List + ): ExportedDeclaration { val typeParameters = klass.typeParameters.map { it.name.identifier } // TODO: Handle non-exported super types @@ -317,7 +372,11 @@ class ExportModelGenerator( ) } - private fun exportAsEnumMember(candidate: IrDeclarationWithName, enumEntries: List): ExportedDeclaration? { + private fun exportAsEnumMember( + candidate: IrDeclarationWithName, + enumEntriesToOrdinal: Map + ): ExportedDeclaration? { + val enumEntries = enumEntriesToOrdinal.keys return when (candidate) { is IrProperty -> { if (candidate.isEnumFakeOverriddenDeclaration(context)) { @@ -326,9 +385,8 @@ class ExportModelGenerator( .map { it.getExportedIdentifier() } .map { ExportedType.LiteralType.StringLiteralType(it) } .reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) } - "ordinal" -> enumEntries - .map { enumEntries.indexOf(it) } - .map { ExportedType.LiteralType.NumberLiteralType(it) } + "ordinal" -> enumEntriesToOrdinal + .map { (_, ordinal) -> ExportedType.LiteralType.NumberLiteralType(ordinal) } .reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) } else -> null } @@ -341,7 +399,7 @@ class ExportModelGenerator( is IrField -> { if (candidate.origin == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) { - exportEnumEntry(candidate, enumEntries) + exportEnumEntry(candidate, enumEntriesToOrdinal) } else { null } @@ -490,6 +548,14 @@ sealed class Exportability { class Prohibited(val reason: String) : Exportability() } +private class ExportedClassDeclarationsInfo( + val members: List, + val nestedClasses: List +) { + operator fun component1() = members + operator fun component2() = nestedClasses +} + private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true