JVM IR: initialize enum entries without invokedynamic
#KT-57316 Fixed
This commit is contained in:
committed by
Space Team
parent
9c2c11f7e9
commit
562b27db4e
+8
-64
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
+13
-5
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -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>()
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
Vendored
-1
@@ -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
|
||||
|
||||
Vendored
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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 = "") {
|
||||
|
||||
+1
-1
@@ -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 = "") {
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user