• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / jmockit1 / 496

15 Nov 2025 05:33PM UTC coverage: 72.192% (-0.008%) from 72.2%
496

push

github

web-flow
Merge pull request #412 from hazendaz/renovate/major-spring-core

Update spring core to v7 (major)

5677 of 8360 branches covered (67.91%)

Branch coverage included in aggregate %.

11922 of 16018 relevant lines covered (74.43%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.23
/main/src/main/java/mockit/asm/controlFlow/Label.java
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 {
1✔
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;
1✔
176
    }
177

178
    public boolean isDebug() {
179
        return (status & Status.DEBUG) != 0;
1✔
180
    }
181

182
    public boolean isResolved() {
183
        return (status & Status.RESOLVED) != 0;
1✔
184
    }
185

186
    boolean isPushed() {
187
        return (status & Status.PUSHED) != 0;
1✔
188
    }
189

190
    public boolean isTarget() {
191
        return (status & Status.TARGET) != 0;
1✔
192
    }
193

194
    public boolean isStoringFrame() {
195
        return (status & Status.STORE) != 0;
1✔
196
    }
197

198
    public boolean isReachable() {
199
        return (status & Status.REACHABLE) != 0;
×
200
    }
201

202
    public void markAsDebug() {
203
        status |= Status.DEBUG;
1✔
204
    }
1✔
205

206
    private void markAsResolved() {
207
        status |= Status.RESOLVED;
1✔
208
    }
1✔
209

210
    void markAsPushed() {
211
        status |= Status.PUSHED;
1✔
212
    }
1✔
213

214
    public void markAsTarget() {
215
        status |= Status.TARGET;
1✔
216
    }
1✔
217

218
    void markAsStoringFrame() {
219
        status |= Status.STORE;
1✔
220
    }
1✔
221

222
    void markAsReachable() {
223
        status |= Status.REACHABLE;
1✔
224
    }
1✔
225

226
    void markAsTarget(@NonNull Label target) {
227
        status |= target.status & Status.TARGET;
1✔
228
    }
1✔
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()) {
1✔
248
            int reference = position - source;
1✔
249

250
            if (wideOffset) {
1!
251
                out.putInt(reference);
×
252
            } else {
253
                out.putShort(reference);
1✔
254
            }
255
        } else if (wideOffset) {
1✔
256
            addReference(-1 - source, out.getLength());
1✔
257
            out.putInt(-1);
1✔
258
        } else {
259
            addReference(source, out.getLength());
1✔
260
            out.putShort(-1);
1✔
261
        }
262
    }
1✔
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) {
1✔
277
            srcAndRefPositions = new int[6];
1✔
278
        }
279

280
        if (referenceCount >= srcAndRefPositions.length) {
1✔
281
            int[] a = new int[srcAndRefPositions.length + 6];
1✔
282
            System.arraycopy(srcAndRefPositions, 0, a, 0, srcAndRefPositions.length);
1✔
283
            srcAndRefPositions = a;
1✔
284
        }
285

286
        srcAndRefPositions[referenceCount++] = sourcePosition;
1✔
287
        srcAndRefPositions[referenceCount++] = referencePosition;
1✔
288
    }
1✔
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();
1✔
301

302
        byte[] data = methodBytecode.getData();
1✔
303
        int pos = methodBytecode.getLength();
1✔
304
        position = pos;
1✔
305
        int[] srcAndRefPos = srcAndRefPositions;
1✔
306

307
        for (int i = 0, refCount = referenceCount; i < refCount; i += 2) {
1✔
308
            int source = srcAndRefPos[i];
1✔
309
            int reference = srcAndRefPos[i + 1];
1✔
310
            int offset;
311

312
            if (source >= 0) {
1✔
313
                offset = pos - source;
1✔
314
            } else {
315
                offset = pos + source + 1;
1✔
316
                data[reference] = (byte) (offset >>> 24);
1✔
317
                reference++;
1✔
318
                data[reference] = (byte) (offset >>> 16);
1✔
319
                reference++;
1✔
320
            }
321

322
            data[reference] = (byte) (offset >>> 8);
1✔
323
            reference++;
1✔
324
            data[reference] = (byte) offset;
1✔
325
        }
326
    }
1✔
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;
1✔
338
    }
339

340
    @NonNegative
341
    int decrementInputStackTop() {
342
        return --inputStackTop;
1✔
343
    }
344

345
    void decrementInputStackTop(@NonNegative int amount) {
346
        inputStackTop -= amount;
1✔
347
    }
1✔
348

349
    /**
350
     * Returns this label's {@link #successor}, if any.
351
     */
352
    @Nullable
353
    public Label getSuccessor() {
354
        return successor;
1✔
355
    }
356

357
    @Nullable
358
    public Label setSuccessors(@NonNull Edge edge) {
359
        edge.setNext(successors);
1✔
360
        successors = edge;
1✔
361
        return successor;
1✔
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;
1✔
369

370
        if (top > outputStackMax) {
1✔
371
            outputStackMax = top;
1✔
372
        }
373
    }
1✔
374

375
    @Override
376
    public String toString() {
377
        return "L" + System.identityHashCode(this);
×
378
    }
379
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc