@@ -32,55 +32,7 @@ import org.utbot.engine.MockStrategy.NO_MOCKS
3232import org.utbot.engine.overrides.UtArrayMock
3333import org.utbot.engine.overrides.UtLogicMock
3434import org.utbot.engine.overrides.UtOverrideMock
35- import org.utbot.engine.pc.NotBoolExpression
36- import org.utbot.engine.pc.UtAddNoOverflowExpression
37- import org.utbot.engine.pc.UtAddrExpression
38- import org.utbot.engine.pc.UtAndBoolExpression
39- import org.utbot.engine.pc.UtArrayApplyForAll
40- import org.utbot.engine.pc.UtArrayExpressionBase
41- import org.utbot.engine.pc.UtArraySelectExpression
42- import org.utbot.engine.pc.UtArraySetRange
43- import org.utbot.engine.pc.UtArraySort
44- import org.utbot.engine.pc.UtBoolExpression
45- import org.utbot.engine.pc.UtBoolOpExpression
46- import org.utbot.engine.pc.UtBvConst
47- import org.utbot.engine.pc.UtBvLiteral
48- import org.utbot.engine.pc.UtByteSort
49- import org.utbot.engine.pc.UtCastExpression
50- import org.utbot.engine.pc.UtCharSort
51- import org.utbot.engine.pc.UtContextInitializer
52- import org.utbot.engine.pc.UtExpression
53- import org.utbot.engine.pc.UtFalse
54- import org.utbot.engine.pc.UtInstanceOfExpression
55- import org.utbot.engine.pc.UtIntSort
56- import org.utbot.engine.pc.UtIsExpression
57- import org.utbot.engine.pc.UtIteExpression
58- import org.utbot.engine.pc.UtLongSort
59- import org.utbot.engine.pc.UtMkTermArrayExpression
60- import org.utbot.engine.pc.UtNegExpression
61- import org.utbot.engine.pc.UtOrBoolExpression
62- import org.utbot.engine.pc.UtPrimitiveSort
63- import org.utbot.engine.pc.UtShortSort
64- import org.utbot.engine.pc.UtSolver
65- import org.utbot.engine.pc.UtSolverStatusSAT
66- import org.utbot.engine.pc.UtSubNoOverflowExpression
67- import org.utbot.engine.pc.UtTrue
68- import org.utbot.engine.pc.addrEq
69- import org.utbot.engine.pc.align
70- import org.utbot.engine.pc.cast
71- import org.utbot.engine.pc.findTheMostNestedAddr
72- import org.utbot.engine.pc.isInteger
73- import org.utbot.engine.pc.mkAnd
74- import org.utbot.engine.pc.mkBVConst
75- import org.utbot.engine.pc.mkBoolConst
76- import org.utbot.engine.pc.mkChar
77- import org.utbot.engine.pc.mkEq
78- import org.utbot.engine.pc.mkFpConst
79- import org.utbot.engine.pc.mkInt
80- import org.utbot.engine.pc.mkNot
81- import org.utbot.engine.pc.mkOr
82- import org.utbot.engine.pc.select
83- import org.utbot.engine.pc.store
35+ import org.utbot.engine.pc.*
8436import org.utbot.engine.selectors.PathSelector
8537import org.utbot.engine.selectors.StrategyOption
8638import org.utbot.engine.selectors.coveredNewSelector
@@ -111,6 +63,7 @@ import org.utbot.engine.util.statics.concrete.makeEnumStaticFieldsUpdates
11163import org.utbot.engine.util.statics.concrete.makeSymbolicValuesFromEnumConcreteValues
11264import org.utbot.framework.PathSelectorType
11365import org.utbot.framework.UtSettings
66+ import org.utbot.framework.UtSettings.checkNpeForFinalFields
11467import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
11568import org.utbot.framework.UtSettings.enableFeatureProcess
11669import org.utbot.framework.UtSettings.pathSelectorStepsLimit
@@ -1985,6 +1938,10 @@ class UtBotSymbolicEngine(
19851938 is JInstanceFieldRef -> {
19861939 val instance = (base.resolve() as ObjectValue )
19871940 recordInstanceFieldRead(instance.addr, field)
1941+
1942+ // We know that [base] is not null as we are accessing its field (dot access).
1943+ // At the same time, we don't want to check for NPE if [base] is a final field
1944+ // (or if it is a non-nullable field).
19881945 nullPointerExceptionCheck(instance.addr)
19891946
19901947 val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2191,8 +2148,41 @@ class UtBotSymbolicEngine(
21912148 val chunkId = hierarchy.chunkIdForField(objectType, field)
21922149 val createdField = createField(objectType, addr, field.type, chunkId, mockInfoGenerator)
21932150
2194- if (field.type is RefLikeType && field.shouldBeNotNull()) {
2195- queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2151+ if (field.type is RefLikeType ) {
2152+ if (field.shouldBeNotNull()) {
2153+ queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2154+ }
2155+
2156+ // We suppose that accessing final fields in system classes can't produce NullPointerException
2157+ // because they are properly initialized in corresponding constructors. It is therefore
2158+ // desirable to avoid the generation of redundant test cases for NPE branches.
2159+ //
2160+ // At the same time, we can't always add the "not null" hard constraint for the field: it would break
2161+ // some special cases like `Optional<T>` class, which uses the null value of its final field
2162+ // as a marker of an empty value.
2163+ //
2164+ // The engine checks for NPE and creates an NPE branch every time the address is used
2165+ // as a base of a dot call (i.e., a method call or a field access);
2166+ // see [UtBotSymbolicEngine.nullPointerExceptionCheck]). The problem is what at that moment, we would have
2167+ // no way to check whether the address corresponds to a final field, as the corresponding node
2168+ // of the global graph would refer to a local variable. The only place where we have the complete
2169+ // information about the field is this method.
2170+ //
2171+ // We use the following approach. If the field is final and belongs to a system class,
2172+ // we mark it as a speculatively non-nullable in the memory. During the NPE check
2173+ // we will add two constraints to the NPE branch: "address has not been speculatively marked
2174+ // as non-nullable", and "address is null".
2175+ //
2176+ // For final fields, this condition can't be satisfied, as we speculatively mark final fields
2177+ // as non-nullable here. As a result, the NPE branch would be discarded. If a field is not final,
2178+ // the condition is satisfiable, so the NPE branch would stay alive.
2179+ //
2180+ // We limit this approach to the system classes only, because it is hard to speculatively assume
2181+ // something about non-nullability of final fields in the user code.
2182+
2183+ if (field.isFinal && ! field.declaringClass.isApplicationClass && ! checkNpeForFinalFields) {
2184+ markAsSpeculativelyNotNull(createdField.addr)
2185+ }
21962186 }
21972187
21982188 return createdField
@@ -2362,6 +2352,10 @@ class UtBotSymbolicEngine(
23622352 queuedSymbolicStateUpdates + = MemoryUpdate (touchedAddresses = persistentListOf(addr))
23632353 }
23642354
2355+ private fun markAsSpeculativelyNotNull (addr : UtAddrExpression ) {
2356+ queuedSymbolicStateUpdates + = MemoryUpdate (speculativelyNotNullAddresses = persistentListOf(addr))
2357+ }
2358+
23652359 /* *
23662360 * Add a memory update to reflect that a field was read.
23672361 *
@@ -3280,9 +3274,10 @@ class UtBotSymbolicEngine(
32803274 private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
32813275 val canBeNull = addrEq(addr, nullObjectAddr)
32823276 val canNotBeNull = mkNot(canBeNull)
3277+ val notFinalAndNull = mkAnd(mkEq(memory.isSpeculativelyNotNull(addr), mkFalse()), canBeNull)
32833278
32843279 if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3285- implicitlyThrowException(NullPointerException (), setOf (canBeNull ))
3280+ implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
32863281 }
32873282
32883283 queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments