@@ -1946,10 +1946,6 @@ class UtBotSymbolicEngine(
1946
1946
is JInstanceFieldRef -> {
1947
1947
val instance = (base.resolve() as ObjectValue )
1948
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).
1953
1949
nullPointerExceptionCheck(instance.addr)
1954
1950
1955
1951
val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2161,34 +2157,8 @@ class UtBotSymbolicEngine(
2161
2157
queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
2162
2158
}
2163
2159
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) {
2160
+ // See docs/SpeculativeFieldNonNullability.md for details
2161
+ if (field.isFinal && field.declaringClass.isLibraryClass && ! checkNpeForFinalFields) {
2192
2162
markAsSpeculativelyNotNull(createdField.addr)
2193
2163
}
2194
2164
}
@@ -3284,10 +3254,11 @@ class UtBotSymbolicEngine(
3284
3254
private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
3285
3255
val canBeNull = addrEq(addr, nullObjectAddr)
3286
3256
val canNotBeNull = mkNot(canBeNull)
3287
- val notFinalAndNull = mkAnd(mkEq(memory.isSpeculativelyNotNull(addr), mkFalse()), canBeNull)
3257
+ val notMarked = mkEq(memory.isSpeculativelyNotNull(addr), mkFalse())
3258
+ val notMarkedAndNull = mkAnd(notMarked, canBeNull)
3288
3259
3289
3260
if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3290
- implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
3261
+ implicitlyThrowException(NullPointerException (), setOf (notMarkedAndNull ))
3291
3262
}
3292
3263
3293
3264
queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments