[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>
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.util.OperatorNameConventions
internal val functionReferencePhase = makeIrFilePhase(
::FunctionReferenceLowering,
@@ -105,6 +105,9 @@ class WasmBackendContext(
val wasmSymbols: WasmSymbols = WasmSymbols(this@WasmBackendContext, symbolTable)
override val reflectionSymbols: ReflectionSymbols get() = wasmSymbols.reflectionSymbols
override val enumEntries = wasmSymbols.enumEntries
override val createEnumEntries = wasmSymbols.createEnumEntries
override val propertyLazyInitialization: PropertyLazyInitialization =
PropertyLazyInitialization(enabled = propertyLazyInitialization, eagerInitialization = wasmSymbols.eagerInitialization)
@@ -207,10 +207,14 @@ private val enumEntryCreateGetInstancesFunsLoweringPhase = makeWasmModulePhase(
)
private val enumSyntheticFunsLoweringPhase = makeWasmModulePhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase, enumClassCreateInitializerLoweringPhase)
{ EnumSyntheticFunctionsAndPropertiesLowering(it, syntheticFieldsShouldBeReinitialized = true) },
name = "EnumSyntheticFunctionsAndPropertiesLowering",
description = "Implement `valueOf`, `values` and `entries`",
prerequisite = setOf(
enumClassConstructorLoweringPhase,
enumClassCreateInitializerLoweringPhase,
enumEntryCreateGetInstancesFunsLoweringPhase
)
)
private val enumUsageLoweringPhase = makeWasmModulePhase(
@@ -574,6 +578,11 @@ val wasmPhases = NamedCompilerPhase(
enumClassConstructorLoweringPhase then
enumClassConstructorBodyLoweringPhase then
enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase then
enumEntryCreateGetInstancesFunsLoweringPhase then
enumSyntheticFunsLoweringPhase then
sharedVariablesLoweringPhase then
propertyReferenceLowering then
@@ -596,11 +605,6 @@ val wasmPhases = NamedCompilerPhase(
jsInteropFunctionsLowering then
jsInteropFunctionCallsLowering then
enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase then
enumEntryCreateGetInstancesFunsLoweringPhase then
enumSyntheticFunsLoweringPhase then
enumUsageLoweringPhase then
enumEntryRemovalLoweringPhase then
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -35,6 +36,8 @@ class WasmSymbols(
private val kotlinTopLevelPackage: PackageViewDescriptor =
context.module.getPackage(FqName("kotlin"))
private val enumsInternalPackage: PackageViewDescriptor =
context.module.getPackage(FqName("kotlin.enums"))
private val wasmInternalPackage: PackageViewDescriptor =
context.module.getPackage(FqName("kotlin.wasm.internal"))
private val collectionsPackage: PackageViewDescriptor =
@@ -100,6 +103,11 @@ class WasmSymbols(
override val returnIfSuspended =
getInternalFunction("returnIfSuspended")
val enumEntries = getIrClass(FqName.fromSegments(listOf("kotlin", "enums", "EnumEntries")))
val createEnumEntries = findFunctions(enumsInternalPackage.memberScope, Name.identifier("enumEntries"))
.find { it.valueParameters.firstOrNull()?.type?.isFunctionType == true }
.let { symbolTable.referenceSimpleFunction(it!!) }
val coroutineEmptyContinuation: IrPropertySymbol = symbolTable.referenceProperty(
getProperty(FqName.fromSegments(listOf("kotlin", "wasm", "internal", "EmptyContinuation")))
)
@@ -350,6 +350,16 @@ fun IrBuilderWithScope.irVararg(elementType: IrType, values: List<IrExpression>)
fun IrBuilderWithScope.irRawFunctionReferefence(type: IrType, symbol: IrFunctionSymbol) =
IrRawFunctionReferenceImpl(startOffset, endOffset, type, symbol)
fun IrBuilderWithScope.irFunctionReference(type: IrType, symbol: IrFunctionSymbol) =
IrFunctionReferenceImpl(
startOffset,
endOffset,
type,
symbol,
symbol.owner.typeParameters.size,
symbol.owner.valueParameters.size
)
fun IrBuilderWithScope.irTry(type: IrType, tryResult: IrExpression, catches: List<IrCatch>, finallyExpression: IrExpression?) =
IrTryImpl(startOffset, endOffset, type, tryResult, catches, finallyExpression)
+3 -2
View File
@@ -1,6 +1,7 @@
// !LANGUAGE: +EnumEntries
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: NATIVE
// IGNORE_BACKEND: JS, JVM
// IGNORE_LIGHT_ANALYSIS
// FULL_JDK
// WITH_STDLIB
enum class MyEnum {
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM_IR
// AFTER KT-53649 - TARGET_BACKEND: NATIVE
// AFTER KT-53649 - TARGET_BACKEND: NATIVE, JS_IR
// WITH_STDLIB
// MODULE: lib
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM_IR
// AFTER KT-53649 - TARGET_BACKEND: NATIVE
// AFTER KT-53649 - TARGET_BACKEND: NATIVE, JS_IR
// WITH_STDLIB
// MODULE: lib
@@ -1,5 +1,5 @@
// TARGET_BACKEND: JVM_IR
// AFTER KT-53649 - TARGET_BACKEND: NATIVE
// AFTER KT-53649 - TARGET_BACKEND: NATIVE, JS_IR
// WITH_STDLIB
// MODULE: lib
@@ -1,5 +1,4 @@
// TARGET_BACKEND: JVM_IR
// TARGET_BACKEND: NATIVE
// IGNORE_BACKEND: JS, JVM, WASM
// WITH_STDLIB
// MODULE: lib
@@ -9,7 +8,7 @@ enum class MyEnum {
Nope, OK
}
// MODULE: caller(lib)
// MODULE: main(lib)
// !LANGUAGE: +EnumEntries
// FILE: Box.kt
@OptIn(ExperimentalStdlibApi::class)
@@ -1,7 +1,6 @@
// !LANGUAGE: +EnumEntries
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: NATIVE
// IGNORE_BACKEND: JS, JVM
// WITH_STDLIB
enum class EnumWithClash {
@@ -16077,6 +16077,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/enum/enumConstructorParameterClashWithDefaults.kt");
}
@Test
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntries.kt");
}
@Test
@TestMetadata("enumEntriesMultimoduleNoMappings.kt")
public void testEnumEntriesMultimoduleNoMappings() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesMultimoduleNoMappings.kt");
}
@Test
@TestMetadata("enumEntriesNameClashes.kt")
public void testEnumEntriesNameClashes() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesNameClashes.kt");
}
@Test
@TestMetadata("enumEntryMembers.kt")
public void testEnumEntryMembers() throws Exception {
@@ -13171,6 +13171,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/enum/bigEnum.kt");
}
@TestMetadata("enumEntries.kt")
public void ignoreEnumEntries() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntries.kt");
}
@TestMetadata("enumEntriesMultimoduleNoMappings.kt")
public void ignoreEnumEntriesMultimoduleNoMappings() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesMultimoduleNoMappings.kt");
}
@TestMetadata("enumEntriesNameClashes.kt")
public void ignoreEnumEntriesNameClashes() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesNameClashes.kt");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@@ -12419,6 +12419,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/enum/enumConstructorParameterClashWithDefaults.kt");
}
@Test
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntries.kt");
}
@Test
@TestMetadata("enumEntriesMultimoduleNoMappings.kt")
public void testEnumEntriesMultimoduleNoMappings() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesMultimoduleNoMappings.kt");
}
@Test
@TestMetadata("enumEntriesNameClashes.kt")
public void testEnumEntriesNameClashes() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesNameClashes.kt");
}
@Test
@TestMetadata("enumEntryMembers.kt")
public void testEnumEntryMembers() throws Exception {
@@ -12503,6 +12503,24 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/enum/enumConstructorParameterClashWithDefaults.kt");
}
@Test
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntries.kt");
}
@Test
@TestMetadata("enumEntriesMultimoduleNoMappings.kt")
public void testEnumEntriesMultimoduleNoMappings() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesMultimoduleNoMappings.kt");
}
@Test
@TestMetadata("enumEntriesNameClashes.kt")
public void testEnumEntriesNameClashes() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesNameClashes.kt");
}
@Test
@TestMetadata("enumEntryMembers.kt")
public void testEnumEntryMembers() throws Exception {
@@ -11104,6 +11104,21 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/enum/enumConstructorParameterClashWithDefaults.kt");
}
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntries.kt");
}
@TestMetadata("enumEntriesMultimoduleNoMappings.kt")
public void testEnumEntriesMultimoduleNoMappings() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesMultimoduleNoMappings.kt");
}
@TestMetadata("enumEntriesNameClashes.kt")
public void testEnumEntriesNameClashes() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntriesNameClashes.kt");
}
@TestMetadata("enumEntryMembers.kt")
public void testEnumEntryMembers() throws Exception {
runTest("compiler/testData/codegen/box/enum/enumEntryMembers.kt");
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
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.types.defaultType
import org.jetbrains.kotlin.ir.types.typeWith
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.enums
@ExperimentalStdlibApi
@SinceKotlin("1.8")
public sealed interface EnumEntries<E : Enum<E>>
@PublishedApi
@ExperimentalStdlibApi
@SinceKotlin("1.8")
internal fun <E : Enum<E>> enumEntries(entriesProvider: () -> Array<E>): EnumEntries<E> = EnumEntriesList(entriesProvider)
@SinceKotlin("1.8")
@ExperimentalStdlibApi
private class EnumEntriesList<E : Enum<E>>(val entriesProvider: () -> Array<E>) : EnumEntries<E>