001: /***
002: * ASM: a very small and fast Java bytecode manipulation framework
003: * Copyright (c) 2000-2007 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 org.objectweb.asm.Label;
032: import org.objectweb.asm.MethodVisitor;
033: import org.objectweb.asm.Opcodes;
034: import org.objectweb.asm.Type;
035:
036: import java.util.ArrayList;
037: import java.util.HashMap;
038: import java.util.List;
039: import java.util.Map;
040:
041: /**
042: * A {@link org.objectweb.asm.MethodAdapter} to insert before, after and around
043: * advices in methods and constructors. <p> The behavior for constructors is
044: * like this: <ol>
045: *
046: * <li>as long as the INVOKESPECIAL for the object initialization has not been
047: * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
048: *
049: * <li>when this one is reached, it is only added in the ctor code visitor and
050: * a JP invoke is added</li>
051: *
052: * <li>after that, only the other code visitor receives the instructions</li>
053: *
054: * </ol>
055: *
056: * @author Eugene Kuleshov
057: * @author Eric Bruneton
058: */
059: public abstract class AdviceAdapter extends GeneratorAdapter implements
060: Opcodes {
061: private static final Object THIS = new Object();
062: private static final Object OTHER = new Object();
063:
064: protected int methodAccess;
065: protected String methodDesc;
066:
067: private boolean constructor;
068: private boolean super Initialized;
069: private List stackFrame;
070: private Map branches;
071:
072: /**
073: * Creates a new {@link AdviceAdapter}.
074: *
075: * @param mv the method visitor to which this adapter delegates calls.
076: * @param access the method's access flags (see {@link Opcodes}).
077: * @param name the method's name.
078: * @param desc the method's descriptor (see {@link Type Type}).
079: */
080: protected AdviceAdapter(final MethodVisitor mv, final int access,
081: final String name, final String desc) {
082: super (mv, access, name, desc);
083: methodAccess = access;
084: methodDesc = desc;
085:
086: constructor = "<init>".equals(name);
087: }
088:
089: public void visitCode() {
090: mv.visitCode();
091: if (constructor) {
092: stackFrame = new ArrayList();
093: branches = new HashMap();
094: } else {
095: super Initialized = true;
096: onMethodEnter();
097: }
098: }
099:
100: public void visitLabel(final Label label) {
101: mv.visitLabel(label);
102:
103: if (constructor && branches != null) {
104: List frame = (List) branches.get(label);
105: if (frame != null) {
106: stackFrame = frame;
107: branches.remove(label);
108: }
109: }
110: }
111:
112: public void visitInsn(final int opcode) {
113: if (constructor) {
114: int s;
115: switch (opcode) {
116: case RETURN: // empty stack
117: onMethodExit(opcode);
118: break;
119:
120: case IRETURN: // 1 before n/a after
121: case FRETURN: // 1 before n/a after
122: case ARETURN: // 1 before n/a after
123: case ATHROW: // 1 before n/a after
124: popValue();
125: onMethodExit(opcode);
126: break;
127:
128: case LRETURN: // 2 before n/a after
129: case DRETURN: // 2 before n/a after
130: popValue();
131: popValue();
132: onMethodExit(opcode);
133: break;
134:
135: case NOP:
136: case LALOAD: // remove 2 add 2
137: case DALOAD: // remove 2 add 2
138: case LNEG:
139: case DNEG:
140: case FNEG:
141: case INEG:
142: case L2D:
143: case D2L:
144: case F2I:
145: case I2B:
146: case I2C:
147: case I2S:
148: case I2F:
149: case ARRAYLENGTH:
150: break;
151:
152: case ACONST_NULL:
153: case ICONST_M1:
154: case ICONST_0:
155: case ICONST_1:
156: case ICONST_2:
157: case ICONST_3:
158: case ICONST_4:
159: case ICONST_5:
160: case FCONST_0:
161: case FCONST_1:
162: case FCONST_2:
163: case F2L: // 1 before 2 after
164: case F2D:
165: case I2L:
166: case I2D:
167: pushValue(OTHER);
168: break;
169:
170: case LCONST_0:
171: case LCONST_1:
172: case DCONST_0:
173: case DCONST_1:
174: pushValue(OTHER);
175: pushValue(OTHER);
176: break;
177:
178: case IALOAD: // remove 2 add 1
179: case FALOAD: // remove 2 add 1
180: case AALOAD: // remove 2 add 1
181: case BALOAD: // remove 2 add 1
182: case CALOAD: // remove 2 add 1
183: case SALOAD: // remove 2 add 1
184: case POP:
185: case IADD:
186: case FADD:
187: case ISUB:
188: case LSHL: // 3 before 2 after
189: case LSHR: // 3 before 2 after
190: case LUSHR: // 3 before 2 after
191: case L2I: // 2 before 1 after
192: case L2F: // 2 before 1 after
193: case D2I: // 2 before 1 after
194: case D2F: // 2 before 1 after
195: case FSUB:
196: case FMUL:
197: case FDIV:
198: case FREM:
199: case FCMPL: // 2 before 1 after
200: case FCMPG: // 2 before 1 after
201: case IMUL:
202: case IDIV:
203: case IREM:
204: case ISHL:
205: case ISHR:
206: case IUSHR:
207: case IAND:
208: case IOR:
209: case IXOR:
210: case MONITORENTER:
211: case MONITOREXIT:
212: popValue();
213: break;
214:
215: case POP2:
216: case LSUB:
217: case LMUL:
218: case LDIV:
219: case LREM:
220: case LADD:
221: case LAND:
222: case LOR:
223: case LXOR:
224: case DADD:
225: case DMUL:
226: case DSUB:
227: case DDIV:
228: case DREM:
229: popValue();
230: popValue();
231: break;
232:
233: case IASTORE:
234: case FASTORE:
235: case AASTORE:
236: case BASTORE:
237: case CASTORE:
238: case SASTORE:
239: case LCMP: // 4 before 1 after
240: case DCMPL:
241: case DCMPG:
242: popValue();
243: popValue();
244: popValue();
245: break;
246:
247: case LASTORE:
248: case DASTORE:
249: popValue();
250: popValue();
251: popValue();
252: popValue();
253: break;
254:
255: case DUP:
256: pushValue(peekValue());
257: break;
258:
259: case DUP_X1:
260: s = stackFrame.size();
261: stackFrame.add(s - 2, stackFrame.get(s - 1));
262: break;
263:
264: case DUP_X2:
265: s = stackFrame.size();
266: stackFrame.add(s - 3, stackFrame.get(s - 1));
267: break;
268:
269: case DUP2:
270: s = stackFrame.size();
271: stackFrame.add(s - 2, stackFrame.get(s - 1));
272: stackFrame.add(s - 2, stackFrame.get(s - 1));
273: break;
274:
275: case DUP2_X1:
276: s = stackFrame.size();
277: stackFrame.add(s - 3, stackFrame.get(s - 1));
278: stackFrame.add(s - 3, stackFrame.get(s - 1));
279: break;
280:
281: case DUP2_X2:
282: s = stackFrame.size();
283: stackFrame.add(s - 4, stackFrame.get(s - 1));
284: stackFrame.add(s - 4, stackFrame.get(s - 1));
285: break;
286:
287: case SWAP:
288: s = stackFrame.size();
289: stackFrame.add(s - 2, stackFrame.get(s - 1));
290: stackFrame.remove(s);
291: break;
292: }
293: } else {
294: switch (opcode) {
295: case RETURN:
296: case IRETURN:
297: case FRETURN:
298: case ARETURN:
299: case LRETURN:
300: case DRETURN:
301: case ATHROW:
302: onMethodExit(opcode);
303: break;
304: }
305: }
306: mv.visitInsn(opcode);
307: }
308:
309: public void visitVarInsn(final int opcode, final int var) {
310: super .visitVarInsn(opcode, var);
311:
312: if (constructor) {
313: switch (opcode) {
314: case ILOAD:
315: case FLOAD:
316: pushValue(OTHER);
317: break;
318: case LLOAD:
319: case DLOAD:
320: pushValue(OTHER);
321: pushValue(OTHER);
322: break;
323: case ALOAD:
324: pushValue(var == 0 ? THIS : OTHER);
325: break;
326: case ASTORE:
327: case ISTORE:
328: case FSTORE:
329: popValue();
330: break;
331: case LSTORE:
332: case DSTORE:
333: popValue();
334: popValue();
335: break;
336: }
337: }
338: }
339:
340: public void visitFieldInsn(final int opcode, final String owner,
341: final String name, final String desc) {
342: mv.visitFieldInsn(opcode, owner, name, desc);
343:
344: if (constructor) {
345: char c = desc.charAt(0);
346: boolean longOrDouble = c == 'J' || c == 'D';
347: switch (opcode) {
348: case GETSTATIC:
349: pushValue(OTHER);
350: if (longOrDouble) {
351: pushValue(OTHER);
352: }
353: break;
354: case PUTSTATIC:
355: popValue();
356: if (longOrDouble) {
357: popValue();
358: }
359: break;
360: case PUTFIELD:
361: popValue();
362: if (longOrDouble) {
363: popValue();
364: popValue();
365: }
366: break;
367: // case GETFIELD:
368: default:
369: if (longOrDouble) {
370: pushValue(OTHER);
371: }
372: }
373: }
374: }
375:
376: public void visitIntInsn(final int opcode, final int operand) {
377: mv.visitIntInsn(opcode, operand);
378:
379: if (constructor && opcode != NEWARRAY) {
380: pushValue(OTHER);
381: }
382: }
383:
384: public void visitLdcInsn(final Object cst) {
385: mv.visitLdcInsn(cst);
386:
387: if (constructor) {
388: pushValue(OTHER);
389: if (cst instanceof Double || cst instanceof Long) {
390: pushValue(OTHER);
391: }
392: }
393: }
394:
395: public void visitMultiANewArrayInsn(final String desc,
396: final int dims) {
397: mv.visitMultiANewArrayInsn(desc, dims);
398:
399: if (constructor) {
400: for (int i = 0; i < dims; i++) {
401: popValue();
402: }
403: pushValue(OTHER);
404: }
405: }
406:
407: public void visitTypeInsn(final int opcode, final String type) {
408: mv.visitTypeInsn(opcode, type);
409:
410: // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
411: if (constructor && opcode == NEW) {
412: pushValue(OTHER);
413: }
414: }
415:
416: public void visitMethodInsn(final int opcode, final String owner,
417: final String name, final String desc) {
418: mv.visitMethodInsn(opcode, owner, name, desc);
419:
420: if (constructor) {
421: Type[] types = Type.getArgumentTypes(desc);
422: for (int i = 0; i < types.length; i++) {
423: popValue();
424: if (types[i].getSize() == 2) {
425: popValue();
426: }
427: }
428: switch (opcode) {
429: // case INVOKESTATIC:
430: // break;
431:
432: case INVOKEINTERFACE:
433: case INVOKEVIRTUAL:
434: popValue(); // objectref
435: break;
436:
437: case INVOKESPECIAL:
438: Object type = popValue(); // objectref
439: if (type == THIS && !super Initialized) {
440: onMethodEnter();
441: super Initialized = true;
442: // once super has been initialized it is no longer
443: // necessary to keep track of stack state
444: constructor = false;
445: }
446: break;
447: }
448:
449: Type returnType = Type.getReturnType(desc);
450: if (returnType != Type.VOID_TYPE) {
451: pushValue(OTHER);
452: if (returnType.getSize() == 2) {
453: pushValue(OTHER);
454: }
455: }
456: }
457: }
458:
459: public void visitJumpInsn(final int opcode, final Label label) {
460: mv.visitJumpInsn(opcode, label);
461:
462: if (constructor) {
463: switch (opcode) {
464: case IFEQ:
465: case IFNE:
466: case IFLT:
467: case IFGE:
468: case IFGT:
469: case IFLE:
470: case IFNULL:
471: case IFNONNULL:
472: popValue();
473: break;
474:
475: case IF_ICMPEQ:
476: case IF_ICMPNE:
477: case IF_ICMPLT:
478: case IF_ICMPGE:
479: case IF_ICMPGT:
480: case IF_ICMPLE:
481: case IF_ACMPEQ:
482: case IF_ACMPNE:
483: popValue();
484: popValue();
485: break;
486:
487: case JSR:
488: pushValue(OTHER);
489: break;
490: }
491: addBranch(label);
492: }
493: }
494:
495: public void visitLookupSwitchInsn(final Label dflt,
496: final int[] keys, final Label[] labels) {
497: mv.visitLookupSwitchInsn(dflt, keys, labels);
498:
499: if (constructor) {
500: popValue();
501: addBranches(dflt, labels);
502: }
503: }
504:
505: public void visitTableSwitchInsn(final int min, final int max,
506: final Label dflt, final Label[] labels) {
507: mv.visitTableSwitchInsn(min, max, dflt, labels);
508:
509: if (constructor) {
510: popValue();
511: addBranches(dflt, labels);
512: }
513: }
514:
515: private void addBranches(final Label dflt, final Label[] labels) {
516: addBranch(dflt);
517: for (int i = 0; i < labels.length; i++) {
518: addBranch(labels[i]);
519: }
520: }
521:
522: private void addBranch(final Label label) {
523: if (branches.containsKey(label)) {
524: return;
525: }
526: branches.put(label, new ArrayList(stackFrame));
527: }
528:
529: private Object popValue() {
530: return stackFrame.remove(stackFrame.size() - 1);
531: }
532:
533: private Object peekValue() {
534: return stackFrame.get(stackFrame.size() - 1);
535: }
536:
537: private void pushValue(final Object o) {
538: stackFrame.add(o);
539: }
540:
541: /**
542: * Called at the beginning of the method or after super class class call in
543: * the constructor. <br><br>
544: *
545: * <i>Custom code can use or change all the local variables, but should not
546: * change state of the stack.</i>
547: */
548: protected void onMethodEnter() {
549: }
550:
551: /**
552: * Called before explicit exit from the method using either return or throw.
553: * Top element on the stack contains the return value or exception instance.
554: * For example:
555: *
556: * <pre>
557: * public void onMethodExit(int opcode) {
558: * if(opcode==RETURN) {
559: * visitInsn(ACONST_NULL);
560: * } else if(opcode==ARETURN || opcode==ATHROW) {
561: * dup();
562: * } else {
563: * if(opcode==LRETURN || opcode==DRETURN) {
564: * dup2();
565: * } else {
566: * dup();
567: * }
568: * box(Type.getReturnType(this.methodDesc));
569: * }
570: * visitIntInsn(SIPUSH, opcode);
571: * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
572: * }
573: *
574: * // an actual call back method
575: * public static void onExit(int opcode, Object param) {
576: * ...
577: * </pre>
578: *
579: * <br><br>
580: *
581: * <i>Custom code can use or change all the local variables, but should not
582: * change state of the stack.</i>
583: *
584: * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
585: * DRETURN or ATHROW
586: *
587: */
588: protected void onMethodExit(int opcode) {
589: }
590:
591: // TODO onException, onMethodCall
592:
593: }
|