This commit is contained in:
Anton Bannykh
2019-12-25 14:40:32 +03:00
committed by Anton Bannykh
parent 3165556c6b
commit d76bfdf860
4 changed files with 401 additions and 282 deletions
@@ -158,9 +158,6 @@ class JsIrBackendContext(
private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME) private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_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 callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
val intrinsics = JsIntrinsics(irBuiltIns, this) val intrinsics = JsIntrinsics(irBuiltIns, this)
@@ -150,10 +150,45 @@ private val enumClassConstructorLoweringPhase = makeJsModulePhase(
description = "Transform Enum Class into regular Class" description = "Transform Enum Class into regular Class"
) )
private val enumClassLoweringPhase = makeJsModulePhase( private val enumClassConstructorBodyLoweringPhase = makeJsModulePhase(
::EnumClassLowering, ::EnumClassConstructorBodyTransformer,
name = "EnumClassLowering", name = "EnumClassConstructorBodyLowering",
description = "Transform Enum Class into regular Class", 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) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
@@ -161,7 +196,14 @@ private val enumUsageLoweringPhase = makeJsModulePhase(
::EnumUsageLowering, ::EnumUsageLowering,
name = "EnumUsageLowering", name = "EnumUsageLowering",
description = "Replace enum access with invocation of corresponding function", 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( private val sharedVariablesLoweringPhase = makeJsModulePhase(
@@ -469,6 +511,7 @@ val jsPhases = namedIrModulePhase(
lateinitUsageLoweringPhase then lateinitUsageLoweringPhase then
tailrecLoweringPhase then tailrecLoweringPhase then
enumClassConstructorLoweringPhase then enumClassConstructorLoweringPhase then
enumClassConstructorBodyLoweringPhase then
sharedVariablesLoweringPhase then sharedVariablesLoweringPhase then
localDelegatedPropertiesLoweringPhase then localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then localDeclarationsLoweringPhase then
@@ -483,8 +526,13 @@ val jsPhases = namedIrModulePhase(
initializersLoweringPhase then initializersLoweringPhase then
initializersCleanupLoweringPhase then initializersCleanupLoweringPhase then
// Common prefix ends // Common prefix ends
enumClassLoweringPhase then enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase then
enumEntryCreateGetInstancesFunsLoweringPhase then
enumSyntheticFunsLoweringPhase then
enumUsageLoweringPhase then enumUsageLoweringPhase then
enumEntryRemovalLoweringPhase then
suspendFunctionsLoweringPhase then suspendFunctionsLoweringPhase then
returnableBlockLoweringPhase then returnableBlockLoweringPhase then
forLoopsLoweringPhase then forLoopsLoweringPhase then
@@ -19,4 +19,12 @@ class JsMapping : DefaultMapping() {
val objectToInstanceField = newMapping<IrClass, IrField>() val objectToInstanceField = newMapping<IrClass, IrField>()
val classToSyntheticPrimaryConstructor = newMapping<IrClass, IrConstructor>() val classToSyntheticPrimaryConstructor = newMapping<IrClass, IrConstructor>()
val privateMemberToCorrespondingStatic = newMapping<IrFunction, IrSimpleFunction>() 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>()
} }
@@ -5,16 +5,19 @@
package org.jetbrains.kotlin.ir.backend.js.lower 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.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen 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.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET 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.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral 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.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.*
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.symbols.impl.IrConstructorSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType 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.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import java.util.*
class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass { class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irFile: IrFile) { private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression { override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumEntry = expression.symbol.owner val enumEntry = expression.symbol.owner
val klass = enumEntry.parent as IrClass val klass = enumEntry.parent as IrClass
@@ -54,7 +54,7 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
} }
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) = 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) 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) = private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) { enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(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
}
}
} }
@@ -107,52 +89,30 @@ private fun createEntryAccessorName(enumName: String, enumEntry: IrEnumEntry) =
private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType
class EnumClassConstructorLowering(val context: CommonBackendContext) : DeclarationContainerLoweringPass { // Should be applied recursively
override fun lower(irDeclarationContainer: IrDeclarationContainer) { class EnumClassConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration is IrClass && declaration.isEnumClass &&
!declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
) {
EnumClassConstructorTransformer(context, declaration).transform()
} else null
}
}
}
class EnumClassConstructorTransformer(val context: CommonBackendContext, private val irClass: IrClass) { private var IrConstructor.newConstructor by context.mapping.enumConstructorToNewConstructor
private val builder = context.createIrBuilder(irClass.symbol) private var IrClass.correspondingEntry by context.mapping.enumClassToCorrespondingEnumEntry
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>() private var IrValueDeclaration.valueParameter by context.mapping.enumConstructorOldToNewValueParameters
private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructor>()
fun transform(): List<IrDeclaration> { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
// Add `name` and `ordinal` parameters to enum class constructors (declaration.parent as? IrClass)?.let { irClass ->
lowerEnumConstructorsSignature() 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) { if (declaration is IrConstructor) {
transformEnumConstructor(declaration, irClass) // Add `name` and `ordinal` parameters to enum class constructors
} else return listOf(transformEnumConstructor(declaration, irClass))
declaration }
if (declaration is IrEnumEntry) {
declaration.correspondingClass?.let { klass ->
klass.correspondingEntry = declaration
}
}
} }
return null
} }
private fun transformEnumConstructor(enumConstructor: IrConstructor, enumClass: IrClass): IrConstructor { 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("name", 0, context.irBuiltIns.stringType).also { it.parent = this }
valueParameters += JsIrBuilder.buildValueParameter("ordinal", 1, context.irBuiltIns.intType).also { it.parent = this } valueParameters += JsIrBuilder.buildValueParameter("ordinal", 1, context.irBuiltIns.intType).also { it.parent = this }
copyParameterDeclarationsFrom(enumConstructor) 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() { private fun lowerEnumConstructorsBody(constructor: IrConstructor) {
irClass.declarations.filterIsInstance<IrConstructor>().forEach { IrEnumClassConstructorTransformer(constructor).transformBody()
IrEnumClassConstructorTransformer(it).transformBody()
}
} }
private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() { private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() {
@@ -205,11 +242,7 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
} }
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
var delegatingConstructor = expression.symbol.owner val delegatingConstructor = expression.symbol.owner. let { it.newConstructor ?: it }
val constructorWasTransformed = delegatingConstructor.symbol in loweredEnumConstructors
if (constructorWasTransformed)
delegatingConstructor = loweredEnumConstructors[delegatingConstructor.symbol]!!
return builder.irDelegatingConstructorCall(delegatingConstructor).apply { return builder.irDelegatingConstructorCall(delegatingConstructor).apply {
var valueArgIdx = 0 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) -> private fun lowerEnumEntryClassConstructors(irClass: IrClass, entry: IrEnumEntry, constructor: IrConstructor) {
val oldParameters = oldCtorSymbol.owner.valueParameters constructor.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(irClass, entry, true))
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 inner class IrEnumEntryClassConstructorTransformer(
private fun lowerEnumEntryClassConstructors() { val irClass: IrClass,
for (entry in enumEntries) { val entry: IrEnumEntry,
entry.correspondingClass?.constructors?.forEach { val isInsideConstructor: Boolean
it.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(entry, true)) ) :
}
}
}
private inner class IrEnumEntryClassConstructorTransformer(val entry: IrEnumEntry, val isInsideConstructor: Boolean) :
IrElementTransformerVoid() { 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) = private fun buildConstructorCall(constructor: IrConstructor) =
if (isInsideConstructor) if (isInsideConstructor)
builder.irDelegatingConstructorCall(constructor) builder.irDelegatingConstructorCall(constructor)
@@ -266,11 +283,11 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression { override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
var constructor = expression.symbol.owner var constructor = expression.symbol.owner
val constructorWasTransformed = constructor.symbol in loweredEnumConstructors val constructorWasTransformed = constructor.newConstructor != null
// Enum entry class constructors are not transformed // Enum entry class constructors are not transformed
if (constructorWasTransformed) if (constructorWasTransformed)
constructor = loweredEnumConstructors[constructor.symbol]!! constructor = constructor.newConstructor!!
return buildConstructorCall(constructor).apply { return buildConstructorCall(constructor).apply {
var valueArgIdx = 0 var valueArgIdx = 0
@@ -287,64 +304,209 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
} }
} }
private fun lowerEnumEntryInitializerExpression() { private fun lowerEnumEntryInitializerExpression(irClass: IrClass, entry: IrEnumEntry) {
for (entry in enumEntries) { entry.initializerExpression =
entry.initializerExpression = entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(irClass, entry, false), null)
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(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) { class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
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
fun transform(): List<IrDeclaration> { private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
// Create instance variable for each enum entry initialized with `null` override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
val entryInstances = createEnumEntryInstanceVariables() if (declaration is IrEnumEntry) {
val irClass = declaration.parentAsClass
// Create boolean flag that indicates if entry instances were initialized. if (irClass.goodEnum) {
val entryInstancesInitializedVar = createEntryInstancesInitializedVar() // Create instance variable for each enum entry initialized with `null`
return listOf(declaration, createEnumEntryInstanceVariable(irClass, 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(entryInstancesInitializedVar, entryInstances) return null
// 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
} }
private fun createEnumEntryInstanceVariable(irClass: IrClass, enumEntry: IrEnumEntry): IrField {
val enumName = irClass.name.identifier
private fun createEnumValueOfBody(): IrBody { val result = buildField {
val valueOfFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF) 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 nameParameter = valueOfFun.valueParameters[0]
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
return context.createIrBuilder(valueOfFun.symbol).run { return context.createIrBuilder(valueOfFun.symbol).run {
irBlockBody { irBlockBody {
+irReturn( +irReturn(
irWhen( irWhen(
irClass.defaultType, irClass.defaultType,
enumEntries.map { irClass.enumEntries.map {
irBranch( 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)) } + irElseBranch(irCall(throwISESymbol))
) )
@@ -353,131 +515,35 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
} }
} }
private fun createEnumValuesBody(): IrBody { private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES) val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
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 val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run { return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody { irBlockBody {
+irReturn( +irReturn(
enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) } irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
.toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType) .toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
) )
} }
} }
} }
}
// Should be applied recursively
private fun lowerEnumEntryClassConstructors(entryInstances: List<IrVariable>) { class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
for ((entry, instance) in enumEntries.zip(entryInstances)) { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
entry.correspondingClass?.constructors?.forEach { // 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()) {
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body return listOfNotNull(declaration.correspondingClass)
(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 { return null
// 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)
} }
} }