[K/JS, K/WASM] feat(Enum.entries): add support of new enum static field for JS and WASM.

This commit is contained in:
Artem Kobzar
2022-08-29 16:04:12 +00:00
committed by Space
parent bd6aff7f6a
commit 8b826a865b
22 changed files with 231 additions and 37 deletions
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.SymbolTable
@@ -46,7 +47,11 @@ interface JsCommonBackendContext : CommonBackendContext {
val suiteFun: IrSimpleFunctionSymbol?
val testFun: IrSimpleFunctionSymbol?
val enumEntries: IrClassSymbol
val createEnumEntries: IrSimpleFunctionSymbol
fun createTestContainerFun(irFile: IrFile): IrSimpleFunction
}
// TODO: investigate if it could be removed
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -141,6 +142,7 @@ class JsIrBackendContext(
// TODO: what is more clear way reference this getter?
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
private val ENUMS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("enums"))
private val JS_POLYFILLS_PACKAGE = JS_PACKAGE_FQNAME.child(Name.identifier("polyfill"))
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
@@ -188,6 +190,11 @@ class JsIrBackendContext(
override val coroutineSymbols =
JsCommonCoroutineSymbols(symbolTable, module, this)
override val enumEntries = getIrClass(ENUMS_PACKAGE_FQNAME.child(Name.identifier("EnumEntries")))
override val createEnumEntries = getFunctions(ENUMS_PACKAGE_FQNAME.child(Name.identifier("enumEntries")))
.find { it.valueParameters.firstOrNull()?.type?.isFunctionType == true }
.let { symbolTable.referenceSimpleFunction(it!!) }
override val ir = object : Ir<JsIrBackendContext>(this, irModuleFragment) {
override val symbols = object : Symbols<JsIrBackendContext>(this@JsIrBackendContext, irBuiltIns, symbolTable) {
override val throwNullPointerException =
@@ -337,10 +337,14 @@ private val enumEntryCreateGetInstancesFunsLoweringPhase = makeDeclarationTransf
)
private val enumSyntheticFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase, enumClassCreateInitializerLoweringPhase)
{ EnumSyntheticFunctionsAndPropertiesLowering(it, supportRawFunctionReference = true) },
name = "EnumSyntheticFunctionsAndPropertiesLowering",
description = "Implement `valueOf, `values` and `entries`",
prerequisite = setOf(
enumClassConstructorLoweringPhase,
enumClassCreateInitializerLoweringPhase,
enumEntryCreateGetInstancesFunsLoweringPhase
)
)
private val enumUsageLoweringPhase = makeBodyLoweringPhase(
@@ -20,13 +20,10 @@ import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -461,10 +458,16 @@ private val IrClass.isInstantiableEnum: Boolean
private val IrDeclaration.parentEnumClassOrNull: IrClass?
get() = parents.filterIsInstance<IrClass>().firstOrNull { it.isInstantiableEnum }
class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private const val ENTRIES_FIELD_NAME = "\$ENTRIES"
class EnumSyntheticFunctionsAndPropertiesLowering(
val context: JsCommonBackendContext,
private val supportRawFunctionReference: Boolean = false,
private val syntheticFieldsShouldBeReinitialized: Boolean = false,
) : DeclarationTransformer {
private val IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
private val IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
private val IrClass.enumArrayType get() = context.irBuiltIns.arrayClass.typeWith(defaultType)
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrConstructor && declaration.isPrimary && declaration.parentEnumClassOrNull != null &&
@@ -490,7 +493,7 @@ class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : Decl
statements += when (kind) {
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody(declaration, enumClass)
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody(declaration, enumClass)
IrSyntheticBodyKind.ENUM_ENTRIES -> TODO("NOT IMPLEMENTED ON JS")
IrSyntheticBodyKind.ENUM_ENTRIES -> createEnumEntriesBody(declaration, enumClass)
}.statements
}
}
@@ -502,6 +505,56 @@ class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : Decl
private val throwISESymbol = context.ir.symbols.throwISE
private fun createEnumEntriesBody(entriesGetter: IrFunction, enumClass: IrClass): IrBlockBody {
val entriesField = enumClass.addEnumEntriesField()
return context.createIrBuilder(entriesGetter.symbol).run {
irBlockBody {
if (syntheticFieldsShouldBeReinitialized) {
+irIfThen(
irEqualsNull(irGetField(null, entriesField)),
irSetField(null, entriesField, entriesField.initializer!!.expression)
)
}
+irReturn(irGetField(null, entriesField))
}
}
}
private fun IrClass.addEnumEntriesField(): IrField {
return buildEntriesField(searchForValuesFunction())
}
private fun IrClass.searchForValuesFunction(): IrFunction {
return declarations.find { it is IrFunction && it.isStatic && it.returnType.isArray() } as IrFunction
}
private fun IrClass.buildEntriesField(entriesHelper: IrFunction): IrField = with(context) {
addField {
name = Name.identifier(ENTRIES_FIELD_NAME)
type = enumEntries.defaultType
visibility = PRIVATE
origin = IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRIES
isFinal = true
isStatic = true
}.apply {
initializer = context.createIrBuilder(symbol).run {
irExprBody(irCall(createEnumEntries).apply {
val referenceType = context.irBuiltIns.functionN(0).typeWith(enumEntries.defaultType)
putValueArgument(0, referenceFor(entriesHelper, referenceType))
})
}
}
}
private fun IrBuilderWithScope.referenceFor(function: IrFunction, type: IrType): IrDeclarationReference {
return if (supportRawFunctionReference) {
irRawFunctionReferefence(type, function.symbol)
} else {
irFunctionReference(type, function.symbol)
}
}
private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody {
val nameParameter = valueOfFun.valueParameters[0]
@@ -524,14 +577,12 @@ class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : Decl
private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody {
return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody {
val instances = irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
+irReturn(
IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFun.returnType, irClass.defaultType, instances)
)
}
irBlockBody { +irReturn(arrayOfEnumEntriesOf(irClass)) }
}
}
private fun IrBuilderWithScope.arrayOfEnumEntriesOf(enumClass: IrClass) =
irVararg(enumClass.defaultType, enumClass.enumEntries.map { irCall(it.getInstanceFun!!) })
}
private val IrClass.enumEntries: List<IrEnumEntry>