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