[JS IR] Refactor export model generator

^KT-37916 fixed
^KT-44494 fixed
This commit is contained in:
Ilya Goncharov
2021-10-29 13:57:15 +03:00
committed by Space
parent 9fdb0a23cd
commit 2584f45b1c
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.keysToMap
class ExportModelGenerator( class ExportModelGenerator(
val context: JsIrBackendContext, val context: JsIrBackendContext,
@@ -58,7 +59,13 @@ class ExportModelGenerator(
return when (candidate) { return when (candidate) {
is IrSimpleFunction -> exportFunction(candidate) is IrSimpleFunction -> exportFunction(candidate)
is IrProperty -> exportProperty(candidate) is IrProperty -> exportProperty(candidate)
is IrClass -> exportClass(candidate) is IrClass -> {
if (candidate.isEnumClass) {
exportEnumClass(candidate)
} else {
exportClass(candidate)
}
}
is IrField -> null is IrField -> null
else -> error("Can't export declaration $candidate") else -> error("Can't export declaration $candidate")
} }
@@ -135,37 +142,38 @@ class ExportModelGenerator(
) )
} }
private fun exportEnumEntry(field: IrField, enumEntries: List<IrEnumEntry>): ExportedProperty { private fun exportEnumEntry(field: IrField, enumEntries: Map<IrEnumEntry, Int>): ExportedProperty {
val irEnumEntry = context.mapping.fieldToEnumEntry[field] val irEnumEntry = context.mapping.fieldToEnumEntry[field]
?: error("Unable to find enum entry for ${field.fqNameWhenAvailable}") ?: error("Unable to find enum entry for ${field.fqNameWhenAvailable}")
val parentClass = field.parent as IrClass val parentClass = field.parent as IrClass
val name = irEnumEntry.getExportedIdentifier() 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", name = "name",
type = ExportedType.LiteralType.StringLiteralType(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", name = "ordinal",
type = ExportedType.LiteralType.NumberLiteralType(ordinal), type = ExportedType.LiteralType.NumberLiteralType(ordinal),
mutable = false,
isMember = true,
isStatic = false,
isAbstract = false,
isProtected = false,
irGetter = null,
irSetter = null,
) )
val type = ExportedType.InlineInterfaceType( val type = ExportedType.InlineInterfaceType(
listOf(nameProperty, ordinalProperty) 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<IrField>()
.mapNotNull { context.mapping.fieldToEnumEntry[it] }
val enumEntriesToOrdinal: Map<IrEnumEntry, Int> =
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<ExportedDeclaration>() val members = mutableListOf<ExportedDeclaration>()
val nestedClasses = mutableListOf<ExportedClass>() val nestedClasses = mutableListOf<ExportedClass>()
val enumEntries = if (klass.isEnumClass) {
klass
.declarations
.filterIsInstance<IrField>()
.mapNotNull { context.mapping.fieldToEnumEntry[it] }
} else null
for (declaration in klass.declarations) { for (declaration in klass.declarations) {
val candidate = getExportCandidate(declaration) ?: continue val candidate = getExportCandidate(declaration) ?: continue
if (!shouldDeclarationBeExported(candidate, context)) continue if (!shouldDeclarationBeExported(candidate, context)) continue
if (enumEntries != null) { val processingResult = specialProcessing(candidate)
val enumExportedMember = exportAsEnumMember(candidate, enumEntries) if (processingResult != null) {
if (enumExportedMember != null) { members.add(processingResult)
members.add(enumExportedMember) continue
continue
}
} }
when (candidate) { when (candidate) {
@@ -256,8 +300,8 @@ class ExportModelGenerator(
is IrField -> { is IrField -> {
assert( assert(
candidate.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE || candidate.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE ||
candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS candidate.origin == IrDeclarationOrigin.FIELD_FOR_OUTER_THIS ||
|| candidate.correspondingPropertySymbol != null candidate.correspondingPropertySymbol != null
) { ) {
"Unexpected field without property ${candidate.fqNameWhenAvailable}" "Unexpected field without property ${candidate.fqNameWhenAvailable}"
} }
@@ -267,6 +311,17 @@ class ExportModelGenerator(
} }
} }
return ExportedClassDeclarationsInfo(
members,
nestedClasses
)
}
private fun exportClass(
klass: IrClass,
members: List<ExportedDeclaration>,
nestedClasses: List<ExportedClass>
): ExportedDeclaration {
val typeParameters = klass.typeParameters.map { it.name.identifier } val typeParameters = klass.typeParameters.map { it.name.identifier }
// TODO: Handle non-exported super types // TODO: Handle non-exported super types
@@ -317,7 +372,11 @@ class ExportModelGenerator(
) )
} }
private fun exportAsEnumMember(candidate: IrDeclarationWithName, enumEntries: List<IrEnumEntry>): ExportedDeclaration? { private fun exportAsEnumMember(
candidate: IrDeclarationWithName,
enumEntriesToOrdinal: Map<IrEnumEntry, Int>
): ExportedDeclaration? {
val enumEntries = enumEntriesToOrdinal.keys
return when (candidate) { return when (candidate) {
is IrProperty -> { is IrProperty -> {
if (candidate.isEnumFakeOverriddenDeclaration(context)) { if (candidate.isEnumFakeOverriddenDeclaration(context)) {
@@ -326,9 +385,8 @@ class ExportModelGenerator(
.map { it.getExportedIdentifier() } .map { it.getExportedIdentifier() }
.map { ExportedType.LiteralType.StringLiteralType(it) } .map { ExportedType.LiteralType.StringLiteralType(it) }
.reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) } .reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) }
"ordinal" -> enumEntries "ordinal" -> enumEntriesToOrdinal
.map { enumEntries.indexOf(it) } .map { (_, ordinal) -> ExportedType.LiteralType.NumberLiteralType(ordinal) }
.map { ExportedType.LiteralType.NumberLiteralType(it) }
.reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) } .reduce { acc: ExportedType, s: ExportedType -> ExportedType.UnionType(acc, s) }
else -> null else -> null
} }
@@ -341,7 +399,7 @@ class ExportModelGenerator(
is IrField -> { is IrField -> {
if (candidate.origin == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) { if (candidate.origin == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) {
exportEnumEntry(candidate, enumEntries) exportEnumEntry(candidate, enumEntriesToOrdinal)
} else { } else {
null null
} }
@@ -490,6 +548,14 @@ sealed class Exportability {
class Prohibited(val reason: String) : Exportability() class Prohibited(val reason: String) : Exportability()
} }
private class ExportedClassDeclarationsInfo(
val members: List<ExportedDeclaration>,
val nestedClasses: List<ExportedClass>
) {
operator fun component1() = members
operator fun component2() = nestedClasses
}
private val IrClassifierSymbol.isInterface private val IrClassifierSymbol.isInterface
get() = (owner as? IrClass)?.isInterface == true get() = (owner as? IrClass)?.isInterface == true