From 8d93d6b286c346dfb16124c8c5e4ba9c510330a0 Mon Sep 17 00:00:00 2001 From: Yury Kamenev Date: Fri, 20 Jan 2023 10:41:56 +0300 Subject: [PATCH] Added symbolic wrappers for Ut iterators --- .../examples/collections/ListIteratorsTest.kt | 27 +++++ .../examples/collections/SetIteratorsTest.kt | 15 +++ .../overrides/collections/UtArrayList.java | 39 ++++++- .../overrides/collections/UtLinkedList.java | 8 +- .../engine/CollectionIteratorWrappers.kt | 108 ++++++++++++++++++ .../org/utbot/engine/CollectionWrappers.kt | 5 + .../kotlin/org/utbot/engine/ObjectWrappers.kt | 27 ++++- .../org/utbot/framework/util/SootUtils.kt | 4 +- .../examples/collections/ListIterators.java | 23 ++++ .../examples/collections/SetIterators.java | 13 +++ 10 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/CollectionIteratorWrappers.kt diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/ListIteratorsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/ListIteratorsTest.kt index 377e2d1f83..7701250a43 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/ListIteratorsTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/ListIteratorsTest.kt @@ -5,8 +5,10 @@ import org.utbot.framework.plugin.api.CodegenLanguage import kotlin.math.min import org.junit.jupiter.api.Test import org.utbot.testcheckers.eq +import org.utbot.testcheckers.withoutConcrete import org.utbot.testing.CodeGeneration import org.utbot.testing.DoNotCalculate +import org.utbot.testing.FullWithAssumptions import org.utbot.testing.UtValueTestCaseChecker import org.utbot.testing.ignoreExecutionsNumber @@ -19,6 +21,31 @@ internal class ListIteratorsTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { + @Test + fun testReturnIterator() { + withoutConcrete { // We need to check that a real class is returned but not `Ut` one + check( + ListIterators::returnIterator, + ignoreExecutionsNumber, + { l, r -> l.isEmpty() && r!!.asSequence().toList().isEmpty() }, + { l, r -> l.isNotEmpty() && r!!.asSequence().toList() == l }, + coverage = FullWithAssumptions(assumeCallsNumber = 1) + ) + } + } + + @Test + fun testReturnListIterator() { + withoutConcrete { // We need to check that a real class is returned but not `Ut` one + check( + ListIterators::returnListIterator, + ignoreExecutionsNumber, + { l, r -> l.isEmpty() && r!!.asSequence().toList().isEmpty() }, + { l, r -> l.isNotEmpty() && r!!.asSequence().toList() == l }, + coverage = FullWithAssumptions(assumeCallsNumber = 1) + ) + } + } @Test fun testIterate() { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/SetIteratorsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/SetIteratorsTest.kt index 4d7d7f40e0..5480af6807 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/SetIteratorsTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/collections/SetIteratorsTest.kt @@ -3,7 +3,9 @@ package org.utbot.examples.collections import org.utbot.framework.plugin.api.CodegenLanguage import org.junit.jupiter.api.Test import org.utbot.testcheckers.ge +import org.utbot.testcheckers.withoutConcrete import org.utbot.testing.CodeGeneration +import org.utbot.testing.FullWithAssumptions import org.utbot.testing.UtValueTestCaseChecker import org.utbot.testing.between import org.utbot.testing.ignoreExecutionsNumber @@ -18,6 +20,19 @@ class SetIteratorsTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { + @Test + fun testReturnIterator() { + withoutConcrete { // We need to check that a real class is returned but not `Ut` one + check( + SetIterators::returnIterator, + ignoreExecutionsNumber, + { s, r -> s.isEmpty() && r!!.asSequence().toSet().isEmpty() }, + { s, r -> s.isNotEmpty() && r!!.asSequence().toSet() == s }, + coverage = FullWithAssumptions(assumeCallsNumber = 1) + ) + } + } + @Test fun testIteratorHasNext() { check( diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java index 73ce70af93..818526ab4d 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtArrayList.java @@ -322,7 +322,7 @@ public int lastIndexOf(Object o) { @Override public Iterator iterator() { preconditionCheck(); - return new UtArrayListIterator(0); + return new UtArrayListSimpleIterator(0); } @NotNull @@ -405,6 +405,43 @@ public List subList(int fromIndex, int toIndex) { return this.toList().subList(fromIndex, toIndex); } + public class UtArrayListSimpleIterator implements Iterator { + int index; + int prevIndex = -1; + + UtArrayListSimpleIterator(int index) { + rangeCheckForAdd(index); + this.index = index; + } + + @Override + public boolean hasNext() { + preconditionCheck(); + return index != elementData.end; + } + + @Override + public E next() { + preconditionCheck(); + if (index == elementData.end) { + throw new NoSuchElementException(); + } + prevIndex = index; + return elementData.get(index++); + } + + @Override + public void remove() { + preconditionCheck(); + if (prevIndex == -1) { + throw new IllegalStateException(); + } + elementData.end--; + elementData.remove(prevIndex); + prevIndex = -1; + } + } + public class UtArrayListIterator implements ListIterator { int index; int prevIndex = -1; diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java index 577c08aa11..986b043262 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/collections/UtLinkedList.java @@ -435,6 +435,8 @@ public List subList(int fromIndex, int toIndex) { @Override public Iterator iterator() { preconditionCheck(); + + // Some implementations of `iterator` return an instance of ListIterator return new UtLinkedListIterator(elementData.begin); } @@ -449,7 +451,7 @@ public ListIterator listIterator() { @Override public Iterator descendingIterator() { preconditionCheck(); - return new ReverseIteratorWrapper(elementData.end); + return new UtReverseIterator(elementData.end); } @Override @@ -467,12 +469,12 @@ public Stream parallelStream() { return stream(); } - public class ReverseIteratorWrapper implements ListIterator { + public class UtReverseIterator implements ListIterator { int index; int prevIndex = -1; - ReverseIteratorWrapper(int index) { + UtReverseIterator(int index) { this.index = index; } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionIteratorWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionIteratorWrappers.kt new file mode 100644 index 0000000000..42a9390624 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionIteratorWrappers.kt @@ -0,0 +1,108 @@ +package org.utbot.engine + +import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator +import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator +import org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator +import org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtReferenceModel +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.methodId +import soot.SootClass +import soot.SootMethod +import kotlin.reflect.KClass +import kotlin.reflect.jvm.jvmName + +/** + * Abstract wrapper for iterator of [java.util.Collection]. + */ +abstract class CollectionIteratorWrapper(overriddenClass: KClass<*>) : BaseOverriddenWrapper(overriddenClass.jvmName) { + protected abstract val modelName: String + protected abstract val javaCollectionClassId: ClassId + protected abstract val iteratorMethodId: MethodId + protected abstract val iteratorClassId: ClassId + + override fun Traverser.overrideInvoke( + wrapper: ObjectValue, + method: SootMethod, + parameters: List + ): List? = null + + override fun value(resolver: Resolver, wrapper: ObjectValue): UtModel = resolver.run { + val addr = holder.concreteAddr(wrapper.addr) + val fieldModels = collectFieldModels(wrapper.addr, overriddenClass.type) + + val containerFieldId = overriddenClass.enclosingClassField + val containerFieldModel = fieldModels[containerFieldId] as UtReferenceModel + + val instantiationCall = UtExecutableCallModel( + instance = containerFieldModel, + executable = iteratorMethodId, + params = emptyList() + ) + + UtAssembleModel(addr, iteratorClassId, modelName, instantiationCall) + } +} + +class IteratorOfListWrapper : CollectionIteratorWrapper(UtArrayListSimpleIterator::class) { + override val modelName: String = "iteratorOfList" + override val javaCollectionClassId: ClassId = java.util.List::class.id + override val iteratorClassId: ClassId = java.util.Iterator::class.id + override val iteratorMethodId: MethodId = methodId( + classId = javaCollectionClassId, + name = "iterator", + returnType = iteratorClassId, + arguments = emptyArray() + ) +} + +class ListIteratorOfListWrapper : CollectionIteratorWrapper(UtArrayListIterator::class) { + override val modelName: String = "listIteratorOfList" + override val javaCollectionClassId: ClassId = java.util.List::class.id + override val iteratorClassId: ClassId = java.util.ListIterator::class.id + override val iteratorMethodId: MethodId = methodId( + classId = javaCollectionClassId, + name = "listIterator", + returnType = iteratorClassId, + arguments = emptyArray() + ) +} + +class IteratorOfSetWrapper : CollectionIteratorWrapper(UtHashSetIterator::class) { + override val modelName: String = "iteratorOfSet" + override val javaCollectionClassId: ClassId = java.util.Set::class.id + override val iteratorClassId: ClassId = java.util.Iterator::class.id + override val iteratorMethodId: MethodId = methodId( + classId = javaCollectionClassId, + name = "iterator", + returnType = iteratorClassId, + arguments = emptyArray() + ) +} + +class ReverseIteratorWrapper :CollectionIteratorWrapper(UtReverseIterator::class) { + override val modelName: String = "reverseIterator" + override val javaCollectionClassId: ClassId = java.util.Deque::class.id + override val iteratorClassId: ClassId = java.util.Iterator::class.id + override val iteratorMethodId: MethodId = methodId( + classId = javaCollectionClassId, + name = "descendingIterator", + returnType = iteratorClassId, + arguments = emptyArray() + ) +} + +internal val SootClass.enclosingClassField: FieldId + get() { + require(isInnerClass) { + "Cannot get field for enclosing class of non-inner class $this" + } + + return getFieldByName("this$0").fieldId + } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt index 84623b03f3..705a6fb105 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/CollectionWrappers.kt @@ -460,6 +460,11 @@ val LINKED_HASH_MAP_TYPE: RefType val HASH_MAP_TYPE: RefType get() = Scene.v().getSootClass(java.util.HashMap::class.java.canonicalName).type +val ITERATOR_TYPE: RefType + get() = Scene.v().getSootClass(java.util.Iterator::class.java.canonicalName).type +val LIST_ITERATOR_TYPE: RefType + get() = Scene.v().getSootClass(java.util.ListIterator::class.java.canonicalName).type + val STREAM_TYPE: RefType get() = Scene.v().getSootClass(java.util.stream.Stream::class.java.canonicalName).type diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt index 88d022c430..83e20e7f99 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt @@ -16,9 +16,14 @@ import org.utbot.engine.UtStreamClass.UT_STREAM import org.utbot.engine.overrides.collections.AssociativeArray import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray import org.utbot.engine.overrides.collections.UtArrayList +import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator +import org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator import org.utbot.engine.overrides.collections.UtHashMap import org.utbot.engine.overrides.collections.UtHashSet +import org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator import org.utbot.engine.overrides.collections.UtLinkedList +import org.utbot.engine.overrides.collections.UtLinkedList.UtLinkedListIterator +import org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator import org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck import org.utbot.engine.overrides.collections.UtOptional import org.utbot.engine.overrides.collections.UtOptionalDouble @@ -67,6 +72,7 @@ import soot.Scene import soot.SootClass import soot.SootMethod import kotlin.reflect.KClass +import kotlin.reflect.jvm.jvmName typealias TypeToBeWrapped = RefType typealias WrapperType = RefType @@ -123,6 +129,13 @@ val classToWrapper: MutableMap = putSootClass(java.util.HashMap::class, UtHashMap::class) putSootClass(java.util.concurrent.ConcurrentHashMap::class, UtHashMap::class) + // Iterators + putSootClass(java.util.Iterator::class, UtArrayListSimpleIterator::class) + putSootClass(java.util.ListIterator::class, UtArrayListIterator::class) + putSootClass(UtLinkedListIterator::class, UtLinkedListIterator::class) + putSootClass(UtReverseIterator::class, UtReverseIterator::class) + putSootClass(UtHashSetIterator::class, UtHashSetIterator::class) + putSootClass(java.util.stream.BaseStream::class, UT_STREAM.className) putSootClass(java.util.stream.Stream::class, UT_STREAM.className) putSootClass(java.util.stream.IntStream::class, UT_INT_STREAM.className) @@ -158,7 +171,7 @@ val wrapperToClass: Map> = private fun MutableMap.putSootClass( key: KClass<*>, value: KClass<*> -) = putSootClass(key, Scene.v().getSootClass(value.java.canonicalName).type) +) = putSootClass(key, Scene.v().getSootClass(value.jvmName).type) // It is important to use `jvmName` because `canonicalName` replaces `$` for nested classes to `.` private fun MutableMap.putSootClass( key: KClass<*>, @@ -173,7 +186,7 @@ private fun MutableMap.putSootClass( private fun MutableMap.putSootClass( key: KClass<*>, value: RefType -) = put(Scene.v().getSootClass(key.java.canonicalName).type, value) +) = put(Scene.v().getSootClass(key.jvmName).type, value) // It is important to use `jvmName` because `canonicalName` replaces `$` for nested classes to `.` private val wrappers: Map ObjectValue> = mutableMapOf( wrap(java.lang.StringBuilder::class) { type, addr -> objectValue(type, addr, UtStringBuilderWrapper()) }, @@ -235,6 +248,10 @@ private val wrappers: Map ObjectValue> = wrap(java.util.HashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) }, wrap(java.util.concurrent.ConcurrentHashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) }, + // iterator wrappers + wrap(java.util.Iterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfListWrapper()) }, + wrap(java.util.ListIterator::class) { _, addr -> objectValue(LIST_ITERATOR_TYPE, addr, ListIteratorOfListWrapper()) }, + // stream wrappers wrap(java.util.stream.BaseStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, wrap(java.util.stream.Stream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, @@ -272,6 +289,12 @@ private val wrappers: Map ObjectValue> = wrap(UtHashMap::class) { _, addr -> objectValue(HASH_MAP_TYPE, addr, MapWrapper()) }, + wrap(UtArrayListSimpleIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfListWrapper()) }, + wrap(UtArrayListIterator::class) { _, addr -> objectValue(LIST_ITERATOR_TYPE, addr, ListIteratorOfListWrapper()) }, + 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` + wrap(UtReverseIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, ReverseIteratorWrapper()) }, + wrap(UtHashSetIterator::class) { _, addr -> objectValue(ITERATOR_TYPE, addr, IteratorOfSetWrapper()) }, + wrap(UtStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, wrap(UtIntStream::class) { _, addr -> objectValue(INT_STREAM_TYPE, addr, IntStreamWrapper()) }, wrap(UtLongStream::class) { _, addr -> objectValue(LONG_STREAM_TYPE, addr, LongStreamWrapper()) }, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt index 371cf2fdc1..e95e76f5ea 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt @@ -2,6 +2,7 @@ package org.utbot.framework.util import org.utbot.common.FileUtil import org.utbot.engine.jimpleBody +import org.utbot.engine.overrides.collections.UtLinkedList import org.utbot.engine.pureJavaSignature import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.ExecutableId @@ -159,11 +160,12 @@ private val classesToLoad = arrayOf( org.utbot.engine.overrides.collections.UtOptionalLong::class, org.utbot.engine.overrides.collections.UtOptionalDouble::class, org.utbot.engine.overrides.collections.UtArrayList::class, + org.utbot.engine.overrides.collections.UtArrayList.UtArrayListSimpleIterator::class, org.utbot.engine.overrides.collections.UtArrayList.UtArrayListIterator::class, org.utbot.engine.overrides.collections.UtLinkedList::class, org.utbot.engine.overrides.collections.UtLinkedListWithNullableCheck::class, org.utbot.engine.overrides.collections.UtLinkedList.UtLinkedListIterator::class, - org.utbot.engine.overrides.collections.UtLinkedList.ReverseIteratorWrapper::class, + org.utbot.engine.overrides.collections.UtLinkedList.UtReverseIterator::class, org.utbot.engine.overrides.collections.UtHashSet::class, org.utbot.engine.overrides.collections.UtHashSet.UtHashSetIterator::class, org.utbot.engine.overrides.collections.UtHashMap::class, diff --git a/utbot-sample/src/main/java/org/utbot/examples/collections/ListIterators.java b/utbot-sample/src/main/java/org/utbot/examples/collections/ListIterators.java index ce55011cea..8bdd6219ed 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/collections/ListIterators.java +++ b/utbot-sample/src/main/java/org/utbot/examples/collections/ListIterators.java @@ -1,11 +1,34 @@ package org.utbot.examples.collections; +import org.utbot.api.mock.UtMock; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class ListIterators { + @SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) + Iterator returnIterator(List list) { + UtMock.assume(list != null); + + if (list.isEmpty()) { + return list.iterator(); + } else { + return list.iterator(); + } + } + + @SuppressWarnings("IfStatementWithIdenticalBranches") + ListIterator returnListIterator(List list) { + UtMock.assume(list != null); + + if (list.isEmpty()) { + return list.listIterator(); + } else { + return list.listIterator(); + } + } List iterate(List list) { Iterator iterator = list.iterator(); diff --git a/utbot-sample/src/main/java/org/utbot/examples/collections/SetIterators.java b/utbot-sample/src/main/java/org/utbot/examples/collections/SetIterators.java index 91ed859aae..0f332e0a85 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/collections/SetIterators.java +++ b/utbot-sample/src/main/java/org/utbot/examples/collections/SetIterators.java @@ -1,9 +1,22 @@ package org.utbot.examples.collections; +import org.utbot.api.mock.UtMock; + import java.util.Iterator; import java.util.Set; public class SetIterators { + @SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) + Iterator returnIterator(Set set) { + UtMock.assume(set != null); + + if (set.isEmpty()) { + return set.iterator(); + } else { + return set.iterator(); + } + } + int iteratorHasNext(Set s) { Iterator iterator = s.iterator(); if (!iterator.hasNext()) {