Skip to content

Commit fe3b562

Browse files
DamtevVassiliy-Kudryashov
authored andcommitted
Fixed NPE for processing static field as first statement in MUT (#433)
1 parent 67c77f1 commit fe3b562

File tree

1 file changed

+56
-39
lines changed

1 file changed

+56
-39
lines changed

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -906,23 +906,34 @@ class UtBotSymbolicEngine(
906906
// Gets concrete value, converts to symbolic value
907907
val declaringClass = field.declaringClass
908908

909-
val (edge, updates) = if (declaringClass.isEnum) {
909+
val updates = if (declaringClass.isEnum) {
910910
makeConcreteUpdatesForEnums(fieldId, declaringClass, stmt)
911911
} else {
912-
makeConcreteUpdatesForNonEnumStaticField(field, fieldId, declaringClass)
912+
makeConcreteUpdatesForNonEnumStaticField(field, fieldId, declaringClass, stmt)
913913
}
914914

915+
// a static initializer can be the first statement in method so there will be no last edge
916+
// for example, as it is during Enum::values method analysis:
917+
// public static ClassWithEnum$StatusEnum[] values()
918+
// {
919+
// ClassWithEnum$StatusEnum[] $r0, $r2;
920+
// java.lang.Object $r1;
921+
922+
// $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
923+
val edge = environment.state.lastEdge ?: globalGraph.succ(stmt)
924+
915925
val newState = environment.state.updateQueued(edge, updates)
916926
pathSelector.offer(newState)
917927

918928
return true
919929
}
920930

931+
@Suppress("UnnecessaryVariable")
921932
private fun makeConcreteUpdatesForEnums(
922933
fieldId: FieldId,
923934
declaringClass: SootClass,
924935
stmt: Stmt
925-
): Pair<Edge, SymbolicStateUpdate> {
936+
): SymbolicStateUpdate {
926937
val type = declaringClass.type
927938
val jClass = type.id.jClass
928939

@@ -966,46 +977,21 @@ class UtBotSymbolicEngine(
966977
meaningfulStaticFields = meaningfulStaticFields.map { it.first.fieldId }.toPersistentSet()
967978
)
968979

969-
var allUpdates = staticFieldUpdates + nonStaticFieldsUpdates + initializedStaticFieldsMemoryUpdate
970-
971-
// we need to make locals update if it is an assignment statement
972-
// for enums we have only two types for assignment with enums — enum constant or $VALUES field
973-
// for example, a jimple body for Enum::values method starts with the following lines:
974-
// public static ClassWithEnum$StatusEnum[] values()
975-
// {
976-
// ClassWithEnum$StatusEnum[] $r0, $r2;
977-
// java.lang.Object $r1;
978-
// $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
979-
// $r1 = virtualinvoke $r0.<java.lang.Object: java.lang.Object clone()>();
980-
981-
// so, we have to make an update for the local $r0
982-
if (stmt is JAssignStmt) {
983-
val local = stmt.leftOp as JimpleLocal
984-
val localUpdate = localMemoryUpdate(
985-
local.variable to curFieldSymbolicValueForLocalVariable
986-
)
987-
988-
allUpdates += localUpdate
989-
}
990-
991-
// enum static initializer can be the first statement in method so there will be no last edge
992-
// for example, as it is during Enum::values method analysis:
993-
// public static ClassWithEnum$StatusEnum[] values()
994-
// {
995-
// ClassWithEnum$StatusEnum[] $r0, $r2;
996-
// java.lang.Object $r1;
997-
998-
// $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
999-
val edge = environment.state.lastEdge ?: globalGraph.succ(stmt)
980+
val allUpdates = staticFieldUpdates +
981+
nonStaticFieldsUpdates +
982+
initializedStaticFieldsMemoryUpdate +
983+
createConcreteLocalValueUpdate(stmt, curFieldSymbolicValueForLocalVariable)
1000984

1001-
return edge to allUpdates
985+
return allUpdates
1002986
}
1003987

988+
@Suppress("UnnecessaryVariable")
1004989
private fun makeConcreteUpdatesForNonEnumStaticField(
1005990
field: SootField,
1006991
fieldId: FieldId,
1007-
declaringClass: SootClass
1008-
): Pair<Edge, SymbolicStateUpdate> {
992+
declaringClass: SootClass,
993+
stmt: Stmt
994+
): SymbolicStateUpdate {
1009995
val concreteValue = extractConcreteValue(field, declaringClass)
1010996
val (symbolicResult, symbolicStateUpdate) = toMethodResult(concreteValue, field.type)
1011997
val symbolicValue = (symbolicResult as SymbolicSuccess).value
@@ -1019,9 +1005,40 @@ class UtBotSymbolicEngine(
10191005
field = field,
10201006
value = valueToExpression(symbolicValue, field.type)
10211007
)
1022-
val allUpdates = symbolicStateUpdate + initializedFieldUpdate + objectUpdate
1008+
val allUpdates = symbolicStateUpdate +
1009+
initializedFieldUpdate +
1010+
objectUpdate +
1011+
createConcreteLocalValueUpdate(stmt, symbolicValue)
1012+
1013+
return allUpdates
1014+
}
1015+
1016+
/**
1017+
* Creates a local update consisting [symbolicValue] for a local variable from [stmt] in case [stmt] is [JAssignStmt].
1018+
*/
1019+
private fun createConcreteLocalValueUpdate(
1020+
stmt: Stmt,
1021+
symbolicValue: SymbolicValue?,
1022+
): LocalMemoryUpdate {
1023+
// we need to make locals update if it is an assignment statement
1024+
// for enums we have only two types for assignment with enums — enum constant or $VALUES field
1025+
// for example, a jimple body for Enum::values method starts with the following lines:
1026+
// public static ClassWithEnum$StatusEnum[] values()
1027+
// {
1028+
// ClassWithEnum$StatusEnum[] $r0, $r2;
1029+
// java.lang.Object $r1;
1030+
// $r0 = <ClassWithEnum$StatusEnum: ClassWithEnum$StatusEnum[] $VALUES>;
1031+
// $r1 = virtualinvoke $r0.<java.lang.Object: java.lang.Object clone()>();
1032+
1033+
// so, we have to make an update for the local $r0
10231034

1024-
return environment.state.lastEdge!! to allUpdates
1035+
return if (stmt is JAssignStmt) {
1036+
val local = stmt.leftOp as JimpleLocal
1037+
1038+
localMemoryUpdate(local.variable to symbolicValue)
1039+
} else {
1040+
LocalMemoryUpdate()
1041+
}
10251042
}
10261043

10271044
// Some fields are inaccessible with reflection, so we have to instantiate it by ourselves.

0 commit comments

Comments
 (0)