From d37b616e1f32422c3e56631139e50f482c42b9ce Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 30 Jun 2020 14:58:22 +0300 Subject: [PATCH] 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 --- .../backend/jvm/codegen/ClassCodegen.kt | 2 +- .../backend/jvm/codegen/FunctionCodegen.kt | 4 +- .../jvm/lower/SyntheticAccessorLowering.kt | 3 + .../testData/codegen/bytecodeListing/enum.kt | 21 +++++++ .../testData/codegen/bytecodeListing/enum.txt | 60 +++++++++++++++++++ .../inline/specialEnumFunction.kt | 12 ---- .../codegen/BytecodeListingTestGenerated.java | 5 ++ .../ir/IrBytecodeListingTestGenerated.java | 5 ++ 8 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/enum.kt create mode 100644 compiler/testData/codegen/bytecodeListing/enum.txt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index c23133c0062..4552cbb0682 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -245,7 +245,7 @@ abstract class ClassCodegen protected constructor( if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) { 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 object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) { override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index 1300032b14f..b3b4e9d5163 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -70,7 +70,8 @@ class FunctionCodegen( } 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 object : AnnotationCodegen(classCodegen, context, skipNullabilityAnnotations) { @@ -145,6 +146,7 @@ class FunctionCodegen( val modalityFlag = when ((this as? IrSimpleFunction)?.modality) { Modality.FINAL -> when { origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0 + origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> 0 parentAsClass.isInterface && body != null -> 0 parentAsClass.isAnnotationClass && !isStatic -> Opcodes.ACC_ABSTRACT else -> Opcodes.ACC_FINAL diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index f649fa0b6df..69f52e650cc 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -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 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. if (!withSuper && !declaration.visibility.isPrivate && !declaration.visibility.isProtected) return true diff --git a/compiler/testData/codegen/bytecodeListing/enum.kt b/compiler/testData/codegen/bytecodeListing/enum.kt new file mode 100644 index 00000000000..a193aa6cfd4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/enum.kt @@ -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 +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/enum.txt b/compiler/testData/codegen/bytecodeListing/enum.txt new file mode 100644 index 00000000000..bb8fadbfc6c --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/enum.txt @@ -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 (): void + private method (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 (): void + private method (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 (): void + private method (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 (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 (): void + private method (p0: java.lang.String, p1: int): void + public synthetic method (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[] +} diff --git a/compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt b/compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt index 62c928e4f47..bcbce3b8778 100644 --- a/compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt +++ b/compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt @@ -13,7 +13,6 @@ fun main() { value() } -// JVM_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; @@ -22,14 +21,3 @@ fun main() { //1 INVOKESTATIC Z\.valueOf \(Ljava/lang/String;\)LZ; //1 public static valueOf //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; \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 1ae91508dd5..8081ae7c57f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -54,6 +54,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { 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") public void testExtension() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/extension.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index ecb9ecbd650..06ebc42deab 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -54,6 +54,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes 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") public void testExtension() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/extension.kt");