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