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