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