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