From 035172c30d47102138e3c9253587ae8481aba219 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Wed, 19 Apr 2023 19:36:17 +0200 Subject: [PATCH] [KxSerialization] Fix "IllegalAccessError: Update to static final field" Fixed #KT-57647 For value classes, if you add code to companion anonymous init block in IR, it will be executed in the instance constructor. This causes an error when initializing static fields, because writing to them can only occur from the method. The solution is to transfer the static field initialization code from an anonymous init block to the IR initializer of this field --- .../AbstractJvmBlackBoxCodegenTestBase.kt | 10 ------ .../codegen/BaseCodegenConfiguration.kt | 16 +++++++++ .../compiler/backend/ir/BaseIrGenerator.kt | 4 +-- .../backend/ir/IrBuilderWithPluginContext.kt | 24 ++++++++++++++ .../testData/codegen/Basic.asm.ir.txt | 2 -- .../testData/codegen/Sealed.asm.ir.txt | 2 -- .../testData/jdk11BoxIr/kt57647.kt | 31 +++++++++++++++++ .../SerializationJdk11IrBoxTestGenerated.java | 33 +++++++++++++++++++ .../kotlinx/serialization/TestGenerator.kt | 4 +++ .../kotlinx/serialization/runners/BoxTests.kt | 7 ++++ .../serializationConfiguration.kt | 15 +++++++-- 11 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt create mode 100644 plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationJdk11IrBoxTestGenerated.java diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractJvmBlackBoxCodegenTestBase.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractJvmBlackBoxCodegenTestBase.kt index c50fc680c52db..90372c10bfc3f 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractJvmBlackBoxCodegenTestBase.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractJvmBlackBoxCodegenTestBase.kt @@ -102,14 +102,4 @@ abstract class AbstractJvmBlackBoxCodegenTestBase.inlineHandlers() { ::SMAPDumpHandler ) } + +fun TestConfigurationBuilder.configureModernJavaTest(jdkKind: TestJdkKind, jvmTarget: JvmTarget) { + defaultDirectives { + JvmEnvironmentConfigurationDirectives.JDK_KIND with jdkKind + JvmEnvironmentConfigurationDirectives.JVM_TARGET with jvmTarget + +ConfigurationDirectives.WITH_STDLIB + +CodegenTestDirectives.USE_JAVAC_BASED_ON_JVM_TARGET + +CodegenTestDirectives.IGNORE_DEXING + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index 7d4faf6bd4331..37c8bea771153 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -376,8 +376,8 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override val kSerializerType = kSerializerClass.typeWith(compilerContext.irBuiltIns.anyType) val arrayType = compilerContext.irBuiltIns.arrayClass.typeWith(kSerializerType) - return addValPropertyWithJvmField(arrayType, SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME) { - +createArrayOfExpression(kSerializerType, cacheableSerializers.map { it ?: irNull() }) + return addValPropertyWithJvmFieldInitializer(arrayType, SerialEntityNames.CACHED_CHILD_SERIALIZERS_PROPERTY_NAME) { + createArrayOfExpression(kSerializerType, cacheableSerializers.map { it ?: irNull() }) } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 291f7789699f8..a263cba8589b1 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -162,6 +162,30 @@ interface IrBuilderWithPluginContext { } } + fun IrClass.addValPropertyWithJvmFieldInitializer( + type: IrType, + name: Name, + visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE, + initializer: IrBuilderWithScope.() -> IrExpression + ): IrProperty { + return generateSimplePropertyWithBackingField(name, type, this, visibility).apply { + val field = backingField!! + + val builder = DeclarationIrBuilder( + compilerContext, + field.symbol, + field.startOffset, + field.endOffset + ) + field.initializer = IrExpressionBodyImpl(builder.initializer()) + + val annotationCtor = compilerContext.jvmFieldClassSymbol.constructors.single { it.owner.isPrimary } + val annotationType = compilerContext.jvmFieldClassSymbol.defaultType + + field.annotations += IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, annotationType, annotationCtor) + } + } + /** * Add all statements to the builder, except the last one. * The last statement should be an expression, it will return as a result diff --git a/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt b/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt index 8e34eb406ced9..f797bb22d0448 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt @@ -261,8 +261,6 @@ public final class ListOfUsers : java/lang/Object { AASTORE ALOAD (0) PUTSTATIC (ListOfUsers, $childSerializers, [Lkotlinx/serialization/KSerializer;) - LABEL (L1) - LINENUMBER (13) RETURN } diff --git a/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt b/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt index a59f9ea6e47c8..0d0861a6be112 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt @@ -222,8 +222,6 @@ public final class Container : java/lang/Object { AASTORE ALOAD (0) PUTSTATIC (Container, $childSerializers, [Lkotlinx/serialization/KSerializer;) - LABEL (L1) - LINENUMBER (19) RETURN } diff --git a/plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt b/plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt new file mode 100644 index 0000000000000..9aabae2ee3fa6 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB +// IGNORE_DEXING + +import kotlinx.serialization.* +import java.util.UUID + +@Serializable +@JvmInline +value class Id(val id: @Contextual UUID) { + companion object { + fun random() = Id(UUID.randomUUID()) + } +} + +@Serializable +@JvmInline +value class Parametrized(val l: List) + +fun pageMain () { + val id: Id = Id.random() + println(id) +} + + +fun box(): String { + println(System.getProperty("java.version")) + pageMain() + return "OK" +} diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationJdk11IrBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationJdk11IrBoxTestGenerated.java new file mode 100644 index 0000000000000..60e758d6e8c54 --- /dev/null +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationJdk11IrBoxTestGenerated.java @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2023 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 org.jetbrains.kotlinx.serialization.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlinx.serialization.TestGeneratorKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/kotlinx-serialization/testData/jdk11BoxIr") +@TestDataPath("$PROJECT_ROOT") +public class SerializationJdk11IrBoxTestGenerated extends AbstractSerializationJdk11IrBoxTest { + @Test + public void testAllFilesPresentInJdk11BoxIr() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/jdk11BoxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("kt57647.kt") + public void testKt57647() throws Exception { + runTest("plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt"); + } +} diff --git a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt index 746e97ab4cd06..38ad8a0bf7389 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt @@ -44,6 +44,10 @@ fun main(args: Array) { model("boxIr") } + testClass { + model("jdk11BoxIr") + } + testClass { model("boxIr") model("firMembers") diff --git a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/runners/BoxTests.kt b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/runners/BoxTests.kt index 24c359b84b1b7..54ec49cd504bb 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/runners/BoxTests.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/runners/BoxTests.kt @@ -18,6 +18,13 @@ open class AbstractSerializationIrBoxTest : AbstractIrBlackBoxCodegenTest() { } } +open class AbstractSerializationJdk11IrBoxTest : AbstractIrBlackBoxCodegenTest() { + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + builder.configureForKotlinxSerialization(useJdk11 = true) + } +} + open class AbstractSerializationWithoutRuntimeIrBoxTest : AbstractIrBlackBoxCodegenTest() { override fun configure(builder: TestConfigurationBuilder) { super.configure(builder) diff --git a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt index 29abbf9f29620..72c6b56822ea6 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt @@ -8,16 +8,19 @@ package org.jetbrains.kotlinx.serialization import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter import org.jetbrains.kotlin.test.bind import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.runners.codegen.configureModernJavaTest import org.jetbrains.kotlin.test.services.EnvironmentConfigurator import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationComponentRegistrar import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIntrinsicsState -import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationExtensionRegistrar import java.io.File private val librariesPaths = listOfNotNull(RuntimeLibraryInClasspathTest.coreLibraryPath, RuntimeLibraryInClasspathTest.jsonLibraryPath) @@ -45,8 +48,16 @@ class SerializationRuntimeClasspathProvider(testServices: TestServices) : Runtim } } -fun TestConfigurationBuilder.configureForKotlinxSerialization(noLibraries: Boolean = false) { +fun TestConfigurationBuilder.configureForKotlinxSerialization( + noLibraries: Boolean = false, + useJdk11: Boolean = false +) { useConfigurators(::SerializationEnvironmentConfigurator.bind(noLibraries)) + + if (useJdk11) { + configureModernJavaTest(TestJdkKind.FULL_JDK_11, JvmTarget.JVM_11) + } + if (!noLibraries) { useCustomRuntimeClasspathProviders(::SerializationRuntimeClasspathProvider) }