blob: f4edfa4da4509e05f34563aa18e98f2fa1de815a [file] [log] [blame]
Clement Escoffier042a0ec2007-06-24 15:11:33 +00001/***
2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2005 INRIA, France Telecom
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
29 */
30package org.objectweb.asm.commons;
31
32import java.util.ArrayList;
33import java.util.HashMap;
34
35import org.objectweb.asm.Label;
36import org.objectweb.asm.MethodVisitor;
37import org.objectweb.asm.Opcodes;
38import org.objectweb.asm.Type;
39
40/**
41 * A {@link org.objectweb.asm.MethodAdapter} to insert before, after and around
42 * advices in methods and constructors. <p> The behavior for constructors is
43 * like this: <ol>
44 *
45 * <li>as long as the INVOKESPECIAL for the object initialization has not been
46 * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
47 *
48 * <li>when this one is reached, it is only added in the ctor code visitor and
49 * a JP invoke is added</li>
50 *
51 * <li>after that, only the other code visitor receives the instructions</li>
52 *
53 * </ol>
54 *
55 * @author Eugene Kuleshov
56 * @author Eric Bruneton
57 */
58public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
59{
60 private static final Object THIS = new Object();
61 private static final Object OTHER = new Object();
62
63 protected int methodAccess;
64 protected String methodDesc;
65
66 private boolean constructor;
67 private boolean superInitialized;
68 private ArrayList stackFrame;
69 private HashMap branches;
70
71 /**
72 * Creates a new {@link AdviceAdapter}.
73 *
74 * @param mv the method visitor to which this adapter delegates calls.
75 * @param access the method's access flags (see {@link Opcodes}).
76 * @param name the method's name.
77 * @param desc the method's descriptor (see {@link Type Type}).
78 */
79 public AdviceAdapter(
80 final MethodVisitor mv,
81 final int access,
82 final String name,
83 final String desc)
84 {
85 super(mv, access, name, desc);
86 methodAccess = access;
87 methodDesc = desc;
88
89 constructor = "<init>".equals(name);
90 }
91
92 public void visitCode() {
93 mv.visitCode();
94 if (!constructor) {
95 superInitialized = true;
96 onMethodEnter();
97 } else {
98 stackFrame = new ArrayList();
99 branches = new HashMap();
100 }
101 }
102
103 public void visitLabel(final Label label) {
104 mv.visitLabel(label);
105
106 if (constructor && branches != null) {
107 ArrayList frame = (ArrayList) branches.get(label);
108 if (frame != null) {
109 stackFrame = frame;
110 branches.remove(label);
111 }
112 }
113 }
114
115 public void visitInsn(final int opcode) {
116 if (constructor) {
117 switch (opcode) {
118 case RETURN: // empty stack
119 onMethodExit(opcode);
120 break;
121
122 case IRETURN: // 1 before n/a after
123 case FRETURN: // 1 before n/a after
124 case ARETURN: // 1 before n/a after
125 case ATHROW: // 1 before n/a after
126 popValue();
127 popValue();
128 onMethodExit(opcode);
129 break;
130
131 case LRETURN: // 2 before n/a after
132 case DRETURN: // 2 before n/a after
133 popValue();
134 popValue();
135 onMethodExit(opcode);
136 break;
137
138 case NOP:
139 case LALOAD: // remove 2 add 2
140 case DALOAD: // remove 2 add 2
141 case LNEG:
142 case DNEG:
143 case FNEG:
144 case INEG:
145 case L2D:
146 case D2L:
147 case F2I:
148 case I2B:
149 case I2C:
150 case I2S:
151 case I2F:
152 case Opcodes.ARRAYLENGTH:
153 break;
154
155 case ACONST_NULL:
156 case ICONST_M1:
157 case ICONST_0:
158 case ICONST_1:
159 case ICONST_2:
160 case ICONST_3:
161 case ICONST_4:
162 case ICONST_5:
163 case FCONST_0:
164 case FCONST_1:
165 case FCONST_2:
166 case F2L: // 1 before 2 after
167 case F2D:
168 case I2L:
169 case I2D:
170 pushValue(OTHER);
171 break;
172
173 case LCONST_0:
174 case LCONST_1:
175 case DCONST_0:
176 case DCONST_1:
177 pushValue(OTHER);
178 pushValue(OTHER);
179 break;
180
181 case IALOAD: // remove 2 add 1
182 case FALOAD: // remove 2 add 1
183 case AALOAD: // remove 2 add 1
184 case BALOAD: // remove 2 add 1
185 case CALOAD: // remove 2 add 1
186 case SALOAD: // remove 2 add 1
187 case POP:
188 case IADD:
189 case FADD:
190 case ISUB:
191 case LSHL: // 3 before 2 after
192 case LSHR: // 3 before 2 after
193 case LUSHR: // 3 before 2 after
194 case L2I: // 2 before 1 after
195 case L2F: // 2 before 1 after
196 case D2I: // 2 before 1 after
197 case D2F: // 2 before 1 after
198 case FSUB:
199 case FMUL:
200 case FDIV:
201 case FREM:
202 case FCMPL: // 2 before 1 after
203 case FCMPG: // 2 before 1 after
204 case IMUL:
205 case IDIV:
206 case IREM:
207 case ISHL:
208 case ISHR:
209 case IUSHR:
210 case IAND:
211 case IOR:
212 case IXOR:
213 case MONITORENTER:
214 case MONITOREXIT:
215 popValue();
216 break;
217
218 case POP2:
219 case LSUB:
220 case LMUL:
221 case LDIV:
222 case LREM:
223 case LADD:
224 case LAND:
225 case LOR:
226 case LXOR:
227 case DADD:
228 case DMUL:
229 case DSUB:
230 case DDIV:
231 case DREM:
232 popValue();
233 popValue();
234 break;
235
236 case IASTORE:
237 case FASTORE:
238 case AASTORE:
239 case BASTORE:
240 case CASTORE:
241 case SASTORE:
242 case LCMP: // 4 before 1 after
243 case DCMPL:
244 case DCMPG:
245 popValue();
246 popValue();
247 popValue();
248 break;
249
250 case LASTORE:
251 case DASTORE:
252 popValue();
253 popValue();
254 popValue();
255 popValue();
256 break;
257
258 case DUP:
259 pushValue(peekValue());
260 break;
261
262 case DUP_X1:
263 // TODO optimize this
264 {
265 Object o1 = popValue();
266 Object o2 = popValue();
267 pushValue(o1);
268 pushValue(o2);
269 pushValue(o1);
270 }
271 break;
272
273 case DUP_X2:
274 // TODO optimize this
275 {
276 Object o1 = popValue();
277 Object o2 = popValue();
278 Object o3 = popValue();
279 pushValue(o1);
280 pushValue(o3);
281 pushValue(o2);
282 pushValue(o1);
283 }
284 break;
285
286 case DUP2:
287 // TODO optimize this
288 {
289 Object o1 = popValue();
290 Object o2 = popValue();
291 pushValue(o2);
292 pushValue(o1);
293 pushValue(o2);
294 pushValue(o1);
295 }
296 break;
297
298 case DUP2_X1:
299 // TODO optimize this
300 {
301 Object o1 = popValue();
302 Object o2 = popValue();
303 Object o3 = popValue();
304 pushValue(o2);
305 pushValue(o1);
306 pushValue(o3);
307 pushValue(o2);
308 pushValue(o1);
309 }
310 break;
311
312 case DUP2_X2:
313 // TODO optimize this
314 {
315 Object o1 = popValue();
316 Object o2 = popValue();
317 Object o3 = popValue();
318 Object o4 = popValue();
319 pushValue(o2);
320 pushValue(o1);
321 pushValue(o4);
322 pushValue(o3);
323 pushValue(o2);
324 pushValue(o1);
325 }
326 break;
327
328 case SWAP: {
329 Object o1 = popValue();
330 Object o2 = popValue();
331 pushValue(o1);
332 pushValue(o2);
333 }
334 break;
335 }
336 } else {
337 switch (opcode) {
338 case RETURN:
339 case IRETURN:
340 case FRETURN:
341 case ARETURN:
342 case LRETURN:
343 case DRETURN:
344 case ATHROW:
345 onMethodExit(opcode);
346 break;
347 }
348 }
349 mv.visitInsn(opcode);
350 }
351
352 public void visitVarInsn(final int opcode, final int var) {
353 super.visitVarInsn(opcode, var);
354
355 if (constructor) {
356 switch (opcode) {
357 case ILOAD:
358 case FLOAD:
359 pushValue(OTHER);
360 break;
361 case LLOAD:
362 case DLOAD:
363 pushValue(OTHER);
364 pushValue(OTHER);
365 break;
366 case ALOAD:
367 pushValue(var == 0 ? THIS : OTHER);
368 break;
369 case ASTORE:
370 case ISTORE:
371 case FSTORE:
372 popValue();
373 break;
374 case LSTORE:
375 case DSTORE:
376 popValue();
377 popValue();
378 break;
379 }
380 }
381 }
382
383 public void visitFieldInsn(
384 final int opcode,
385 final String owner,
386 final String name,
387 final String desc)
388 {
389 mv.visitFieldInsn(opcode, owner, name, desc);
390
391 if (constructor) {
392 char c = desc.charAt(0);
393 boolean longOrDouble = c == 'J' || c == 'D';
394 switch (opcode) {
395 case GETSTATIC:
396 pushValue(OTHER);
397 if (longOrDouble) {
398 pushValue(OTHER);
399 }
400 break;
401 case PUTSTATIC:
402 popValue();
403 if (longOrDouble) {
404 popValue();
405 }
406 break;
407 case PUTFIELD:
408 popValue();
409 if (longOrDouble) {
410 popValue();
411 popValue();
412 }
413 break;
414 // case GETFIELD:
415 default:
416 if (longOrDouble) {
417 pushValue(OTHER);
418 }
419 }
420 }
421 }
422
423 public void visitIntInsn(final int opcode, final int operand) {
424 mv.visitIntInsn(opcode, operand);
425
426 if (constructor && opcode!=NEWARRAY) {
427 pushValue(OTHER);
428 }
429 }
430
431 public void visitLdcInsn(final Object cst) {
432 mv.visitLdcInsn(cst);
433
434 if (constructor) {
435 pushValue(OTHER);
436 if (cst instanceof Double || cst instanceof Long) {
437 pushValue(OTHER);
438 }
439 }
440 }
441
442 public void visitMultiANewArrayInsn(final String desc, final int dims) {
443 mv.visitMultiANewArrayInsn(desc, dims);
444
445 if (constructor) {
446 for (int i = 0; i < dims; i++) {
447 popValue();
448 }
449 pushValue(OTHER);
450 }
451 }
452
453 public void visitTypeInsn(final int opcode, final String name) {
454 mv.visitTypeInsn(opcode, name);
455
456 // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
457 if (constructor && opcode == NEW) {
458 pushValue(OTHER);
459 }
460 }
461
462 public void visitMethodInsn(
463 final int opcode,
464 final String owner,
465 final String name,
466 final String desc)
467 {
468 mv.visitMethodInsn(opcode, owner, name, desc);
469
470 if (constructor) {
471 Type[] types = Type.getArgumentTypes(desc);
472 for (int i = 0; i < types.length; i++) {
473 popValue();
474 if (types[i].getSize() == 2) {
475 popValue();
476 }
477 }
478 switch (opcode) {
479 // case INVOKESTATIC:
480 // break;
481
482 case INVOKEINTERFACE:
483 case INVOKEVIRTUAL:
484 popValue(); // objectref
485 break;
486
487 case INVOKESPECIAL:
488 Object type = popValue(); // objectref
489 if (type == THIS && !superInitialized) {
490 onMethodEnter();
491 superInitialized = true;
492 // once super has been initialized it is no longer
493 // necessary to keep track of stack state
494 constructor = false;
495 }
496 break;
497 }
498
499 Type returnType = Type.getReturnType(desc);
500 if (returnType != Type.VOID_TYPE) {
501 pushValue(OTHER);
502 if (returnType.getSize() == 2) {
503 pushValue(OTHER);
504 }
505 }
506 }
507 }
508
509 public void visitJumpInsn(final int opcode, final Label label) {
510 mv.visitJumpInsn(opcode, label);
511
512 if (constructor) {
513 switch (opcode) {
514 case IFEQ:
515 case IFNE:
516 case IFLT:
517 case IFGE:
518 case IFGT:
519 case IFLE:
520 case IFNULL:
521 case IFNONNULL:
522 popValue();
523 break;
524
525 case IF_ICMPEQ:
526 case IF_ICMPNE:
527 case IF_ICMPLT:
528 case IF_ICMPGE:
529 case IF_ICMPGT:
530 case IF_ICMPLE:
531 case IF_ACMPEQ:
532 case IF_ACMPNE:
533 popValue();
534 popValue();
535 break;
536
537 case JSR:
538 pushValue(OTHER);
539 break;
540 }
541 addBranch(label);
542 }
543 }
544
545 public void visitLookupSwitchInsn(
546 final Label dflt,
547 final int[] keys,
548 final Label[] labels)
549 {
550 mv.visitLookupSwitchInsn(dflt, keys, labels);
551
552 if (constructor) {
553 popValue();
554 addBranches(dflt, labels);
555 }
556 }
557
558 public void visitTableSwitchInsn(
559 final int min,
560 final int max,
561 final Label dflt,
562 final Label[] labels)
563 {
564 mv.visitTableSwitchInsn(min, max, dflt, labels);
565
566 if (constructor) {
567 popValue();
568 addBranches(dflt, labels);
569 }
570 }
571
572 private void addBranches(final Label dflt, final Label[] labels) {
573 addBranch(dflt);
574 for (int i = 0; i < labels.length; i++) {
575 addBranch(labels[i]);
576 }
577 }
578
579 private void addBranch(final Label label) {
580 if (branches.containsKey(label)) {
581 return;
582 }
583 ArrayList frame = new ArrayList();
584 frame.addAll(stackFrame);
585 branches.put(label, frame);
586 }
587
588 private Object popValue() {
589 return stackFrame.remove(stackFrame.size() - 1);
590 }
591
592 private Object peekValue() {
593 return stackFrame.get(stackFrame.size() - 1);
594 }
595
596 private void pushValue(final Object o) {
597 stackFrame.add(o);
598 }
599
600 /**
601 * Called at the beginning of the method or after super class class call in
602 * the constructor. <br><br>
603 *
604 * <i>Custom code can use or change all the local variables, but should not
605 * change state of the stack.</i>
606 */
607 protected abstract void onMethodEnter();
608
609 /**
610 * Called before explicit exit from the method using either return or throw.
611 * Top element on the stack contains the return value or exception instance.
612 * For example:
613 *
614 * <pre>
615 * public void onMethodExit(int opcode) {
616 * if(opcode==RETURN) {
617 * visitInsn(ACONST_NULL);
618 * } else if(opcode==ARETURN || opcode==ATHROW) {
619 * dup();
620 * } else {
621 * if(opcode==LRETURN || opcode==DRETURN) {
622 * dup2();
623 * } else {
624 * dup();
625 * }
626 * box(Type.getReturnType(this.methodDesc));
627 * }
628 * visitIntInsn(SIPUSH, opcode);
629 * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
630 * }
631 *
632 * // an actual call back method
633 * public static void onExit(int opcode, Object param) {
634 * ...
635 * </pre>
636 *
637 * <br><br>
638 *
639 * <i>Custom code can use or change all the local variables, but should not
640 * change state of the stack.</i>
641 *
642 * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
643 * DRETURN or ATHROW
644 *
645 */
646 protected abstract void onMethodExit(int opcode);
647
648 // TODO onException, onMethodCall
649
650}