1
1
package org.utbot.contest.usvm.jc
2
2
3
- import io.ksmt.utils.asExpr
4
3
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
8
11
import org.usvm.api.JcCoverage
9
12
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
14
14
import org.usvm.instrumentation.executor.UTestConcreteExecutor
15
15
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
17
21
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
26
22
import org.usvm.machine.state.JcState
27
23
import org.usvm.machine.state.localIdx
28
24
import org.usvm.memory.ULValue
29
25
import org.usvm.memory.UReadOnlyMemory
30
26
import org.usvm.memory.URegisterStackLValue
31
27
import org.usvm.model.UModelBase
32
- import org.usvm.types.first
33
- import org.usvm.types.firstOrNull
34
28
35
29
/* *
36
30
* A class, responsible for resolving a single [JcExecution] for a specific method from a symbolic state.
@@ -45,12 +39,12 @@ class JcTestExecutor(
45
39
/* *
46
40
* Resolves a [JcTest] from a [method] from a [state].
47
41
*/
48
- fun execute (method : JcTypedMethod , state : JcState ): JcExecution {
42
+ fun execute (method : JcTypedMethod , state : JcState , stringConstants : Map < String , UConcreteHeapRef > ): JcExecution {
49
43
val model = state.models.first()
50
44
51
45
val ctx = state.ctx
52
46
53
- val memoryScope = MemoryScope (ctx, model, model, method)
47
+ val memoryScope = MemoryScope (ctx, model, model, stringConstants, method)
54
48
55
49
val uTest = memoryScope.createUTest()
56
50
@@ -76,20 +70,21 @@ class JcTestExecutor(
76
70
* @param memory a read-only memory to read [ULValue]s from.
77
71
*/
78
72
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) {
84
79
85
- private val resolvedCache = mutableMapOf< UConcreteHeapAddress , Pair < UTestExpression , List < UTestInst >>>( )
80
+ override val decoderApi = JcTestExecutorDecoderApi (ctx )
86
81
87
82
fun createUTest (): UTest {
88
83
val thisInstance = if (! method.isStatic) {
89
84
val ref = URegisterStackLValue (ctx.addressSort, idx = 0 )
90
85
resolveLValue(ref, method.enclosingType)
91
86
} else {
92
- UTestNullExpression (ctx.cp.objectType) to listOf ()
87
+ UTestNullExpression (ctx.cp.objectType)
93
88
}
94
89
95
90
val parameters = method.parameters.mapIndexed { idx, param ->
@@ -98,230 +93,19 @@ class JcTestExecutor(
98
93
resolveLValue(ref, param.type)
99
94
}
100
95
101
- val initStmts = thisInstance.second + parameters.flatMap { it.second }
96
+ val initStmts = decoderApi.initializerInstructions()
102
97
val callExpr = if (method.isStatic) {
103
- UTestStaticMethodCall (method.method, parameters.map { it.first } )
98
+ UTestStaticMethodCall (method.method, parameters)
104
99
} else {
105
- UTestMethodCall (thisInstance.first , method.method, parameters.map { it.first } )
100
+ UTestMethodCall (thisInstance, method.method, parameters)
106
101
}
107
102
return UTest (initStmts, callExpr)
108
103
}
109
104
105
+ override fun allocateClassInstance (type : JcClassType ): UTestExpression =
106
+ UTestAllocateMemoryCall (type.jcClass)
110
107
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
325
110
}
326
-
327
- }
111
+ }
0 commit comments