diff options
author | mvicsokolova <82594708+mvicsokolova@users.noreply.github.com> | 2024-02-29 13:55:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-29 13:55:10 +0100 |
commit | 37b4bdfdef64a9a0f16f32119d364e32e6751d05 (patch) | |
tree | c4065c269123d323b43279861320457e23668d8a | |
parent | c945b3a08434e8f863d066e245d41b78003c33bc (diff) | |
download | kotlinx.atomicfu-37b4bdfdef64a9a0f16f32119d364e32e6751d05.tar.gz |
Replace SynchronizedObject reference with kotlin/Any in class Metadata (#404)
MetadataTransformer: replace kotlinx/atomicfu/SynchronizedObject reference with kotlin/Any in class Metadata.
See: https://youtrack.jetbrains.com/issue/KT-63413
10 files changed, 187 insertions, 16 deletions
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt index ea393e4..2b32a5d 100644 --- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt +++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt @@ -77,6 +77,9 @@ class MetadataTransformer( // Skip supertype if it is SynchronizedObject (it is an alias to Any) supertypes.forEach { type -> if (type.abbreviatedType?.classifier == SynchronizedObjectAlias) { + KmType().apply { + visitClass("kotlin/Any") + }.accept(super.visitSupertype(flagsOf(Flag.IS_PUBLIC, Flag.IS_OPEN))!!) transformed = true } else type.accept(super.visitSupertype(type.flags)!!) diff --git a/integration-testing/examples/multi-module-test/consumer/build.gradle.kts b/integration-testing/examples/multi-module-test/consumer/build.gradle.kts new file mode 100644 index 0000000..6e66bb4 --- /dev/null +++ b/integration-testing/examples/multi-module-test/consumer/build.gradle.kts @@ -0,0 +1,40 @@ +/* + * + * * Copyright 2016-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + * + */ + +plugins { + kotlin("multiplatform") version libs.versions.kotlinVersion.get() + application +} + +repositories { + mavenCentral() +} + +kotlin { + jvmToolchain(11) + + jvm { + withJava() + } + + sourceSets { + all { + languageSettings.apply { + languageVersion = "2.0" + } + } + + val commonMain by getting { + dependencies { + implementation(project(":producer")) + } + } + } +} + +application { + mainClass.set("MainKt") +} diff --git a/integration-testing/examples/multi-module-test/consumer/src/jvmMain/kotlin/Main.kt b/integration-testing/examples/multi-module-test/consumer/src/jvmMain/kotlin/Main.kt new file mode 100644 index 0000000..88cbf22 --- /dev/null +++ b/integration-testing/examples/multi-module-test/consumer/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,12 @@ +val debugTrace: DebugTrace? get() = null + +inline fun <R> withClue(clue: Any?, thunk: () -> R): R { + println("clue: $clue") + return thunk() +} + +fun main() { + withClue(debugTrace ?: "dcdc") { + println("dvdvd") + } +} diff --git a/integration-testing/examples/multi-module-test/gradle.properties b/integration-testing/examples/multi-module-test/gradle.properties new file mode 100644 index 0000000..756c4f1 --- /dev/null +++ b/integration-testing/examples/multi-module-test/gradle.properties @@ -0,0 +1,4 @@ +kotlin_version=1.9.21 +atomicfu_version=0.23.2-SNAPSHOT + +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g diff --git a/integration-testing/examples/multi-module-test/producer/build.gradle.kts b/integration-testing/examples/multi-module-test/producer/build.gradle.kts new file mode 100644 index 0000000..802080a --- /dev/null +++ b/integration-testing/examples/multi-module-test/producer/build.gradle.kts @@ -0,0 +1,43 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + + dependencies { + val atomicfuVersion = libs.versions.atomicfuVersion.get() + classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfuVersion") + } +} + +plugins { + kotlin("multiplatform") version libs.versions.kotlinVersion.get() +} + +apply(plugin = "kotlinx-atomicfu") + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + jvmToolchain(11) + + jvm() + + sourceSets { + all { + languageSettings.apply { + languageVersion = "2.0" + } + } + + // This is necessary for now, due to the problem with dependency configuration, see #403 + val jvmMain by getting { + dependencies { + api("org.jetbrains.kotlinx:atomicfu:0.23.2") + } + } + } +} diff --git a/integration-testing/examples/multi-module-test/producer/src/jvmMain/kotlin/DebugTrace.kt b/integration-testing/examples/multi-module-test/producer/src/jvmMain/kotlin/DebugTrace.kt new file mode 100644 index 0000000..2eeada7 --- /dev/null +++ b/integration-testing/examples/multi-module-test/producer/src/jvmMain/kotlin/DebugTrace.kt @@ -0,0 +1,4 @@ +import kotlinx.atomicfu.* +import kotlinx.atomicfu.locks.SynchronizedObject + +class DebugTrace : SynchronizedObject() diff --git a/integration-testing/examples/multi-module-test/settings.gradle.kts b/integration-testing/examples/multi-module-test/settings.gradle.kts new file mode 100644 index 0000000..7ec4476 --- /dev/null +++ b/integration-testing/examples/multi-module-test/settings.gradle.kts @@ -0,0 +1,21 @@ +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + version("atomicfuVersion", providers.gradleProperty("atomicfu_version").orNull) + version("kotlinVersion", providers.gradleProperty("kotlin_version").orNull) + } + } +} + +include(":producer", ":consumer") + +rootProject.name = "multi-module-test" diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MultiModuleTest.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MultiModuleTest.kt new file mode 100644 index 0000000..68b7e72 --- /dev/null +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/MultiModuleTest.kt @@ -0,0 +1,30 @@ +package kotlinx.atomicfu.gradle.plugin.test.cases + +import kotlinx.atomicfu.gradle.plugin.test.framework.checker.buildAndCheckBytecode +import kotlinx.atomicfu.gradle.plugin.test.framework.runner.* +import kotlin.test.* + +/** + * This test checks kotlinx-atomicfu plugin application to one module within a project containing multiple modules. + * + * `multi-module-test` is also a reproducer for the problem with leftovers of atomicfu references in Metadata, see KT-63413 + */ +class MultiModuleTest { + private val multiModuleTest: GradleBuild = createGradleBuildFromSources("multi-module-test") + + @Test + fun testMppWithDisabledJvmIrTransformation() { + multiModuleTest.enableJvmIrTransformation = false + val buildResult = multiModuleTest.cleanAndBuild() + assertTrue(buildResult.isSuccessful, buildResult.output) + multiModuleTest.buildAndCheckBytecode() + } + + @Test + fun testMppWithEnabledJvmIrTransformation() { + multiModuleTest.enableJvmIrTransformation = true + val buildResult = multiModuleTest.cleanAndBuild() + assertTrue(buildResult.isSuccessful, buildResult.output) + multiModuleTest.buildAndCheckBytecode() + } +} diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/smoke/ArtifactCheckerSmokeTest.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/smoke/ArtifactCheckerSmokeTest.kt index a9c5b1c..aceda89 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/smoke/ArtifactCheckerSmokeTest.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/cases/smoke/ArtifactCheckerSmokeTest.kt @@ -66,7 +66,7 @@ class ArtifactCheckerSmokeTest { " d2=[\"LIntArithmetic;\",\"\",\"()V\",\"_x\",\"Lkotlinx/atomicfu/AtomicInt;\",\"doWork\",\"\",\"finalValue\",\"\",\"jvm-sample\"]\n" + " )" - override fun checkReferences() { + override fun checkReferences(buildDir: File) { assertTrue(atomicfuString.toByteArray().findAtomicfuRef()) assertFalse(noAtomicfuString.toByteArray().findAtomicfuRef()) assertTrue(metadataString.toByteArray().findAtomicfuRef()) @@ -77,6 +77,6 @@ class ArtifactCheckerSmokeTest { @Test fun testAtomicfuReferenciesLookup() { - checker.checkReferences() + checker.checkClassesInBuildDirectories() } } diff --git a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/ArtifactChecker.kt b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/ArtifactChecker.kt index 4f504fc..799c077 100644 --- a/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/ArtifactChecker.kt +++ b/integration-testing/src/functionalTest/kotlin/kotlinx.atomicfu.gradle.plugin.test/framework/checker/ArtifactChecker.kt @@ -18,12 +18,13 @@ internal abstract class ArtifactChecker(private val targetDir: File) { protected val projectName = targetDir.name.substringBeforeLast("-") - val buildDir - get() = targetDir.resolve("build").also { - require(it.exists() && it.isDirectory) { "Could not find `build/` directory in the target directory of the project $projectName: ${targetDir.path}" } + fun checkClassesInBuildDirectories() { + targetDir.walkTopDown().filter { it.isDirectory && it.name == "build" }.forEach { + checkReferences(it) } + } - abstract fun checkReferences() + protected abstract fun checkReferences(buildDir: File) protected fun ByteArray.findAtomicfuRef(): Boolean { loop@for (i in 0 .. this.size - ATOMIC_FU_REF.size) { @@ -36,14 +37,27 @@ internal abstract class ArtifactChecker(private val targetDir: File) { } } -private class BytecodeChecker(targetDir: File) : ArtifactChecker(targetDir) { +private class BytecodeChecker(private val gradleBuild: GradleBuild) : ArtifactChecker(gradleBuild.targetDir) { - override fun checkReferences() { - val atomicfuDir = buildDir.resolve("classes/atomicfu/") - (if (atomicfuDir.exists() && atomicfuDir.isDirectory) atomicfuDir else buildDir).let { - it.walkBottomUp().filter { it.isFile && it.name.endsWith(".class") }.forEach { clazz -> - assertFalse(clazz.readBytes().eraseMetadata().findAtomicfuRef(), "Found kotlinx/atomicfu in class file ${clazz.path}") + override fun checkReferences(buildDir: File) { + // Do not check metadata for kotlinx-atomicfu references if the compiler plugin is applied. + if (gradleBuild.enableJvmIrTransformation) { + buildDir.walkDirAndCheckBytecode(skipMetadata = true) + } else { + val atomicfuDir = buildDir.resolve("classes/atomicfu/") + if (atomicfuDir.exists() && atomicfuDir.isDirectory) { + atomicfuDir.walkDirAndCheckBytecode(skipMetadata = false) + } + } + + } + + private fun File.walkDirAndCheckBytecode(skipMetadata: Boolean) { + walkBottomUp().filter { it.isFile && it.name.endsWith(".class") }.forEach { clazz -> + val atomicfuRefFound = clazz.readBytes().let { + if (skipMetadata) it.eraseMetadata().findAtomicfuRef() else it.findAtomicfuRef() } + assertFalse(atomicfuRefFound, "Found kotlinx/atomicfu in class file ${clazz.path}") } } @@ -51,7 +65,7 @@ private class BytecodeChecker(targetDir: File) : ArtifactChecker(targetDir) { // so for now we check that there are no ATOMIC_FU_REF left in the class bytecode excluding metadata. // This may be reverted after the fix in the compiler plugin transformer (See #254). private fun ByteArray.eraseMetadata(): ByteArray { - val cw = ClassWriter(ClassWriter.COMPUTE_MAXS or ClassWriter.COMPUTE_FRAMES) + val cw = ClassWriter(0) ClassReader(this).accept(object : ClassVisitor(Opcodes.ASM9, cw) { override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { return if (descriptor == KOTLIN_METADATA_DESC) null else super.visitAnnotation(descriptor, visible) @@ -92,7 +106,7 @@ private class KlibChecker(targetDir: File) : ArtifactChecker(targetDir) { return output.toString() } - override fun checkReferences() { + override fun checkReferences(buildDir: File) { val classesDir = buildDir.resolve("classes/kotlin/") if (classesDir.exists() && classesDir.isDirectory) { classesDir.walkBottomUp().singleOrNull { it.isFile && it.name == "$projectName.klib" }?.let { klib -> @@ -112,12 +126,12 @@ private class KlibChecker(targetDir: File) : ArtifactChecker(targetDir) { internal fun GradleBuild.buildAndCheckBytecode() { val buildResult = cleanAndBuild() require(buildResult.isSuccessful) { "Build of the project $projectName failed:\n ${buildResult.output}" } - BytecodeChecker(this.targetDir).checkReferences() + BytecodeChecker(this).checkClassesInBuildDirectories() } // TODO: klib checks are skipped for now because of this problem KT-61143 internal fun GradleBuild.buildAndCheckNativeKlib() { val buildResult = cleanAndBuild() require(buildResult.isSuccessful) { "Build of the project $projectName failed:\n ${buildResult.output}" } - KlibChecker(this.targetDir).checkReferences() + KlibChecker(this.targetDir).checkClassesInBuildDirectories() } |