Skip to content

Commit d25e3bb

Browse files
committed
Actualise JcTestExecutor
1 parent 5c82e57 commit d25e3bb

File tree

3 files changed

+158
-248
lines changed

3 files changed

+158
-248
lines changed

utbot-junit-contest/src/main/kotlin/org/utbot/contest/usvm/ContestUsvm.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ fun runUsvmGeneration(
172172
) { state ->
173173
val jcExecution = accumulateMeasureTime("executor.execute(${cut.classId.name})", timeStats, state.entrypoint) {
174174
runCatching {
175-
executor.execute(state.entrypoint.typedMethod, state)
175+
executor.execute(state.entrypoint.typedMethod, state, jcContainer.machine.stringConstants)
176176
}.getOrElse { e ->
177177
logger.error(e) { "executor.execute(${state.entrypoint}) failed" }
178178
return@analyzeAsync
Lines changed: 31 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,30 @@
11
package org.utbot.contest.usvm.jc
22

3-
import io.ksmt.utils.asExpr
43
import kotlinx.coroutines.runBlocking
5-
import org.jacodb.api.*
6-
import org.jacodb.api.ext.*
7-
import org.usvm.*
4+
import org.jacodb.api.JcClassType
5+
import org.jacodb.api.JcClasspath
6+
import org.jacodb.api.JcType
7+
import org.jacodb.api.JcTypedMethod
8+
import org.jacodb.api.ext.objectType
9+
import org.usvm.UConcreteHeapRef
10+
import org.usvm.UExpr
811
import org.usvm.api.JcCoverage
912
import org.usvm.api.JcTest
10-
import org.usvm.api.typeStreamOf
11-
import org.usvm.collection.array.UArrayIndexLValue
12-
import org.usvm.collection.array.length.UArrayLengthLValue
13-
import org.usvm.collection.field.UFieldLValue
13+
import org.usvm.api.util.JcTestStateResolver
1414
import org.usvm.instrumentation.executor.UTestConcreteExecutor
1515
import org.usvm.instrumentation.testcase.UTest
16-
import org.usvm.instrumentation.testcase.api.*
16+
import org.usvm.instrumentation.testcase.api.UTestAllocateMemoryCall
17+
import org.usvm.instrumentation.testcase.api.UTestExpression
18+
import org.usvm.instrumentation.testcase.api.UTestMethodCall
19+
import org.usvm.instrumentation.testcase.api.UTestNullExpression
20+
import org.usvm.instrumentation.testcase.api.UTestStaticMethodCall
1721
import org.usvm.machine.JcContext
18-
import org.usvm.machine.extractBool
19-
import org.usvm.machine.extractByte
20-
import org.usvm.machine.extractChar
21-
import org.usvm.machine.extractDouble
22-
import org.usvm.machine.extractFloat
23-
import org.usvm.machine.extractInt
24-
import org.usvm.machine.extractLong
25-
import org.usvm.machine.extractShort
2622
import org.usvm.machine.state.JcState
2723
import org.usvm.machine.state.localIdx
2824
import org.usvm.memory.ULValue
2925
import org.usvm.memory.UReadOnlyMemory
3026
import org.usvm.memory.URegisterStackLValue
3127
import org.usvm.model.UModelBase
32-
import org.usvm.types.first
33-
import org.usvm.types.firstOrNull
3428

3529
/**
3630
* A class, responsible for resolving a single [JcExecution] for a specific method from a symbolic state.
@@ -45,12 +39,12 @@ class JcTestExecutor(
4539
/**
4640
* Resolves a [JcTest] from a [method] from a [state].
4741
*/
48-
fun execute(method: JcTypedMethod, state: JcState): JcExecution {
42+
fun execute(method: JcTypedMethod, state: JcState, stringConstants: Map<String, UConcreteHeapRef>): JcExecution {
4943
val model = state.models.first()
5044

5145
val ctx = state.ctx
5246

53-
val memoryScope = MemoryScope(ctx, model, model, method)
47+
val memoryScope = MemoryScope(ctx, model, model, stringConstants, method)
5448

5549
val uTest = memoryScope.createUTest()
5650

@@ -76,20 +70,21 @@ class JcTestExecutor(
7670
* @param memory a read-only memory to read [ULValue]s from.
7771
*/
7872
private class MemoryScope(
79-
private val ctx: JcContext,
80-
private val model: UModelBase<JcType>,
81-
private val memory: UReadOnlyMemory<JcType>,
82-
private val method: JcTypedMethod,
83-
) {
73+
ctx: JcContext,
74+
model: UModelBase<JcType>,
75+
memory: UReadOnlyMemory<JcType>,
76+
stringConstants: Map<String, UConcreteHeapRef>,
77+
method: JcTypedMethod,
78+
) : JcTestStateResolver<UTestExpression>(ctx, model, memory, stringConstants, method) {
8479

85-
private val resolvedCache = mutableMapOf<UConcreteHeapAddress, Pair<UTestExpression, List<UTestInst>>>()
80+
override val decoderApi = JcTestExecutorDecoderApi(ctx)
8681

8782
fun createUTest(): UTest {
8883
val thisInstance = if (!method.isStatic) {
8984
val ref = URegisterStackLValue(ctx.addressSort, idx = 0)
9085
resolveLValue(ref, method.enclosingType)
9186
} else {
92-
UTestNullExpression(ctx.cp.objectType) to listOf()
87+
UTestNullExpression(ctx.cp.objectType)
9388
}
9489

9590
val parameters = method.parameters.mapIndexed { idx, param ->
@@ -98,230 +93,19 @@ class JcTestExecutor(
9893
resolveLValue(ref, param.type)
9994
}
10095

101-
val initStmts = thisInstance.second + parameters.flatMap { it.second }
96+
val initStmts = decoderApi.initializerInstructions()
10297
val callExpr = if (method.isStatic) {
103-
UTestStaticMethodCall(method.method, parameters.map { it.first })
98+
UTestStaticMethodCall(method.method, parameters)
10499
} else {
105-
UTestMethodCall(thisInstance.first, method.method, parameters.map { it.first })
100+
UTestMethodCall(thisInstance, method.method, parameters)
106101
}
107102
return UTest(initStmts, callExpr)
108103
}
109104

105+
override fun allocateClassInstance(type: JcClassType): UTestExpression =
106+
UTestAllocateMemoryCall(type.jcClass)
110107

111-
fun resolveLValue(lvalue: ULValue<*, *>, type: JcType): Pair<UTestExpression, List<UTestInst>> =
112-
resolveExpr(memory.read(lvalue), type)
113-
114-
115-
fun resolveExpr(expr: UExpr<out USort>, type: JcType): Pair<UTestExpression, List<UTestInst>> =
116-
when (type) {
117-
is JcPrimitiveType -> resolvePrimitive(expr, type)
118-
is JcRefType -> resolveReference(expr.asExpr(ctx.addressSort), type)
119-
else -> error("Unexpected type: $type")
120-
}
121-
122-
fun resolvePrimitive(
123-
expr: UExpr<out USort>, type: JcPrimitiveType
124-
): Pair<UTestExpression, List<UTestInst>> {
125-
val exprInModel = evaluateInModel(expr)
126-
return when (type) {
127-
ctx.cp.boolean -> UTestBooleanExpression(
128-
value = extractBool(exprInModel) ?: false,
129-
type = ctx.cp.boolean
130-
)
131-
ctx.cp.short -> UTestShortExpression(
132-
value = extractShort(exprInModel) ?: 0,
133-
type = ctx.cp.short
134-
)
135-
ctx.cp.int -> UTestIntExpression(
136-
value = extractInt(exprInModel) ?: 0,
137-
type = ctx.cp.int
138-
)
139-
ctx.cp.long -> UTestLongExpression(
140-
value = extractLong(exprInModel) ?: 0L,
141-
type = ctx.cp.long
142-
)
143-
ctx.cp.float -> UTestFloatExpression(
144-
value = extractFloat(exprInModel) ?: 0.0f,
145-
type = ctx.cp.float
146-
)
147-
ctx.cp.double -> UTestDoubleExpression(
148-
value = extractDouble(exprInModel) ?: 0.0,
149-
type = ctx.cp.double
150-
)
151-
ctx.cp.byte -> UTestByteExpression(
152-
value = extractByte(exprInModel) ?: 0,
153-
type = ctx.cp.byte
154-
)
155-
ctx.cp.char -> UTestCharExpression(
156-
value = extractChar(exprInModel) ?: '\u0000',
157-
type = ctx.cp.char
158-
)
159-
ctx.cp.void -> UTestNullExpression(
160-
type = ctx.cp.void
161-
)
162-
else -> error("Unexpected type: ${type.typeName}")
163-
}.let { it to listOf() }
164-
}
165-
166-
fun resolveReference(heapRef: UHeapRef, type: JcRefType): Pair<UTestExpression, List<UTestInst>> {
167-
val ref = evaluateInModel(heapRef) as UConcreteHeapRef
168-
if (ref.address == NULL_ADDRESS) {
169-
return UTestNullExpression(type) to listOf()
170-
}
171-
// to find a type, we need to understand the source of the object
172-
val typeStream = if (ref.address <= INITIAL_INPUT_ADDRESS) {
173-
// input object
174-
model.typeStreamOf(ref)
175-
} else {
176-
// allocated object
177-
memory.typeStreamOf(ref)
178-
}.filterBySupertype(type)
179-
180-
// We filter allocated object type stream, because it could be stored in the input array,
181-
// which resolved to a wrong type, since we do not build connections between element types
182-
// and array types right now.
183-
// In such cases, we need to resolve this element to null.
184-
185-
val evaluatedType = typeStream.firstOrNull() ?: return UTestNullExpression(type) to listOf()
186-
187-
// We check for the type stream emptiness firsly and only then for the resolved cache,
188-
// because even if the object is already resolved, it could be incompatible with the [type], if it
189-
// is an element of an array of the wrong type.
190-
191-
return resolvedCache.getOrElse(ref.address) {
192-
when (evaluatedType) {
193-
is JcArrayType -> resolveArray(ref, heapRef, evaluatedType)
194-
is JcClassType -> resolveObject(ref, heapRef, evaluatedType)
195-
else -> error("Unexpected type: $type")
196-
}
197-
}
198-
}
199-
200-
private fun resolveArray(
201-
ref: UConcreteHeapRef, heapRef: UHeapRef, type: JcArrayType
202-
): Pair<UTestExpression, List<UTestInst>> {
203-
val arrayDescriptor = ctx.arrayDescriptorOf(type)
204-
val lengthRef = UArrayLengthLValue(heapRef, arrayDescriptor, ctx.sizeSort)
205-
val resolvedLength = resolveLValue(lengthRef, ctx.cp.int).first as UTestIntExpression
206-
// TODO hack
207-
val length =
208-
if (resolvedLength.value in 0..10_000) {
209-
resolvedLength
210-
} else {
211-
UTestIntExpression(0, ctx.cp.int)
212-
}
213-
214-
val cellSort = ctx.typeToSort(type.elementType)
215-
216-
fun resolveElement(idx: Int): Pair<UTestExpression, List<UTestInst>> {
217-
val elemRef = UArrayIndexLValue(cellSort, heapRef, ctx.mkBv(idx), arrayDescriptor)
218-
return resolveLValue(elemRef, type.elementType)
219-
}
220-
221-
val arrayInstance = UTestCreateArrayExpression(type.elementType, length)
222-
223-
val arraySetters = buildList {
224-
for (i in 0 until length.value) {
225-
with(resolveElement(i)) {
226-
add(UTestArraySetStatement(arrayInstance, UTestIntExpression(i, ctx.cp.int), first))
227-
addAll(second)
228-
}
229-
}
230-
}
231-
232-
resolvedCache[ref.address] = arrayInstance to arraySetters
233-
return arrayInstance to arraySetters
234-
}
235-
236-
private fun resolveObject(
237-
ref: UConcreteHeapRef, heapRef: UHeapRef, type: JcRefType
238-
): Pair<UTestExpression, List<UTestInst>> {
239-
240-
if (type.jcClass == ctx.classType.jcClass && ref.address <= INITIAL_STATIC_ADDRESS) {
241-
// Note that non-negative addresses are possible only for the result value.
242-
return resolveAllocatedClass(ref)
243-
}
244-
245-
if (type.jcClass == ctx.stringType.jcClass && ref.address <= INITIAL_STATIC_ADDRESS) {
246-
// Note that non-negative addresses are possible only for the result value.
247-
return resolveAllocatedString(ref)
248-
}
249-
250-
val anyEnumAncestor = type.getEnumAncestorOrNull()
251-
if (anyEnumAncestor != null) {
252-
return resolveEnumValue(heapRef, anyEnumAncestor)
253-
}
254-
255-
256-
val exprs = mutableListOf<UTestExpression>()
257-
val instance = UTestAllocateMemoryCall(type.jcClass)
258-
259-
val fieldSetters = mutableListOf<UTestInst>()
260-
resolvedCache[ref.address] = instance to fieldSetters
261-
262-
exprs.add(instance)
263-
264-
val fields =
265-
generateSequence(type.jcClass) { it.superClass }
266-
.map { it.toType() }
267-
.flatMap { it.declaredFields }
268-
.filter { !it.isStatic }
269-
270-
for (field in fields) {
271-
val lvalue = UFieldLValue(ctx.typeToSort(field.fieldType), heapRef, field.field)
272-
val fieldValue = resolveLValue(lvalue, field.fieldType)
273-
val uTestSetFieldStmt = UTestSetFieldStatement(instance, field.field, fieldValue.first)
274-
fieldSetters.addAll(fieldValue.second)
275-
fieldSetters.add(uTestSetFieldStmt)
276-
}
277-
return instance to fieldSetters
278-
}
279-
280-
private fun resolveEnumValue(
281-
heapRef: UHeapRef,
282-
enumAncestor: JcClassOrInterface
283-
): Pair<UTestExpression, List<UTestInst>> {
284-
with(ctx) {
285-
val ordinalLValue = UFieldLValue(sizeSort, heapRef, enumOrdinalField)
286-
val ordinalFieldValue = resolveLValue(ordinalLValue, cp.int).first as UTestIntExpression
287-
val enumField = enumAncestor.enumValues?.get(ordinalFieldValue.value)
288-
?: error("Cant find enum field with index ${ordinalFieldValue.value}")
289-
290-
return UTestGetStaticFieldExpression(enumField) to listOf()
291-
}
292-
}
293-
294-
private fun resolveAllocatedClass(ref: UConcreteHeapRef): Pair<UTestExpression, List<UTestInst>> {
295-
val classTypeField = ctx.classTypeSyntheticField
296-
val classTypeLValue = UFieldLValue(ctx.addressSort, ref, classTypeField)
297-
val classTypeRef = memory.read(classTypeLValue) as? UConcreteHeapRef
298-
?: error("No type for allocated class")
299-
300-
val classType = memory.typeStreamOf(classTypeRef).first()
301-
return UTestClassExpression(classType) to listOf()
302-
}
303-
304-
private fun resolveAllocatedString(ref: UConcreteHeapRef): Pair<UTestExpression, List<UTestInst>> {
305-
val valueField = ctx.stringValueField
306-
val strValueLValue = UFieldLValue(ctx.typeToSort(valueField.fieldType), ref, valueField.field)
307-
return resolveLValue(strValueLValue, valueField.fieldType)
308-
}
309-
310-
/**
311-
* If we resolve state after, [expr] is read from a state memory, so it requires concretization via [model].
312-
*
313-
* @return a concretized expression.
314-
*/
315-
private fun <T : USort> evaluateInModel(expr: UExpr<T>): UExpr<T> {
316-
return model.eval(expr)
317-
}
318-
319-
// TODO simple org.jacodb.api.ext.JcClasses.isEnum does not work with enums with abstract methods
320-
private fun JcRefType.getEnumAncestorOrNull(): JcClassOrInterface? =
321-
jcClass.getAllSuperHierarchyIncludingThis().firstOrNull { it.isEnum }
322-
323-
private fun JcClassOrInterface.getAllSuperHierarchyIncludingThis() =
324-
(sequenceOf(this) + allSuperHierarchySequence)
108+
// todo: looks incorrect
109+
override fun allocateString(value: UTestExpression): UTestExpression = value
325110
}
326-
327-
}
111+
}

0 commit comments

Comments
 (0)