[K/JS, K/WASM] feat(Enum.entries): add support of new enum static field for JS and WASM.
This commit is contained in:
+5
@@ -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(
|
||||
|
||||
+64
-13
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user