[JS IR BE] Fix EnumClassLowering

* make it incremental
 * fix access to enum entry from different module
 * drop ExternalEnumLowering
This commit is contained in:
Roman Artemev
2018-10-25 18:17:33 +03:00
committed by romanart
parent 7f215d3f52
commit b86e2e6dc0
4 changed files with 64 additions and 73 deletions
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -113,7 +112,8 @@ class JsIrBackendContext(
private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, () -> IrExpression>()
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
val coroutineGetContext: IrFunctionSymbol
@@ -73,7 +73,6 @@ fun compile(
moduleFragment.files.forEach { irFile -> extension.generate(irFile, context, psi2IrContext.bindingContext) }
}
ExternalEnumLowering(context).runOnFilesPostfix(moduleFragment)
MoveExternalDeclarationsToSeparatePlace().lower(moduleFragment)
ExpectDeclarationsRemoving(context).lower(moduleFragment)
CoroutineIntrinsicLowering(context).lower(moduleFragment)
@@ -8,22 +8,26 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedPropertyDescriptor
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.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
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.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
@@ -34,10 +38,43 @@ import java.util.*
class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetEnumValue(expression: IrGetEnumValue) =
context.enumEntryToGetInstanceFunction[expression.symbol]!!.invoke()
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumEntry = expression.symbol.owner
val klass = enumEntry.parent as IrClass
return if (klass.isExternal) lowerExternalEnumEntry(enumEntry, klass) else lowerEnumEntry(enumEntry, klass)
}
})
}
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.enumEntryExternalToInstanceField.getOrPut(enumEntry.symbol) { createFieldForEntry(enumEntry, klass) }.let {
JsIrBuilder.buildGetField(it.symbol, classAsReceiver(klass), null, klass.defaultType)
}
private fun classAsReceiver(irClass: IrClass): IrExpression {
val intrinsic = context.intrinsics.jsClass
return JsIrBuilder.buildCall(intrinsic, context.irBuiltIns.anyType, listOf(irClass.defaultType))
}
private fun createFieldForEntry(entry: IrEnumEntry, irClass: IrClass): IrField {
val descriptor = WrappedPropertyDescriptor()
val symbol = IrFieldSymbolImpl(descriptor)
return entry.run {
IrFieldImpl(startOffset, endOffset, origin, symbol, name, irClass.defaultType, Visibilities.PUBLIC, false, true, true).also {
descriptor.bind(it)
it.parent = irClass
irClass.declarations += it
}
}
}
private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
JsIrBuilder.buildFunction(createEntryAccessorName(klass.name.identifier, enumEntry)).also {
it.returnType = enumEntry.getType(klass)
it.parent = klass
}
}.run { JsIrBuilder.buildCall(symbol) }
}
class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass {
@@ -55,6 +92,12 @@ class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerL
}
}
private fun createEntryAccessorName(enumName: String, enumEntry: IrEnumEntry) =
"${enumName}_${enumEntry.name.identifier}_getInstance"
private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType
class EnumClassTransformer(val context: JsIrBackendContext, private val irClass: IrClass) {
private val builder = context.createIrBuilder(irClass.symbol)
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
@@ -138,8 +181,7 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
irClass.defaultType,
enumEntries.map {
irBranch(
irEquals(irString(it.name.identifier), irGet(nameParameter)),
entryInstanceToFunction[it.symbol]!!.invoke()
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(entryInstanceToFunction[it.symbol]!!)
)
} + irElseBranch(irCall(throwISESymbol))
)
@@ -163,7 +205,7 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody {
+irReturn(
enumEntries.map { entryInstanceToFunction[it.symbol]!!.invoke() }
enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) }
.toArrayLiteral(valuesFun.returnType, irClass.defaultType)
)
}
@@ -209,7 +251,7 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
}
private fun createEnumEntryInstanceVariables() = enumEntries.map { enumEntry ->
val type = enumEntry.getType().makeNullable()
val type = enumEntry.getType(irClass).makeNullable()
val name = "${enumName}_${enumEntry.name.identifier}_instance"
builder.run {
scope.createTemporaryVariable(irImplicitCast(irNull(), type), name)
@@ -237,14 +279,13 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
initEntryInstancesFun: IrSimpleFunction,
entryInstances: List<IrVariable>
) = enumEntries.mapIndexed { index, enumEntry ->
buildFunction(
name = "${enumName}_${enumEntry.name.identifier}_getInstance",
returnType = enumEntry.getType()
) {
+irCall(initEntryInstancesFun)
+irReturn(irGet(entryInstances[index]))
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
buildFunction(createEntryAccessorName(enumName, enumEntry), enumEntry.getType(irClass))
}.apply {
context.enumEntryToGetInstanceFunction[enumEntry.symbol] = { JsIrBuilder.buildCall(this@apply.symbol) }
body = context.createIrBuilder(symbol).irBlockBody(this) {
+irCall(initEntryInstancesFun)
+irReturn(irGet(entryInstances[index]))
}
}
}
@@ -365,6 +406,14 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
}
}
private fun buildFunction(
name: String,
returnType: IrType = context.irBuiltIns.unitType
) = JsIrBuilder.buildFunction(name).also {
it.returnType = returnType
it.parent = irClass
}
private fun buildFunction(
name: String,
returnType: IrType = context.irBuiltIns.unitType,
@@ -377,5 +426,4 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
private fun IrEnumEntry.getType() = (correspondingClass ?: irClass).defaultType
}
@@ -1,56 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedPropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isEnumClass
// TODO: Shouldn't it be lazy pass?
class ExternalEnumLowering(val context: JsIrBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
if (!irClass.isExternal) return
if (!irClass.isEnumClass) return
irClass.declarations.filterIsInstance<IrEnumEntry>().forEach {
val enumField = createFieldForEntry(it, irClass)
context.enumEntryToGetInstanceFunction[it.symbol] = {
JsIrBuilder.buildGetField(enumField.symbol, classAsReceiver(irClass), null, irClass.defaultType)
}
}
}
private fun classAsReceiver(irClass: IrClass): IrExpression {
val intrinsic = context.intrinsics.jsClass
return JsIrBuilder.buildCall(intrinsic, context.irBuiltIns.anyType, listOf(irClass.defaultType))
}
private fun createFieldForEntry(entry: IrEnumEntry, irClass: IrClass): IrField {
val descriptor = WrappedPropertyDescriptor()
val symbol = IrFieldSymbolImpl(descriptor)
return entry.run {
IrFieldImpl(startOffset, endOffset, origin, symbol, name, irClass.defaultType, Visibilities.DEFAULT_VISIBILITY, false, true, true ).also { f ->
descriptor.bind(f)
f.parent = irClass
irClass.declarations += f
}
}
}
}