1 package mockit.asm.methods;
2
3 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.FIELDORMETH;
4 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.IINC_INSN;
5 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.IMPLVAR;
6 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.INDYMETH;
7 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.ITFMETH;
8 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.LABEL;
9 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.LABELW;
10 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.LDCW_INSN;
11 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.LDC_INSN;
12 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.LOOK_INSN;
13 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.MANA_INSN;
14 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.NOARG;
15 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.SBYTE;
16 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.SHORT;
17 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.TABL_INSN;
18 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.TYPE_INSN;
19 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.VAR;
20 import static mockit.asm.jvmConstants.JVMInstruction.InstructionType.WIDE_INSN;
21 import static mockit.asm.jvmConstants.Opcodes.IINC;
22 import static mockit.asm.jvmConstants.Opcodes.ILOAD;
23 import static mockit.asm.jvmConstants.Opcodes.ILOAD_0;
24 import static mockit.asm.jvmConstants.Opcodes.INVOKEINTERFACE;
25 import static mockit.asm.jvmConstants.Opcodes.INVOKEVIRTUAL;
26 import static mockit.asm.jvmConstants.Opcodes.ISTORE;
27 import static mockit.asm.jvmConstants.Opcodes.ISTORE_0;
28
29 import edu.umd.cs.findbugs.annotations.NonNull;
30 import edu.umd.cs.findbugs.annotations.Nullable;
31
32 import mockit.asm.AnnotatedReader;
33 import mockit.asm.annotations.AnnotationVisitor;
34 import mockit.asm.classes.ClassReader;
35 import mockit.asm.classes.ClassVisitor;
36 import mockit.asm.controlFlow.Label;
37 import mockit.asm.jvmConstants.ConstantPoolTypes;
38 import mockit.asm.jvmConstants.JVMInstruction;
39 import mockit.asm.util.MethodHandle;
40
41 import org.checkerframework.checker.index.qual.NonNegative;
42
43 @SuppressWarnings("OverlyComplexClass")
44 public final class MethodReader extends AnnotatedReader {
45 @NonNull
46 private final ClassReader cr;
47 @NonNull
48 private final ClassVisitor cv;
49
50 @Nullable
51 private String[] throwsClauseTypes;
52
53
54
55
56 private String name;
57
58
59
60
61 private String desc;
62
63 @NonNegative
64 private int methodStartCodeIndex;
65 @NonNegative
66 private int bodyStartCodeIndex;
67 @NonNegative
68 private int parameterAnnotationsCodeIndex;
69
70
71
72
73
74 private Label[] labels;
75
76
77
78
79 private MethodVisitor mv;
80
81 public MethodReader(@NonNull ClassReader cr, @NonNull ClassVisitor cv) {
82 super(cr);
83 this.cr = cr;
84 this.cv = cv;
85 }
86
87
88
89
90
91
92
93 @NonNegative
94 public int readMethods() {
95 for (int methodCount = readUnsignedShort(); methodCount > 0; methodCount--) {
96 readMethod();
97 }
98
99 return codeIndex;
100 }
101
102 private void readMethod() {
103 readMethodDeclaration();
104 parameterAnnotationsCodeIndex = 0;
105
106 readAttributes();
107
108 int currentCodeIndex = codeIndex;
109 readMethodBody();
110 codeIndex = currentCodeIndex;
111 }
112
113 private void readMethodDeclaration() {
114 access = readUnsignedShort();
115 name = readNonnullUTF8();
116 desc = readNonnullUTF8();
117
118 methodStartCodeIndex = codeIndex;
119 bodyStartCodeIndex = 0;
120 throwsClauseTypes = null;
121 }
122
123 @Nullable
124 @Override
125 protected Boolean readAttribute(@NonNull String attributeName) {
126 switch (attributeName) {
127 case "Code":
128 bodyStartCodeIndex = codeIndex;
129 return false;
130 case "Exceptions":
131 readExceptionsInThrowsClause();
132 return true;
133 case "RuntimeVisibleParameterAnnotations":
134 parameterAnnotationsCodeIndex = codeIndex;
135 return false;
136 default:
137 return null;
138 }
139 }
140
141 private void readExceptionsInThrowsClause() {
142 int n = readUnsignedShort();
143 String[] typeDescs = new String[n];
144
145 for (int i = 0; i < n; i++) {
146 typeDescs[i] = readNonnullClass();
147 }
148
149 throwsClauseTypes = typeDescs;
150 }
151
152 private void readMethodBody() {
153 mv = cv.visitMethod(access, name, desc, signature, throwsClauseTypes);
154
155 if (mv == null) {
156 return;
157 }
158
159 if (mv instanceof MethodWriter) {
160 copyMethodBody();
161 return;
162 }
163
164 readAnnotations(mv);
165 readAnnotationsOnAllParameters();
166
167 if (bodyStartCodeIndex > 0) {
168 codeIndex = bodyStartCodeIndex;
169 readCode();
170 }
171
172 mv.visitEnd();
173 }
174
175
176
177
178
179
180
181 private void copyMethodBody() {
182
183
184 MethodWriter mw = (MethodWriter) mv;
185 mw.classReaderOffset = methodStartCodeIndex;
186 mw.classReaderLength = codeIndex - methodStartCodeIndex;
187 }
188
189 private void readAnnotationsOnAllParameters() {
190 if (parameterAnnotationsCodeIndex > 0) {
191 codeIndex = parameterAnnotationsCodeIndex;
192 int parameters = readUnsignedByte();
193
194 for (int i = 0; i < parameters; i++) {
195 readParameterAnnotations(i);
196 }
197 }
198 }
199
200 private void readParameterAnnotations(@NonNegative int parameterIndex) {
201 for (int annotationCount = readUnsignedShort(); annotationCount > 0; annotationCount--) {
202 String annotationTypeDesc = readNonnullUTF8();
203 AnnotationVisitor av = mv.visitParameterAnnotation(parameterIndex, annotationTypeDesc);
204 readAnnotationValues(av);
205 }
206 }
207
208 private void readCode() {
209 int maxStack = readUnsignedShort();
210 codeIndex += 2;
211
212 int codeLength = readInt();
213 labels = new Label[codeLength + 2];
214
215
216 int codeStartIndex = codeIndex;
217 int codeEndIndex = codeStartIndex + codeLength;
218
219 readAllLabelsInCodeBlock(codeStartIndex, codeEndIndex);
220 readTryCatchBlocks();
221
222
223 int varTableCodeIndex = 0;
224 int[] typeTable = null;
225
226 for (int attributeCount = readUnsignedShort(); attributeCount > 0; attributeCount--) {
227 String attrName = readNonnullUTF8();
228 int codeOffset = readInt();
229
230 switch (attrName) {
231 case "LocalVariableTable":
232 varTableCodeIndex = codeIndex;
233 readLocalVariableTable();
234 break;
235 case "LocalVariableTypeTable":
236 typeTable = readLocalVariableTypeTable();
237 break;
238 case "LineNumberTable":
239 readLineNumberTable();
240 break;
241 default:
242 codeIndex += codeOffset;
243 }
244 }
245
246 readBytecodeInstructionsInCodeBlock(codeStartIndex, codeEndIndex);
247 visitEndLabel(codeLength);
248 readLocalVariableTables(varTableCodeIndex, typeTable);
249 mv.visitMaxStack(maxStack);
250 }
251
252 private void readAllLabelsInCodeBlock(@NonNegative int codeStart, @NonNegative int codeEnd) {
253 getOrCreateLabel(codeEnd - codeStart + 1);
254
255 while (codeIndex < codeEnd) {
256 int offset = codeIndex - codeStart;
257 readLabelForInstructionIfAny(offset);
258 }
259 }
260
261 @NonNull
262 private Label getOrCreateLabel(@NonNegative int offset) {
263 Label label = labels[offset];
264
265 if (label == null) {
266 label = new Label();
267 labels[offset] = label;
268 }
269
270 return label;
271 }
272
273 private void readLabelForInstructionIfAny(@NonNegative int offset) {
274 int opcode = readUnsignedByte();
275 byte instructionType = JVMInstruction.TYPE[opcode];
276 boolean tablInsn = instructionType == TABL_INSN;
277
278 if (tablInsn || instructionType == LOOK_INSN) {
279 readLabelsForSwitchInstruction(offset, tablInsn);
280 } else {
281 readLabelsForNonSwitchInstruction(offset, instructionType);
282 }
283 }
284
285 private void readLabelsForSwitchInstruction(@NonNegative int offset, boolean tableNotLookup) {
286 readSwitchDefaultLabel(offset);
287
288 int caseCount;
289
290 if (tableNotLookup) {
291 int min = readInt();
292 int max = readInt();
293 caseCount = max - min + 1;
294 } else {
295 caseCount = readInt();
296 }
297
298 while (caseCount > 0) {
299 if (!tableNotLookup) {
300 codeIndex += 4;
301 }
302
303 int caseOffset = offset + readInt();
304 getOrCreateLabel(caseOffset);
305 caseCount--;
306 }
307 }
308
309 @NonNull
310 private Label readSwitchDefaultLabel(@NonNegative int offset) {
311 codeIndex += 3 - (offset & 3);
312
313 int defaultLabelOffset = readInt();
314 return getOrCreateLabel(offset + defaultLabelOffset);
315 }
316
317 @SuppressWarnings("OverlyLongMethod")
318 private void readLabelsForNonSwitchInstruction(@NonNegative int offset, byte instructionType) {
319 int codeIndexSize = 0;
320
321
322 switch (instructionType) {
323 case NOARG:
324 case IMPLVAR:
325 return;
326 case LABEL:
327 int labelOffset = offset + readShort();
328 getOrCreateLabel(labelOffset);
329 return;
330 case LABELW:
331 int labelOffsetW = offset + readInt();
332 getOrCreateLabel(labelOffsetW);
333 return;
334 case WIDE_INSN:
335 int opcode = readUnsignedByte();
336 codeIndexSize = opcode == IINC ? 4 : 2;
337 break;
338 case VAR:
339 case SBYTE:
340 case LDC_INSN:
341 codeIndexSize = 1;
342 break;
343 case SHORT:
344 case LDCW_INSN:
345 case TYPE_INSN:
346 case FIELDORMETH:
347 case IINC_INSN:
348 codeIndexSize = 2;
349 break;
350 case ITFMETH:
351 case INDYMETH:
352 codeIndexSize = 4;
353 break;
354 case MANA_INSN:
355 codeIndexSize = 3;
356 }
357
358 codeIndex += codeIndexSize;
359 }
360
361
362
363
364 private void readTryCatchBlocks() {
365 for (int blockCount = readUnsignedShort(); blockCount > 0; blockCount--) {
366 Label start = getOrCreateLabel(readUnsignedShort());
367 Label end = getOrCreateLabel(readUnsignedShort());
368 Label handler = getOrCreateLabel(readUnsignedShort());
369 String type = readUTF8(readItem());
370
371 mv.visitTryCatchBlock(start, end, handler, type);
372 }
373 }
374
375 private void readLocalVariableTable() {
376 for (int localVarCount = readUnsignedShort(); localVarCount > 0; localVarCount--) {
377 int labelOffset = readUnsignedShort();
378 getOrCreateDebugLabel(labelOffset);
379
380 labelOffset += readUnsignedShort();
381 getOrCreateDebugLabel(labelOffset);
382
383 codeIndex += 6;
384 }
385 }
386
387 @NonNull
388 private Label getOrCreateDebugLabel(@NonNegative int offset) {
389 Label label = labels[offset];
390
391 if (label == null) {
392 label = new Label();
393 label.markAsDebug();
394 labels[offset] = label;
395 }
396
397 return label;
398 }
399
400 @NonNull
401 private int[] readLocalVariableTypeTable() {
402 int typeTableSize = 3 * readUnsignedShort();
403 int[] typeTable = new int[typeTableSize];
404
405 while (typeTableSize > 0) {
406 int startIndex = readUnsignedShort();
407 int signatureCodeIndex = codeIndex + 4;
408 codeIndex += 6;
409 int varIndex = readUnsignedShort();
410
411 typeTableSize--;
412 typeTable[typeTableSize] = signatureCodeIndex;
413 typeTableSize--;
414 typeTable[typeTableSize] = varIndex;
415 typeTableSize--;
416 typeTable[typeTableSize] = startIndex;
417 }
418
419 return typeTable;
420 }
421
422 private void readLineNumberTable() {
423 for (int lineCount = readUnsignedShort(); lineCount > 0; lineCount--) {
424 int labelOffset = readUnsignedShort();
425 Label debugLabel = getOrCreateDebugLabel(labelOffset);
426 debugLabel.line = readUnsignedShort();
427 }
428 }
429
430 @SuppressWarnings({ "OverlyComplexMethod", "OverlyLongMethod" })
431 private void readBytecodeInstructionsInCodeBlock(@NonNegative int codeStartIndex, @NonNegative int codeEndIndex) {
432 codeIndex = codeStartIndex;
433
434 while (codeIndex < codeEndIndex) {
435 int offset = codeIndex - codeStartIndex;
436 visitLabelAndLineNumber(offset);
437
438 int opcode = readUnsignedByte();
439
440
441 switch (JVMInstruction.TYPE[opcode]) {
442 case NOARG:
443 mv.visitInsn(opcode);
444 break;
445 case VAR:
446 readVariableAccessInstruction(opcode);
447 break;
448 case IMPLVAR:
449 readInstructionWithImplicitVariable(opcode);
450 break;
451 case TYPE_INSN:
452 readTypeInsn(opcode);
453 break;
454 case LABEL:
455 readJump(opcode, offset);
456 break;
457 case LABELW:
458 readWideJump(opcode, offset);
459 break;
460 case LDC_INSN:
461 readLDC();
462 break;
463 case LDCW_INSN:
464 readLDCW();
465 break;
466 case IINC_INSN:
467 readIInc();
468 break;
469 case SBYTE:
470 readInstructionTakingASignedByte(opcode);
471 break;
472 case SHORT:
473 readInstructionTakingASignedShort(opcode);
474 break;
475 case TABL_INSN:
476 readSwitchInstruction(offset, true);
477 break;
478 case LOOK_INSN:
479 readSwitchInstruction(offset, false);
480 break;
481 case MANA_INSN:
482 readMultiANewArray();
483 break;
484 case WIDE_INSN:
485 readWideInstruction();
486 break;
487 case FIELDORMETH:
488 case ITFMETH:
489 readFieldOrInvokeInstruction(opcode);
490 break;
491 case INDYMETH:
492 readInvokeDynamicInstruction();
493 break;
494 }
495 }
496 }
497
498 private void visitLabelAndLineNumber(@NonNegative int offset) {
499 Label label = labels[offset];
500
501 if (label != null) {
502 mv.visitLabel(label);
503
504 int lineNumber = label.line;
505
506 if (lineNumber > 0) {
507 mv.visitLineNumber(lineNumber, label);
508 }
509 }
510 }
511
512 private void readVariableAccessInstruction(int opcode) {
513 int varIndex = readUnsignedByte();
514 mv.visitVarInsn(opcode, varIndex);
515 }
516
517 private void readInstructionWithImplicitVariable(int opcode) {
518 int opcodeBase;
519
520 if (opcode > ISTORE) {
521 opcode -= ISTORE_0;
522 opcodeBase = ISTORE;
523 } else {
524 opcode -= ILOAD_0;
525 opcodeBase = ILOAD;
526 }
527
528 int localVarOpcode = opcodeBase + (opcode >> 2);
529 int varIndex = opcode & 3;
530
531 mv.visitVarInsn(localVarOpcode, varIndex);
532 }
533
534 private void readTypeInsn(int opcode) {
535 String typeDesc = readNonnullClass();
536 mv.visitTypeInsn(opcode, typeDesc);
537 }
538
539 private void readJump(int opcode, @NonNegative int offset) {
540 short targetIndex = readShort();
541 Label targetLabel = labels[offset + targetIndex];
542 mv.visitJumpInsn(opcode, targetLabel);
543 }
544
545 private void readWideJump(int opcode, @NonNegative int offset) {
546 int targetIndex = readInt();
547 Label targetLabel = labels[offset + targetIndex];
548 mv.visitJumpInsn(opcode - 33, targetLabel);
549 }
550
551 private void readLDC() {
552 int constIndex = readUnsignedByte();
553 Object cst = readConst(constIndex);
554 mv.visitLdcInsn(cst);
555 }
556
557 private void readLDCW() {
558 Object cst = readConstItem();
559 mv.visitLdcInsn(cst);
560 }
561
562 private void readIInc() {
563 int varIndex = readUnsignedByte();
564 int increment = readSignedByte();
565 mv.visitIincInsn(varIndex, increment);
566 }
567
568 private void readInstructionTakingASignedByte(int opcode) {
569 int operand = readSignedByte();
570 mv.visitIntInsn(opcode, operand);
571 }
572
573 private void readInstructionTakingASignedShort(int opcode) {
574 int operand = readShort();
575 mv.visitIntInsn(opcode, operand);
576 }
577
578 private void readSwitchInstruction(@NonNegative int offset, boolean tableNotLookup) {
579 Label dfltLabel = readSwitchDefaultLabel(offset);
580 int min;
581 int max;
582 int caseCount;
583 int[] keys;
584
585 if (tableNotLookup) {
586 min = readInt();
587 max = readInt();
588 caseCount = max - min + 1;
589 keys = null;
590 } else {
591 min = max = 0;
592 caseCount = readInt();
593 keys = new int[caseCount];
594 }
595
596 Label[] handlerLabels = readSwitchCaseLabels(offset, caseCount, keys);
597
598 if (tableNotLookup) {
599 mv.visitTableSwitchInsn(min, max, dfltLabel, handlerLabels);
600 } else {
601 mv.visitLookupSwitchInsn(dfltLabel, keys, handlerLabels);
602 }
603 }
604
605 @NonNull
606 private Label[] readSwitchCaseLabels(@NonNegative int offset, @NonNegative int caseCount, @Nullable int[] keys) {
607 Label[] caseLabels = new Label[caseCount];
608
609 for (int i = 0; i < caseCount; i++) {
610 if (keys != null) {
611 keys[i] = readInt();
612 }
613
614 int labelOffset = offset + readInt();
615 caseLabels[i] = labels[labelOffset];
616 }
617
618 return caseLabels;
619 }
620
621 private void readMultiANewArray() {
622 String arrayTypeDesc = readNonnullClass();
623 int dims = readUnsignedByte();
624 mv.visitMultiANewArrayInsn(arrayTypeDesc, dims);
625 }
626
627 private void readWideInstruction() {
628 int opcode = readUnsignedByte();
629 int varIndex = readUnsignedShort();
630
631 if (opcode == IINC) {
632 int increment = readShort();
633 mv.visitIincInsn(varIndex, increment);
634 } else {
635 mv.visitVarInsn(opcode, varIndex);
636 codeIndex += 2;
637 }
638 }
639
640 private void readFieldOrInvokeInstruction(int opcode) {
641 int ownerCodeIndex = readItem();
642 String owner = readNonnullClass(ownerCodeIndex);
643 int nameCodeIndex = readItem(ownerCodeIndex + 2);
644 String memberName = readNonnullUTF8(nameCodeIndex);
645 String memberDesc = readNonnullUTF8(nameCodeIndex + 2);
646
647 if (opcode < INVOKEVIRTUAL) {
648 mv.visitFieldInsn(opcode, owner, memberName, memberDesc);
649 } else {
650 boolean itf = code[ownerCodeIndex - 1] == ConstantPoolTypes.IMETHOD_REF;
651 mv.visitMethodInsn(opcode, owner, memberName, memberDesc, itf);
652
653 if (opcode == INVOKEINTERFACE) {
654 codeIndex += 2;
655 }
656 }
657 }
658
659 private void readInvokeDynamicInstruction() {
660 int cpIndex = readItem();
661 int bsmStartIndex = readUnsignedShort(cpIndex);
662 int nameCodeIndex = readItem(cpIndex + 2);
663
664 String bsmName = readNonnullUTF8(nameCodeIndex);
665 String bsmDesc = readNonnullUTF8(nameCodeIndex + 2);
666
667 int bsmCodeIndex = cr.getBSMCodeIndex(bsmStartIndex);
668 MethodHandle bsmHandle = readMethodHandleItem(bsmCodeIndex);
669 int bsmArgCount = readUnsignedShort(bsmCodeIndex + 2);
670 bsmCodeIndex += 4;
671 Object[] bsmArgs = new Object[bsmArgCount];
672
673 for (int i = 0; i < bsmArgCount; i++) {
674 bsmArgs[i] = readConstItem(bsmCodeIndex);
675 bsmCodeIndex += 2;
676 }
677
678 mv.visitInvokeDynamicInsn(bsmName, bsmDesc, bsmHandle, bsmArgs);
679 codeIndex += 2;
680 }
681
682 private void visitEndLabel(@NonNegative int codeLength) {
683 Label label = labels[codeLength];
684
685 if (label != null) {
686 mv.visitLabel(label);
687 }
688 }
689
690 private void readLocalVariableTables(@NonNegative int varTableCodeIndex, @Nullable int[] typeTable) {
691 if (varTableCodeIndex > 0) {
692 codeIndex = varTableCodeIndex;
693
694 for (int localVarCount = readUnsignedShort(); localVarCount > 0; localVarCount--) {
695 int start = readUnsignedShort();
696 int length = readUnsignedShort();
697 String varName = readNonnullUTF8();
698 String varDesc = readNonnullUTF8();
699 int index = readUnsignedShort();
700 String varSignature = typeTable == null ? null : getLocalVariableSignature(typeTable, start, index);
701
702 mv.visitLocalVariable(varName, varDesc, varSignature, labels[start], labels[start + length], index);
703 }
704 }
705 }
706
707 @Nullable
708 private String getLocalVariableSignature(@NonNull int[] typeTable, @NonNegative int start, @NonNegative int index) {
709 for (int i = 0, n = typeTable.length; i < n; i += 3) {
710 if (typeTable[i] == start && typeTable[i + 1] == index) {
711 return readNonnullUTF8(typeTable[i + 2]);
712 }
713 }
714
715 return null;
716 }
717 }