Enums
This commit is contained in:
committed by
Anton Bannykh
parent
3165556c6b
commit
d76bfdf860
@@ -158,9 +158,6 @@ class JsIrBackendContext(
|
||||
private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
|
||||
private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
|
||||
|
||||
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
|
||||
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
|
||||
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
|
||||
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
|
||||
|
||||
val intrinsics = JsIntrinsics(irBuiltIns, this)
|
||||
|
||||
@@ -150,10 +150,45 @@ private val enumClassConstructorLoweringPhase = makeJsModulePhase(
|
||||
description = "Transform Enum Class into regular Class"
|
||||
)
|
||||
|
||||
private val enumClassLoweringPhase = makeJsModulePhase(
|
||||
::EnumClassLowering,
|
||||
name = "EnumClassLowering",
|
||||
description = "Transform Enum Class into regular Class",
|
||||
private val enumClassConstructorBodyLoweringPhase = makeJsModulePhase(
|
||||
::EnumClassConstructorBodyTransformer,
|
||||
name = "EnumClassConstructorBodyLowering",
|
||||
description = "Transform Enum Class into regular Class"
|
||||
)
|
||||
|
||||
|
||||
private val enumEntryInstancesLoweringPhase = makeJsModulePhase(
|
||||
::EnumEntryInstancesLowering,
|
||||
name = "EnumEntryInstancesLowering",
|
||||
description = "Create instance variable for each enum entry initialized with `null`",
|
||||
prerequisite = setOf(enumClassConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
private val enumEntryInstancesBodyLoweringPhase = makeJsModulePhase(
|
||||
::EnumEntryInstancesBodyLowering,
|
||||
name = "EnumEntryInstancesBodyLowering",
|
||||
description = "Insert enum entry field initialization into correxposnding class constructors",
|
||||
prerequisite = setOf(enumEntryInstancesLoweringPhase)
|
||||
)
|
||||
|
||||
private val enumClassCreateInitializerLoweringPhase = makeJsModulePhase(
|
||||
::EnumClassCreateInitializerLowering,
|
||||
name = "EnumClassCreateInitializerLowering",
|
||||
description = "Create initializer for enum entries",
|
||||
prerequisite = setOf(enumClassConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
private val enumEntryCreateGetInstancesFunsLoweringPhase = makeJsModulePhase(
|
||||
::EnumEntryCreateGetInstancesFunsLowering,
|
||||
name = "EnumEntryCreateGetInstancesFunsLowering",
|
||||
description = "Create enumEntry_getInstance functions",
|
||||
prerequisite = setOf(enumClassConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
private val enumSyntheticFunsLoweringPhase = makeJsModulePhase(
|
||||
::EnumSyntheticFunctionsLowering,
|
||||
name = "EnumSyntheticFunctionsLowering",
|
||||
description = "Implement `valueOf` and `values`",
|
||||
prerequisite = setOf(enumClassConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
@@ -161,7 +196,14 @@ private val enumUsageLoweringPhase = makeJsModulePhase(
|
||||
::EnumUsageLowering,
|
||||
name = "EnumUsageLowering",
|
||||
description = "Replace enum access with invocation of corresponding function",
|
||||
prerequisite = setOf(enumClassLoweringPhase)
|
||||
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
|
||||
)
|
||||
|
||||
private val enumEntryRemovalLoweringPhase = makeJsModulePhase(
|
||||
::EnumClassRemoveEntriesLowering,
|
||||
name = "EnumEntryRemovalLowering",
|
||||
description = "Replace enum entry with corresponding class",
|
||||
prerequisite = setOf(enumUsageLoweringPhase)
|
||||
)
|
||||
|
||||
private val sharedVariablesLoweringPhase = makeJsModulePhase(
|
||||
@@ -469,6 +511,7 @@ val jsPhases = namedIrModulePhase(
|
||||
lateinitUsageLoweringPhase then
|
||||
tailrecLoweringPhase then
|
||||
enumClassConstructorLoweringPhase then
|
||||
enumClassConstructorBodyLoweringPhase then
|
||||
sharedVariablesLoweringPhase then
|
||||
localDelegatedPropertiesLoweringPhase then
|
||||
localDeclarationsLoweringPhase then
|
||||
@@ -483,8 +526,13 @@ val jsPhases = namedIrModulePhase(
|
||||
initializersLoweringPhase then
|
||||
initializersCleanupLoweringPhase then
|
||||
// Common prefix ends
|
||||
enumClassLoweringPhase then
|
||||
enumEntryInstancesLoweringPhase then
|
||||
enumEntryInstancesBodyLoweringPhase then
|
||||
enumClassCreateInitializerLoweringPhase then
|
||||
enumEntryCreateGetInstancesFunsLoweringPhase then
|
||||
enumSyntheticFunsLoweringPhase then
|
||||
enumUsageLoweringPhase then
|
||||
enumEntryRemovalLoweringPhase then
|
||||
suspendFunctionsLoweringPhase then
|
||||
returnableBlockLoweringPhase then
|
||||
forLoopsLoweringPhase then
|
||||
|
||||
@@ -19,4 +19,12 @@ class JsMapping : DefaultMapping() {
|
||||
val objectToInstanceField = newMapping<IrClass, IrField>()
|
||||
val classToSyntheticPrimaryConstructor = newMapping<IrClass, IrConstructor>()
|
||||
val privateMemberToCorrespondingStatic = newMapping<IrFunction, IrSimpleFunction>()
|
||||
|
||||
val enumEntryToGetInstanceFun = newMapping<IrEnumEntry, IrSimpleFunction>()
|
||||
val enumEntryToInstanceField = newMapping<IrEnumEntry, IrField>()
|
||||
val enumConstructorToNewConstructor = newMapping<IrConstructor, IrConstructor>()
|
||||
val enumClassToCorrespondingEnumEntry = newMapping<IrClass, IrEnumEntry>()
|
||||
val enumConstructorOldToNewValueParameters = newMapping<IrValueDeclaration, IrValueParameter>()
|
||||
val enumEntryToCorrespondingField = newMapping<IrEnumEntry, IrField>()
|
||||
val enumClassToInitEntryInstancesFun = newMapping<IrClass, IrSimpleFunction>()
|
||||
}
|
||||
+339
-273
@@ -5,16 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
|
||||
import org.jetbrains.kotlin.backend.common.getOrPut
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
|
||||
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.backend.common.lower.parents
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
|
||||
@@ -26,11 +29,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -40,11 +39,12 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import java.util.*
|
||||
|
||||
class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
|
||||
val enumEntry = expression.symbol.owner
|
||||
val klass = enumEntry.parent as IrClass
|
||||
@@ -54,7 +54,7 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
}
|
||||
|
||||
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
|
||||
context.enumEntryExternalToInstanceField.getOrPut(enumEntry.symbol) { createFieldForEntry(enumEntry, klass) }.let {
|
||||
context.mapping.enumEntryToInstanceField.getOrPut(enumEntry) { createFieldForEntry(enumEntry, klass) }.let {
|
||||
JsIrBuilder.buildGetField(it.symbol, classAsReceiver(klass), null, klass.defaultType)
|
||||
}
|
||||
|
||||
@@ -80,25 +80,7 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
}
|
||||
|
||||
private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
|
||||
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
|
||||
JsIrBuilder.buildFunction(
|
||||
createEntryAccessorName(klass.name.identifier, enumEntry),
|
||||
returnType = enumEntry.getType(klass),
|
||||
parent = klass
|
||||
)
|
||||
}.run { JsIrBuilder.buildCall(symbol) }
|
||||
}
|
||||
|
||||
class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.transformDeclarationsFlat { declaration ->
|
||||
if (declaration is IrClass && declaration.isEnumClass &&
|
||||
!declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
|
||||
) {
|
||||
EnumClassTransformer(context, declaration).transform()
|
||||
} else null
|
||||
}
|
||||
}
|
||||
enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(symbol) }
|
||||
}
|
||||
|
||||
|
||||
@@ -107,52 +89,30 @@ private fun createEntryAccessorName(enumName: String, enumEntry: IrEnumEntry) =
|
||||
|
||||
private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType
|
||||
|
||||
class EnumClassConstructorLowering(val context: CommonBackendContext) : DeclarationContainerLoweringPass {
|
||||
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||
irDeclarationContainer.transformDeclarationsFlat { declaration ->
|
||||
if (declaration is IrClass && declaration.isEnumClass &&
|
||||
!declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
|
||||
) {
|
||||
EnumClassConstructorTransformer(context, declaration).transform()
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should be applied recursively
|
||||
class EnumClassConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
|
||||
|
||||
class EnumClassConstructorTransformer(val context: CommonBackendContext, private val irClass: IrClass) {
|
||||
private val builder = context.createIrBuilder(irClass.symbol)
|
||||
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructor>()
|
||||
private var IrConstructor.newConstructor by context.mapping.enumConstructorToNewConstructor
|
||||
private var IrClass.correspondingEntry by context.mapping.enumClassToCorrespondingEnumEntry
|
||||
private var IrValueDeclaration.valueParameter by context.mapping.enumConstructorOldToNewValueParameters
|
||||
|
||||
fun transform(): List<IrDeclaration> {
|
||||
// Add `name` and `ordinal` parameters to enum class constructors
|
||||
lowerEnumConstructorsSignature()
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
(declaration.parent as? IrClass)?.let { irClass ->
|
||||
if (!irClass.isEnumClass || irClass.descriptor.isExpect || irClass.isEffectivelyExternal()) return null
|
||||
|
||||
// Pass these parameters to delegating constructor calls
|
||||
lowerEnumConstructorsBody()
|
||||
|
||||
// The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones.
|
||||
fixReferencesToConstructorParameters()
|
||||
|
||||
// Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s.
|
||||
// Add `name` and `ordinal` parameters.
|
||||
lowerEnumEntryClassConstructors()
|
||||
|
||||
// Lower `IrEnumConstructorCall`s to corresponding `IrCall`s.
|
||||
// Add `name` and `ordinal` constant parameters only for calls to the "enum class" constructors ("enum entry class" constructors
|
||||
// already delegate these parameters)
|
||||
lowerEnumEntryInitializerExpression()
|
||||
|
||||
return listOf(irClass)
|
||||
}
|
||||
|
||||
private fun lowerEnumConstructorsSignature() {
|
||||
irClass.declarations.transform { declaration ->
|
||||
if (declaration is IrConstructor) {
|
||||
transformEnumConstructor(declaration, irClass)
|
||||
} else
|
||||
declaration
|
||||
// Add `name` and `ordinal` parameters to enum class constructors
|
||||
return listOf(transformEnumConstructor(declaration, irClass))
|
||||
}
|
||||
|
||||
if (declaration is IrEnumEntry) {
|
||||
declaration.correspondingClass?.let { klass ->
|
||||
klass.correspondingEntry = declaration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun transformEnumConstructor(enumConstructor: IrConstructor, enumClass: IrClass): IrConstructor {
|
||||
@@ -177,17 +137,94 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
|
||||
valueParameters += JsIrBuilder.buildValueParameter("name", 0, context.irBuiltIns.stringType).also { it.parent = this }
|
||||
valueParameters += JsIrBuilder.buildValueParameter("ordinal", 1, context.irBuiltIns.intType).also { it.parent = this }
|
||||
copyParameterDeclarationsFrom(enumConstructor)
|
||||
body = enumConstructor.body
|
||||
loweredEnumConstructors[enumConstructor.symbol] = this
|
||||
|
||||
this.acceptVoid(PatchDeclarationParentsVisitor(enumClass))
|
||||
val newConstructor = this
|
||||
enumConstructor.newConstructor = this
|
||||
|
||||
enumConstructor.body?.let { oldBody ->
|
||||
body = IrBlockBodyImpl(oldBody.startOffset, oldBody.endOffset) {
|
||||
statements += (oldBody as IrBlockBody).statements
|
||||
|
||||
context.fixReferencesToConstructorParameters(enumClass, this)
|
||||
|
||||
acceptVoid(PatchDeclarationParentsVisitor(enumClass))
|
||||
|
||||
body = this
|
||||
}
|
||||
}
|
||||
|
||||
// TODO except for `fixReferencesToConstructorParameters` this code seems to be obsolete
|
||||
val oldParameters = enumConstructor.valueParameters
|
||||
val newParameters = valueParameters
|
||||
oldParameters.forEach { old ->
|
||||
// TODO Match by index?
|
||||
val new = newParameters.single { it.name == old.name }
|
||||
old.valueParameter = new
|
||||
|
||||
old.defaultValue?.let { default ->
|
||||
new.defaultValue = IrExpressionBodyImpl(default.startOffset, default.endOffset) {
|
||||
expression = default.expression
|
||||
expression.patchDeclarationParents(newConstructor)
|
||||
context.fixReferencesToConstructorParameters(enumClass, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones.
|
||||
private fun JsCommonBackendContext.fixReferencesToConstructorParameters(irClass: IrClass, body: IrBody) {
|
||||
body.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
private val builder = createIrBuilder(irClass.symbol)
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
mapping.enumConstructorOldToNewValueParameters[expression.symbol.owner]?.let {
|
||||
return builder.irGet(it)
|
||||
}
|
||||
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class EnumClassConstructorBodyTransformer(val context: JsCommonBackendContext) : BodyLoweringPass {
|
||||
|
||||
private var IrConstructor.newConstructor by context.mapping.enumConstructorToNewConstructor
|
||||
private var IrClass.correspondingEntry by context.mapping.enumClassToCorrespondingEnumEntry
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
|
||||
(container.parent as? IrClass)?.let { irClass ->
|
||||
|
||||
// TODO Don't apply to everything
|
||||
context.fixReferencesToConstructorParameters(irClass, irBody)
|
||||
|
||||
if (container is IrConstructor) {
|
||||
|
||||
if (irClass.goodEnum) {
|
||||
// Pass new parameters to delegating constructor calls
|
||||
lowerEnumConstructorsBody(container)
|
||||
}
|
||||
|
||||
irClass.correspondingEntry?.let { enumEntry ->
|
||||
// Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s.
|
||||
// Add `name` and `ordinal` parameters.
|
||||
lowerEnumEntryClassConstructors(irClass, enumEntry, container)
|
||||
}
|
||||
}
|
||||
|
||||
if (container is IrEnumEntry) {
|
||||
// Lower `IrEnumConstructorCall`s to corresponding `IrCall`s.
|
||||
// Add `name` and `ordinal` constant parameters only for calls to the "enum class" constructors ("enum entry class" constructors
|
||||
// already delegate these parameters)
|
||||
lowerEnumEntryInitializerExpression(irClass, container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerEnumConstructorsBody() {
|
||||
irClass.declarations.filterIsInstance<IrConstructor>().forEach {
|
||||
IrEnumClassConstructorTransformer(it).transformBody()
|
||||
}
|
||||
private fun lowerEnumConstructorsBody(constructor: IrConstructor) {
|
||||
IrEnumClassConstructorTransformer(constructor).transformBody()
|
||||
}
|
||||
|
||||
private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() {
|
||||
@@ -205,11 +242,7 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
var delegatingConstructor = expression.symbol.owner
|
||||
val constructorWasTransformed = delegatingConstructor.symbol in loweredEnumConstructors
|
||||
|
||||
if (constructorWasTransformed)
|
||||
delegatingConstructor = loweredEnumConstructors[delegatingConstructor.symbol]!!
|
||||
val delegatingConstructor = expression.symbol.owner. let { it.newConstructor ?: it }
|
||||
|
||||
return builder.irDelegatingConstructorCall(delegatingConstructor).apply {
|
||||
var valueArgIdx = 0
|
||||
@@ -223,41 +256,25 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixReferencesToConstructorParameters() {
|
||||
val fromOldToNewParameter = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
|
||||
|
||||
loweredEnumConstructors.forEach { (oldCtorSymbol, newCtor) ->
|
||||
val oldParameters = oldCtorSymbol.owner.valueParameters
|
||||
val newParameters = newCtor.valueParameters
|
||||
|
||||
oldParameters.forEach { old ->
|
||||
fromOldToNewParameter[old.symbol] = newParameters.single { it.name == old.name }
|
||||
}
|
||||
}
|
||||
|
||||
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
fromOldToNewParameter[expression.symbol]?.let {
|
||||
return builder.irGet(it)
|
||||
}
|
||||
|
||||
return super.visitGetValue(expression)
|
||||
}
|
||||
})
|
||||
private fun lowerEnumEntryClassConstructors(irClass: IrClass, entry: IrEnumEntry, constructor: IrConstructor) {
|
||||
constructor.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(irClass, entry, true))
|
||||
}
|
||||
|
||||
|
||||
private fun lowerEnumEntryClassConstructors() {
|
||||
for (entry in enumEntries) {
|
||||
entry.correspondingClass?.constructors?.forEach {
|
||||
it.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(entry, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class IrEnumEntryClassConstructorTransformer(val entry: IrEnumEntry, val isInsideConstructor: Boolean) :
|
||||
private inner class IrEnumEntryClassConstructorTransformer(
|
||||
val irClass: IrClass,
|
||||
val entry: IrEnumEntry,
|
||||
val isInsideConstructor: Boolean
|
||||
) :
|
||||
IrElementTransformerVoid() {
|
||||
|
||||
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
|
||||
private val builder = context.createIrBuilder(irClass.symbol)
|
||||
|
||||
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
|
||||
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
|
||||
|
||||
private fun buildConstructorCall(constructor: IrConstructor) =
|
||||
if (isInsideConstructor)
|
||||
builder.irDelegatingConstructorCall(constructor)
|
||||
@@ -266,11 +283,11 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
|
||||
|
||||
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
|
||||
var constructor = expression.symbol.owner
|
||||
val constructorWasTransformed = constructor.symbol in loweredEnumConstructors
|
||||
val constructorWasTransformed = constructor.newConstructor != null
|
||||
|
||||
// Enum entry class constructors are not transformed
|
||||
if (constructorWasTransformed)
|
||||
constructor = loweredEnumConstructors[constructor.symbol]!!
|
||||
constructor = constructor.newConstructor!!
|
||||
|
||||
return buildConstructorCall(constructor).apply {
|
||||
var valueArgIdx = 0
|
||||
@@ -287,64 +304,209 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerEnumEntryInitializerExpression() {
|
||||
for (entry in enumEntries) {
|
||||
entry.initializerExpression =
|
||||
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(entry, false), null)
|
||||
}
|
||||
private fun lowerEnumEntryInitializerExpression(irClass: IrClass, entry: IrEnumEntry) {
|
||||
entry.initializerExpression =
|
||||
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(irClass, entry, false), null)
|
||||
}
|
||||
|
||||
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
|
||||
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
|
||||
private val IrClass.goodEnum: Boolean
|
||||
get() = isEnumClass && !descriptor.isExpect && !isEffectivelyExternal()
|
||||
|
||||
class EnumClassTransformer(val context: JsIrBackendContext, private val irClass: IrClass) {
|
||||
private val builder = context.createIrBuilder(irClass.symbol)
|
||||
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
private val enumName = irClass.name.identifier
|
||||
private val throwISESymbol = context.throwISEsymbol
|
||||
class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
|
||||
|
||||
fun transform(): List<IrDeclaration> {
|
||||
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
|
||||
|
||||
// Create instance variable for each enum entry initialized with `null`
|
||||
val entryInstances = createEnumEntryInstanceVariables()
|
||||
|
||||
// Create boolean flag that indicates if entry instances were initialized.
|
||||
val entryInstancesInitializedVar = createEntryInstancesInitializedVar()
|
||||
|
||||
// Create function that initializes all enum entry instances using `IrEnumEntry.initializationExpression`.
|
||||
// It should be called on the first `IrGetEnumValue`, consecutive calls to this function will do nothing.
|
||||
val initEntryInstancesFun = createInitEntryInstancesFun(entryInstancesInitializedVar, entryInstances)
|
||||
|
||||
// Create entry instance getters. These are used to lower `IrGetEnumValue`.
|
||||
val entryGetInstanceFuns = createGetEntryInstanceFuns(initEntryInstancesFun, entryInstances)
|
||||
|
||||
// Create body for `values` and `valueOf` functions
|
||||
lowerSyntheticFunctions()
|
||||
|
||||
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
|
||||
replaceIrEntriesWithCorrespondingClasses()
|
||||
|
||||
return listOf(irClass) + entryInstances + listOf(entryInstancesInitializedVar, initEntryInstancesFun) + entryGetInstanceFuns
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrEnumEntry) {
|
||||
val irClass = declaration.parentAsClass
|
||||
if (irClass.goodEnum) {
|
||||
// Create instance variable for each enum entry initialized with `null`
|
||||
return listOf(declaration, createEnumEntryInstanceVariable(irClass, declaration))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createEnumEntryInstanceVariable(irClass: IrClass, enumEntry: IrEnumEntry): IrField {
|
||||
val enumName = irClass.name.identifier
|
||||
|
||||
private fun createEnumValueOfBody(): IrBody {
|
||||
val valueOfFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF)
|
||||
val result = buildField {
|
||||
name = Name.identifier("${enumName}_${enumEntry.name.identifier}_instance")
|
||||
type = enumEntry.getType(irClass).makeNullable()
|
||||
isStatic = true
|
||||
}.apply {
|
||||
parent = irClass
|
||||
val builder = context.createIrBuilder(irClass.symbol)
|
||||
initializer = builder.run { irExprBody(irImplicitCast(irNull(), type)) }
|
||||
}
|
||||
|
||||
enumEntry.correspondingField = result
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class EnumEntryInstancesBodyLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
|
||||
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
if (container is IrConstructor && container.constructedClass.kind == ClassKind.ENUM_ENTRY) {
|
||||
val entryClass = container.constructedClass
|
||||
val enum = entryClass.parentAsClass
|
||||
if (enum.goodEnum) {
|
||||
val entry = enum.declarations.filterIsInstance<IrEnumEntry>().find { it.correspondingClass === entryClass }!!
|
||||
(irBody as IrBlockBody).statements.add(0, context.createIrBuilder(container.symbol).run {
|
||||
irSetField(null, entry.correspondingField!!, irGet(entryClass.thisReceiver!!))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EnumClassCreateInitializerLowering(val context: JsIrBackendContext) : DeclarationTransformer {
|
||||
|
||||
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
|
||||
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrClass && declaration.goodEnum) {
|
||||
// Create boolean flag that indicates if entry instances were initialized.
|
||||
val entryInstancesInitializedVar = createEntryInstancesInitializedVar(declaration)
|
||||
|
||||
// Create function that initializes all enum entry instances using `IrEnumEntry.initializationExpression`.
|
||||
// It should be called on the first `IrGetEnumValue`, consecutive calls to this function will do nothing.
|
||||
val initEntryInstancesFun = createInitEntryInstancesFun(declaration, entryInstancesInitializedVar)
|
||||
|
||||
declaration.initEntryInstancesFun = initEntryInstancesFun
|
||||
|
||||
// TODO Why not move to upper level?
|
||||
declaration.declarations += entryInstancesInitializedVar
|
||||
declaration.declarations += initEntryInstancesFun
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createEntryInstancesInitializedVar(irClass: IrClass): IrField = buildField {
|
||||
val enumName = irClass.name.identifier
|
||||
name = Name.identifier("${enumName}_entriesInitialized")
|
||||
type = context.irBuiltIns.booleanType
|
||||
isStatic = true
|
||||
}.apply {
|
||||
parent = irClass
|
||||
val builder = context.createIrBuilder(irClass.symbol)
|
||||
initializer = builder.run { irExprBody(irBoolean(false)) }
|
||||
}
|
||||
|
||||
private fun createInitEntryInstancesFun(irClass: IrClass, entryInstancesInitializedField: IrField): IrSimpleFunction =
|
||||
buildFunction(irClass, "${irClass.name.identifier}_initEntries", context.irBuiltIns.unitType).also {
|
||||
it.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||
statements += context.createIrBuilder(it.symbol).irBlockBody(it) {
|
||||
+irIfThen(irGetField(null, entryInstancesInitializedField), irReturnUnit())
|
||||
+irSetField(null, entryInstancesInitializedField, irBoolean(true))
|
||||
|
||||
irClass.declarations.filterIsInstance<IrEnumEntry>().forEach { entry ->
|
||||
entry.correspondingField?.let { instanceField ->
|
||||
+irSetField(null, instanceField, entry.initializerExpression!!.expression)
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
// entry.initializerExpression can have local declarations
|
||||
it.acceptVoid(PatchDeclarationParentsVisitor(irClass))
|
||||
}.statements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFunction(
|
||||
irClass: IrClass,
|
||||
name: String,
|
||||
returnType: IrType
|
||||
) = JsIrBuilder.buildFunction(name, returnType, irClass)
|
||||
|
||||
class EnumEntryCreateGetInstancesFunsLowering(val context: JsIrBackendContext): DeclarationTransformer {
|
||||
|
||||
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
|
||||
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrEnumEntry) {
|
||||
val irClass = declaration.parentAsClass
|
||||
if (irClass.goodEnum) {
|
||||
// Create entry instance getters. These are used to lower `IrGetEnumValue`.
|
||||
val entryGetInstanceFun = createGetEntryInstanceFun(irClass, declaration, irClass.initEntryInstancesFun!!)
|
||||
|
||||
// TODO prettify
|
||||
entryGetInstanceFun.parent = irClass.parent
|
||||
(irClass.parent as IrDeclarationContainer).declarations += entryGetInstanceFun
|
||||
|
||||
return listOf(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createGetEntryInstanceFun(irClass: IrClass, enumEntry: IrEnumEntry, initEntryInstancesFun: IrSimpleFunction): IrSimpleFunction {
|
||||
|
||||
return context.mapping.enumEntryToGetInstanceFun.getOrPut(enumEntry) { buildFunction(irClass, createEntryAccessorName(irClass.name.identifier, enumEntry), enumEntry.getType(irClass)) }
|
||||
.also {
|
||||
it.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||
statements += context.createIrBuilder(it.symbol).irBlockBody(it) {
|
||||
+irCall(initEntryInstancesFun)
|
||||
+irReturn(irGetField(null, enumEntry.correspondingField!!))
|
||||
}.statements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext): DeclarationTransformer {
|
||||
|
||||
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrSimpleFunction) {
|
||||
(declaration.body as? IrSyntheticBody)?.let { body ->
|
||||
val kind = body.kind
|
||||
|
||||
declaration.parents.filterIsInstance<IrClass>().firstOrNull { it.goodEnum }?.let { irClass ->
|
||||
declaration.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||
statements += when (kind) {
|
||||
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody(declaration, irClass)
|
||||
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody(declaration, irClass)
|
||||
}.statements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private val throwISESymbol = context.throwISEsymbol
|
||||
|
||||
// TODO cache
|
||||
private val IrClass.enumEntries: List<IrEnumEntry>
|
||||
get() = declarations.filterIsInstance<IrEnumEntry>()
|
||||
|
||||
private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody {
|
||||
val nameParameter = valueOfFun.valueParameters[0]
|
||||
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
|
||||
|
||||
return context.createIrBuilder(valueOfFun.symbol).run {
|
||||
irBlockBody {
|
||||
+irReturn(
|
||||
irWhen(
|
||||
irClass.defaultType,
|
||||
enumEntries.map {
|
||||
irClass.enumEntries.map {
|
||||
irBranch(
|
||||
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(entryInstanceToFunction[it.symbol]!!)
|
||||
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(it.getInstanceFun!!)
|
||||
)
|
||||
} + irElseBranch(irCall(throwISESymbol))
|
||||
)
|
||||
@@ -353,131 +515,35 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEnumValuesBody(): IrBody {
|
||||
val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES)
|
||||
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
|
||||
private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
|
||||
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
|
||||
|
||||
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, context.intrinsics.arrayLiteral).apply {
|
||||
putValueArgument(0, irVararg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody {
|
||||
val backendContext = context
|
||||
return context.createIrBuilder(valuesFun.symbol).run {
|
||||
irBlockBody {
|
||||
+irReturn(
|
||||
enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) }
|
||||
irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
|
||||
.toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun lowerEnumEntryClassConstructors(entryInstances: List<IrVariable>) {
|
||||
for ((entry, instance) in enumEntries.zip(entryInstances)) {
|
||||
entry.correspondingClass?.constructors?.forEach {
|
||||
|
||||
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body
|
||||
(it.body as? IrBlockBody)?.apply {
|
||||
statements.add(0, context.createIrBuilder(it.symbol).run {
|
||||
irSetVar(instance.symbol, irGet(entry.correspondingClass!!.thisReceiver!!))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEnumEntryInstanceVariables() = enumEntries.map { enumEntry ->
|
||||
val result = buildField {
|
||||
name = Name.identifier("${enumName}_${enumEntry.name.identifier}_instance")
|
||||
type = enumEntry.getType(irClass).makeNullable()
|
||||
isStatic = true
|
||||
}.apply {
|
||||
parent = irClass
|
||||
initializer = builder.run { irExprBody(irImplicitCast(irNull(), type)) }
|
||||
}
|
||||
|
||||
enumEntry.correspondingClass?.constructors?.forEach {
|
||||
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body
|
||||
(it.body as? IrBlockBody)?.apply {
|
||||
statements.add(0, context.createIrBuilder(it.symbol).run {
|
||||
irSetField(null, result, irGet(enumEntry.correspondingClass!!.thisReceiver!!))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
private fun replaceIrEntriesWithCorrespondingClasses() {
|
||||
irClass.transformDeclarationsFlat {
|
||||
listOfNotNull(if (it is IrEnumEntry) it.correspondingClass else it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerSyntheticFunctions() {
|
||||
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitSyntheticBody(body: IrSyntheticBody): IrBody {
|
||||
return when (body.kind) {
|
||||
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody()
|
||||
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun createGetEntryInstanceFuns(
|
||||
initEntryInstancesFun: IrSimpleFunction,
|
||||
entryInstances: List<IrField>
|
||||
) = enumEntries.mapIndexed { index, enumEntry ->
|
||||
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
|
||||
buildFunction(createEntryAccessorName(enumName, enumEntry), enumEntry.getType(irClass))
|
||||
}.apply {
|
||||
body = context.createIrBuilder(symbol).irBlockBody(this) {
|
||||
+irCall(initEntryInstancesFun)
|
||||
+irReturn(irGetField(null, entryInstances[index]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createInitEntryInstancesFun(
|
||||
entryInstancesInitializedField: IrField,
|
||||
entryInstances: List<IrField>
|
||||
) = buildFunction("${enumName}_initEntries") {
|
||||
+irIfThen(irGetField(null, entryInstancesInitializedField), irReturnUnit())
|
||||
+irSetField(null, entryInstancesInitializedField, irBoolean(true))
|
||||
for ((entry, instanceField) in enumEntries.zip(entryInstances)) {
|
||||
+irSetField(null, instanceField, entry.initializerExpression!!.expression)
|
||||
}
|
||||
}.also {
|
||||
// entry.initializerExpression can have local declarations
|
||||
it.acceptVoid(PatchDeclarationParentsVisitor(irClass))
|
||||
}
|
||||
|
||||
private fun createEntryInstancesInitializedVar(): IrField = buildField {
|
||||
name = Name.identifier("${enumName}_entriesInitialized")
|
||||
type = context.irBuiltIns.booleanType
|
||||
isStatic = true
|
||||
}.apply {
|
||||
parent = irClass
|
||||
initializer = builder.run { irExprBody(irBoolean(false)) }
|
||||
}
|
||||
|
||||
|
||||
private fun findFunctionDescriptorForMemberWithSyntheticBodyKind(kind: IrSyntheticBodyKind): IrFunction =
|
||||
irClass.declarations.asSequence().filterIsInstance<IrFunction>()
|
||||
.first {
|
||||
it.body.let { body ->
|
||||
body is IrSyntheticBody && body.kind == kind
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFunction(
|
||||
name: String,
|
||||
returnType: IrType = context.irBuiltIns.unitType
|
||||
) = JsIrBuilder.buildFunction(name, returnType, irClass)
|
||||
|
||||
private fun buildFunction(
|
||||
name: String,
|
||||
returnType: IrType = context.irBuiltIns.unitType,
|
||||
bodyBuilder: IrBlockBodyBuilder.() -> Unit
|
||||
) = JsIrBuilder.buildFunction(name, returnType, irClass).also {
|
||||
it.body = context.createIrBuilder(it.symbol).irBlockBody(it, bodyBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
// Should be applied recursively
|
||||
class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
|
||||
if (declaration is IrEnumEntry && !declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()) {
|
||||
return listOfNotNull(declaration.correspondingClass)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user