Skip to content

Commit dc6b0ab

Browse files
Update Soot and choose an analyzable JRE #373 (#890)
1 parent daa8aaa commit dc6b0ab

File tree

21 files changed

+188
-67
lines changed

21 files changed

+188
-67
lines changed

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ junit4_platform_version=1.9.0
77
mockito_version=3.5.13
88
z3_version=4.8.9.1
99
z3_java_api_version=4.8.9
10-
soot_commit_hash=c6c78d9
10+
soot_commit_hash=1f34746
11+
# previous soot_commit_hash=c6c78d9
1112
kotlin_version=1.7.10
1213
log4j2_version=2.13.3
1314
coroutines_version=1.6.3

utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.MockStrategyApi
3030
import org.utbot.framework.plugin.api.TestCaseGenerator
3131
import org.utbot.framework.plugin.api.TreatOverflowAsError
3232
import org.utbot.framework.plugin.api.UtMethodTestSet
33+
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
3334
import org.utbot.summary.summarize
3435
import java.io.File
3536
import java.net.URLClassLoader
@@ -46,7 +47,7 @@ private val logger = KotlinLogging.logger {}
4647
abstract class GenerateTestsAbstractCommand(name: String, help: String) :
4748
CliktCommand(name = name, help = help) {
4849

49-
abstract val classPath:String?
50+
abstract val classPath: String?
5051

5152
private val mockStrategy by option("-m", "--mock-strategy", help = "Defines the mock strategy")
5253
.choice(
@@ -179,7 +180,11 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
179180
}
180181
}
181182

182-
protected fun generateTest(classUnderTest: ClassId, testClassname: String, testSets: List<UtMethodTestSet>): String =
183+
protected fun generateTest(
184+
classUnderTest: ClassId,
185+
testClassname: String,
186+
testSets: List<UtMethodTestSet>
187+
): String =
183188
initializeCodeGenerator(
184189
testFramework,
185190
classUnderTest
@@ -191,7 +196,12 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
191196
// TODO: SAT-1566 Set UtSettings parameters.
192197
UtSettings.treatOverflowAsError = treatOverflowAsError == TreatOverflowAsError.AS_ERROR
193198

194-
return TestCaseGenerator(workingDirectory, classPathNormalized, System.getProperty("java.class.path"))
199+
return TestCaseGenerator(
200+
workingDirectory,
201+
classPathNormalized,
202+
System.getProperty("java.class.path"),
203+
JdkInfoDefaultProvider().info
204+
)
195205
}
196206

197207
private fun initializeCodeGenerator(testFramework: String, classUnderTest: ClassId): CodeGenerator {

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import java.nio.file.Paths
55

66
data class JdkInfo(
77
val path: Path,
8-
@Suppress("unused")
98
val version: Int
109
)
1110

@@ -26,6 +25,9 @@ interface JdkInfoProvider {
2625
val info: JdkInfo
2726
}
2827

28+
/**
29+
* Gets [JdkInfo] from the current process.
30+
*/
2931
open class JdkInfoDefaultProvider : JdkInfoProvider {
3032
override val info: JdkInfo =
3133
JdkInfo(Paths.get(System.getProperty("java.home")), fetchJavaVersion(System.getProperty("java.version")))

utbot-framework-test/src/test/java/org/utbot/examples/manual/UtBotJavaApiTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.utbot.framework.codegen.MockitoStaticMocking;
2626
import org.utbot.framework.plugin.api.*;
2727
import org.utbot.framework.plugin.api.util.UtContext;
28+
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider;
2829
import org.utbot.framework.util.Snippet;
2930
import org.utbot.framework.util.SootUtils;
3031

@@ -1226,7 +1227,7 @@ public void testOnObjectWithArrayOfComplexArrays() {
12261227

12271228
@Test
12281229
public void testFuzzingSimple() {
1229-
SootUtils.INSTANCE.runSoot(StringSwitchExample.class, false);
1230+
SootUtils.INSTANCE.runSoot(StringSwitchExample.class, false, new JdkInfoDefaultProvider().getInfo());
12301231
UtBotJavaApi.setStopConcreteExecutorOnExit(false);
12311232

12321233
String classpath = getClassPath(StringSwitchExample.class);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.utbot.bytecode.versions
2+
3+
import org.junit.jupiter.api.Assertions.assertFalse
4+
import org.junit.jupiter.api.Assertions.assertNotNull
5+
import org.junit.jupiter.api.Assertions.assertNull
6+
import org.junit.jupiter.api.Assertions.assertTrue
7+
import org.junit.jupiter.api.Disabled
8+
import org.junit.jupiter.api.Test
9+
import org.utbot.examples.objects.SimpleDataClass
10+
import org.utbot.framework.util.SootUtils.runSoot
11+
import soot.Scene
12+
13+
@Suppress("UNREACHABLE_CODE")
14+
@Disabled("TODO: https://github.com/UnitTestBot/UTBotJava/issues/891")
15+
class SootTest {
16+
@Test
17+
fun `no method isBlank in JDK 8`() {
18+
runSoot(
19+
SimpleDataClass::class.java,
20+
forceReload = true,
21+
TODO("Get JDK 8")
22+
)
23+
24+
val stringClass = Scene.v().getSootClass("java.lang.String")
25+
assertFalse(stringClass.isPhantomClass)
26+
27+
val isBlankMethod = stringClass.getMethodByNameUnsafe("isBlank") // no such method in JDK 8
28+
assertNull(isBlankMethod)
29+
}
30+
31+
@Test
32+
fun `method isBlank exists in JDK 11`() {
33+
runSoot(
34+
SimpleDataClass::class.java,
35+
forceReload = true,
36+
TODO("Get JDK 11")
37+
)
38+
39+
val stringClass = Scene.v().getSootClass("java.lang.String")
40+
assertFalse(stringClass.isPhantomClass)
41+
42+
val isBlankMethod = stringClass.getMethodByNameUnsafe("isBlank") // there is such method in JDK 11
43+
assertNotNull(isBlankMethod)
44+
}
45+
46+
@Test
47+
fun `no records in JDK 11`() {
48+
runSoot(
49+
SimpleDataClass::class.java,
50+
forceReload = true,
51+
TODO("Get JDK 11")
52+
)
53+
54+
val stringClass = Scene.v().getSootClass("java.lang.Record") // must not exist in JDK 11
55+
assertTrue(stringClass.isPhantomClass)
56+
}
57+
58+
@Test
59+
fun `records exists in JDK 17`() {
60+
runSoot(
61+
SimpleDataClass::class.java,
62+
forceReload = true,
63+
TODO("Get JDK 17")
64+
)
65+
66+
val stringClass = Scene.v().getSootClass("java.lang.Record") // must exist in JDK 17
67+
assertFalse(stringClass.isPhantomClass)
68+
}
69+
}

utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package org.utbot.framework.assemble
22

3+
import org.junit.jupiter.api.AfterEach
4+
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Assertions.assertTrue
6+
import org.junit.jupiter.api.BeforeEach
7+
import org.junit.jupiter.api.Disabled
8+
import org.junit.jupiter.api.Test
39
import org.utbot.examples.assemble.AssembleTestUtils
410
import org.utbot.examples.assemble.ComplexField
511
import org.utbot.examples.assemble.DirectAccess
@@ -23,15 +29,16 @@ import org.utbot.examples.assemble.constructors.InheritComplexConstructor
2329
import org.utbot.examples.assemble.constructors.InheritPrimitiveConstructor
2430
import org.utbot.examples.assemble.constructors.PrimitiveConstructor
2531
import org.utbot.examples.assemble.constructors.PrimitiveConstructorWithDefaultField
32+
import org.utbot.examples.assemble.constructors.PrivateConstructor
2633
import org.utbot.examples.assemble.constructors.PseudoComplexConstructor
2734
import org.utbot.examples.assemble.defaults.DefaultField
2835
import org.utbot.examples.assemble.defaults.DefaultFieldModifiedInConstructor
2936
import org.utbot.examples.assemble.defaults.DefaultFieldWithDirectAccessor
30-
import org.utbot.examples.assemble.constructors.PrivateConstructor
3137
import org.utbot.examples.assemble.defaults.DefaultFieldWithSetter
3238
import org.utbot.examples.assemble.defaults.DefaultPackagePrivateField
3339
import org.utbot.examples.assemble.statics.StaticField
3440
import org.utbot.framework.plugin.api.ClassId
41+
import org.utbot.framework.plugin.api.ExecutableId
3542
import org.utbot.framework.plugin.api.FieldId
3643
import org.utbot.framework.plugin.api.MethodId
3744
import org.utbot.framework.plugin.api.UtArrayModel
@@ -42,22 +49,15 @@ import org.utbot.framework.plugin.api.UtNullModel
4249
import org.utbot.framework.plugin.api.UtPrimitiveModel
4350
import org.utbot.framework.plugin.api.util.UtContext
4451
import org.utbot.framework.plugin.api.util.UtContext.Companion.setUtContext
52+
import org.utbot.framework.plugin.api.util.executableId
4553
import org.utbot.framework.plugin.api.util.id
4654
import org.utbot.framework.plugin.api.util.intArrayClassId
4755
import org.utbot.framework.plugin.api.util.intClassId
56+
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
57+
import org.utbot.framework.util.SootUtils
4858
import org.utbot.framework.util.instanceCounter
4959
import org.utbot.framework.util.modelIdCounter
5060
import kotlin.reflect.full.functions
51-
import org.junit.jupiter.api.AfterEach
52-
import org.junit.jupiter.api.Assertions.assertEquals
53-
import org.junit.jupiter.api.Assertions.assertTrue
54-
import org.junit.jupiter.api.BeforeEach
55-
import org.junit.jupiter.api.Disabled
56-
import org.junit.jupiter.api.Test
57-
import org.utbot.framework.plugin.api.ExecutableId
58-
import org.utbot.framework.plugin.api.util.executableId
59-
import org.utbot.framework.util.SootUtils
60-
import org.utbot.framework.util.sootMethod
6161

6262
/**
6363
* Test classes must be located in the same folder as [AssembleTestUtils] class.
@@ -72,7 +72,7 @@ class AssembleModelGeneratorTests {
7272
instanceCounter.set(0)
7373
modelIdCounter.set(0)
7474
statementsChain = mutableListOf()
75-
SootUtils.runSoot(AssembleTestUtils::class.java, forceReload = false)
75+
SootUtils.runSoot(AssembleTestUtils::class.java, forceReload = false, jdkInfo = JdkInfoDefaultProvider().info)
7676
context = setUtContext(UtContext(AssembleTestUtils::class.java.classLoader))
7777
}
7878

utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
2424
import org.junit.jupiter.api.Assertions.assertTrue
2525
import org.junit.jupiter.api.BeforeEach
2626
import org.junit.jupiter.api.Test
27+
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
2728
import org.utbot.framework.util.SootUtils
2829

2930
internal class UtBotFieldModificatorsTest {
@@ -169,7 +170,11 @@ internal class UtBotFieldModificatorsTest {
169170
}
170171

171172
private fun initAnalysis() {
172-
SootUtils.runSoot(PrimitiveModifications::class.java, forceReload = false)
173+
SootUtils.runSoot(
174+
PrimitiveModifications::class.java,
175+
forceReload = false,
176+
jdkInfo = JdkInfoDefaultProvider().info
177+
)
173178
fieldsModificatorsSearcher = UtBotFieldsModificatorsSearcher()
174179
}
175180

utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.utbot.framework.plugin.api.util.primitiveByWrapper
2929
import org.utbot.framework.plugin.api.util.stringClassId
3030
import org.utbot.framework.plugin.api.util.withUtContext
3131
import org.utbot.framework.plugin.api.util.wrapperByPrimitive
32+
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
3233
import org.utbot.fuzzer.FuzzedValue
3334
import org.utbot.fuzzer.ModelProvider
3435
import org.utbot.fuzzer.ModelProvider.Companion.yieldValue
@@ -112,7 +113,7 @@ object UtBotJavaApi {
112113

113114
testSets.addAll(withUtContext(utContext) {
114115
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
115-
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
116+
TestCaseGenerator(buildPath, classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
116117
.generate(
117118
methodsForAutomaticGeneration.map {
118119
it.methodToBeTestedFromUserInput.executableId
@@ -172,7 +173,7 @@ object UtBotJavaApi {
172173

173174
return withUtContext(UtContext(classUnderTest.classLoader)) {
174175
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
175-
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
176+
TestCaseGenerator(buildPath, classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
176177
.generate(
177178
methodsForAutomaticGeneration.map {
178179
it.methodToBeTestedFromUserInput.executableId

utbot-framework/src/main/kotlin/org/utbot/framework/modifications/ConstructorAnalyzer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class ConstructorAnalyzer {
209209
private fun hasSuspiciousInstructions(jimpleBody: JimpleBody): Boolean =
210210
jimpleBody.units.any {
211211
it !is JIdentityStmt
212-
&& !(it is JAssignStmt && it.rightBox.value !is InvokeExpr)
212+
&& !(it is JAssignStmt && it.rightOp !is InvokeExpr)
213213
&& it !is JInvokeStmt
214214
&& it !is JReturnStmt
215215
&& it !is JReturnVoidStmt

utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.utbot.framework.plugin.api.util.id
3434
import org.utbot.framework.plugin.api.util.intArrayClassId
3535
import org.utbot.framework.plugin.api.util.utContext
3636
import org.utbot.framework.plugin.api.util.withUtContext
37+
import org.utbot.framework.plugin.services.JdkInfo
3738
import org.utbot.framework.util.SootUtils
3839
import org.utbot.framework.util.jimpleBody
3940
import org.utbot.framework.util.toModel
@@ -53,16 +54,18 @@ import kotlin.reflect.KCallable
5354
* Note: the instantiating of [TestCaseGenerator] may take some time,
5455
* because it requires initializing Soot for the current [buildDir] and [classpath].
5556
*
57+
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's code.
5658
* @param forceSootReload forces to reinitialize Soot even if the previous buildDir equals to [buildDir] and previous
5759
* classpath equals to [classpath]. This is the case for plugin scenario, as the source code may be modified.
5860
*/
5961
open class TestCaseGenerator(
6062
private val buildDir: Path,
6163
private val classpath: String?,
6264
private val dependencyPaths: String,
65+
private val jdkInfo: JdkInfo,
6366
val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
6467
val isCanceled: () -> Boolean = { false },
65-
val forceSootReload: Boolean = true
68+
val forceSootReload: Boolean = true,
6669
) {
6770
private val logger: KLogger = KotlinLogging.logger {}
6871
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
@@ -82,7 +85,7 @@ open class TestCaseGenerator(
8285
}
8386

8487
timeoutLogger.trace().bracket("Soot initialization") {
85-
SootUtils.runSoot(buildDir, classpath, forceSootReload)
88+
SootUtils.runSoot(buildDir, classpath, forceSootReload, jdkInfo)
8689
}
8790

8891
//warmup

utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import org.utbot.engine.overrides.strings.UtStringBuffer
4040
import org.utbot.engine.overrides.strings.UtStringBuilder
4141
import org.utbot.engine.pureJavaSignature
4242
import org.utbot.framework.plugin.api.ExecutableId
43+
import org.utbot.framework.plugin.services.JdkInfo
4344
import soot.G
4445
import soot.PackManager
4546
import soot.Scene
@@ -55,24 +56,28 @@ object SootUtils {
5556
/**
5657
* Runs Soot in tests if it hasn't already been done.
5758
*
59+
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's
60+
* code.
5861
* @param forceReload forces to reinitialize Soot even if the [previousBuildDir] equals to the class buildDir.
5962
*/
60-
fun runSoot(clazz: java.lang.Class<*>, forceReload: kotlin.Boolean) {
63+
fun runSoot(clazz: java.lang.Class<*>, forceReload: kotlin.Boolean, jdkInfo: JdkInfo) {
6164
val buildDir = FileUtil.locateClassPath(clazz) ?: FileUtil.isolateClassFiles(clazz)
6265
val buildDirPath = buildDir.toPath()
6366

64-
runSoot(buildDirPath, null, forceReload)
67+
runSoot(buildDirPath, null, forceReload, jdkInfo)
6568
}
6669

6770

6871
/**
72+
* @param jdkInfo specifies the JRE and the runtime library version used for analysing system classes and user's
73+
* code.
6974
* @param forceReload forces to reinitialize Soot even if the [previousBuildDir] equals to [buildDirPath] and
7075
* [previousClassPath] equals to [classPath].
7176
*/
72-
fun runSoot(buildDirPath: Path, classPath: String?, forceReload: kotlin.Boolean) {
77+
fun runSoot(buildDirPath: Path, classPath: String?, forceReload: kotlin.Boolean, jdkInfo: JdkInfo) {
7378
synchronized(this) {
7479
if (buildDirPath != previousBuildDir || classPath != previousClassPath || forceReload) {
75-
initSoot(buildDirPath, classPath)
80+
initSoot(buildDirPath, classPath, jdkInfo)
7681
previousBuildDir = buildDirPath
7782
previousClassPath = classPath
7883
}
@@ -84,12 +89,14 @@ object SootUtils {
8489
}
8590

8691
/**
87-
Convert code to Jimple
92+
* Convert code to Jimple
8893
*/
89-
private fun initSoot(buildDir: Path, classpath: String?) {
94+
private fun initSoot(buildDir: Path, classpath: String?, jdkInfo: JdkInfo) {
9095
G.reset()
9196
val options = Options.v()
9297

98+
G.v().initJdk(G.JreInfo(jdkInfo.path.toString(), jdkInfo.version)) // init Soot with the right jdk
99+
93100
options.apply {
94101
set_prepend_classpath(true)
95102
// set true to debug. Disabled because of a bug when two different variables

0 commit comments

Comments
 (0)