JVM_IR: Fix enum classes ABI

1. Enum entry fields don't have nullability annotations.

2. Enum class special methods (values, valueOf) are not 'final'
(although they probably should be, javac generates corresponding
methods without ACC_FINAL flag).

3. Enum class special methods don't have nullability annotations.

4. Don't generate synthetic accessor for enum entry class constructor.

KT-37019 KT-37020 KT-37021
This commit is contained in:
Dmitry Petrov
2020-06-30 14:58:22 +03:00
parent f753824429
commit d37b616e1f
8 changed files with 98 additions and 14 deletions
@@ -245,7 +245,7 @@ abstract class ClassCodegen protected constructor(
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) { if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
val skipNullabilityAnnotations = val skipNullabilityAnnotations =
flags and Opcodes.ACC_SYNTHETIC != 0 || flags and Opcodes.ACC_PRIVATE != 0 || flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_ENUM) != 0 ||
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) { object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor { override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
@@ -70,7 +70,8 @@ class FunctionCodegen(
} }
if (irFunction.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && if (irFunction.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER &&
irFunction.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR irFunction.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR &&
irFunction.origin != IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER
) { ) {
val skipNullabilityAnnotations = flags and Opcodes.ACC_PRIVATE != 0 || flags and Opcodes.ACC_SYNTHETIC != 0 val skipNullabilityAnnotations = flags and Opcodes.ACC_PRIVATE != 0 || flags and Opcodes.ACC_SYNTHETIC != 0
object : AnnotationCodegen(classCodegen, context, skipNullabilityAnnotations) { object : AnnotationCodegen(classCodegen, context, skipNullabilityAnnotations) {
@@ -145,6 +146,7 @@ class FunctionCodegen(
val modalityFlag = when ((this as? IrSimpleFunction)?.modality) { val modalityFlag = when ((this as? IrSimpleFunction)?.modality) {
Modality.FINAL -> when { Modality.FINAL -> when {
origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0 origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0
origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> 0
parentAsClass.isInterface && body != null -> 0 parentAsClass.isInterface && body != null -> 0
parentAsClass.isAnnotationClass && !isStatic -> Opcodes.ACC_ABSTRACT parentAsClass.isAnnotationClass && !isStatic -> Opcodes.ACC_ABSTRACT
else -> Opcodes.ACC_FINAL else -> Opcodes.ACC_FINAL
@@ -576,6 +576,9 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
// There is never a problem with visibility of inline functions, as those don't end up as Java entities // There is never a problem with visibility of inline functions, as those don't end up as Java entities
if (declaration is IrFunction && declaration.isInline) return true if (declaration is IrFunction && declaration.isInline) return true
// Enum entry constructors are generated as package-private and are accessed only from corresponding enum class
if (declaration is IrConstructor && declaration.constructedClass.isEnumEntry) return true
// `internal` maps to public and requires no accessor. // `internal` maps to public and requires no accessor.
if (!withSuper && !declaration.visibility.isPrivate && !declaration.visibility.isProtected) return true if (!withSuper && !declaration.visibility.isPrivate && !declaration.visibility.isProtected) return true
+21
View File
@@ -0,0 +1,21 @@
enum class SimpleEnum {
A, B, C
}
enum class WithConstructor(val x: String) {
A("1"), B("2"), C("3")
}
enum class WithEntryClass {
A {
override fun foo() {}
}
;
abstract fun foo()
}
annotation class Ann
enum class WithAnnotations {
@Ann A, @Ann B
}
+60
View File
@@ -0,0 +1,60 @@
@java.lang.annotation.Retention
@kotlin.Metadata
public annotation class Ann
@kotlin.Metadata
public final enum class SimpleEnum {
private synthetic final static field $VALUES: SimpleEnum[]
public final enum static field A: SimpleEnum
public final enum static field B: SimpleEnum
public final enum static field C: SimpleEnum
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
public static method valueOf(p0: java.lang.String): SimpleEnum
public static method values(): SimpleEnum[]
}
@kotlin.Metadata
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
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
public static method valueOf(p0: java.lang.String): WithAnnotations
public static method values(): WithAnnotations[]
}
@kotlin.Metadata
public final enum class WithConstructor {
private synthetic final static field $VALUES: WithConstructor[]
public final enum static field A: WithConstructor
public final enum static field B: WithConstructor
public final enum static field C: WithConstructor
private final field x: java.lang.String
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int, p2: java.lang.String): void
public final @org.jetbrains.annotations.NotNull method getX(): java.lang.String
public static method valueOf(p0: java.lang.String): WithConstructor
public static method values(): WithConstructor[]
}
@kotlin.Metadata
final class WithEntryClass$A {
inner class WithEntryClass$A
method <init>(p0: java.lang.String, p1: int): void
public method foo(): void
}
@kotlin.Metadata
public abstract enum class WithEntryClass {
private synthetic final static field $VALUES: WithEntryClass[]
public final enum static field A: WithEntryClass
inner class WithEntryClass$A
static method <clinit>(): void
private method <init>(p0: java.lang.String, p1: int): void
public synthetic method <init>(p0: java.lang.String, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void
public abstract method foo(): void
public static method valueOf(p0: java.lang.String): WithEntryClass
public static method values(): WithEntryClass[]
}
@@ -13,7 +13,6 @@ fun main() {
value<Z>() value<Z>()
} }
// JVM_TEMPLATES
//2 reifiedOperationMarker //2 reifiedOperationMarker
//1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ICONST_0\s*ANEWARRAY java/lang/Enum //1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ICONST_0\s*ANEWARRAY java/lang/Enum
//1 INVOKESTATIC Z\.values \(\)\[LZ; //1 INVOKESTATIC Z\.values \(\)\[LZ;
@@ -22,14 +21,3 @@ fun main() {
//1 INVOKESTATIC Z\.valueOf \(Ljava/lang/String;\)LZ; //1 INVOKESTATIC Z\.valueOf \(Ljava/lang/String;\)LZ;
//1 public static valueOf //1 public static valueOf
//2 INVOKESTATIC java/lang/Enum.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum; //2 INVOKESTATIC java/lang/Enum.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum;
// JVM_IR_TEMPLATES
//2 reifiedOperationMarker
//1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ICONST_0\s*ANEWARRAY java/lang/Enum
//1 INVOKESTATIC Z\.values \(\)\[LZ;
//4 valueOf
//1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ACONST_NULL\s*ALOAD 2\s*INVOKESTATIC java/lang/Enum\.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum;
//1 INVOKESTATIC Z\.valueOf \(Ljava/lang/String;\)LZ;
//1 public final static valueOf
// ^ valueOf is generated as 'public final static' in JVM_IR
//2 INVOKESTATIC java/lang/Enum.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum;
@@ -54,6 +54,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.kt"); runTest("compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.kt");
} }
@TestMetadata("enum.kt")
public void testEnum() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/enum.kt");
}
@TestMetadata("extension.kt") @TestMetadata("extension.kt")
public void testExtension() throws Exception { public void testExtension() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/extension.kt"); runTest("compiler/testData/codegen/bytecodeListing/extension.kt");
@@ -54,6 +54,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.kt"); runTest("compiler/testData/codegen/bytecodeListing/emptyMultifileFacade.kt");
} }
@TestMetadata("enum.kt")
public void testEnum() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/enum.kt");
}
@TestMetadata("extension.kt") @TestMetadata("extension.kt")
public void testExtension() throws Exception { public void testExtension() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/extension.kt"); runTest("compiler/testData/codegen/bytecodeListing/extension.kt");