JVM IR: initialize enum entries without invokedynamic

#KT-57316 Fixed
This commit is contained in:
Alexander Udalov
2023-03-21 01:13:41 +01:00
committed by Space Team
parent 9c2c11f7e9
commit 562b27db4e
21 changed files with 31 additions and 95 deletions
@@ -14,14 +14,12 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod
import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
@@ -30,14 +28,11 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -52,7 +47,6 @@ internal val enumClassPhase = makeIrFilePhase(
)
private const val VALUES_HELPER_FUNCTION_NAME = "\$values"
private const val ENTRIES_HELPER_FUNCTION_NAME = "\$entries"
private const val ENTRIES_FIELD_NAME = "\$ENTRIES"
private class EnumClassLowering(private val context: JvmBackendContext) : ClassLoweringPass {
@@ -131,17 +125,8 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
error("The frontend must have checked if the feature is supported while emitting the IR")
}
else -> {
// Constructs the synthetic $entries() function that returns plain $VALUES without copy
val entriesHelperFunction = buildEntriesHelperFunction(valuesField)
/*
* Add synthetic $ENTRIES field and binds its initializer to
* ```
* val supplier: () -> E[] = indy LMF $entries
* $ENTRIES = EnumEntries(supplier)
* ```
*/
buildEntriesField(entriesHelperFunction)
// Add synthetic $ENTRIES field and bind its initializer to `EnumEntries($VALUES)`.
buildEntriesField(valuesField)
}
}
@@ -179,17 +164,6 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
}
}
private fun buildEntriesHelperFunction(valuesField: IrField): IrFunction = irClass.addFunction {
name = Name.identifier(ENTRIES_HELPER_FUNCTION_NAME)
returnType = enumArrayType
visibility = DescriptorVisibilities.PRIVATE
origin = IrDeclarationOrigin.SYNTHETIC_HELPER_FOR_ENUM_VALUES
}.apply {
body = context.createJvmIrBuilder(symbol).run {
irExprBody(irGetField(null, valuesField))
}
}
private fun buildValuesField(valuesHelperFunction: IrFunction): IrField = irClass.addField {
name = Name.identifier(ImplementationBodyCodegen.ENUM_VALUES_FIELD_NAME)
type = enumArrayType
@@ -205,7 +179,7 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
}
}
private fun buildEntriesField(entriesHelper: IrFunction): IrField = irClass.addField {
private fun buildEntriesField(valuesField: IrField): IrField = irClass.addField {
name = Name.identifier(ENTRIES_FIELD_NAME)
type = context.ir.symbols.enumEntries.defaultType
visibility = DescriptorVisibilities.PRIVATE
@@ -214,7 +188,11 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
isStatic = true
}.apply {
initializer = context.createJvmIrBuilder(symbol).run {
irCreateEnumEntriesIndy(entriesHelper, enumArrayType, this@EnumClassLowering.context)
irExprBody(
irCall(this@EnumClassLowering.context.ir.symbols.createEnumEntries).apply {
putValueArgument(0, irGetField(null, valuesField))
}
)
}
}
@@ -343,37 +321,3 @@ private class EnumClassLowering(private val context: JvmBackendContext) : ClassL
}
}
}
internal fun IrBuilderWithScope.irCreateEnumEntriesIndy(
functionThatReturnsEnumArray: IrFunction, // values() or $entries()
enumArrayType: IrType,
jvmContext: JvmBackendContext
) = irExprBody(irBlock {
val symbols = jvmContext.ir.symbols
val samClass = context.irBuiltIns.functionN(0)
val type = samClass.typeWith(enumArrayType)
val sam = type.getClass()!!.getSingleAbstractMethod()!!.symbol
/*
* Indy to LMF call:
* INVOKEDYNAMIC get()Lkotlin/jvm/functions/Function0; [
* // handle kind 0x6 : INVOKESTATIC
* java/lang/invoke/LambdaMetafactory.metafactory
* // arguments:
* ()Ljava/lang/Object;,
* // handle kind 0x6 : INVOKESTATIC
* $entries() [LEnum[, // or values()
* ()Ljava/util/List;
* ]
*/
val indyCall = irCall(symbols.indyLambdaMetafactoryIntrinsic, type).apply {
putTypeArgument(0, type)
putValueArgument(0, irRawFunctionReference(context.irBuiltIns.anyType, sam))
putValueArgument(1, IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, functionThatReturnsEnumArray.symbol, 0, 0))
putValueArgument(2, irRawFunctionReference(context.irBuiltIns.anyType, sam))
putValueArgument(3, irVararg(context.irBuiltIns.anyType, emptyList()))
putValueArgument(4, irBoolean(false))
}
+irCall(symbols.createEnumEntries).apply {
putValueArgument(0, indyCall)
}
})
@@ -15,13 +15,16 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.declarations.addField
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
@@ -123,9 +126,14 @@ class EnumExternalEntriesLowering(private val context: JvmBackendContext) : File
for ((enum, field) in mappingState.mappings) {
val enumValues = enum.findEnumValuesFunction(context)
val enumArrayType = field.type
val builder = context.createIrBuilder(field.symbol)
field.initializer = builder.irCreateEnumEntriesIndy(enumValues, enumArrayType, context)
field.initializer =
context.createIrBuilder(field.symbol).run {
irExprBody(
irCall(this@EnumExternalEntriesLowering.context.ir.symbols.createEnumEntries).apply {
putValueArgument(0, irCall(enumValues))
}
)
}
}
if (mappingState.mappings.isNotEmpty()) {
@@ -230,8 +230,8 @@ class JvmSymbols(
private val enumEntriesKt: IrClassSymbol = createClass(FqName("kotlin.enums.EnumEntriesKt")) { klass ->
klass.addFunction("enumEntries", enumEntries.defaultType, isStatic = true).apply {
addValueParameter("entriesProvider",
irBuiltIns.functionN(0).typeWith(irBuiltIns.arrayClass.typeWith(klass.typeParameters.map { it.defaultType })))
val e = addTypeParameter("E", irBuiltIns.enumClass.defaultType)
addValueParameter("entries", irBuiltIns.arrayClass.typeWith(e.defaultType))
}
}
@@ -13,8 +13,6 @@ public class foo/Kotlin : java/lang/Enum {
public final static foo.Kotlin A
private final static foo.Kotlin[] $entries()
private final static foo.Kotlin[] $values()
static void <clinit>()
@@ -11,7 +11,6 @@ public final enum class E {
private synthetic final static field $ENTRIES: kotlin.enums.EnumEntries
private synthetic final static field $VALUES: E[]
public final enum static field X: E
private synthetic final static method $entries(): E[]
private synthetic final static method $values(): E[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -11,7 +11,6 @@ public final enum class E {
private synthetic final static field $ENTRIES: kotlin.enums.EnumEntries
private synthetic final static field $VALUES: E[]
public final enum static field X: E
private synthetic final static method $entries(): E[]
private synthetic final static method $values(): E[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -16,7 +16,6 @@ public final enum class E {
private synthetic final static field $VALUES: E[]
public final enum static field NOT_OK: E
public final enum static field OK: E
private synthetic final static method $entries(): E[]
private synthetic final static method $values(): E[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -25,7 +25,6 @@ public final enum class E {
private synthetic final static field $VALUES: E[]
public final enum static field NOT_OK: E
public final enum static field OK: E
private synthetic final static method $entries(): E[]
private synthetic final static method $values(): E[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -13,7 +13,6 @@ public final enum class E {
private synthetic final static field $VALUES: E[]
public final static @org.jetbrains.annotations.NotNull field Companion: E$Companion
public final enum static field X: E
private synthetic final static method $entries(): E[]
private synthetic final static method $values(): E[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 2 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: enumValues.kt
enum class ABC(val x: String = "") {
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 2 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: enumValues.kt
enum class ABC(val x: String = "") {
@@ -6,7 +6,6 @@ public final enum class Test {
public deprecated final enum static @kotlin.Deprecated(message="") field ENTRY1: Test
public final enum static field ENTRY2: Test
public deprecated final enum static @kotlin.Deprecated(message="") field ENTRY3: Test
private synthetic final static method $entries(): Test[]
private synthetic final static method $values(): Test[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
-4
View File
@@ -12,7 +12,6 @@ public final enum class SimpleEnum {
public final enum static field A: SimpleEnum
public final enum static field B: SimpleEnum
public final enum static field C: SimpleEnum
private synthetic final static method $entries(): SimpleEnum[]
private synthetic final static method $values(): SimpleEnum[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -28,7 +27,6 @@ public final enum class WithAnnotations {
private synthetic final static field $VALUES: WithAnnotations[]
public final enum static @Ann field A: WithAnnotations
public final enum static @Ann field B: WithAnnotations
private synthetic final static method $entries(): WithAnnotations[]
private synthetic final static method $values(): WithAnnotations[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -46,7 +44,6 @@ public final enum class WithConstructor {
public final enum static field B: WithConstructor
public final enum static field C: WithConstructor
private final @org.jetbrains.annotations.NotNull field x: java.lang.String
private synthetic final static method $entries(): WithConstructor[]
private synthetic final static method $values(): WithConstructor[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int, p2: java.lang.String): void
@@ -71,7 +68,6 @@ public abstract enum class WithEntryClass {
private synthetic final static field $VALUES: WithEntryClass[]
public final enum static field A: WithEntryClass
final inner class WithEntryClass$A
private synthetic final static method $entries(): WithEntryClass[]
private synthetic final static method $values(): WithEntryClass[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -12,7 +12,6 @@ public final enum class SimpleEnum {
public final enum static field A: SimpleEnum
public final enum static field B: SimpleEnum
public final enum static field C: SimpleEnum
private synthetic final static method $entries(): SimpleEnum[]
private synthetic final static method $values(): SimpleEnum[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -28,7 +27,6 @@ public final enum class WithAnnotations {
private synthetic final static field $VALUES: WithAnnotations[]
public final enum static @Ann field A: WithAnnotations
public final enum static @Ann field B: WithAnnotations
private synthetic final static method $entries(): WithAnnotations[]
private synthetic final static method $values(): WithAnnotations[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -46,7 +44,6 @@ public final enum class WithConstructor {
public final enum static field B: WithConstructor
public final enum static field C: WithConstructor
private final @org.jetbrains.annotations.NotNull field x: java.lang.String
private synthetic final static method $entries(): WithConstructor[]
private synthetic final static method $values(): WithConstructor[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int, p2: java.lang.String): void
@@ -71,7 +68,6 @@ public abstract enum class WithEntryClass {
private synthetic final static field $VALUES: WithEntryClass[]
public final enum static field A: WithEntryClass
final inner class WithEntryClass$A
private synthetic final static method $entries(): WithEntryClass[]
private synthetic final static method $values(): WithEntryClass[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -34,7 +34,6 @@ public final enum class TestEnum {
private synthetic final static field $VALUES: TestEnum[]
public final enum static field ANSWER: TestEnum
private final field z: int
private synthetic final static method $entries(): TestEnum[]
private synthetic final static method $values(): TestEnum[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int, p2: int): void
@@ -13,7 +13,6 @@ public final enum class A$B$C {
public static method valueOf(p0: java.lang.String): A$B$C
public static @org.jetbrains.annotations.NotNull method getEntries(): kotlin.enums.EnumEntries
private synthetic final static method $values(): A$B$C[]
private synthetic final static method $entries(): A$B$C[]
static method <clinit>(): void
}
@@ -3,5 +3,10 @@ enum class Foo {
open fun result() = "Fail"
}
// JVM_TEMPLATES
// There are two CHECKCASTs, one in Foo.valueOf and one in Foo.values
// 2 CHECKCAST
// JVM_IR_TEMPLATES
// For JVM IR, there's an additional checkcast of `$ENTRIES` to `[Ljava/lang/Enum;` in the static initializer.
// 3 CHECKCAST
+1 -1
View File
@@ -5,7 +5,7 @@ enum class MyEnum {
E
}
// 1 INVOKEDYNAMIC
// 0 INVOKEDYNAMIC
// 1 kotlin.enums.EnumEntries<MyEnum> getEntries\(\)
// 1 private final static synthetic Lkotlin/enums/EnumEntries; \$ENTRIES
// 1 public static getEntries\(\)Lkotlin/enums/EnumEntries;
@@ -11,7 +11,6 @@ public final enum class Enum {
// source: 'allOpenOnNotClasses.kt'
private synthetic final static field $ENTRIES: kotlin.enums.EnumEntries
private synthetic final static field $VALUES: Enum[]
private synthetic final static method $entries(): Enum[]
private synthetic final static method $values(): Enum[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -5,7 +5,6 @@ final enum class TraceTest$Status {
private synthetic final static field $VALUES: TraceTest$Status[]
public final enum static field END: TraceTest$Status
public final enum static field START: TraceTest$Status
private synthetic final static method $entries(): TraceTest$Status[]
private synthetic final static method $values(): TraceTest$Status[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
@@ -6,7 +6,6 @@ public final enum class Colors {
private synthetic final static field $VALUES: Colors[]
public final enum static field RED: Colors
public final enum static field WHITE: Colors
private synthetic final static method $entries(): Colors[]
private synthetic final static method $values(): Colors[]
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void