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