From d8d643b7d7f9037c3c7eaef62a9fc319fac00ca4 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Fri, 21 Apr 2023 12:37:32 +0000 Subject: [PATCH] [KxSerialization] Fix "IllegalAccessError: Update to static final field" Fixes #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 Merge-request: KT-MR-9633 Merged-by: Sergey Shanshin --- .../AbstractJvmBlackBoxCodegenTestBase.kt | 10 ------ .../codegen/BaseCodegenConfiguration.kt | 15 +++++++++ .../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 | 32 ++++++++++++++++++ .../SerializationJdk11IrBoxTestGenerated.java | 33 +++++++++++++++++++ .../kotlinx/serialization/TestGenerator.kt | 4 +++ .../kotlinx/serialization/runners/BoxTests.kt | 7 ++++ .../serializationConfiguration.kt | 12 +++++-- 11 files changed, 126 insertions(+), 19 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 7d0c50ad34d..e5ae70d911a 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 @@ -111,14 +111,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 e52ab68aa53..5accdd4fe6f 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 @@ -398,8 +398,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 26188ba6cde..15e61d0caad 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 @@ -161,6 +161,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 de47e441be2..b8c9a985160 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Basic.asm.ir.txt @@ -262,8 +262,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 48e503823ed..0fef1c2ecb7 100644 --- a/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt +++ b/plugins/kotlinx-serialization/testData/codegen/Sealed.asm.ir.txt @@ -223,8 +223,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 00000000000..64801a78ea8 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/jdk11BoxIr/kt57647.kt @@ -0,0 +1,32 @@ +// 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 00000000000..60e758d6e8c --- /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 19ab5efdc4e..14768d756b2 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/TestGenerator.kt @@ -48,6 +48,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 cecdd28d20a..47812a871f0 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 @@ -19,6 +19,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 e2609859fb3..7a96b2ca93a 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/serializationConfiguration.kt @@ -8,17 +8,18 @@ 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.fir.extensions.FirExtensionRegistrarAdapter +import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.test.TestJdkKind 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) @@ -57,10 +58,15 @@ class SerializationRuntimeClasspathJsProvider(testServices: TestServices) : Runt fun TestConfigurationBuilder.configureForKotlinxSerialization( noLibraries: Boolean = false, - target: TargetBackend = TargetBackend.JVM + target: TargetBackend = TargetBackend.JVM, + useJdk11: Boolean = false ) { useConfigurators(::SerializationEnvironmentConfigurator.bind(noLibraries)) + if (useJdk11) { + configureModernJavaTest(TestJdkKind.FULL_JDK_11, JvmTarget.JVM_11) + } + if (!noLibraries) { when (target) { TargetBackend.JVM, TargetBackend.JVM_IR -> useCustomRuntimeClasspathProviders(::SerializationRuntimeClasspathJvmProvider)