Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.utbot.examples.lambda

import org.junit.jupiter.api.Test
import org.utbot.testcheckers.eq
import org.utbot.testing.DoNotCalculate
import org.utbot.testing.UtValueTestCaseChecker

class ThrowingWithLambdaExampleTest : UtValueTestCaseChecker(testClass = ThrowingWithLambdaExample::class) {
@Test
fun testAnyExample() {
check(
ThrowingWithLambdaExample::anyExample,
eq(4),
{ l, _, _ -> l == null },
{ l, _, r -> l.isEmpty() && r == false },
{ l, _, r -> l.isNotEmpty() && 42 in l && r == true },
{ l, _, r -> l.isNotEmpty() && 42 !in l && r == false },
coverage = DoNotCalculate // TODO failed coverage calculation
)
}
}
12 changes: 12 additions & 0 deletions utbot-framework/src/main/kotlin/org/utbot/engine/ObjectCounter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.utbot.engine

import java.util.concurrent.atomic.AtomicInteger

/**
* Counts new objects during execution. Used to give new addresses for objects in [Traverser] and [Resolver].
*/
data class ObjectCounter(val initialValue: Int) {
private val internalCounter = AtomicInteger(initialValue)

fun createNewAddr(): Int = internalCounter.getAndIncrement()
}
27 changes: 25 additions & 2 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import org.utbot.engine.types.TypeRegistry
import org.utbot.engine.types.TypeResolver
import org.utbot.framework.plugin.api.visible.UtStreamConsumingException
import org.utbot.framework.plugin.api.UtStreamConsumingFailure
import org.utbot.framework.plugin.api.util.isStatic

// hack
const val MAX_LIST_SIZE = 10
Expand Down Expand Up @@ -130,7 +131,8 @@ class Resolver(
private val typeResolver: TypeResolver,
val holder: UtSolverStatusSAT,
methodUnderTest: ExecutableId,
private val softMaxArraySize: Int
private val softMaxArraySize: Int,
private val objectCounter: ObjectCounter
) {

private val classLoader: ClassLoader
Expand Down Expand Up @@ -529,7 +531,28 @@ class Resolver(

if (sootClass.isLambda) {
return constructLambda(concreteAddr, sootClass).also { lambda ->
lambda.capturedValues += collectFieldModels(addr, actualType).values
val collectedFieldModels = collectFieldModels(addr, actualType).toMutableMap()

if (!lambda.lambdaMethodId.isStatic) {
val thisInstanceField = FieldId(declaringClass = sootClass.id, name = "cap0")

if (thisInstanceField !in collectedFieldModels || collectedFieldModels[thisInstanceField] is UtNullModel) {
// Non-static lambda has to have `this` instance captured as `cap0` field that cannot be null,
// so if we do not have it as field or it is null (for example, an exception was thrown before initializing lambda),
// we need to construct `this` instance by ourselves.
// Since we do not know its fields, we create an empty object of the corresponding type that will be
// constructed in codegen using reflection.
val thisInstanceClassId = sootClass.name.substringBeforeLast("\$lambda").let {
Scene.v().getSootClass(it)
}.id
val thisInstanceModel =
UtCompositeModel(objectCounter.createNewAddr(), thisInstanceClassId, isMock = false)

collectedFieldModels[thisInstanceField] = thisInstanceModel
}
}

lambda.capturedValues += collectedFieldModels.values
}
}

Expand Down
5 changes: 3 additions & 2 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,10 @@ class Traverser(

private var queuedSymbolicStateUpdates = SymbolicStateUpdate()

private val objectCounter = AtomicInteger(TypeRegistry.objectCounterInitialValue)
internal val objectCounter = ObjectCounter(TypeRegistry.objectCounterInitialValue)

private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression {
val newAddr = objectCounter.getAndIncrement()
val newAddr = objectCounter.createNewAddr()
// return negative address for objects created inside static initializer
// to make their address space be intersected with address space of
// parameters of method under symbolic execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ class UtBotSymbolicEngine(
typeResolver,
state.solver.lastStatus as UtSolverStatusSAT,
methodUnderTest,
softMaxArraySize
softMaxArraySize,
traverser.objectCounter
)

val resolvedParameters = state.methodUnderTestParameters
Expand Down Expand Up @@ -444,8 +445,16 @@ class UtBotSymbolicEngine(
Predictors.testName.provide(state.path, predictedTestName, "")

// resolving
val resolver =
Resolver(hierarchy, memory, typeRegistry, typeResolver, holder, methodUnderTest, softMaxArraySize)
val resolver = Resolver(
hierarchy,
memory,
typeRegistry,
typeResolver,
holder,
methodUnderTest,
softMaxArraySize,
traverser.objectCounter
)

val (modelsBefore, modelsAfter, instrumentation) = resolver.resolveModels(parameters)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.utbot.examples.lambda;

import org.utbot.api.mock.UtMock;

public class ThrowingWithLambdaExample {
// This example mostly checks that we can construct non-static lambda even if it's init section was not analyzed
// (e.g., an exception was thrown before it).
boolean anyExample(int[] values, IntPredicate predicate) {
UtMock.assume(predicate != null);

for (int value : values) {
if (predicate.test(value)) {
return true;
}
}

return false;
}

// To make this lambda non-static, we need to make it use `this` instance.
@SuppressWarnings({"unused", "ConstantConditions"})
IntPredicate nonStaticIntPredicate = x -> this != null && x == 42;

interface IntPredicate {
boolean test(int value);
}
}