1
2
3
4
5
6 package mockit.asm.methods;
7
8 import static mockit.asm.jvmConstants.Opcodes.DLOAD;
9 import static mockit.asm.jvmConstants.Opcodes.DSTORE;
10 import static mockit.asm.jvmConstants.Opcodes.GOTO;
11 import static mockit.asm.jvmConstants.Opcodes.GOTO_W;
12 import static mockit.asm.jvmConstants.Opcodes.IINC;
13 import static mockit.asm.jvmConstants.Opcodes.ILOAD;
14 import static mockit.asm.jvmConstants.Opcodes.INVOKEDYNAMIC;
15 import static mockit.asm.jvmConstants.Opcodes.INVOKEINTERFACE;
16 import static mockit.asm.jvmConstants.Opcodes.ISTORE;
17 import static mockit.asm.jvmConstants.Opcodes.LDC;
18 import static mockit.asm.jvmConstants.Opcodes.LDC2_W;
19 import static mockit.asm.jvmConstants.Opcodes.LDC_W;
20 import static mockit.asm.jvmConstants.Opcodes.LLOAD;
21 import static mockit.asm.jvmConstants.Opcodes.LOOKUPSWITCH;
22 import static mockit.asm.jvmConstants.Opcodes.LSTORE;
23 import static mockit.asm.jvmConstants.Opcodes.MULTIANEWARRAY;
24 import static mockit.asm.jvmConstants.Opcodes.SIPUSH;
25 import static mockit.asm.jvmConstants.Opcodes.TABLESWITCH;
26 import static mockit.asm.jvmConstants.Opcodes.WIDE;
27
28 import edu.umd.cs.findbugs.annotations.NonNull;
29 import edu.umd.cs.findbugs.annotations.Nullable;
30
31 import mockit.asm.SignatureWriter;
32 import mockit.asm.annotations.AnnotationVisitor;
33 import mockit.asm.classes.ClassWriter;
34 import mockit.asm.constantPool.ClassMemberItem;
35 import mockit.asm.constantPool.DynamicItem;
36 import mockit.asm.constantPool.Item;
37 import mockit.asm.constantPool.LongValueItem;
38 import mockit.asm.constantPool.StringItem;
39 import mockit.asm.controlFlow.CFGAnalysis;
40 import mockit.asm.controlFlow.Frame;
41 import mockit.asm.controlFlow.Label;
42 import mockit.asm.controlFlow.StackMapTableWriter;
43 import mockit.asm.exceptionHandling.ExceptionHandling;
44 import mockit.asm.jvmConstants.Access;
45 import mockit.asm.jvmConstants.Opcodes;
46 import mockit.asm.types.JavaType;
47 import mockit.asm.util.ByteVector;
48 import mockit.asm.util.MethodHandle;
49
50 import org.checkerframework.checker.index.qual.NonNegative;
51
52
53
54
55
56 @SuppressWarnings({ "OverlyCoupledClass", "ClassWithTooManyFields", "OverlyComplexClass" })
57 public final class MethodWriter extends MethodVisitor {
58
59
60
61 @NonNull
62 public final ClassWriter cw;
63
64
65
66
67 private final int nameItemIndex;
68
69
70
71
72 private final int descItemIndex;
73
74
75
76
77 @NonNull
78 private final String descriptor;
79
80 @Nullable
81 private final SignatureWriter signatureWriter;
82
83
84
85
86
87 @NonNegative
88 int classReaderOffset;
89
90
91
92
93
94 @NonNegative
95 int classReaderLength;
96
97 @Nullable
98 private final ExceptionsWriter exceptionsWriter;
99
100
101
102
103 @Nullable
104 private AnnotationVisitor[] parameterAnnotations;
105
106
107
108
109 @NonNull
110 private final ByteVector code;
111
112 @NonNull
113 private final ExceptionHandling exceptionHandling;
114 @NonNull
115 private final StackMapTableWriter stackMapTableWriter;
116 @NonNull
117 private final LocalVariableTableWriter localVariableTableWriter;
118 @NonNull
119 private final LineNumberTableWriter lineNumberTableWriter;
120 @NonNull
121 private final CFGAnalysis cfgAnalysis;
122
123 private final boolean computeFrames;
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public MethodWriter(@NonNull ClassWriter cw, int access, @NonNull String name, @NonNull String desc,
144 @Nullable String signature, @Nullable String[] exceptions, boolean computeFrames) {
145 super(cw.getConstantPoolGeneration(), "<init>".equals(name) ? access | Access.CONSTRUCTOR : access);
146 this.cw = cw;
147 nameItemIndex = cp.newUTF8(name);
148 descItemIndex = cp.newUTF8(desc);
149 descriptor = desc;
150 signatureWriter = signature == null ? null : new SignatureWriter(cp, signature);
151 exceptionsWriter = exceptions == null ? null : new ExceptionsWriter(cp, exceptions);
152 code = new ByteVector();
153 this.computeFrames = computeFrames;
154 exceptionHandling = new ExceptionHandling(cp);
155 stackMapTableWriter = new StackMapTableWriter(cp, cw.isJava6OrNewer(), access, desc);
156 localVariableTableWriter = new LocalVariableTableWriter(cp);
157 lineNumberTableWriter = new LineNumberTableWriter(cp);
158 cfgAnalysis = new CFGAnalysis(cp, cw.getInternalClassName(), code, computeFrames);
159
160 createMarkerAttributes(cw.getClassVersion());
161 }
162
163 @NonNull
164 @Override
165 public AnnotationVisitor visitParameterAnnotation(@NonNegative int parameter, @NonNull String desc) {
166 AnnotationVisitor aw = new AnnotationVisitor(cp, desc);
167
168 if (parameterAnnotations == null) {
169 int numParameters = JavaType.getArgumentTypes(descriptor).length;
170 parameterAnnotations = new AnnotationVisitor[numParameters];
171 }
172
173 aw.setNext(parameterAnnotations[parameter]);
174 parameterAnnotations[parameter] = aw;
175
176 return aw;
177 }
178
179 @Override
180 public void visitInsn(int opcode) {
181
182 code.putByte(opcode);
183
184 cfgAnalysis.updateCurrentBlockForZeroOperandInstruction(opcode);
185 }
186
187 @Override
188 public void visitIntInsn(int opcode, int operand) {
189 cfgAnalysis.updateCurrentBlockForSingleIntOperandInstruction(opcode, operand);
190
191
192 if (opcode == SIPUSH) {
193 code.put12(SIPUSH, operand);
194 } else {
195 code.put11(opcode, operand);
196 }
197 }
198
199 @Override
200 public void visitVarInsn(int opcode, @NonNegative int varIndex) {
201 cfgAnalysis.updateCurrentBlockForLocalVariableInstruction(opcode, varIndex);
202
203 updateMaxLocals(opcode, varIndex);
204
205
206 if (varIndex < 4) {
207 int opt;
208
209 if (opcode < ISTORE) {
210 opt = 26 + (opcode - ILOAD << 2) + varIndex;
211 } else {
212 opt = 59 + (opcode - ISTORE << 2) + varIndex;
213 }
214
215 code.putByte(opt);
216 } else if (varIndex >= 256) {
217 code.putByte(WIDE).put12(opcode, varIndex);
218 } else {
219 code.put11(opcode, varIndex);
220 }
221
222 if (opcode >= ISTORE && computeFrames && exceptionHandling.hasHandlers()) {
223 visitLabel(new Label());
224 }
225 }
226
227 private void updateMaxLocals(int opcode, @NonNegative int varIndex) {
228 int n = opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE ? varIndex + 2
229 : varIndex + 1;
230 stackMapTableWriter.updateMaxLocals(n);
231 }
232
233 @Override
234 public void visitTypeInsn(int opcode, @NonNull String typeDesc) {
235 StringItem typeItem = cp.newClassItem(typeDesc);
236 cfgAnalysis.updateCurrentBlockForTypeInstruction(opcode, typeItem);
237
238
239 code.put12(opcode, typeItem.index);
240 }
241
242 @Override
243 public void visitFieldInsn(int opcode, @NonNull String owner, @NonNull String name, @NonNull String desc) {
244 ClassMemberItem fieldItem = cp.newFieldItem(owner, name, desc);
245 cfgAnalysis.updateCurrentBlockForFieldInstruction(opcode, fieldItem, desc);
246
247
248 code.put12(opcode, fieldItem.index);
249 }
250
251 @Override
252 public void visitMethodInsn(int opcode, @NonNull String owner, @NonNull String name, @NonNull String desc,
253 boolean itf) {
254 ClassMemberItem invokeItem = cp.newMethodItem(owner, name, desc, itf);
255 cfgAnalysis.updateCurrentBlockForInvokeInstruction(invokeItem, opcode, desc);
256
257
258 code.put12(opcode, invokeItem.index);
259
260 if (opcode == INVOKEINTERFACE) {
261 int argSize = invokeItem.getArgSizeComputingIfNeeded(desc);
262 code.put11(argSize >> 2, 0);
263 }
264 }
265
266 @Override
267 public void visitInvokeDynamicInsn(@NonNull String name, @NonNull String desc, @NonNull MethodHandle bsm,
268 @NonNull Object... bsmArgs) {
269 DynamicItem invokeItem = cw.addInvokeDynamicReference(name, desc, bsm, bsmArgs);
270 cfgAnalysis.updateCurrentBlockForInvokeInstruction(invokeItem, INVOKEDYNAMIC, desc);
271
272
273 code.put12(INVOKEDYNAMIC, invokeItem.index);
274 code.putShort(0);
275 }
276
277 @Override
278 public void visitJumpInsn(int opcode, @NonNull Label label) {
279 Label nextInsn = cfgAnalysis.updateCurrentBlockForJumpInstruction(opcode, label);
280
281
282 if (label.isResolved() && label.position - code.getLength() < Short.MIN_VALUE) {
283
284
285
286
287
288 if (opcode != GOTO) {
289
290
291 if (nextInsn != null) {
292 nextInsn.markAsTarget();
293 }
294
295 code.putByte(opcode <= 166 ? (opcode + 1 ^ 1) - 1 : opcode ^ 1);
296 code.putShort(8);
297 }
298
299 code.putByte(GOTO_W);
300 label.put(code, code.getLength() - 1, true);
301 } else {
302
303
304
305 code.putByte(opcode);
306 label.put(code, code.getLength() - 1, false);
307 }
308
309 cfgAnalysis.updateCurrentBlockForJumpTarget(opcode, nextInsn);
310 }
311
312 @Override
313 public void visitLabel(@NonNull Label label) {
314 cfgAnalysis.updateCurrentBlockForLabelBeforeNextInstruction(label);
315 }
316
317 @Override
318 public void visitLdcInsn(@NonNull Object cst) {
319 Item constItem = cp.newConstItem(cst);
320 cfgAnalysis.updateCurrentBlockForLDCInstruction(constItem);
321
322
323 int index = constItem.index;
324
325 if (constItem instanceof LongValueItem) {
326 code.put12(LDC2_W, index);
327 } else if (index >= 256) {
328 code.put12(LDC_W, index);
329 } else {
330 code.put11(LDC, index);
331 }
332 }
333
334 @Override
335 public void visitIincInsn(@NonNegative int varIndex, int increment) {
336 cfgAnalysis.updateCurrentBlockForIINCInstruction(varIndex);
337
338
339 int n = varIndex + 1;
340 stackMapTableWriter.updateMaxLocals(n);
341
342
343 if (varIndex > 255 || increment > 127 || increment < -128) {
344 code.putByte(WIDE).put12(IINC, varIndex).putShort(increment);
345 } else {
346 code.putByte(IINC).put11(varIndex, increment);
347 }
348 }
349
350 @Override
351 public void visitTableSwitchInsn(int min, int max, @NonNull Label dflt, @NonNull Label... labels) {
352
353 int source = code.getLength();
354 code.putByte(TABLESWITCH);
355 code.roundUpLength();
356 dflt.put(code, source, true);
357 code.putInt(min).putInt(max);
358
359 for (Label label : labels) {
360 label.put(code, source, true);
361 }
362
363 cfgAnalysis.updateCurrentBlockForSwitchInstruction(dflt, labels);
364 }
365
366 @Override
367 public void visitLookupSwitchInsn(@NonNull Label dflt, @NonNull int[] keys, @NonNull Label[] labels) {
368
369 int source = code.getLength();
370 code.putByte(LOOKUPSWITCH);
371 code.roundUpLength();
372 dflt.put(code, source, true);
373 code.putInt(labels.length);
374
375 for (int i = 0; i < labels.length; i++) {
376 code.putInt(keys[i]);
377 labels[i].put(code, source, true);
378 }
379
380 cfgAnalysis.updateCurrentBlockForSwitchInstruction(dflt, labels);
381 }
382
383 @Override
384 public void visitMultiANewArrayInsn(@NonNull String desc, @NonNegative int dims) {
385 StringItem arrayTypeItem = cp.newClassItem(desc);
386 cfgAnalysis.updateCurrentBlockForMULTIANEWARRAYInstruction(arrayTypeItem, dims);
387
388
389 code.put12(MULTIANEWARRAY, arrayTypeItem.index).putByte(dims);
390 }
391
392 @Override
393 public void visitTryCatchBlock(@NonNull Label start, @NonNull Label end, @NonNull Label handler,
394 @Nullable String type) {
395 exceptionHandling.addHandler(start, end, handler, type);
396 }
397
398 @Override
399 public void visitLocalVariable(@NonNull String name, @NonNull String desc, @Nullable String signature,
400 @NonNull Label start, @NonNull Label end, @NonNegative int index) {
401 int localsCount = localVariableTableWriter.addLocalVariable(name, desc, signature, start, end, index);
402 stackMapTableWriter.updateMaxLocals(localsCount);
403 }
404
405 @Override
406 public void visitLineNumber(@NonNegative int line, @NonNull Label start) {
407 lineNumberTableWriter.addLineNumber(line, start);
408 }
409
410 @Override
411 public void visitMaxStack(@NonNegative int maxStack) {
412 int computedMaxStack;
413
414 if (computeFrames) {
415 exceptionHandling.completeControlFlowGraphWithExceptionHandlerBlocksFromComputedFrames();
416
417 Frame firstFrame = cfgAnalysis.getFirstFrame();
418 stackMapTableWriter.createAndVisitFirstFrame(firstFrame, cw.getInternalClassName(), descriptor,
419 classOrMemberAccess);
420
421 computedMaxStack = cfgAnalysis.computeMaxStackSizeFromComputedFrames();
422 visitAllFramesToBeStoredInStackMap();
423 } else {
424
425 exceptionHandling.completeControlFlowGraphWithExceptionHandlerBlocks();
426
427 computedMaxStack = cfgAnalysis.computeMaxStackSize();
428 computedMaxStack = Math.max(maxStack, computedMaxStack);
429 }
430
431 stackMapTableWriter.setMaxStack(computedMaxStack);
432 }
433
434 private void visitAllFramesToBeStoredInStackMap() {
435 Label label = cfgAnalysis.getLabelForFirstBasicBlock();
436
437 while (label != null) {
438 Frame frame = label.getFrame();
439
440 if (label.isStoringFrame()) {
441 stackMapTableWriter.visitFrame(frame);
442 }
443
444 label = label.getSuccessor();
445 }
446 }
447
448
449
450
451 @NonNegative
452 public int getSize() {
453 if (classReaderOffset > 0) {
454 return 6 + classReaderLength;
455 }
456
457 int size = 8 + getMarkerAttributesSize() + getAnnotationsSize() + getParameterAnnotationsSize();
458 int codeLength = code.getLength();
459
460 if (codeLength > 0) {
461 if (codeLength > 65536) {
462 throw new RuntimeException("Method code too large!");
463 }
464
465 cp.newUTF8("Code");
466
467 size += 18 + codeLength + exceptionHandling.getSize();
468 size += localVariableTableWriter.getSize();
469 size += lineNumberTableWriter.getSize();
470 size += stackMapTableWriter.getSize();
471 }
472
473 if (exceptionsWriter != null) {
474 size += exceptionsWriter.getSize();
475 }
476
477 if (signatureWriter != null) {
478 size += signatureWriter.getSize();
479 }
480
481 return size;
482 }
483
484 @NonNegative
485 private int getParameterAnnotationsSize() {
486 int size = 0;
487
488 if (parameterAnnotations != null) {
489 cp.newUTF8("RuntimeVisibleParameterAnnotations");
490
491 int n = parameterAnnotations.length;
492 size += 7 + 2 * n;
493
494 for (int i = n - 1; i >= 0; i--) {
495 AnnotationVisitor parameterAnnotation = parameterAnnotations[i];
496 size += parameterAnnotation == null ? 0 : parameterAnnotation.getSize();
497 }
498 }
499
500 return size;
501 }
502
503
504
505
506 @Override
507 protected void put(@NonNull ByteVector out) {
508 putAccess(out, Access.CONSTRUCTOR);
509 out.putShort(nameItemIndex);
510 out.putShort(descItemIndex);
511
512 if (classReaderOffset > 0) {
513 out.putByteArray(cw.code, classReaderOffset, classReaderLength);
514 return;
515 }
516
517 putMethodAttributeCount(out);
518 putMethodCode(out);
519
520 if (exceptionsWriter != null) {
521 exceptionsWriter.put(out);
522 }
523
524 putMarkerAttributes(out);
525
526 if (signatureWriter != null) {
527 signatureWriter.put(out);
528 }
529
530 putAnnotationAttributes(out);
531 }
532
533 private void putMethodAttributeCount(@NonNull ByteVector out) {
534 int attributeCount = getMarkerAttributeCount();
535
536 if (code.getLength() > 0) {
537 attributeCount++;
538 }
539
540 if (exceptionsWriter != null) {
541 attributeCount++;
542 }
543
544 if (signatureWriter != null) {
545 attributeCount++;
546 }
547
548 if (annotations != null) {
549 attributeCount++;
550 }
551
552 if (parameterAnnotations != null) {
553 attributeCount++;
554 }
555
556 out.putShort(attributeCount);
557 }
558
559 private void putMethodCode(@NonNull ByteVector out) {
560 if (code.getLength() > 0) {
561 putCodeSize(out);
562 stackMapTableWriter.putMaxStackAndLocals(out);
563 out.putInt(code.getLength()).putByteVector(code);
564 exceptionHandling.put(out);
565
566 int codeAttributeCount = localVariableTableWriter.getAttributeCount();
567
568 if (lineNumberTableWriter.hasLineNumbers()) {
569 codeAttributeCount++;
570 }
571
572 if (stackMapTableWriter.hasStackMap()) {
573 codeAttributeCount++;
574 }
575
576 out.putShort(codeAttributeCount);
577 localVariableTableWriter.put(out);
578 lineNumberTableWriter.put(out);
579 stackMapTableWriter.put(out);
580 }
581 }
582
583 private void putCodeSize(@NonNull ByteVector out) {
584 int size = 12 + code.getLength() + exceptionHandling.getSize() + localVariableTableWriter.getSize()
585 + lineNumberTableWriter.getSize() + stackMapTableWriter.getSize();
586
587 out.putShort(cp.newUTF8("Code")).putInt(size);
588 }
589
590 private void putAnnotationAttributes(@NonNull ByteVector out) {
591 putAnnotations(out);
592
593 if (parameterAnnotations != null) {
594 out.putShort(cp.newUTF8("RuntimeVisibleParameterAnnotations"));
595 AnnotationVisitor.put(out, parameterAnnotations);
596 }
597 }
598
599 @Nullable
600 public Label getCurrentBlock() {
601 return cfgAnalysis.getLabelForCurrentBasicBlock();
602 }
603 }