@@ -1938,10 +1938,6 @@ class UtBotSymbolicEngine(
19381938 is JInstanceFieldRef -> {
19391939 val instance = (base.resolve() as ObjectValue )
19401940 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).
19451941 nullPointerExceptionCheck(instance.addr)
19461942
19471943 val objectType = if (instance.concrete?.value is BaseOverriddenWrapper ) {
@@ -2153,34 +2149,8 @@ class UtBotSymbolicEngine(
21532149 queuedSymbolicStateUpdates + = mkNot(mkEq(createdField.addr, nullObjectAddr)).asHardConstraint()
21542150 }
21552151
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) {
21842154 markAsSpeculativelyNotNull(createdField.addr)
21852155 }
21862156 }
@@ -3274,10 +3244,11 @@ class UtBotSymbolicEngine(
32743244 private fun nullPointerExceptionCheck (addr : UtAddrExpression ) {
32753245 val canBeNull = addrEq(addr, nullObjectAddr)
32763246 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)
32783249
32793250 if (environment.method.checkForNPE(environment.state.executionStack.size)) {
3280- implicitlyThrowException(NullPointerException (), setOf (notFinalAndNull ))
3251+ implicitlyThrowException(NullPointerException (), setOf (notMarkedAndNull ))
32813252 }
32823253
32833254 queuedSymbolicStateUpdates + = canNotBeNull.asHardConstraint()
0 commit comments