1 package mockit.asm.controlFlow;
2
3 import static mockit.asm.jvmConstants.Opcodes.ATHROW;
4 import static mockit.asm.jvmConstants.Opcodes.GETFIELD;
5 import static mockit.asm.jvmConstants.Opcodes.GETSTATIC;
6 import static mockit.asm.jvmConstants.Opcodes.GOTO;
7 import static mockit.asm.jvmConstants.Opcodes.INVOKEDYNAMIC;
8 import static mockit.asm.jvmConstants.Opcodes.INVOKESTATIC;
9 import static mockit.asm.jvmConstants.Opcodes.IRETURN;
10 import static mockit.asm.jvmConstants.Opcodes.NEW;
11 import static mockit.asm.jvmConstants.Opcodes.NEWARRAY;
12 import static mockit.asm.jvmConstants.Opcodes.PUTFIELD;
13 import static mockit.asm.jvmConstants.Opcodes.PUTSTATIC;
14 import static mockit.asm.jvmConstants.Opcodes.RETURN;
15
16 import edu.umd.cs.findbugs.annotations.NonNull;
17 import edu.umd.cs.findbugs.annotations.Nullable;
18
19 import mockit.asm.constantPool.ConstantPoolGeneration;
20 import mockit.asm.constantPool.Item;
21 import mockit.asm.constantPool.LongValueItem;
22 import mockit.asm.constantPool.StringItem;
23 import mockit.asm.constantPool.TypeOrMemberItem;
24 import mockit.asm.jvmConstants.JVMInstruction;
25 import mockit.asm.util.ByteVector;
26
27 import org.checkerframework.checker.index.qual.NonNegative;
28
29
30
31
32
33
34
35
36 @SuppressWarnings("OverlyComplexClass")
37 public final class CFGAnalysis {
38 @NonNull
39 private final ConstantPoolGeneration cp;
40 @NonNull
41 private final String classDesc;
42 @NonNull
43 private final ByteVector code;
44
45
46
47
48
49 private final boolean computeFrames;
50
51
52
53
54
55
56 @NonNull
57 private final Label labels;
58
59
60
61
62 @Nullable
63 private Label previousBlock;
64
65
66
67
68 @Nullable
69 private Label currentBlock;
70
71
72
73
74
75
76 @NonNegative
77 private int stackSize;
78
79
80
81
82
83
84 @NonNegative
85 private int maxStackSize;
86
87 public CFGAnalysis(@NonNull ConstantPoolGeneration cp, @NonNull String classDesc, @NonNull ByteVector code,
88 boolean computeFrames) {
89 this.cp = cp;
90 this.classDesc = classDesc;
91 this.code = code;
92 this.computeFrames = computeFrames;
93
94 labels = new Label();
95 labels.markAsPushed();
96 updateCurrentBlockForLabelBeforeNextInstruction(labels);
97 }
98
99 @NonNull
100 public Label getLabelForFirstBasicBlock() {
101 return labels;
102 }
103
104 public Frame getFirstFrame() {
105 return labels.frame;
106 }
107
108 @Nullable
109 public Label getLabelForCurrentBasicBlock() {
110 return currentBlock;
111 }
112
113 public void updateCurrentBlockForZeroOperandInstruction(int opcode) {
114 if (currentBlock != null) {
115 if (computeFrames) {
116 currentBlock.frame.execute(opcode);
117 } else {
118 int sizeVariation = JVMInstruction.SIZE[opcode];
119 updateStackSize(sizeVariation);
120 }
121
122
123 if (opcode >= IRETURN && opcode <= RETURN || opcode == ATHROW) {
124 noSuccessor();
125 }
126 }
127 }
128
129
130 private void updateStackSize(int sizeVariation) {
131 int newSize = stackSize + sizeVariation;
132
133 if (newSize > maxStackSize) {
134 maxStackSize = newSize;
135 }
136
137 stackSize = newSize;
138 }
139
140
141
142
143
144 private void noSuccessor() {
145 if (computeFrames) {
146 Label l = new Label();
147 l.frame = new Frame(cp, l);
148 l.resolve(code);
149
150 previousBlock.successor = l;
151 previousBlock = l;
152 } else {
153
154 currentBlock.outputStackMax = maxStackSize;
155 }
156
157 currentBlock = null;
158 }
159
160
161
162
163
164
165
166
167
168 private void addSuccessor(int info, @NonNull Label successor) {
169
170 Edge edge = new Edge(info, successor);
171
172
173
174 currentBlock.setSuccessors(edge);
175 }
176
177 public void updateCurrentBlockForSingleIntOperandInstruction(int opcode, int operand) {
178 if (currentBlock != null) {
179 if (computeFrames) {
180 currentBlock.frame.executeINT(opcode, operand);
181 } else if (opcode != NEWARRAY) {
182
183 updateStackSize(1);
184 }
185 }
186 }
187
188 public void updateCurrentBlockForLocalVariableInstruction(int opcode, @NonNegative int varIndex) {
189 if (currentBlock != null) {
190 if (computeFrames) {
191 currentBlock.frame.executeVAR(opcode, varIndex);
192 } else {
193 int sizeVariation = JVMInstruction.SIZE[opcode];
194 updateStackSize(sizeVariation);
195 }
196 }
197 }
198
199 public void updateCurrentBlockForTypeInstruction(int opcode, @NonNull StringItem typeItem) {
200 if (currentBlock != null) {
201 if (computeFrames) {
202 currentBlock.frame.executeTYPE(opcode, code.getLength(), typeItem);
203 } else if (opcode == NEW) {
204
205 updateStackSize(1);
206 }
207 }
208 }
209
210 public void updateCurrentBlockForFieldInstruction(int opcode, @NonNull TypeOrMemberItem fieldItem,
211 @NonNull String fieldTypeDesc) {
212 if (currentBlock != null) {
213 if (computeFrames) {
214 currentBlock.frame.execute(opcode, fieldItem);
215 } else {
216 char typeCode = fieldTypeDesc.charAt(0);
217 int sizeVariation = computeSizeVariationForFieldAccess(opcode, typeCode);
218 updateStackSize(sizeVariation);
219 }
220 }
221 }
222
223 private static int computeSizeVariationForFieldAccess(int fieldAccessOpcode, char fieldTypeCode) {
224 boolean doubleSizeType = fieldTypeCode == 'D' || fieldTypeCode == 'J';
225
226 switch (fieldAccessOpcode) {
227 case GETSTATIC:
228 return doubleSizeType ? 2 : 1;
229 case PUTSTATIC:
230 return doubleSizeType ? -2 : -1;
231 case GETFIELD:
232 return doubleSizeType ? 1 : 0;
233 case PUTFIELD:
234 return doubleSizeType ? -3 : -2;
235 default:
236 throw new IllegalArgumentException("Unknown field access opcode: " + fieldAccessOpcode);
237 }
238 }
239
240 public void updateCurrentBlockForInvokeInstruction(@NonNull TypeOrMemberItem invokeItem, int opcode,
241 @NonNull String desc) {
242 if (currentBlock != null) {
243 if (computeFrames) {
244 currentBlock.frame.execute(opcode, invokeItem);
245 } else {
246 int argSize = invokeItem.getArgSizeComputingIfNeeded(desc);
247 int sizeVariation = -(argSize >> 2) + (argSize & 0x03);
248
249 if (opcode == INVOKESTATIC || opcode == INVOKEDYNAMIC) {
250 sizeVariation++;
251 }
252
253 updateStackSize(sizeVariation);
254 }
255 }
256 }
257
258 @Nullable
259 public Label updateCurrentBlockForJumpInstruction(int opcode, @NonNull Label label) {
260 Label nextInsn = null;
261
262 if (currentBlock != null) {
263 if (computeFrames) {
264 currentBlock.frame.executeJUMP(opcode);
265
266
267 label.getFirst().markAsTarget();
268
269
270 addSuccessor(Edge.NORMAL, label);
271
272 if (opcode != GOTO) {
273
274 nextInsn = new Label();
275 }
276 } else {
277
278 stackSize += JVMInstruction.SIZE[opcode];
279 addSuccessor(stackSize, label);
280 }
281 }
282
283 return nextInsn;
284 }
285
286 public void updateCurrentBlockForJumpTarget(int opcode, @Nullable Label nextInsn) {
287 if (currentBlock != null) {
288 if (nextInsn != null) {
289
290
291
292 updateCurrentBlockForLabelBeforeNextInstruction(nextInsn);
293 }
294
295 if (opcode == GOTO) {
296 noSuccessor();
297 }
298 }
299 }
300
301 public void updateCurrentBlockForLabelBeforeNextInstruction(@NonNull Label label) {
302
303 label.resolve(code);
304
305 if (label.isDebug()) {
306 return;
307 }
308
309 if (computeFrames) {
310 if (currentBlock != null) {
311 if (label.position == currentBlock.position) {
312
313 currentBlock.markAsTarget(label);
314 label.frame = currentBlock.frame;
315 return;
316 }
317
318
319 addSuccessor(Edge.NORMAL, label);
320 }
321
322
323 currentBlock = label;
324
325 if (label.frame == null) {
326 label.frame = new Frame(cp, label);
327 }
328
329
330 if (previousBlock != null) {
331 if (label.position == previousBlock.position) {
332 previousBlock.markAsTarget(label);
333 label.frame = previousBlock.frame;
334 currentBlock = previousBlock;
335 return;
336 }
337
338 previousBlock.successor = label;
339 }
340 } else {
341 if (currentBlock != null) {
342
343 currentBlock.outputStackMax = maxStackSize;
344 addSuccessor(stackSize, label);
345 }
346
347
348 currentBlock = label;
349
350
351 stackSize = 0;
352 maxStackSize = 0;
353
354
355 if (previousBlock != null) {
356 previousBlock.successor = label;
357 }
358 }
359
360 previousBlock = label;
361 }
362
363 public void updateCurrentBlockForLDCInstruction(@NonNull Item constItem) {
364 if (currentBlock != null) {
365 if (computeFrames) {
366 currentBlock.frame.executeLDC(constItem);
367 } else {
368 int sizeVariation = constItem instanceof LongValueItem ? 2 : 1;
369 updateStackSize(sizeVariation);
370 }
371 }
372 }
373
374 public void updateCurrentBlockForIINCInstruction(@NonNegative int varIndex) {
375 if (currentBlock != null && computeFrames) {
376 currentBlock.frame.executeIINC(varIndex);
377 }
378 }
379
380 public void updateCurrentBlockForSwitchInstruction(@NonNull Label dflt, @NonNull Label[] caseLabels) {
381 if (currentBlock != null) {
382 if (computeFrames) {
383 currentBlock.frame.executeSWITCH();
384
385
386 addSuccessor(Edge.NORMAL, dflt);
387 dflt.getFirst().markAsTarget();
388
389 for (Label label : caseLabels) {
390 addSuccessor(Edge.NORMAL, label);
391 label.getFirst().markAsTarget();
392 }
393 } else {
394
395 stackSize--;
396
397
398 addSuccessor(stackSize, dflt);
399 addSuccessorForEachCase(caseLabels);
400 }
401
402
403 noSuccessor();
404 }
405 }
406
407 private void addSuccessorForEachCase(@NonNull Label[] caseLabels) {
408 for (Label label : caseLabels) {
409 addSuccessor(stackSize, label);
410 }
411 }
412
413 public void updateCurrentBlockForMULTIANEWARRAYInstruction(@NonNull StringItem arrayTypeItem,
414 @NonNegative int dims) {
415 if (currentBlock != null) {
416 if (computeFrames) {
417 currentBlock.frame.executeMULTIANEWARRAY(dims, arrayTypeItem);
418 } else {
419
420
421 stackSize += 1 - dims;
422 }
423 }
424 }
425
426
427
428
429
430
431 @NonNegative
432 public int computeMaxStackSizeFromComputedFrames() {
433 int max = 0;
434 Label changed = labels;
435
436 while (changed != null) {
437
438 Label l = changed;
439 changed = changed.next;
440 l.next = null;
441 Frame frame = l.frame;
442
443
444 if (l.isTarget()) {
445 l.markAsStoringFrame();
446 }
447
448
449 l.markAsReachable();
450
451
452 int blockMax = frame.inputStack.length + l.outputStackMax;
453
454 if (blockMax > max) {
455 max = blockMax;
456 }
457
458 changed = updateSuccessorsOfCurrentBasicBlock(changed, frame, l);
459 }
460
461 return max;
462 }
463
464 @Nullable
465 private Label updateSuccessorsOfCurrentBasicBlock(@Nullable Label changed, @NonNull Frame frame,
466 @NonNull Label label) {
467 Edge edge = label.successors;
468
469 while (edge != null) {
470 Label n = edge.successor.getFirst();
471 boolean change = frame.merge(classDesc, n.frame, edge.info);
472
473 if (change && n.next == null) {
474
475 n.next = changed;
476
477
478 changed = n;
479 }
480
481 edge = edge.next;
482 }
483
484 return changed;
485 }
486
487
488
489
490
491
492
493
494 @NonNegative
495 public int computeMaxStackSize() {
496 int max = 0;
497 Label stack = labels;
498
499 while (stack != null) {
500
501 Label label = stack;
502 stack = stack.next;
503
504
505 int start = label.inputStackTop;
506 int blockMax = start + label.outputStackMax;
507
508
509 if (blockMax > max) {
510 max = blockMax;
511 }
512
513 stack = analyzeBlockSuccessors(stack, label, start);
514 }
515
516 return max;
517 }
518
519 @Nullable
520 private static Label analyzeBlockSuccessors(@Nullable Label stack, @NonNull Label label, @NonNegative int start) {
521 Edge block = label.successors;
522
523 while (block != null) {
524 Label successor = block.successor;
525
526
527 if (!successor.isPushed()) {
528
529 successor.inputStackTop = block.info == Edge.EXCEPTION ? 1 : start + block.info;
530
531
532 successor.markAsPushed();
533 successor.next = stack;
534
535
536 stack = successor;
537 }
538
539 block = block.next;
540 }
541
542 return stack;
543 }
544 }