View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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 }