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