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 }