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