@@ -1938,10 +1938,6 @@ class UtBotSymbolicEngine(
1938
1938
is JInstanceFieldRef -> {
1939
1939
val instance = (base.resolve() as ObjectValue )
1940
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).
1945
1941
nullPointerExceptionCheck(instance.addr)
1946
1942
1947
1943
val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2153,34 +2149,8 @@ class UtBotSymbolicEngine(
2153
2149
queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2154
2150
}
2155
2151
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) {
2152
+ // See docs/SpeculativeFieldNonNullability.md for details
2153
+ if (field.isFinal && field.declaringClass.isLibraryClass && ! checkNpeForFinalFields) {
2184
2154
markAsSpeculativelyNotNull(createdField.addr)
2185
2155
}
2186
2156
}
@@ -3274,10 +3244,11 @@ class UtBotSymbolicEngine(
3274
3244
private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
3275
3245
val canBeNull = addrEq(addr, nullObjectAddr)
3276
3246
val canNotBeNull = mkNot(canBeNull)
3277
- val notFinalAndNull = mkAnd(mkEq(memory.isSpeculativelyNotNull(addr), mkFalse()), canBeNull)
3247
+ val notMarked = mkEq(memory.isSpeculativelyNotNull(addr), mkFalse())
3248
+ val notMarkedAndNull = mkAnd(notMarked, canBeNull)
3278
3249
3279
3250
if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3280
- implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
3251
+ implicitlyThrowException(NullPointerException (), setOf (notMarkedAndNull ))
3281
3252
}
3282
3253
3283
3254
queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments