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