1
2
3
4
5 package mockit.coverage.modification;
6
7 import static mockit.asm.jvmConstants.Opcodes.ICONST_0;
8 import static mockit.asm.jvmConstants.Opcodes.ICONST_1;
9 import static mockit.asm.jvmConstants.Opcodes.INVOKEVIRTUAL;
10
11 import edu.umd.cs.findbugs.annotations.NonNull;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import mockit.asm.controlFlow.Label;
17 import mockit.coverage.lines.BranchCoverageData;
18 import mockit.coverage.lines.LineCoverageData;
19 import mockit.coverage.lines.PerFileLineCoverage;
20
21 import org.checkerframework.checker.index.qual.NonNegative;
22
23 final class CFGTracking {
24 @NonNull
25 private final PerFileLineCoverage lineCoverageInfo;
26 @NonNull
27 private final List<Label> visitedLabels;
28 @NonNull
29 private final List<Label> jumpTargetsForCurrentLine;
30 @NonNull
31 private final List<Integer> pendingBranches;
32 @NonNegative
33 private int lineExpectingInstructionAfterJump;
34 private boolean assertFoundInCurrentLine;
35 private boolean ignoreUntilNextLabel;
36 @NonNegative
37 private int foundPotentialBooleanExpressionValue;
38 @NonNegative
39 private int ignoreUntilNextSwitch;
40
41 CFGTracking(@NonNull PerFileLineCoverage lineCoverageInfo) {
42 this.lineCoverageInfo = lineCoverageInfo;
43 visitedLabels = new ArrayList<>();
44 jumpTargetsForCurrentLine = new ArrayList<>(4);
45 pendingBranches = new ArrayList<>(6);
46 }
47
48 void startNewLine() {
49 if (!pendingBranches.isEmpty()) {
50 pendingBranches.clear();
51 }
52
53 jumpTargetsForCurrentLine.clear();
54 }
55
56 void afterNewLabel(@NonNegative int currentLine, @NonNull Label label) {
57 if (ignoreUntilNextLabel || ignoreUntilNextSwitch > 0) {
58 ignoreUntilNextLabel = false;
59 return;
60 }
61
62 visitedLabels.add(label);
63
64 int jumpTargetIndex = jumpTargetsForCurrentLine.indexOf(label);
65
66 if (jumpTargetIndex >= 0) {
67 label.jumpTargetLine = label.line > 0 ? label.line : currentLine;
68 int targetBranchIndex = 2 * jumpTargetIndex + 1;
69 pendingBranches.add(targetBranchIndex);
70 assertFoundInCurrentLine = false;
71 }
72
73 foundPotentialBooleanExpressionValue = 0;
74 }
75
76 void afterGoto() {
77 assertFoundInCurrentLine = false;
78
79 if (foundPotentialBooleanExpressionValue == 1) {
80 foundPotentialBooleanExpressionValue = 2;
81 }
82 }
83
84 void afterConditionalJump(@NonNull MethodModifier methodModifier, @NonNull Label jumpSource,
85 @NonNull Label jumpTarget) {
86 int currentLine = methodModifier.currentLine;
87
88 if (currentLine == 0 || ignoreUntilNextLabel || ignoreUntilNextSwitch > 0
89 || visitedLabels.contains(jumpTarget)) {
90 assertFoundInCurrentLine = false;
91 return;
92 }
93
94 jumpSource.jumpTargetLine = currentLine;
95
96 if (!jumpTargetsForCurrentLine.contains(jumpTarget)) {
97 jumpTargetsForCurrentLine.add(jumpTarget);
98 }
99
100 LineCoverageData lineData = lineCoverageInfo.getOrCreateLineData(currentLine);
101 int sourceBranchIndex = lineData.addBranchingPoint(jumpSource, jumpTarget);
102 pendingBranches.add(sourceBranchIndex);
103
104 if (assertFoundInCurrentLine) {
105 BranchCoverageData branchData = lineCoverageInfo.getBranchData(currentLine, sourceBranchIndex + 1);
106 branchData.markAsUnreachable();
107 }
108
109 lineExpectingInstructionAfterJump = 0;
110 generateCallToRegisterBranchTargetExecutionIfPending(methodModifier);
111 lineExpectingInstructionAfterJump = currentLine;
112 }
113
114 void generateCallToRegisterBranchTargetExecutionIfPending(@NonNull MethodModifier methodModifier) {
115 if (ignoreUntilNextLabel || ignoreUntilNextSwitch > 0) {
116 return;
117 }
118
119 foundPotentialBooleanExpressionValue = 0;
120
121 if (!pendingBranches.isEmpty()) {
122 for (Integer pendingBranchIndex : pendingBranches) {
123 methodModifier.generateCallToRegisterBranchTargetExecution(pendingBranchIndex);
124 }
125
126 pendingBranches.clear();
127 }
128
129 if (lineExpectingInstructionAfterJump > 0) {
130 if (methodModifier.currentLine > lineExpectingInstructionAfterJump) {
131 lineCoverageInfo.markLastLineSegmentAsEmpty(lineExpectingInstructionAfterJump);
132 }
133
134 lineExpectingInstructionAfterJump = 0;
135 }
136 }
137
138 boolean hasOnlyOneLabelBeingVisited() {
139 return visitedLabels.size() == 1;
140 }
141
142 void registerAssertFoundInCurrentLine() {
143 assertFoundInCurrentLine = true;
144 ignoreUntilNextLabel = true;
145 }
146
147 void beforeNoOperandInstruction(@NonNull MethodModifier methodModifier, @NonNegative int opcode) {
148 if ((opcode == ICONST_0 || opcode == ICONST_1) && foundPotentialBooleanExpressionValue == 0) {
149 generateCallToRegisterBranchTargetExecutionIfPending(methodModifier);
150 foundPotentialBooleanExpressionValue = 1;
151 } else {
152 generateCallToRegisterBranchTargetExecutionIfPending(methodModifier);
153 }
154 }
155
156 void afterMethodInstruction(@NonNegative int opcode, @NonNull String owner, @NonNull String name) {
157 if (opcode == INVOKEVIRTUAL && "hashCode".equals(name) && "java/lang/String".equals(owner)
158 && ignoreUntilNextSwitch == 0) {
159 ignoreUntilNextSwitch = 1;
160 }
161 }
162
163 void beforeLookupSwitchInstruction() {
164 if (ignoreUntilNextSwitch == 1) {
165 ignoreUntilNextSwitch = 2;
166 }
167 }
168 }