Skip to content

Commit c9d2fd5

Browse files
authored
Added symbolic wrappers for Ut iterators (#1725)
1 parent 35fa737 commit c9d2fd5

File tree

10 files changed

+262
-7
lines changed

10 files changed

+262
-7
lines changed

utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/ListIteratorsTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import org.utbot.framework.plugin.api.CodegenLanguage
55
import kotlin.math.min
66
import org.junit.jupiter.api.Test
77
import org.utbot.testcheckers.eq
8+
import org.utbot.testcheckers.withoutConcrete
89
import org.utbot.testing.CodeGeneration
910
import org.utbot.testing.DoNotCalculate
11+
import org.utbot.testing.FullWithAssumptions
1012
import org.utbot.testing.UtValueTestCaseChecker
1113
import org.utbot.testing.ignoreExecutionsNumber
1214

@@ -19,6 +21,31 @@ internal class ListIteratorsTest : UtValueTestCaseChecker(
1921
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
2022
)
2123
) {
24+
@Test
25+
fun testReturnIterator() {
26+
withoutConcrete { // We need to check that a real class is returned but not `Ut` one
27+
check(
28+
ListIterators::returnIterator,
29+
ignoreExecutionsNumber,
30+
{ l, r -> l.isEmpty() && r!!.asSequence().toList().isEmpty() },
31+
{ l, r -> l.isNotEmpty() && r!!.asSequence().toList() == l },
32+
coverage = FullWithAssumptions(assumeCallsNumber = 1)
33+
)
34+
}
35+
}
36+
37+
@Test
38+
fun testReturnListIterator() {
39+
withoutConcrete { // We need to check that a real class is returned but not `Ut` one
40+
check(
41+
ListIterators::returnListIterator,
42+
ignoreExecutionsNumber,
43+
{ l, r -> l.isEmpty() && r!!.asSequence().toList().isEmpty() },
44+
{ l, r -> l.isNotEmpty() && r!!.asSequence().toList() == l },
45+
coverage = FullWithAssumptions(assumeCallsNumber = 1)
46+
)
47+
}
48+
}
2249

2350
@Test
2451
fun testIterate() {

utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/SetIteratorsTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package org.utbot.examples.collections
33
import org.utbot.framework.plugin.api.CodegenLanguage
44
import org.junit.jupiter.api.Test
55
import org.utbot.testcheckers.ge
6+
import org.utbot.testcheckers.withoutConcrete
67
import org.utbot.testing.CodeGeneration
8+
import org.utbot.testing.FullWithAssumptions
79
import org.utbot.testing.UtValueTestCaseChecker
810
import org.utbot.testing.between
911
import org.utbot.testing.ignoreExecutionsNumber
@@ -18,6 +20,19 @@ class SetIteratorsTest : UtValueTestCaseChecker(
1820
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
1921
)
2022
) {
23+
@Test
24+
fun testReturnIterator() {
25+
withoutConcrete { // We need to check that a real class is returned but not `Ut` one
26+
check(
27+
SetIterators::returnIterator,
28+
ignoreExecutionsNumber,
29+
{ s, r -> s.isEmpty() && r!!.asSequence().toSet().isEmpty() },
30+
{ s, r -> s.isNotEmpty() && r!!.asSequence().toSet() == s },
31+
coverage = FullWithAssumptions(assumeCallsNumber = 1)
32+
)
33+
}
34+
}
35+
2136
@Test
2237
fun testIteratorHasNext() {
2338
check(

utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ public int lastIndexOf(Object o) {
322322
@Override
323323
public Iterator<E> iterator() {
324324
preconditionCheck();
325-
return new UtArrayListIterator(0);
325+
return new UtArrayListSimpleIterator(0);
326326
}
327327

328328
@NotNull
@@ -405,6 +405,43 @@ public List<E> subList(int fromIndex, int toIndex) {
405405
return this.toList().subList(fromIndex, toIndex);
406406
}
407407

408+
public class UtArrayListSimpleIterator implements Iterator<E> {
409+
int index;
410+
int prevIndex = -1;
411+
412+
UtArrayListSimpleIterator(int index) {
413+
rangeCheckForAdd(index);
414+
this.index = index;
415+
}
416+
417+
@Override
418+
public boolean hasNext() {
419+
preconditionCheck();
420+
return index != elementData.end;
421+
}
422+
423+
@Override
424+
public E next() {
425+
preconditionCheck();
426+
if (index == elementData.end) {
427+
throw new NoSuchElementException();
428+
}
429+
prevIndex = index;
430+
return elementData.get(index++);
431+
}
432+
433+
@Override
434+
public void remove() {
435+
preconditionCheck();
436+
if (prevIndex == -1) {
437+
throw new IllegalStateException();
438+
}
439+
elementData.end--;
440+
elementData.remove(prevIndex);
441+
prevIndex = -1;
442+
}
443+
}
444+
408445
public class UtArrayListIterator implements ListIterator<E> {
409446
int index;
410447
int prevIndex = -1;

utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ public List<E> subList(int fromIndex, int toIndex) {
435435
@Override
436436
public Iterator<E> iterator() {
437437
preconditionCheck();
438+
439+
// Some implementations of `iterator` return an instance of ListIterator
438440
return new UtLinkedListIterator(elementData.begin);
439441
}
440442

@@ -449,7 +451,7 @@ public ListIterator<E> listIterator() {
449451
@Override
450452
public Iterator<E> descendingIterator() {
451453
preconditionCheck();
452-
return new ReverseIteratorWrapper(elementData.end);
454+
return new UtReverseIterator(elementData.end);
453455
}
454456

455457
@Override
@@ -467,12 +469,12 @@ public Stream<E> parallelStream() {
467469
return stream();
468470
}
469471

470-
public class ReverseIteratorWrapper implements ListIterator<E> {
472+
public class UtReverseIterator implements ListIterator<E> {
471473

472474
int index;
473475
int prevIndex = -1;
474476

475-
ReverseIteratorWrapper(int index) {
477+
UtReverseIterator(int index) {
476478
this.index = index;
477479
}
478480

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.utbot.engine
2+
3+
import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator
4+
import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator
5+
import org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator
6+
import org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator
7+
import org.utbot.framework.plugin.api.ClassId
8+
import org.utbot.framework.plugin.api.FieldId
9+
import org.utbot.framework.plugin.api.MethodId
10+
import org.utbot.framework.plugin.api.UtAssembleModel
11+
import org.utbot.framework.plugin.api.UtExecutableCallModel
12+
import org.utbot.framework.plugin.api.UtModel
13+
import org.utbot.framework.plugin.api.UtReferenceModel
14+
import org.utbot.framework.plugin.api.util.id
15+
import org.utbot.framework.plugin.api.util.methodId
16+
import soot.SootClass
17+
import soot.SootMethod
18+
import kotlin.reflect.KClass
19+
import kotlin.reflect.jvm.jvmName
20+
21+
/**
22+
* Abstract wrapper for iterator of [java.util.Collection].
23+
*/
24+
abstract class CollectionIteratorWrapper(overriddenClass: KClass<*>) : BaseOverriddenWrapper(overriddenClass.jvmName) {
25+
protected abstract val modelName: String
26+
protected abstract val javaCollectionClassId: ClassId
27+
protected abstract val iteratorMethodId: MethodId
28+
protected abstract val iteratorClassId: ClassId
29+
30+
override fun Traverser.overrideInvoke(
31+
wrapper: ObjectValue,
32+
method: SootMethod,
33+
parameters: List<SymbolicValue>
34+
): List<InvokeResult>? = null
35+
36+
override fun value(resolver: Resolver, wrapper: ObjectValue): UtModel = resolver.run {
37+
val addr = holder.concreteAddr(wrapper.addr)
38+
val fieldModels = collectFieldModels(wrapper.addr, overriddenClass.type)
39+
40+
val containerFieldId = overriddenClass.enclosingClassField
41+
val containerFieldModel = fieldModels[containerFieldId] as UtReferenceModel
42+
43+
val instantiationCall = UtExecutableCallModel(
44+
instance = containerFieldModel,
45+
executable = iteratorMethodId,
46+
params = emptyList()
47+
)
48+
49+
UtAssembleModel(addr, iteratorClassId, modelName, instantiationCall)
50+
}
51+
}
52+
53+
class IteratorOfListWrapper : CollectionIteratorWrapper(UtArrayListSimpleIterator::class) {
54+
override val modelName: String = "iteratorOfList"
55+
override val javaCollectionClassId: ClassId = java.util.List::class.id
56+
override val iteratorClassId: ClassId = java.util.Iterator::class.id
57+
override val iteratorMethodId: MethodId = methodId(
58+
classId = javaCollectionClassId,
59+
name = "iterator",
60+
returnType = iteratorClassId,
61+
arguments = emptyArray()
62+
)
63+
}
64+
65+
class ListIteratorOfListWrapper : CollectionIteratorWrapper(UtArrayListIterator::class) {
66+
override val modelName: String = "listIteratorOfList"
67+
override val javaCollectionClassId: ClassId = java.util.List::class.id
68+
override val iteratorClassId: ClassId = java.util.ListIterator::class.id
69+
override val iteratorMethodId: MethodId = methodId(
70+
classId = javaCollectionClassId,
71+
name = "listIterator",
72+
returnType = iteratorClassId,
73+
arguments = emptyArray()
74+
)
75+
}
76+
77+
class IteratorOfSetWrapper : CollectionIteratorWrapper(UtHashSetIterator::class) {
78+
override val modelName: String = "iteratorOfSet"
79+
override val javaCollectionClassId: ClassId = java.util.Set::class.id
80+
override val iteratorClassId: ClassId = java.util.Iterator::class.id
81+
override val iteratorMethodId: MethodId = methodId(
82+
classId = javaCollectionClassId,
83+
name = "iterator",
84+
returnType = iteratorClassId,
85+
arguments = emptyArray()
86+
)
87+
}
88+
89+
class ReverseIteratorWrapper :CollectionIteratorWrapper(UtReverseIterator::class) {
90+
override val modelName: String = "reverseIterator"
91+
override val javaCollectionClassId: ClassId = java.util.Deque::class.id
92+
override val iteratorClassId: ClassId = java.util.Iterator::class.id
93+
override val iteratorMethodId: MethodId = methodId(
94+
classId = javaCollectionClassId,
95+
name = "descendingIterator",
96+
returnType = iteratorClassId,
97+
arguments = emptyArray()
98+
)
99+
}
100+
101+
internal val SootClass.enclosingClassField: FieldId
102+
get() {
103+
require(isInnerClass) {
104+
"Cannot get field for enclosing class of non-inner class $this"
105+
}
106+
107+
return getFieldByName("this$0").fieldId
108+
}

utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,11 @@ val LINKED_HASH_MAP_TYPE: RefType
460460
val HASH_MAP_TYPE: RefType
461461
get() = Scene.v().getSootClass(java.util.HashMap::class.java.canonicalName).type
462462

463+
val ITERATOR_TYPE: RefType
464+
get() = Scene.v().getSootClass(java.util.Iterator::class.java.canonicalName).type
465+
val LIST_ITERATOR_TYPE: RefType
466+
get() = Scene.v().getSootClass(java.util.ListIterator::class.java.canonicalName).type
467+
463468
val STREAM_TYPE: RefType
464469
get() = Scene.v().getSootClass(java.util.stream.Stream::class.java.canonicalName).type
465470

utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ import org.utbot.engine.UtStreamClass.UT_STREAM
1616
import org.utbot.engine.overrides.collections.AssociativeArray
1717
import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray
1818
import org.utbot.engine.overrides.collections.UtArrayList
19+
import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator
20+
import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator
1921
import org.utbot.engine.overrides.collections.UtHashMap
2022
import org.utbot.engine.overrides.collections.UtHashSet
23+
import org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator
2124
import org.utbot.engine.overrides.collections.UtLinkedList
25+
import org.utbot.engine.overrides.collections.UtLinkedList.UtLinkedListIterator
26+
import org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator
2227
import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck
2328
import org.utbot.engine.overrides.collections.UtOptional
2429
import org.utbot.engine.overrides.collections.UtOptionalDouble
@@ -67,6 +72,7 @@ import soot.Scene
6772
import soot.SootClass
6873
import soot.SootMethod
6974
import kotlin.reflect.KClass
75+
import kotlin.reflect.jvm.jvmName
7076

7177
typealias TypeToBeWrapped = RefType
7278
typealias WrapperType = RefType
@@ -123,6 +129,13 @@ val classToWrapper: MutableMap<TypeToBeWrapped, WrapperType> =
123129
putSootClass(java.util.HashMap::class, UtHashMap::class)
124130
putSootClass(java.util.concurrent.ConcurrentHashMap::class, UtHashMap::class)
125131

132+
// Iterators
133+
putSootClass(java.util.Iterator::class, UtArrayListSimpleIterator::class)
134+
putSootClass(java.util.ListIterator::class, UtArrayListIterator::class)
135+
putSootClass(UtLinkedListIterator::class, UtLinkedListIterator::class)
136+
putSootClass(UtReverseIterator::class, UtReverseIterator::class)
137+
putSootClass(UtHashSetIterator::class, UtHashSetIterator::class)
138+
126139
putSootClass(java.util.stream.BaseStream::class, UT_STREAM.className)
127140
putSootClass(java.util.stream.Stream::class, UT_STREAM.className)
128141
putSootClass(java.util.stream.IntStream::class, UT_INT_STREAM.className)
@@ -158,7 +171,7 @@ val wrapperToClass: Map<WrapperType, Set<TypeToBeWrapped>> =
158171
private fun MutableMap<TypeToBeWrapped, WrapperType>.putSootClass(
159172
key: KClass<*>,
160173
value: KClass<*>
161-
) = putSootClass(key, Scene.v().getSootClass(value.java.canonicalName).type)
174+
) = putSootClass(key, Scene.v().getSootClass(value.jvmName).type) // It is important to use `jvmName` because `canonicalName` replaces `$` for nested classes to `.`
162175

163176
private fun MutableMap<TypeToBeWrapped, WrapperType>.putSootClass(
164177
key: KClass<*>,
@@ -173,7 +186,7 @@ private fun MutableMap<TypeToBeWrapped, WrapperType>.putSootClass(
173186
private fun MutableMap<TypeToBeWrapped, WrapperType>.putSootClass(
174187
key: KClass<*>,
175188
value: RefType
176-
) = put(Scene.v().getSootClass(key.java.canonicalName).type, value)
189+
) = put(Scene.v().getSootClass(key.jvmName).type, value) // It is important to use `jvmName` because `canonicalName` replaces `$` for nested classes to `.`
177190

178191
private val wrappers: Map<ClassId, (RefType, UtAddrExpression) -> ObjectValue> = mutableMapOf(
179192
wrap(java.lang.StringBuilder::class) { type, addr -> objectValue(type, addr, UtStringBuilderWrapper()) },
@@ -235,6 +248,10 @@ private val wrappers: Map<ClassId, (RefType, UtAddrExpression) -> ObjectValue> =
235248
wrap(java.util.HashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) },
236249
wrap(java.util.concurrent.ConcurrentHashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) },
237250

251+
// iterator wrappers
252+
wrap(java.util.Iterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfListWrapper()) },
253+
wrap(java.util.ListIterator::class) { _, addr -> objectValue(LIST_ITERATOR_TYPE, addr, ListIteratorOfListWrapper()) },
254+
238255
// stream wrappers
239256
wrap(java.util.stream.BaseStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) },
240257
wrap(java.util.stream.Stream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) },
@@ -272,6 +289,12 @@ private val wrappers: Map<ClassId, (RefType, UtAddrExpression) -> ObjectValue> =
272289

273290
wrap(UtHashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) },
274291

292+
wrap(UtArrayListSimpleIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfListWrapper()) },
293+
wrap(UtArrayListIterator::class) { _, addr -> objectValue(LIST_ITERATOR_TYPE, addr, ListIteratorOfListWrapper()) },
294+
wrap(UtLinkedListIterator::class) { _, addr -> objectValue(LIST_ITERATOR_TYPE, addr, ListIteratorOfListWrapper()) }, // use ListIterator instead of simple Iterator because java.util.LinkedList may return ListIterator for `iterator`
295+
wrap(UtReverseIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, ReverseIteratorWrapper()) },
296+
wrap(UtHashSetIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfSetWrapper()) },
297+
275298
wrap(UtStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) },
276299
wrap(UtIntStream::class) { _, addr -> objectValue(INT_STREAM_TYPE, addr, IntStreamWrapper()) },
277300
wrap(UtLongStream::class) { _, addr -> objectValue(LONG_STREAM_TYPE, addr, LongStreamWrapper()) },

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.utbot.framework.util
22

33
import org.utbot.common.FileUtil
44
import org.utbot.engine.jimpleBody
5+
import org.utbot.engine.overrides.collections.UtLinkedList
56
import org.utbot.engine.pureJavaSignature
67
import org.utbot.framework.UtSettings
78
import org.utbot.framework.plugin.api.ExecutableId
@@ -159,11 +160,12 @@ private val classesToLoad = arrayOf(
159160
org.utbot.engine.overrides.collections.UtOptionalLong::class,
160161
org.utbot.engine.overrides.collections.UtOptionalDouble::class,
161162
org.utbot.engine.overrides.collections.UtArrayList::class,
163+
org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator::class,
162164
org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator::class,
163165
org.utbot.engine.overrides.collections.UtLinkedList::class,
164166
org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck::class,
165167
org.utbot.engine.overrides.collections.UtLinkedList.UtLinkedListIterator::class,
166-
org.utbot.engine.overrides.collections.UtLinkedList.ReverseIteratorWrapper::class,
168+
org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator::class,
167169
org.utbot.engine.overrides.collections.UtHashSet::class,
168170
org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator::class,
169171
org.utbot.engine.overrides.collections.UtHashMap::class,

0 commit comments

Comments
 (0)