@@ -34,55 +34,7 @@ import org.utbot.engine.MockStrategy.NO_MOCKS
34
34
import org.utbot.engine.overrides.UtArrayMock
35
35
import org.utbot.engine.overrides.UtLogicMock
36
36
import org.utbot.engine.overrides.UtOverrideMock
37
- import org.utbot.engine.pc.NotBoolExpression
38
- import org.utbot.engine.pc.UtAddNoOverflowExpression
39
- import org.utbot.engine.pc.UtAddrExpression
40
- import org.utbot.engine.pc.UtAndBoolExpression
41
- import org.utbot.engine.pc.UtArrayApplyForAll
42
- import org.utbot.engine.pc.UtArrayExpressionBase
43
- import org.utbot.engine.pc.UtArraySelectExpression
44
- import org.utbot.engine.pc.UtArraySetRange
45
- import org.utbot.engine.pc.UtArraySort
46
- import org.utbot.engine.pc.UtBoolExpression
47
- import org.utbot.engine.pc.UtBoolOpExpression
48
- import org.utbot.engine.pc.UtBvConst
49
- import org.utbot.engine.pc.UtBvLiteral
50
- import org.utbot.engine.pc.UtByteSort
51
- import org.utbot.engine.pc.UtCastExpression
52
- import org.utbot.engine.pc.UtCharSort
53
- import org.utbot.engine.pc.UtContextInitializer
54
- import org.utbot.engine.pc.UtExpression
55
- import org.utbot.engine.pc.UtFalse
56
- import org.utbot.engine.pc.UtInstanceOfExpression
57
- import org.utbot.engine.pc.UtIntSort
58
- import org.utbot.engine.pc.UtIsExpression
59
- import org.utbot.engine.pc.UtIteExpression
60
- import org.utbot.engine.pc.UtLongSort
61
- import org.utbot.engine.pc.UtMkTermArrayExpression
62
- import org.utbot.engine.pc.UtNegExpression
63
- import org.utbot.engine.pc.UtOrBoolExpression
64
- import org.utbot.engine.pc.UtPrimitiveSort
65
- import org.utbot.engine.pc.UtShortSort
66
- import org.utbot.engine.pc.UtSolver
67
- import org.utbot.engine.pc.UtSolverStatusSAT
68
- import org.utbot.engine.pc.UtSubNoOverflowExpression
69
- import org.utbot.engine.pc.UtTrue
70
- import org.utbot.engine.pc.addrEq
71
- import org.utbot.engine.pc.align
72
- import org.utbot.engine.pc.cast
73
- import org.utbot.engine.pc.findTheMostNestedAddr
74
- import org.utbot.engine.pc.isInteger
75
- import org.utbot.engine.pc.mkAnd
76
- import org.utbot.engine.pc.mkBVConst
77
- import org.utbot.engine.pc.mkBoolConst
78
- import org.utbot.engine.pc.mkChar
79
- import org.utbot.engine.pc.mkEq
80
- import org.utbot.engine.pc.mkFpConst
81
- import org.utbot.engine.pc.mkInt
82
- import org.utbot.engine.pc.mkNot
83
- import org.utbot.engine.pc.mkOr
84
- import org.utbot.engine.pc.select
85
- import org.utbot.engine.pc.store
37
+ import org.utbot.engine.pc.*
86
38
import org.utbot.engine.selectors.PathSelector
87
39
import org.utbot.engine.selectors.StrategyOption
88
40
import org.utbot.engine.selectors.coveredNewSelector
@@ -115,6 +67,7 @@ import org.utbot.engine.util.statics.concrete.makeEnumStaticFieldsUpdates
115
67
import org.utbot.engine.util.statics.concrete.makeSymbolicValuesFromEnumConcreteValues
116
68
import org.utbot.framework.PathSelectorType
117
69
import org.utbot.framework.UtSettings
70
+ import org.utbot.framework.UtSettings.checkNpeForFinalFields
118
71
import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
119
72
import org.utbot.framework.UtSettings.enableFeatureProcess
120
73
import org.utbot.framework.UtSettings.pathSelectorStepsLimit
@@ -1993,6 +1946,10 @@ class UtBotSymbolicEngine(
1993
1946
is JInstanceFieldRef -> {
1994
1947
val instance = (base.resolve() as ObjectValue )
1995
1948
recordInstanceFieldRead(instance.addr, field)
1949
+
1950
+ // We know that [base] is not null as we are accessing its field (dot access).
1951
+ // At the same time, we don't want to check for NPE if [base] is a final field
1952
+ // (or if it is a non-nullable field).
1996
1953
nullPointerExceptionCheck(instance.addr)
1997
1954
1998
1955
val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2199,8 +2156,41 @@ class UtBotSymbolicEngine(
2199
2156
val chunkId = hierarchy.chunkIdForField(objectType, field)
2200
2157
val createdField = createField(objectType, addr, field.type, chunkId, mockInfoGenerator)
2201
2158
2202
- if (field.type is RefLikeType && field.shouldBeNotNull()) {
2203
- queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2159
+ if (field.type is RefLikeType ) {
2160
+ if (field.shouldBeNotNull()) {
2161
+ queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2162
+ }
2163
+
2164
+ // We suppose that accessing final fields in system classes can't produce NullPointerException
2165
+ // because they are properly initialized in corresponding constructors. It is therefore
2166
+ // desirable to avoid the generation of redundant test cases for NPE branches.
2167
+ //
2168
+ // At the same time, we can't always add the "not null" hard constraint for the field: it would break
2169
+ // some special cases like `Optional<T>` class, which uses the null value of its final field
2170
+ // as a marker of an empty value.
2171
+ //
2172
+ // The engine checks for NPE and creates an NPE branch every time the address is used
2173
+ // as a base of a dot call (i.e., a method call or a field access);
2174
+ // see [UtBotSymbolicEngine.nullPointerExceptionCheck]). The problem is what at that moment, we would have
2175
+ // no way to check whether the address corresponds to a final field, as the corresponding node
2176
+ // of the global graph would refer to a local variable. The only place where we have the complete
2177
+ // information about the field is this method.
2178
+ //
2179
+ // We use the following approach. If the field is final and belongs to a system class,
2180
+ // we mark it as a speculatively non-nullable in the memory. During the NPE check
2181
+ // we will add two constraints to the NPE branch: "address has not been speculatively marked
2182
+ // as non-nullable", and "address is null".
2183
+ //
2184
+ // For final fields, this condition can't be satisfied, as we speculatively mark final fields
2185
+ // as non-nullable here. As a result, the NPE branch would be discarded. If a field is not final,
2186
+ // the condition is satisfiable, so the NPE branch would stay alive.
2187
+ //
2188
+ // We limit this approach to the system classes only, because it is hard to speculatively assume
2189
+ // something about non-nullability of final fields in the user code.
2190
+
2191
+ if (field.isFinal && ! field.declaringClass.isApplicationClass && ! checkNpeForFinalFields) {
2192
+ markAsSpeculativelyNotNull(createdField.addr)
2193
+ }
2204
2194
}
2205
2195
2206
2196
return createdField
@@ -2370,6 +2360,10 @@ class UtBotSymbolicEngine(
2370
2360
queuedSymbolicStateUpdates + = MemoryUpdate (touchedAddresses = persistentListOf(addr))
2371
2361
}
2372
2362
2363
+ private fun markAsSpeculativelyNotNull (addr : UtAddrExpression ) {
2364
+ queuedSymbolicStateUpdates + = MemoryUpdate (speculativelyNotNullAddresses = persistentListOf(addr))
2365
+ }
2366
+
2373
2367
/* *
2374
2368
* Add a memory update to reflect that a field was read.
2375
2369
*
@@ -3290,9 +3284,10 @@ class UtBotSymbolicEngine(
3290
3284
private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
3291
3285
val canBeNull = addrEq(addr, nullObjectAddr)
3292
3286
val canNotBeNull = mkNot(canBeNull)
3287
+ val notFinalAndNull = mkAnd(mkEq(memory.isSpeculativelyNotNull(addr), mkFalse()), canBeNull)
3293
3288
3294
3289
if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3295
- implicitlyThrowException(NullPointerException (), setOf (canBeNull ))
3290
+ implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
3296
3291
}
3297
3292
3298
3293
queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments