View Javadoc
1   package mockit.asm.controlFlow;
2   
3   import edu.umd.cs.findbugs.annotations.NonNull;
4   import edu.umd.cs.findbugs.annotations.Nullable;
5   
6   import mockit.asm.util.ByteVector;
7   
8   import org.checkerframework.checker.index.qual.NonNegative;
9   
10  /**
11   * A label represents a position in the bytecode of a method. Labels are used for jump, goto, and switch instructions,
12   * and for try catch blocks. A label designates the <em>instruction</em> that is just after. Note however that there can
13   * be other elements between a label and the instruction it designates (such as other labels, stack map frames, line
14   * numbers, etc.).
15   */
16  public final class Label {
17      /**
18       * Constants for the current status of a label.
19       */
20      interface Status {
21          /**
22           * Indicates if this label is only used for debug attributes. Such a label is not the start of a basic block,
23           * the target of a jump instruction, or an exception handler. It can be safely ignored in control flow graph
24           * analysis algorithms (for optimization purposes).
25           */
26          int DEBUG = 1;
27  
28          /**
29           * Indicates if the position of this label is known.
30           */
31          int RESOLVED = 2;
32  
33          /**
34           * Indicates if this basic block has been pushed in the basic block stack.
35           */
36          int PUSHED = 8;
37  
38          /**
39           * Indicates if this label is the target of a jump instruction, or the start of an exception handler.
40           */
41          int TARGET = 16;
42  
43          /**
44           * Indicates if a stack map frame must be stored for this label.
45           */
46          int STORE = 32;
47  
48          /**
49           * Indicates if this label corresponds to a reachable basic block.
50           */
51          int REACHABLE = 64;
52      }
53  
54      /**
55       * Flags that indicate the {@link Status Status} of this label.
56       */
57      private int status;
58  
59      /**
60       * The line number corresponding to this label, if known.
61       */
62      @NonNegative
63      public int line;
64  
65      /**
66       * Line number of label which is the target of a JMP instruction.
67       */
68      @NonNegative
69      public int jumpTargetLine;
70  
71      /**
72       * The position of this label in the code, if known.
73       */
74      @NonNegative
75      public int position;
76  
77      /**
78       * Number of forward references to this label, times two.
79       */
80      @NonNegative
81      private int referenceCount;
82  
83      /**
84       * Information about forward references. Each forward reference is described by two consecutive integers in this
85       * array: the first one is the position of the first byte of the bytecode instruction that contains the forward
86       * reference, while the second is the position of the first byte of the forward reference itself. In fact the sign
87       * of the first integer indicates if this reference uses 2 or 4 bytes, and its absolute value gives the position of
88       * the bytecode instruction. This array is also used as a bit set to store the subroutines to which a basic block
89       * belongs.
90       */
91      private int[] srcAndRefPositions;
92  
93      // Fields for the control flow and data flow graph analysis algorithms (used to compute the maximum stack size or
94      // the stack map frames).
95      // A control flow graph contains one node per "basic block", and one edge per "jump" from one basic block to
96      // another.
97      // Each node (i.e., each basic block) is represented by the Label object that corresponds to the first instruction
98      // of this basic block.
99      // Each node also stores the list of its successors in the graph, as a linked list of Edge objects.
100     //
101     // The control flow analysis algorithms used to compute the maximum stack size or the stack map frames are similar
102     // and use two steps.
103     // The first step, during the visit of each instruction, builds information about the state of the local variables
104     // and the operand stack
105     // at the end of each basic block, called the "output frame", <em>relatively</em> to the frame state at the
106     // beginning of the basic block,
107     // which is called the "input frame", and which is <em>unknown</em> during this step.
108     // The second step, in {@link MethodWriter#visitMaxStack}, is a fix point algorithm that computes information about
109     // the input frame of
110     // each basic block, from the input state of the first basic block (known from the method signature), and by the
111     // using the previously
112     // computed relative output frames.
113     //
114     // The algorithm used to compute the maximum stack size only computes the relative output and absolute input stack
115     // heights, while the
116     // algorithm used to compute stack map frames computes relative output frames and absolute input frames.
117 
118     /**
119      * Start of the output stack relatively to the input stack. The exact semantics of this field depends on the
120      * algorithm that is used.
121      * <p>
122      * When only the maximum stack size is computed, this field is the number of elements in the input stack.
123      * <p>
124      * When the stack map frames are completely computed, this field is the offset of the first output stack element
125      * relatively to the top of the input stack. This offset is always negative or null. A null offset means that the
126      * output stack must be appended to the input stack. A -n offset means that the first n output stack elements must
127      * replace the top n input stack elements, and that the other elements must be appended to the input stack.
128      */
129     @NonNegative
130     int inputStackTop;
131 
132     /**
133      * Maximum height reached by the output stack, relatively to the top of the input stack. This maximum is always
134      * positive or null.
135      */
136     @NonNegative
137     int outputStackMax;
138 
139     /**
140      * Information about the input and output stack map frames of this basic block. This field is only used for
141      * classfiles of version 1.7+.
142      */
143     Frame frame;
144 
145     /**
146      * The successor of this label, in the order they are visited. This linked list does not include labels used for
147      * debug info only. If the classfile being read is of version 1.7+ then, in addition, it does not contain successive
148      * labels that denote the same bytecode position (in this case only the first label appears in this list).
149      */
150     @Nullable
151     Label successor;
152 
153     /**
154      * The successors of this node in the control flow graph. These successors are stored in a linked list of
155      * {@link Edge Edge} objects, linked to each other by their {@link Edge#next} field.
156      */
157     Edge successors;
158 
159     /**
160      * The next basic block in the basic block stack. This stack is used in the main loop of the fix point algorithm
161      * used in the second step of the control flow analysis algorithms.
162      */
163     @Nullable
164     Label next;
165 
166     /**
167      * Returns the {@link #frame} this basic block belongs to, if any.
168      */
169     public Frame getFrame() {
170         return frame;
171     }
172 
173     public boolean isDebug() {
174         return (status & Status.DEBUG) != 0;
175     }
176 
177     public boolean isResolved() {
178         return (status & Status.RESOLVED) != 0;
179     }
180 
181     boolean isPushed() {
182         return (status & Status.PUSHED) != 0;
183     }
184 
185     public boolean isTarget() {
186         return (status & Status.TARGET) != 0;
187     }
188 
189     public boolean isStoringFrame() {
190         return (status & Status.STORE) != 0;
191     }
192 
193     public boolean isReachable() {
194         return (status & Status.REACHABLE) != 0;
195     }
196 
197     public void markAsDebug() {
198         status |= Status.DEBUG;
199     }
200 
201     private void markAsResolved() {
202         status |= Status.RESOLVED;
203     }
204 
205     void markAsPushed() {
206         status |= Status.PUSHED;
207     }
208 
209     public void markAsTarget() {
210         status |= Status.TARGET;
211     }
212 
213     void markAsStoringFrame() {
214         status |= Status.STORE;
215     }
216 
217     void markAsReachable() {
218         status |= Status.REACHABLE;
219     }
220 
221     void markAsTarget(@NonNull Label target) {
222         status |= target.status & Status.TARGET;
223     }
224 
225     /**
226      * Puts a reference to this label in the bytecode of a method. If the position of the label is known, the offset is
227      * computed and written directly. Otherwise, a null offset is written and a new forward reference is declared for
228      * this label.
229      *
230      * @param out
231      *            the bytecode of the method
232      * @param source
233      *            the position of first byte of the bytecode instruction that contains this label
234      * @param wideOffset
235      *            <code>true</code> if the reference must be stored in 4 bytes, or <code>false</code> if it must be
236      *            stored with 2 bytes
237      *
238      * @throws IllegalArgumentException
239      *             if this label has not been created by the given code writer
240      */
241     public void put(@NonNull ByteVector out, @NonNegative int source, boolean wideOffset) {
242         if (isResolved()) {
243             int reference = position - source;
244 
245             if (wideOffset) {
246                 out.putInt(reference);
247             } else {
248                 out.putShort(reference);
249             }
250         } else if (wideOffset) {
251             addReference(-1 - source, out.getLength());
252             out.putInt(-1);
253         } else {
254             addReference(source, out.getLength());
255             out.putShort(-1);
256         }
257     }
258 
259     /**
260      * Adds a forward reference to this label. This method must be called only for a true forward reference, i.e. only
261      * if this label is not resolved yet. For backward references, the offset of the reference can be, and must be,
262      * computed and stored directly.
263      *
264      * @param sourcePosition
265      *            the position of the referencing instruction, which will be used to compute the offset of this forward
266      *            reference
267      * @param referencePosition
268      *            the position where the offset for this forward reference must be stored
269      */
270     private void addReference(@NonNegative int sourcePosition, @NonNegative int referencePosition) {
271         if (srcAndRefPositions == null) {
272             srcAndRefPositions = new int[6];
273         }
274 
275         if (referenceCount >= srcAndRefPositions.length) {
276             int[] a = new int[srcAndRefPositions.length + 6];
277             System.arraycopy(srcAndRefPositions, 0, a, 0, srcAndRefPositions.length);
278             srcAndRefPositions = a;
279         }
280 
281         srcAndRefPositions[referenceCount++] = sourcePosition;
282         srcAndRefPositions[referenceCount++] = referencePosition;
283     }
284 
285     /**
286      * Resolves all forward references to this label. This method must be called when this label is added to the
287      * bytecode of the method, i.e. when its position becomes known. This method fills in the blanks that where left in
288      * the bytecode by each forward reference previously added to this label.
289      *
290      * @param methodBytecode
291      *            bytecode of the method containing this label
292      */
293     @SuppressWarnings("NumericCastThatLosesPrecision")
294     void resolve(@NonNull ByteVector methodBytecode) {
295         markAsResolved();
296 
297         byte[] data = methodBytecode.getData();
298         int pos = methodBytecode.getLength();
299         position = pos;
300         int[] srcAndRefPos = srcAndRefPositions;
301 
302         for (int i = 0, refCount = referenceCount; i < refCount; i += 2) {
303             int source = srcAndRefPos[i];
304             int reference = srcAndRefPos[i + 1];
305             int offset;
306 
307             if (source >= 0) {
308                 offset = pos - source;
309             } else {
310                 offset = pos + source + 1;
311                 data[reference] = (byte) (offset >>> 24);
312                 reference++;
313                 data[reference] = (byte) (offset >>> 16);
314                 reference++;
315             }
316 
317             data[reference] = (byte) (offset >>> 8);
318             reference++;
319             data[reference] = (byte) offset;
320         }
321     }
322 
323     /**
324      * Returns the first label of the series to which this label belongs. For an isolated label or for the first label
325      * in a series of successive labels, returns the label itself. For other labels, returns the first label of the
326      * series.
327      *
328      * @return the first label of the series to which this label belongs
329      */
330     @NonNull
331     public Label getFirst() {
332         return frame == null ? this : frame.owner;
333     }
334 
335     @NonNegative
336     int decrementInputStackTop() {
337         return --inputStackTop;
338     }
339 
340     void decrementInputStackTop(@NonNegative int amount) {
341         inputStackTop -= amount;
342     }
343 
344     /**
345      * Returns this label's {@link #successor}, if any.
346      */
347     @Nullable
348     public Label getSuccessor() {
349         return successor;
350     }
351 
352     @Nullable
353     public Label setSuccessors(@NonNull Edge edge) {
354         edge.setNext(successors);
355         successors = edge;
356         return successor;
357     }
358 
359     /**
360      * Updates the maximum height reached by the output stack, if needed.
361      */
362     void updateOutputStackMaxHeight(@NonNegative int outputStackTop) {
363         int top = inputStackTop + outputStackTop;
364 
365         if (top > outputStackMax) {
366             outputStackMax = top;
367         }
368     }
369 
370     @Override
371     public String toString() {
372         return "L" + System.identityHashCode(this);
373     }
374 }