1
2
3
4
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
29
30 public final class StackMapTableWriter extends AttributeWriter {
31
32
33
34 interface LocalsAndStackItemsDiff {
35
36
37
38 int SAME_FRAME = 0;
39
40
41
42
43 int SAME_LOCALS_1_STACK_ITEM_FRAME = 64;
44
45
46
47
48 int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247;
49
50
51
52
53
54 int CHOP_FRAME = 248;
55
56
57
58
59 int SAME_FRAME_EXTENDED = 251;
60
61
62
63
64
65 int APPEND_FRAME = 252;
66
67
68
69
70 int FULL_FRAME = 255;
71 }
72
73 private final boolean java6OrNewer;
74
75
76
77
78 @NonNegative
79 private int maxStack;
80
81
82
83
84 @NonNegative
85 private int maxLocals;
86
87
88
89
90 @NonNegative
91 private int frameCount;
92
93
94
95
96 private ByteVector stackMap;
97
98
99
100
101
102
103 private int[] previousFrame;
104
105
106
107
108
109
110
111
112
113
114
115
116 private int[] frameDefinition;
117
118
119
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);
127 this.java6OrNewer = java6OrNewer;
128
129 int size = JavaType.getArgumentsAndReturnSizes(methodDesc) >> 2;
130
131 if ((methodAccess & Access.STATIC) != 0) {
132 size--;
133 }
134
135 maxLocals = size;
136 }
137
138 public void setMaxStack(@NonNegative int maxStack) {
139 this.maxStack = maxStack;
140 }
141
142 public void updateMaxLocals(@NonNegative int n) {
143 if (n > maxLocals) {
144 maxLocals = n;
145 }
146 }
147
148 public void putMaxStackAndLocals(@NonNull ByteVector out) {
149 out.putShort(maxStack).putShort(maxLocals);
150 }
151
152 @NonNegative
153 private int getInstructionOffset() {
154 return frameDefinition[0];
155 }
156
157 private void setInstructionOffset(@NonNegative int offset) {
158 frameDefinition[0] = offset;
159 }
160
161 @NonNegative
162 private int getNumLocals() {
163 return frameDefinition[1];
164 }
165
166 private void setNumLocals(@NonNegative int numLocals) {
167 frameDefinition[1] = numLocals;
168 }
169
170 @NonNegative
171 private int getStackSize() {
172 return frameDefinition[2];
173 }
174
175 private void setStackSize(@NonNegative int stackSize) {
176 frameDefinition[2] = stackSize;
177 }
178
179 private void writeFrameDefinition(@NonNegative int value) {
180 frameDefinition[frameIndex++] = value;
181 }
182
183 public boolean hasStackMap() {
184 return stackMap != null;
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198 private void startFrame(@NonNegative int offset, @NonNegative int nLocals, @NonNegative int nStack) {
199 int n = 3 + nLocals + nStack;
200
201 if (frameDefinition == null || frameDefinition.length < n) {
202 frameDefinition = new int[n];
203 }
204
205 setInstructionOffset(offset);
206 setNumLocals(nLocals);
207 setStackSize(nStack);
208 frameIndex = 3;
209 }
210
211
212
213
214
215 private void endFrame() {
216 if (previousFrame != null) {
217 if (stackMap == null) {
218 setAttribute(java6OrNewer ? "StackMapTable" : "StackMap");
219 stackMap = new ByteVector();
220 }
221
222 writeFrame();
223 frameCount++;
224 }
225
226 previousFrame = frameDefinition;
227 frameDefinition = null;
228 }
229
230
231
232
233 private void writeFrame() {
234 int currentLocalsSize = getNumLocals();
235 int currentStackSize = getStackSize();
236
237 if (java6OrNewer) {
238 writeFrameForJava6OrNewer(currentLocalsSize, currentStackSize);
239 } else {
240 writeFrameForOldVersionOfJava(currentLocalsSize, currentStackSize);
241 }
242 }
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);
252 writeFrame(instructionOffset, localsSize, stackSize);
253 }
254
255 private void writeFrame(@NonNegative int instructionOffset, @NonNegative int localsSize,
256 @NonNegative int stackSize) {
257 stackMap.putShort(instructionOffset);
258
259 stackMap.putShort(localsSize);
260 int lastTypeIndex = 3 + localsSize;
261 writeFrameTypes(3, lastTypeIndex);
262
263 stackMap.putShort(stackSize);
264 writeFrameTypes(lastTypeIndex, lastTypeIndex + stackSize);
265 }
266
267 private void writeFrameForJava6OrNewer(@NonNegative int currentLocalsSize, @NonNegative int currentStackSize) {
268 @NonNegative
269 int previousLocalsSize = previousFrame[1];
270 int k = currentStackSize == 0 ? currentLocalsSize - previousLocalsSize : 0;
271 @NonNegative
272 int delta = getDelta();
273 int type = selectFrameType(currentLocalsSize, currentStackSize, previousLocalsSize, k, delta);
274
275 if (type == CHOP_FRAME) {
276 previousLocalsSize = currentLocalsSize;
277 }
278
279 type = selectFullFrameIfLocalsAreNotTheSame(previousLocalsSize, type);
280
281 switch (type) {
282 case SAME_FRAME:
283 stackMap.putByte(delta);
284 break;
285 case SAME_LOCALS_1_STACK_ITEM_FRAME:
286 writeFrameWithSameLocalsAndOneStackItem(currentLocalsSize, delta);
287 break;
288 case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
289 writeExtendedFrameWithSameLocalsAndOneStackItem(currentLocalsSize, delta);
290 break;
291 case SAME_FRAME_EXTENDED:
292 writeFrameWithSameLocalsAndZeroStackItems(0, delta);
293 break;
294 case CHOP_FRAME:
295 writeFrameWithSameLocalsAndZeroStackItems(k, delta);
296 break;
297 case APPEND_FRAME:
298 writeAppendedFrame(currentLocalsSize, previousLocalsSize, k, delta);
299 break;
300 case FULL_FRAME:
301 writeFullFrame(delta, currentLocalsSize, currentStackSize);
302 break;
303 default:
304 throw new IllegalArgumentException("Unknown frame type: " + type);
305 }
306 }
307
308 @NonNegative
309 private int getDelta() {
310 int offset = getInstructionOffset();
311 return frameCount == 0 ? offset : offset - previousFrame[0] - 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;
318
319 if (currentStackSize == 0) {
320 if (k == 0) {
321 type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
322 } else if (k > 0) {
323 if (k <= 3) {
324 type = APPEND_FRAME;
325 }
326 } else if (k >= -3) {
327 type = CHOP_FRAME;
328 }
329 } else if (currentLocalsSize == previousLocalsSize && currentStackSize == 1) {
330 type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
331 }
332
333 return type;
334 }
335
336 @NonNegative
337 private int selectFullFrameIfLocalsAreNotTheSame(@NonNegative int previousLocalsSize, @NonNegative int type) {
338 if (type != FULL_FRAME) {
339
340 int l = 3;
341
342 for (int j = 0; j < previousLocalsSize; j++) {
343 if (frameDefinition[l] != previousFrame[l]) {
344 return FULL_FRAME;
345 }
346
347 l++;
348 }
349 }
350
351 return type;
352 }
353
354 private void writeFrameWithSameLocalsAndOneStackItem(@NonNegative int localsSize, @NonNegative int delta) {
355 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
356 writeFrameTypes(3 + localsSize, 4 + localsSize);
357 }
358
359 private void writeFrameWithSameLocalsAndZeroStackItems(int k, @NonNegative int delta) {
360 stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
361 }
362
363 private void writeExtendedFrameWithSameLocalsAndOneStackItem(@NonNegative int localsSize, @NonNegative int delta) {
364 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(delta);
365 writeFrameTypes(3 + localsSize, 4 + localsSize);
366 }
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);
371 writeFrameTypes(3 + previousLocalsSize, 3 + currentLocalsSize);
372 }
373
374
375
376
377
378
379
380
381
382
383
384 private void writeFrameTypes(@NonNegative int start, @NonNegative int end) {
385 for (int i = start; i < end; i++) {
386 int type = frameDefinition[i];
387 int dimensions = type & FrameTypeMask.DIM;
388
389 if (dimensions == 0) {
390 writeFrameOfRegularType(type);
391 } else {
392 writeFrameOfArrayType(dimensions, type);
393 }
394 }
395 }
396
397 private void writeFrameOfRegularType(@NonNegative int type) {
398 int typeTableIndex = type & FrameTypeMask.BASE_VALUE;
399
400 switch (type & FrameTypeMask.BASE_KIND) {
401 case FrameTypeMask.OBJECT:
402 String classDesc = cp.getInternalName(typeTableIndex);
403 int classDescIndex = cp.newClass(classDesc);
404 stackMap.putByte(7).putShort(classDescIndex);
405 break;
406 case FrameTypeMask.UNINITIALIZED:
407 UninitializedTypeTableItem uninitializedItemValue = cp.getUninitializedItemValue(typeTableIndex);
408 int typeDesc = uninitializedItemValue.getOffset();
409 stackMap.putByte(8).putShort(typeDesc);
410 break;
411 default:
412 stackMap.putByte(typeTableIndex);
413 }
414 }
415
416 private void writeFrameOfArrayType(@NonNegative int arrayDimensions, @NonNegative int arrayElementType) {
417 StringBuilder sb = new StringBuilder();
418 writeDimensionsIntoArrayDescriptor(sb, arrayDimensions);
419
420 if ((arrayElementType & FrameTypeMask.BASE_KIND) == FrameTypeMask.OBJECT) {
421 String arrayElementTypeDesc = cp.getInternalName(arrayElementType & FrameTypeMask.BASE_VALUE);
422 sb.append('L').append(arrayElementTypeDesc).append(';');
423 } else {
424 char typeCode = getTypeCodeForArrayElements(arrayElementType);
425 sb.append(typeCode);
426 }
427
428 String arrayElementTypeDesc = sb.toString();
429 int typeDescIndex = cp.newClass(arrayElementTypeDesc);
430 stackMap.putByte(7).putShort(typeDescIndex);
431 }
432
433 private static void writeDimensionsIntoArrayDescriptor(@NonNull StringBuilder sb,
434 @NonNegative int arrayDimensions) {
435 arrayDimensions >>= 28;
436
437 while (arrayDimensions-- > 0) {
438 sb.append('[');
439 }
440 }
441
442 private static char getTypeCodeForArrayElements(@NonNegative int arrayElementType) {
443 switch (arrayElementType & 0xF) {
444 case 1:
445 return 'I';
446 case 2:
447 return 'F';
448 case 3:
449 return 'D';
450 case 9:
451 return 'Z';
452 case 10:
453 return 'B';
454 case 11:
455 return 'C';
456 case 12:
457 return 'S';
458 default:
459 return 'J';
460 }
461 }
462
463
464
465
466 public void createAndVisitFirstFrame(@NonNull Frame frame, @NonNull String classDesc, @NonNull String methodDesc,
467 int methodAccess) {
468 JavaType[] args = JavaType.getArgumentTypes(methodDesc);
469 frame.initInputFrame(classDesc, methodAccess, args, maxLocals);
470 visitFrame(frame);
471 }
472
473
474
475
476 public void visitFrame(@NonNull Frame frame) {
477 int[] locals = frame.inputLocals;
478 int nLocal = computeNumberOfLocals(locals);
479
480 int[] stacks = frame.inputStack;
481 int nStack = computeStackSize(stacks);
482
483 startFrame(frame.owner.position, nLocal, nStack);
484 putLocalsOrStackElements(locals, nLocal);
485 putLocalsOrStackElements(stacks, nStack);
486 endFrame();
487 }
488
489
490
491
492
493 @NonNegative
494 private static int computeNumberOfLocals(@NonNull int[] locals) {
495 int nLocal = 0;
496 int nTop = 0;
497
498 for (int i = 0; i < locals.length; i++) {
499 int t = locals[i];
500
501 if (t == FrameTypeMask.TOP) {
502 nTop++;
503 } else {
504 nLocal += nTop + 1;
505 nTop = 0;
506 }
507
508 if (t == FrameTypeMask.LONG || t == FrameTypeMask.DOUBLE) {
509 i++;
510 }
511 }
512
513 return nLocal;
514 }
515
516
517
518
519 @NonNegative
520 private static int computeStackSize(@NonNull int[] stacks) {
521 int nStack = 0;
522
523 for (int i = 0; i < stacks.length; i++) {
524 int t = stacks[i];
525 nStack++;
526
527 if (t == FrameTypeMask.LONG || t == FrameTypeMask.DOUBLE) {
528 i++;
529 }
530 }
531
532 return nStack;
533 }
534
535 private void putLocalsOrStackElements(@NonNull int[] itemIndices, @NonNegative int nItems) {
536 for (int i = 0; nItems > 0; i++, nItems--) {
537 int itemType = itemIndices[i];
538 writeFrameDefinition(itemType);
539
540 if (itemType == FrameTypeMask.LONG || itemType == FrameTypeMask.DOUBLE) {
541 i++;
542 }
543 }
544 }
545
546 @NonNegative
547 @Override
548 public int getSize() {
549 return stackMap == null ? 0 : 8 + stackMap.getLength();
550 }
551
552 @Override
553 public void put(@NonNull ByteVector out) {
554 if (stackMap != null) {
555 put(out, 2 + stackMap.getLength());
556 out.putShort(frameCount);
557 out.putByteVector(stackMap);
558 }
559 }
560 }