• 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.08
/main/src/main/java/mockit/asm/controlFlow/StackMapTableWriter.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 static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.APPEND_FRAME;
9
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.CHOP_FRAME;
10
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.FULL_FRAME;
11
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.SAME_FRAME;
12
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.SAME_FRAME_EXTENDED;
13
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME;
14
import static mockit.asm.controlFlow.StackMapTableWriter.LocalsAndStackItemsDiff.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
15

16
import edu.umd.cs.findbugs.annotations.NonNull;
17

18
import mockit.asm.constantPool.AttributeWriter;
19
import mockit.asm.constantPool.ConstantPoolGeneration;
20
import mockit.asm.constantPool.UninitializedTypeTableItem;
21
import mockit.asm.jvmConstants.Access;
22
import mockit.asm.types.JavaType;
23
import mockit.asm.util.ByteVector;
24

25
import org.checkerframework.checker.index.qual.NonNegative;
26

27
/**
28
 * Writes the "StackMapTable" method attribute (or "StackMap" for classfiles older than Java 6).
29
 */
30
public final class StackMapTableWriter extends AttributeWriter {
31
    /**
32
     * Constants that identify how many locals and stack items a frame has, with respect to its previous frame.
33
     */
34
    interface LocalsAndStackItemsDiff {
35
        /**
36
         * Same locals as the previous frame, number of stack items is zero.
37
         */
38
        int SAME_FRAME = 0; // to 63 (0-3f)
39

40
        /**
41
         * Same locals as the previous frame, number of stack items is 1.
42
         */
43
        int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
44

45
        /**
46
         * Same locals as the previous frame, number of stack items is 1. Offset is bigger then 63.
47
         */
48
        int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
49

50
        /**
51
         * Current locals are the same as the locals in the previous frame, except that the k last locals are absent.
52
         * The value of k is given by the formula 251-frame_type.
53
         */
54
        int CHOP_FRAME = 248; // to 250 (f8-fA)
55

56
        /**
57
         * Same locals as the previous frame, number of stack items is zero. Offset is bigger then 63.
58
         */
59
        int SAME_FRAME_EXTENDED = 251; // fb
60

61
        /**
62
         * Current locals are the same as the locals in the previous frame, except that k additional locals are defined.
63
         * The value of k is given by the formula frame_type-251.
64
         */
65
        int APPEND_FRAME = 252; // to 254 // fc-fe
66

67
        /**
68
         * Full frame.
69
         */
70
        int FULL_FRAME = 255; // ff
71
    }
72

73
    private final boolean java6OrNewer;
74

75
    /**
76
     * Maximum stack size of this method.
77
     */
78
    @NonNegative
79
    private int maxStack;
80

81
    /**
82
     * Maximum number of local variables for this method.
83
     */
84
    @NonNegative
85
    private int maxLocals;
86

87
    /**
88
     * Number of stack map frames in the StackMapTable attribute.
89
     */
90
    @NonNegative
91
    private int frameCount;
92

93
    /**
94
     * The StackMapTable attribute.
95
     */
96
    private ByteVector stackMap;
97

98
    /**
99
     * The last frame that was written in the StackMapTable attribute.
100
     *
101
     * @see #frameDefinition
102
     */
103
    private int[] previousFrame;
104

105
    /**
106
     * The current stack map frame.
107
     * <p>
108
     * The first element contains the offset of the instruction to which the frame corresponds (frameDefinition[0] =
109
     * offset), the second element is the number of locals (frameDefinition[1] = nLocal), and the third one is the
110
     * number of stack elements (frameDefinition[2] = nStack). The local variables start at index 3 (frameDefinition[3
111
     * to 3+nLocal-1]) and are followed by the operand stack values (frameDefinition[3+nLocal...]).
112
     * <p>
113
     * All types are encoded as integers, with the same format as the one used in {@link Label}, but limited to BASE
114
     * types.
115
     */
116
    private int[] frameDefinition;
117

118
    /**
119
     * The current index in {@link #frameDefinition}, when writing new values into the array.
120
     */
121
    @NonNegative
122
    private int frameIndex;
123

124
    public StackMapTableWriter(@NonNull ConstantPoolGeneration cp, boolean java6OrNewer, int methodAccess,
125
            @NonNull String methodDesc) {
126
        super(cp);
1✔
127
        this.java6OrNewer = java6OrNewer;
1✔
128

129
        int size = JavaType.getArgumentsAndReturnSizes(methodDesc) >> 2;
1✔
130

131
        if ((methodAccess & Access.STATIC) != 0) {
1✔
132
            size--;
1✔
133
        }
134

135
        maxLocals = size;
1✔
136
    }
1✔
137

138
    public void setMaxStack(@NonNegative int maxStack) {
139
        this.maxStack = maxStack;
1✔
140
    }
1✔
141

142
    public void updateMaxLocals(@NonNegative int n) {
143
        if (n > maxLocals) {
1✔
144
            maxLocals = n;
1✔
145
        }
146
    }
1✔
147

148
    public void putMaxStackAndLocals(@NonNull ByteVector out) {
149
        out.putShort(maxStack).putShort(maxLocals);
1✔
150
    }
1✔
151

152
    @NonNegative
153
    private int getInstructionOffset() {
154
        return frameDefinition[0];
1✔
155
    }
156

157
    private void setInstructionOffset(@NonNegative int offset) {
158
        frameDefinition[0] = offset;
1✔
159
    }
1✔
160

161
    @NonNegative
162
    private int getNumLocals() {
163
        return frameDefinition[1];
1✔
164
    }
165

166
    private void setNumLocals(@NonNegative int numLocals) {
167
        frameDefinition[1] = numLocals;
1✔
168
    }
1✔
169

170
    @NonNegative
171
    private int getStackSize() {
172
        return frameDefinition[2];
1✔
173
    }
174

175
    private void setStackSize(@NonNegative int stackSize) {
176
        frameDefinition[2] = stackSize;
1✔
177
    }
1✔
178

179
    private void writeFrameDefinition(@NonNegative int value) {
180
        frameDefinition[frameIndex++] = value;
1✔
181
    }
1✔
182

183
    public boolean hasStackMap() {
184
        return stackMap != null;
1✔
185
    }
186

187
    /**
188
     * Starts the visit of a stack map frame. Sets {@link #frameIndex} to the index of the next element to be written in
189
     * this frame.
190
     *
191
     * @param offset
192
     *            the offset of the instruction to which the frame corresponds.
193
     * @param nLocals
194
     *            the number of local variables in the frame.
195
     * @param nStack
196
     *            the number of stack elements in the frame.
197
     */
198
    private void startFrame(@NonNegative int offset, @NonNegative int nLocals, @NonNegative int nStack) {
199
        int n = 3 + nLocals + nStack;
1✔
200

201
        if (frameDefinition == null || frameDefinition.length < n) {
1!
202
            frameDefinition = new int[n];
1✔
203
        }
204

205
        setInstructionOffset(offset);
1✔
206
        setNumLocals(nLocals);
1✔
207
        setStackSize(nStack);
1✔
208
        frameIndex = 3;
1✔
209
    }
1✔
210

211
    /**
212
     * Checks if the visit of the current {@link #frameDefinition frame} is finished, and if yes, write it in the
213
     * StackMapTable attribute.
214
     */
215
    private void endFrame() {
216
        if (previousFrame != null) { // do not write the first frame
1✔
217
            if (stackMap == null) {
1✔
218
                setAttribute(java6OrNewer ? "StackMapTable" : "StackMap");
1!
219
                stackMap = new ByteVector();
1✔
220
            }
221

222
            writeFrame();
1✔
223
            frameCount++;
1✔
224
        }
225

226
        previousFrame = frameDefinition;
1✔
227
        frameDefinition = null;
1✔
228
    }
1✔
229

230
    /**
231
     * Compress and writes the current {@link #frameDefinition frame} in the StackMapTable attribute.
232
     */
233
    private void writeFrame() {
234
        int currentLocalsSize = getNumLocals();
1✔
235
        int currentStackSize = getStackSize();
1✔
236

237
        if (java6OrNewer) {
1!
238
            writeFrameForJava6OrNewer(currentLocalsSize, currentStackSize);
1✔
239
        } else {
240
            writeFrameForOldVersionOfJava(currentLocalsSize, currentStackSize);
×
241
        }
242
    }
1✔
243

244
    private void writeFrameForOldVersionOfJava(@NonNegative int localsSize, @NonNegative int stackSize) {
245
        int instructionOffset = getInstructionOffset();
×
246
        writeFrame(instructionOffset, localsSize, stackSize);
×
247
    }
×
248

249
    private void writeFullFrame(@NonNegative int instructionOffset, @NonNegative int localsSize,
250
            @NonNegative int stackSize) {
251
        stackMap.putByte(FULL_FRAME);
1✔
252
        writeFrame(instructionOffset, localsSize, stackSize);
1✔
253
    }
1✔
254

255
    private void writeFrame(@NonNegative int instructionOffset, @NonNegative int localsSize,
256
            @NonNegative int stackSize) {
257
        stackMap.putShort(instructionOffset);
1✔
258

259
        stackMap.putShort(localsSize);
1✔
260
        int lastTypeIndex = 3 + localsSize;
1✔
261
        writeFrameTypes(3, lastTypeIndex);
1✔
262

263
        stackMap.putShort(stackSize);
1✔
264
        writeFrameTypes(lastTypeIndex, lastTypeIndex + stackSize);
1✔
265
    }
1✔
266

267
    private void writeFrameForJava6OrNewer(@NonNegative int currentLocalsSize, @NonNegative int currentStackSize) {
268
        @NonNegative
269
        int previousLocalsSize = previousFrame[1];
1✔
270
        int k = currentStackSize == 0 ? currentLocalsSize - previousLocalsSize : 0;
1✔
271
        @NonNegative
272
        int delta = getDelta();
1✔
273
        int type = selectFrameType(currentLocalsSize, currentStackSize, previousLocalsSize, k, delta);
1✔
274

275
        if (type == CHOP_FRAME) {
1✔
276
            previousLocalsSize = currentLocalsSize;
1✔
277
        }
278

279
        type = selectFullFrameIfLocalsAreNotTheSame(previousLocalsSize, type);
1✔
280

281
        switch (type) {
1!
282
            case SAME_FRAME:
283
                stackMap.putByte(delta);
1✔
284
                break;
1✔
285
            case SAME_LOCALS_1_STACK_ITEM_FRAME:
286
                writeFrameWithSameLocalsAndOneStackItem(currentLocalsSize, delta);
1✔
287
                break;
1✔
288
            case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
289
                writeExtendedFrameWithSameLocalsAndOneStackItem(currentLocalsSize, delta);
1✔
290
                break;
1✔
291
            case SAME_FRAME_EXTENDED:
292
                writeFrameWithSameLocalsAndZeroStackItems(0, delta);
1✔
293
                break;
1✔
294
            case CHOP_FRAME:
295
                writeFrameWithSameLocalsAndZeroStackItems(k, delta);
1✔
296
                break;
1✔
297
            case APPEND_FRAME:
298
                writeAppendedFrame(currentLocalsSize, previousLocalsSize, k, delta);
1✔
299
                break;
1✔
300
            case FULL_FRAME:
301
                writeFullFrame(delta, currentLocalsSize, currentStackSize);
1✔
302
                break;
1✔
303
            default:
304
                throw new IllegalArgumentException("Unknown frame type: " + type);
×
305
        }
306
    }
1✔
307

308
    @NonNegative
309
    private int getDelta() {
310
        int offset = getInstructionOffset();
1✔
311
        return frameCount == 0 ? offset : offset - previousFrame[0] - 1;
1✔
312
    }
313

314
    @NonNegative
315
    private static int selectFrameType(@NonNegative int currentLocalsSize, @NonNegative int currentStackSize,
316
            @NonNegative int previousLocalsSize, int k, @NonNegative int delta) {
317
        int type = FULL_FRAME;
1✔
318

319
        if (currentStackSize == 0) {
1✔
320
            if (k == 0) {
1✔
321
                type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
1✔
322
            } else if (k > 0) {
1✔
323
                if (k <= 3) {
1✔
324
                    type = APPEND_FRAME;
1✔
325
                }
326
            } else if (k >= -3) {
1✔
327
                type = CHOP_FRAME;
1✔
328
            }
329
        } else if (currentLocalsSize == previousLocalsSize && currentStackSize == 1) {
1✔
330
            type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
1✔
331
        }
332

333
        return type;
1✔
334
    }
335

336
    @NonNegative
337
    private int selectFullFrameIfLocalsAreNotTheSame(@NonNegative int previousLocalsSize, @NonNegative int type) {
338
        if (type != FULL_FRAME) {
1✔
339
            // Verify if locals are the same.
340
            int l = 3;
1✔
341

342
            for (int j = 0; j < previousLocalsSize; j++) {
1✔
343
                if (frameDefinition[l] != previousFrame[l]) {
1✔
344
                    return FULL_FRAME;
1✔
345
                }
346

347
                l++;
1✔
348
            }
349
        }
350

351
        return type;
1✔
352
    }
353

354
    private void writeFrameWithSameLocalsAndOneStackItem(@NonNegative int localsSize, @NonNegative int delta) {
355
        stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
1✔
356
        writeFrameTypes(3 + localsSize, 4 + localsSize);
1✔
357
    }
1✔
358

359
    private void writeFrameWithSameLocalsAndZeroStackItems(int k, @NonNegative int delta) {
360
        stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
1✔
361
    }
1✔
362

363
    private void writeExtendedFrameWithSameLocalsAndOneStackItem(@NonNegative int localsSize, @NonNegative int delta) {
364
        stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(delta);
1✔
365
        writeFrameTypes(3 + localsSize, 4 + localsSize);
1✔
366
    }
1✔
367

368
    private void writeAppendedFrame(@NonNegative int currentLocalsSize, @NonNegative int previousLocalsSize, int k,
369
            @NonNegative int delta) {
370
        stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
1✔
371
        writeFrameTypes(3 + previousLocalsSize, 3 + currentLocalsSize);
1✔
372
    }
1✔
373

374
    /**
375
     * Writes some types of the current {@link #frameDefinition frame} into the StackMapTable attribute. This method
376
     * converts types from the format used in {@link Label} to the format used in StackMapTable attributes. In
377
     * particular, it converts type table indexes to constant pool indexes.
378
     *
379
     * @param start
380
     *            index of the first type in {@link #frameDefinition} to write
381
     * @param end
382
     *            index of last type in {@link #frameDefinition} to write (exclusive)
383
     */
384
    private void writeFrameTypes(@NonNegative int start, @NonNegative int end) {
385
        for (int i = start; i < end; i++) {
1✔
386
            int type = frameDefinition[i];
1✔
387
            int dimensions = type & FrameTypeMask.DIM;
1✔
388

389
            if (dimensions == 0) {
1✔
390
                writeFrameOfRegularType(type);
1✔
391
            } else {
392
                writeFrameOfArrayType(dimensions, type);
1✔
393
            }
394
        }
395
    }
1✔
396

397
    private void writeFrameOfRegularType(@NonNegative int type) {
398
        int typeTableIndex = type & FrameTypeMask.BASE_VALUE;
1✔
399

400
        switch (type & FrameTypeMask.BASE_KIND) {
1✔
401
            case FrameTypeMask.OBJECT:
402
                String classDesc = cp.getInternalName(typeTableIndex);
1✔
403
                int classDescIndex = cp.newClass(classDesc);
1✔
404
                stackMap.putByte(7).putShort(classDescIndex);
1✔
405
                break;
1✔
406
            case FrameTypeMask.UNINITIALIZED:
407
                UninitializedTypeTableItem uninitializedItemValue = cp.getUninitializedItemValue(typeTableIndex);
1✔
408
                int typeDesc = uninitializedItemValue.getOffset();
1✔
409
                stackMap.putByte(8).putShort(typeDesc);
1✔
410
                break;
1✔
411
            default:
412
                stackMap.putByte(typeTableIndex);
1✔
413
        }
414
    }
1✔
415

416
    private void writeFrameOfArrayType(@NonNegative int arrayDimensions, @NonNegative int arrayElementType) {
417
        StringBuilder sb = new StringBuilder();
1✔
418
        writeDimensionsIntoArrayDescriptor(sb, arrayDimensions);
1✔
419

420
        if ((arrayElementType & FrameTypeMask.BASE_KIND) == FrameTypeMask.OBJECT) {
1✔
421
            String arrayElementTypeDesc = cp.getInternalName(arrayElementType & FrameTypeMask.BASE_VALUE);
1✔
422
            sb.append('L').append(arrayElementTypeDesc).append(';');
1✔
423
        } else {
1✔
424
            char typeCode = getTypeCodeForArrayElements(arrayElementType);
1✔
425
            sb.append(typeCode);
1✔
426
        }
427

428
        String arrayElementTypeDesc = sb.toString();
1✔
429
        int typeDescIndex = cp.newClass(arrayElementTypeDesc);
1✔
430
        stackMap.putByte(7).putShort(typeDescIndex);
1✔
431
    }
1✔
432

433
    private static void writeDimensionsIntoArrayDescriptor(@NonNull StringBuilder sb,
434
            @NonNegative int arrayDimensions) {
435
        arrayDimensions >>= 28;
1✔
436

437
        while (arrayDimensions-- > 0) {
1✔
438
            sb.append('[');
1✔
439
        }
440
    }
1✔
441

442
    private static char getTypeCodeForArrayElements(@NonNegative int arrayElementType) {
443
        switch (arrayElementType & 0xF) {
1!
444
            case 1:
445
                return 'I';
1✔
446
            case 2:
447
                return 'F';
×
448
            case 3:
449
                return 'D';
×
450
            case 9:
451
                return 'Z';
1✔
452
            case 10:
453
                return 'B';
1✔
454
            case 11:
455
                return 'C';
1✔
456
            case 12:
457
                return 'S';
×
458
            default:
459
                return 'J';
1✔
460
        }
461
    }
462

463
    /**
464
     * Creates and visits the first (implicit) frame.
465
     */
466
    public void createAndVisitFirstFrame(@NonNull Frame frame, @NonNull String classDesc, @NonNull String methodDesc,
467
            int methodAccess) {
468
        JavaType[] args = JavaType.getArgumentTypes(methodDesc);
1✔
469
        frame.initInputFrame(classDesc, methodAccess, args, maxLocals);
1✔
470
        visitFrame(frame);
1✔
471
    }
1✔
472

473
    /**
474
     * Visits a frame that has been computed from scratch.
475
     */
476
    public void visitFrame(@NonNull Frame frame) {
477
        int[] locals = frame.inputLocals;
1✔
478
        int nLocal = computeNumberOfLocals(locals);
1✔
479

480
        int[] stacks = frame.inputStack;
1✔
481
        int nStack = computeStackSize(stacks);
1✔
482

483
        startFrame(frame.owner.position, nLocal, nStack);
1✔
484
        putLocalsOrStackElements(locals, nLocal);
1✔
485
        putLocalsOrStackElements(stacks, nStack);
1✔
486
        endFrame();
1✔
487
    }
1✔
488

489
    /**
490
     * Computes the number of locals (ignores TOP types that are just after a LONG or a DOUBLE, and all trailing TOP
491
     * types).
492
     */
493
    @NonNegative
494
    private static int computeNumberOfLocals(@NonNull int[] locals) {
495
        int nLocal = 0;
1✔
496
        int nTop = 0;
1✔
497

498
        for (int i = 0; i < locals.length; i++) {
1✔
499
            int t = locals[i];
1✔
500

501
            if (t == FrameTypeMask.TOP) {
1✔
502
                nTop++;
1✔
503
            } else {
504
                nLocal += nTop + 1;
1✔
505
                nTop = 0;
1✔
506
            }
507

508
            if (t == FrameTypeMask.LONG || t == FrameTypeMask.DOUBLE) {
1✔
509
                i++;
1✔
510
            }
511
        }
512

513
        return nLocal;
1✔
514
    }
515

516
    /**
517
     * Computes the stack size (ignores TOP types that are just after a LONG or a DOUBLE).
518
     */
519
    @NonNegative
520
    private static int computeStackSize(@NonNull int[] stacks) {
521
        int nStack = 0;
1✔
522

523
        for (int i = 0; i < stacks.length; i++) {
1✔
524
            int t = stacks[i];
1✔
525
            nStack++;
1✔
526

527
            if (t == FrameTypeMask.LONG || t == FrameTypeMask.DOUBLE) {
1!
528
                i++;
1✔
529
            }
530
        }
531

532
        return nStack;
1✔
533
    }
534

535
    private void putLocalsOrStackElements(@NonNull int[] itemIndices, @NonNegative int nItems) {
536
        for (int i = 0; nItems > 0; i++, nItems--) {
1✔
537
            int itemType = itemIndices[i];
1✔
538
            writeFrameDefinition(itemType);
1✔
539

540
            if (itemType == FrameTypeMask.LONG || itemType == FrameTypeMask.DOUBLE) {
1✔
541
                i++;
1✔
542
            }
543
        }
544
    }
1✔
545

546
    @NonNegative
547
    @Override
548
    public int getSize() {
549
        return stackMap == null ? 0 : 8 + stackMap.getLength();
1✔
550
    }
551

552
    @Override
553
    public void put(@NonNull ByteVector out) {
554
        if (stackMap != null) {
1✔
555
            put(out, 2 + stackMap.getLength());
1✔
556
            out.putShort(frameCount);
1✔
557
            out.putByteVector(stackMap);
1✔
558
        }
559
    }
1✔
560
}
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