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