@@ -32,55 +32,7 @@ import org.utbot.engine.MockStrategy.NO_MOCKS
32
32
import org.utbot.engine.overrides.UtArrayMock
33
33
import org.utbot.engine.overrides.UtLogicMock
34
34
import 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.*
84
36
import org.utbot.engine.selectors.PathSelector
85
37
import org.utbot.engine.selectors.StrategyOption
86
38
import org.utbot.engine.selectors.coveredNewSelector
@@ -111,6 +63,7 @@ import org.utbot.engine.util.statics.concrete.makeEnumStaticFieldsUpdates
111
63
import org.utbot.engine.util.statics.concrete.makeSymbolicValuesFromEnumConcreteValues
112
64
import org.utbot.framework.PathSelectorType
113
65
import org.utbot.framework.UtSettings
66
+ import org.utbot.framework.UtSettings.checkNpeForFinalFields
114
67
import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
115
68
import org.utbot.framework.UtSettings.enableFeatureProcess
116
69
import org.utbot.framework.UtSettings.pathSelectorStepsLimit
@@ -1985,6 +1938,10 @@ class UtBotSymbolicEngine(
1985
1938
is JInstanceFieldRef -> {
1986
1939
val instance = (base.resolve() as ObjectValue )
1987
1940
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).
1988
1945
nullPointerExceptionCheck(instance.addr)
1989
1946
1990
1947
val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2191,8 +2148,41 @@ class UtBotSymbolicEngine(
2191
2148
val chunkId = hierarchy.chunkIdForField(objectType, field)
2192
2149
val createdField = createField(objectType, addr, field.type, chunkId, mockInfoGenerator)
2193
2150
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
+ }
2196
2186
}
2197
2187
2198
2188
return createdField
@@ -2362,6 +2352,10 @@ class UtBotSymbolicEngine(
2362
2352
queuedSymbolicStateUpdates + = MemoryUpdate (touchedAddresses = persistentListOf(addr))
2363
2353
}
2364
2354
2355
+ private fun markAsSpeculativelyNotNull (addr : UtAddrExpression ) {
2356
+ queuedSymbolicStateUpdates + = MemoryUpdate (speculativelyNotNullAddresses = persistentListOf(addr))
2357
+ }
2358
+
2365
2359
/* *
2366
2360
* Add a memory update to reflect that a field was read.
2367
2361
*
@@ -3280,9 +3274,10 @@ class UtBotSymbolicEngine(
3280
3274
private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
3281
3275
val canBeNull = addrEq(addr, nullObjectAddr)
3282
3276
val canNotBeNull = mkNot(canBeNull)
3277
+ val notFinalAndNull = mkAnd(mkEq(memory.isSpeculativelyNotNull(addr), mkFalse()), canBeNull)
3283
3278
3284
3279
if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3285
- implicitlyThrowException(NullPointerException (), setOf (canBeNull ))
3280
+ implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
3286
3281
}
3287
3282
3288
3283
queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments