From 7c84a7777a009b33ed47391085eeb7eda2d45925 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Fri, 1 Nov 2024 18:02:56 +0100 Subject: [PATCH 1/2] Instantiate `testClass` taking into account its enclosing class --- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 17 ++++++++++ .../lincheck/runner/ParallelThreadsRunner.kt | 3 +- .../kotlinx/lincheck/verifier/LTS.kt | 3 +- .../lincheck_test/runner/InnerClassTest.kt | 33 +++++++++++++++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index a6aedc54a..b71617aa9 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -329,3 +329,20 @@ internal inline fun runOutsideIgnoredSection(descriptor: ThreadDescriptor?, internal const val LINCHECK_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck." internal const val LINCHECK_RUNNER_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck.runner." + +internal fun Class.newInstanceRecursive(): T { + @Suppress("UNCHECKED_CAST") + val constructor = this.declaredConstructors.singleOrNull { it.parameterCount == 0 } as? Constructor + if (constructor != null) { + return constructor.newInstance() + } + + if (this.enclosingClass != null) { + val enclosingObject = this.enclosingClass.newInstanceRecursive() + return this.getDeclaredConstructor(this.enclosingClass) + .also { it.isAccessible = true } + .newInstance(enclosingObject) + } + + throw IllegalStateException("No suitable constructor found for ${this.canonicalName}") +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index acf05a799..8a33126e7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -180,8 +180,7 @@ internal open class ParallelThreadsRunner( private var ensuredTestInstanceIsTransformed = false private fun createTestInstance() { - @Suppress("DEPRECATION") - testInstance = testClass.newInstance() + testInstance = testClass.newInstanceRecursive() if (strategy is ModelCheckingStrategy) { // In the model checking mode, we need to ensure // that all the necessary classes and instrumented diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index 2f8974513..fb79eb3f2 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -261,8 +261,7 @@ class LTS(private val sequentialSpecification: Class<*>) { } private fun createInitialStateInstance(): Any { - @Suppress("DEPRECATION") - return sequentialSpecification.newInstance().also { + return sequentialSpecification.newInstanceRecursive().also { // the sequential version of the data structure used for verification // may differ from the original parallel version, // in this case we need to ensure that the sequential class is also instrumented diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt new file mode 100644 index 000000000..33790aa13 --- /dev/null +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt @@ -0,0 +1,33 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2025 JetBrains s.r.o. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.jetbrains.kotlinx.lincheck_test.runner + +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.check +import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions +import org.junit.Test + +class OuterClass { + inner class InnerClass { + @Operation + fun zero() = 0 + } +} + +class InnerClassTest { + @Test + fun someTest() { + StressOptions() + .invocationsPerIteration(1) + .iterations(1) + .check(OuterClass.InnerClass::class) + } +} \ No newline at end of file From 634460f559966420e01afeaa9e359488e6073fe5 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 6 Feb 2025 18:40:29 +0100 Subject: [PATCH 2/2] fixup! Instantiate `testClass` taking into account its enclosing class --- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 4 ++-- .../lincheck/runner/ParallelThreadsRunner.kt | 2 +- .../jetbrains/kotlinx/lincheck/verifier/LTS.kt | 2 +- .../lincheck_test/runner/InnerClassTest.kt | 17 +++++++++++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index b71617aa9..f380e2e23 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -330,7 +330,7 @@ internal inline fun runOutsideIgnoredSection(descriptor: ThreadDescriptor?, internal const val LINCHECK_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck." internal const val LINCHECK_RUNNER_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck.runner." -internal fun Class.newInstanceRecursive(): T { +internal fun Class.newDefaultInstance(): T { @Suppress("UNCHECKED_CAST") val constructor = this.declaredConstructors.singleOrNull { it.parameterCount == 0 } as? Constructor if (constructor != null) { @@ -338,7 +338,7 @@ internal fun Class.newInstanceRecursive(): T { } if (this.enclosingClass != null) { - val enclosingObject = this.enclosingClass.newInstanceRecursive() + val enclosingObject = this.enclosingClass.newDefaultInstance() return this.getDeclaredConstructor(this.enclosingClass) .also { it.isAccessible = true } .newInstance(enclosingObject) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt index 8a33126e7..3f49e0269 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/ParallelThreadsRunner.kt @@ -180,7 +180,7 @@ internal open class ParallelThreadsRunner( private var ensuredTestInstanceIsTransformed = false private fun createTestInstance() { - testInstance = testClass.newInstanceRecursive() + testInstance = testClass.newDefaultInstance() if (strategy is ModelCheckingStrategy) { // In the model checking mode, we need to ensure // that all the necessary classes and instrumented diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt index fb79eb3f2..74189e0e6 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt @@ -261,7 +261,7 @@ class LTS(private val sequentialSpecification: Class<*>) { } private fun createInitialStateInstance(): Any { - return sequentialSpecification.newInstanceRecursive().also { + return sequentialSpecification.newDefaultInstance().also { // the sequential version of the data structure used for verification // may differ from the original parallel version, // in this case we need to ensure that the sequential class is also instrumented diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt index 33790aa13..2ca17fb73 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/runner/InnerClassTest.kt @@ -20,14 +20,27 @@ class OuterClass { @Operation fun zero() = 0 } + + class NestedClass { + @Operation + fun zero() = 0 + } } class InnerClassTest { @Test - fun someTest() { + fun testInnerClass() { StressOptions() .invocationsPerIteration(1) .iterations(1) .check(OuterClass.InnerClass::class) } -} \ No newline at end of file + + @Test + fun testNestedClass() { + StressOptions() + .invocationsPerIteration(1) + .iterations(1) + .check(OuterClass.NestedClass::class) + } +}