Skip to content

Commit e741e96

Browse files
committed
fix nd without loading class
1 parent ef6a6ba commit e741e96

File tree

5 files changed

+80
-90
lines changed

5 files changed

+80
-90
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ open class ClassId @JvmOverloads constructor(
846846
*/
847847
open val allMethods: Sequence<MethodId>
848848
get() = generateSequence(jClass) { it.superclass }
849+
.flatMap { it.interfaces.toMutableList() + it }
849850
.mapNotNull { it.declaredMethods }
850851
.flatMap { it.toList() }
851852
.map { it.executableId }

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/ndd/NonDeterministicBytecodeInserter.kt

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,77 @@ package org.utbot.instrumentation.instrumentation.execution.ndd
33
import org.objectweb.asm.MethodVisitor
44
import org.objectweb.asm.Opcodes
55
import org.objectweb.asm.Type
6-
import org.utbot.framework.plugin.api.ClassId
7-
import org.utbot.framework.plugin.api.MethodId
8-
import org.utbot.framework.plugin.api.util.*
96

107
class NonDeterministicBytecodeInserter {
118
private val internalName = Type.getInternalName(NonDeterministicResultStorage::class.java)
129

13-
private fun ClassId.descriptor(): String = when (this) {
14-
booleanClassId -> "Z"
15-
byteClassId -> "B"
16-
charClassId -> "C"
17-
shortClassId -> "S"
18-
intClassId -> "I"
19-
longClassId -> "J"
20-
floatClassId -> "F"
21-
doubleClassId -> "D"
22-
else -> "Ljava/lang/Object;"
10+
private fun String.getUnifiedParamsTypes(): List<String> {
11+
val list = mutableListOf<String>()
12+
var readObject = false
13+
for (c in this) {
14+
if (c == '(') {
15+
continue
16+
}
17+
if (c == ')') {
18+
break
19+
}
20+
if (readObject) {
21+
if (c == ';') {
22+
readObject = false
23+
list.add("Ljava/lang/Object;")
24+
}
25+
} else if (c == 'L') {
26+
readObject = true
27+
} else {
28+
list.add(c.toString())
29+
}
30+
}
31+
32+
return list
2333
}
2434

25-
private fun MethodId.toStoreDescriptor(): String = buildString {
35+
private fun String.unifyTypeDescriptor(): String =
36+
if (startsWith('L')) {
37+
"Ljava/lang/Object;"
38+
} else {
39+
this
40+
}
41+
42+
private fun String.getReturnType(): String =
43+
substringAfter(')')
44+
45+
private fun getStoreDescriptor(descriptor: String): String = buildString {
2646
append('(')
27-
append(returnType.descriptor())
47+
append(descriptor.getReturnType().unifyTypeDescriptor())
2848
append("Ljava/lang/String;)V")
2949
}
3050

3151
private fun MethodVisitor.invoke(name: String, descriptor: String) {
3252
visitMethodInsn(Opcodes.INVOKESTATIC, internalName, name, descriptor, false)
3353
}
3454

35-
fun insertAfterNDMethod(mv: MethodVisitor, methodId: MethodId) {
55+
fun insertAfterNDMethod(mv: MethodVisitor, owner: String, name: String, descriptor: String, isStatic: Boolean) {
3656
mv.visitInsn(Opcodes.DUP)
37-
mv.visitLdcInsn(NonDeterministicResultStorage.methodToSignature(methodId))
38-
mv.invoke(if (methodId.isStatic) "storeStatic" else "storeCall", methodId.toStoreDescriptor())
57+
mv.visitLdcInsn(NonDeterministicResultStorage.makeSignature(owner, name, descriptor))
58+
mv.invoke(if (isStatic) "storeStatic" else "storeCall", getStoreDescriptor(descriptor))
3959
}
4060

41-
fun insertBeforeNDMethod(mv: MethodVisitor, methodId: MethodId) {
42-
if (methodId.isStatic) {
61+
fun insertBeforeNDMethod(mv: MethodVisitor, descriptor: String, isStatic: Boolean) {
62+
if (isStatic) {
4363
return
4464
}
4565

46-
methodId.parameters.asReversed().forEach {
47-
val desc = it.descriptor()
48-
mv.invoke("putParameter${desc[0]}", "($desc)V")
66+
val params = descriptor.getUnifiedParamsTypes()
67+
68+
params.asReversed().forEach {
69+
mv.invoke("putParameter${it[0]}", "($it)V")
4970
}
5071

5172
mv.visitInsn(Opcodes.DUP)
5273
mv.invoke("saveInstance", "(Ljava/lang/Object;)V")
5374

54-
methodId.parameters.forEach {
55-
val desc = it.descriptor()
56-
mv.invoke("peakParameter${desc[0]}", "()$desc")
75+
params.forEach {
76+
mv.invoke("peakParameter${it[0]}", "()$it")
5777
}
5878
}
5979

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/ndd/NonDeterministicClassVisitor.kt

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,20 @@ package org.utbot.instrumentation.instrumentation.execution.ndd
22

33
import org.objectweb.asm.ClassVisitor
44
import org.objectweb.asm.MethodVisitor
5-
import org.objectweb.asm.commons.Method
6-
import org.utbot.framework.plugin.api.ClassId
7-
import org.utbot.framework.plugin.api.MethodId
8-
import org.utbot.framework.plugin.api.util.executableId
9-
import org.utbot.framework.plugin.api.util.id
10-
import org.utbot.framework.plugin.api.util.utContext
5+
import org.objectweb.asm.Opcodes
116
import org.utbot.instrumentation.Settings
127

138
class NonDeterministicClassVisitor(
149
classVisitor: ClassVisitor,
1510
private val detector: NonDeterministicDetector
1611
) : ClassVisitor(Settings.ASM_API, classVisitor) {
1712

18-
private var currentClass: String? = null
19-
20-
private fun getOwnerClass(owner: String): Class<*> =
21-
utContext.classLoader.loadClass(owner.replace('/', '.'))
22-
23-
private fun getMethodAndOwnerId(owner: String?, name: String?, descriptor: String?): Pair<ClassId, MethodId>? {
24-
if (owner == null || name == null || descriptor == null) {
25-
return null
26-
}
27-
val clazz = getOwnerClass(owner)
28-
val method = clazz.methods.find {
29-
it.name == name && Method.getMethod(it).descriptor == descriptor
30-
} ?: return null
31-
return clazz.id to method.executableId
32-
}
13+
private lateinit var currentClass: String
3314

3415
override fun visit(
3516
version: Int,
3617
access: Int,
37-
name: String?,
18+
name: String,
3819
signature: String?,
3920
superName: String?,
4021
interfaces: Array<out String>?
@@ -45,40 +26,42 @@ class NonDeterministicClassVisitor(
4526

4627
override fun visitMethod(
4728
access: Int,
48-
name: String?,
49-
descriptor: String?,
29+
name: String,
30+
descriptor: String,
5031
signature: String?,
5132
exceptions: Array<out String>?
5233
): MethodVisitor {
5334
val mv = cv.visitMethod(access, name, descriptor, signature, exceptions)
5435
return object : MethodVisitor(Settings.ASM_API, mv) {
5536
override fun visitMethodInsn(
5637
opcodeAndSource: Int,
57-
owner: String?,
58-
name: String?,
59-
descriptor: String?,
38+
owner: String,
39+
name: String,
40+
descriptor: String,
6041
isInterface: Boolean
6142
) {
6243
if (name == "<init>") {
6344
mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface)
64-
owner?.let {
65-
if (detector.isNonDeterministic(getOwnerClass(it).id)) {
66-
detector.inserter.insertAfterNDInstanceConstructor(mv, currentClass!!)
67-
}
45+
if (detector.isNonDeterministicClass(owner)) {
46+
detector.inserter.insertAfterNDInstanceConstructor(mv, currentClass)
6847
}
6948
return
7049
}
7150

72-
getMethodAndOwnerId(owner, name, descriptor)?.let { (caller, method) ->
73-
if (detector.isNonDeterministic(caller, method)) {
74-
detector.inserter.insertBeforeNDMethod(mv, method)
75-
mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface)
76-
detector.inserter.insertAfterNDMethod(mv, method)
77-
return
78-
}
51+
val (isND, isStatic) = if (opcodeAndSource == Opcodes.INVOKESTATIC) {
52+
detector.isNonDeterministicStaticFunction(owner, name, descriptor) to true
53+
} else {
54+
detector.isNonDeterministicClass(owner) to false
55+
}
56+
57+
if (isND) {
58+
detector.inserter.insertBeforeNDMethod(mv, descriptor, isStatic)
59+
mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface)
60+
detector.inserter.insertAfterNDMethod(mv, owner, name, descriptor, isStatic)
61+
} else {
62+
mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface)
7963
}
8064

81-
mv.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface)
8265
}
8366
}
8467
}
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,21 @@
11
package org.utbot.instrumentation.instrumentation.execution.ndd
22

3-
import org.utbot.framework.plugin.api.ClassId
4-
import org.utbot.framework.plugin.api.MethodId
5-
import org.utbot.framework.plugin.api.util.id
6-
import org.utbot.framework.plugin.api.util.isStatic
7-
import kotlin.random.Random
8-
93
class NonDeterministicDetector {
10-
private val nonDeterministicStaticMethods: HashSet<MethodId> = HashSet()
4+
private val nonDeterministicStaticMethods: HashSet<String> = HashSet()
115

12-
private val nonDeterministicClasses: HashSet<ClassId> = buildList {
13-
add(java.util.Random::class.java)
14-
add(Random::class.java)
15-
}.map { it.id }.toHashSet()
6+
private val nonDeterministicClasses: HashSet<String> = buildList {
7+
add("java/util/Random")
8+
add("kotlin/random/Random")
9+
}.toHashSet()
1610

1711
val inserter = NonDeterministicBytecodeInserter()
1812

19-
fun isNonDeterministic(caller: ClassId, method: MethodId): Boolean {
20-
return if (method.isStatic) {
21-
nonDeterministicStaticMethods.contains(method)
22-
} else {
23-
isNonDeterministic(caller)
24-
}
13+
fun isNonDeterministicStaticFunction(owner: String, name: String, descriptor: String): Boolean {
14+
return nonDeterministicStaticMethods.contains("$owner $name$descriptor")
2515
}
2616

27-
fun isNonDeterministic(clazz: ClassId): Boolean {
28-
var cl: ClassId? = clazz
29-
while (cl != null && !nonDeterministicClasses.contains(cl)) {
30-
cl = cl.superclass?.id
31-
}
32-
return nonDeterministicClasses.contains(cl)
17+
fun isNonDeterministicClass(clazz: String): Boolean {
18+
return nonDeterministicClasses.contains(clazz)
3319
}
3420

3521
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/ndd/NonDeterministicResultStorage.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ object NonDeterministicResultStorage {
2828
nextInstanceNumber = 1
2929
}
3030

31-
fun methodToSignature(methodId: MethodId): String {
32-
return "${methodId.classId.name} ${methodId.signature}"
31+
fun makeSignature(owner: String, name: String, descriptor: String): String {
32+
return "$owner $name$descriptor"
3333
}
3434

3535
fun signatureToMethod(signature: String): MethodId? {

0 commit comments

Comments
 (0)