Home | History | Annotate | Download | only in commons
      1 /***
      2  * ASM: a very small and fast Java bytecode manipulation framework
      3  * Copyright (c) 2000-2005 INRIA, France Telecom
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. Neither the name of the copyright holders nor the names of its
     15  *    contributors may be used to endorse or promote products derived from
     16  *    this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     28  * THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 package org.objectweb.asm.commons;
     31 
     32 import java.util.ArrayList;
     33 import java.util.HashMap;
     34 
     35 import org.objectweb.asm.Label;
     36 import org.objectweb.asm.MethodVisitor;
     37 import org.objectweb.asm.Opcodes;
     38 import org.objectweb.asm.Type;
     39 
     40 /**
     41  * A <code>MethodAdapter</code> to dispatch method body instruction
     42  * <p>
     43  * The behavior is like this:
     44  * <ol>
     45  *
     46  * <li>as long as the INVOKESPECIAL for the object initialization has not been
     47  *     reached, every bytecode instruction is dispatched in the ctor code visitor</li>
     48  *
     49  * <li>when this one is reached, it is only added in the ctor code visitor and
     50  *     a JP invoke is added</li>
     51  * <li>after that, only the other code visitor receives the instructions</li>
     52  *
     53  * </ol>
     54  *
     55  * @author Eugene Kuleshov
     56  * @author Eric Bruneton
     57  */
     58 public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
     59     private static final Object THIS = new Object();
     60     private static final Object OTHER = new Object();
     61 
     62     protected int methodAccess;
     63     protected String methodDesc;
     64 
     65     private boolean constructor;
     66     private boolean superInitialized;
     67     private ArrayList stackFrame;
     68     private HashMap branches;
     69 
     70 
     71     /**
     72      * Creates a new {@link AdviceAdapter}.
     73      *
     74      * @param mv the method visitor to which this adapter delegates calls.
     75      * @param access the method's access flags (see {@link Opcodes}).
     76      * @param name the method's name.
     77      * @param desc the method's descriptor (see {@link Type Type}).
     78      */
     79     public AdviceAdapter(MethodVisitor mv, int access, String name, String desc) {
     80         super(mv, access, name, desc);
     81         methodAccess = access;
     82         methodDesc = desc;
     83 
     84         constructor = "<init>".equals(name);
     85         if (!constructor) {
     86             superInitialized = true;
     87             onMethodEnter();
     88         } else {
     89             stackFrame = new ArrayList();
     90             branches = new HashMap();
     91         }
     92     }
     93 
     94     public void visitLabel(Label label) {
     95         mv.visitLabel(label);
     96 
     97         if (constructor && branches != null) {
     98             ArrayList frame = (ArrayList) branches.get(label);
     99             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(
    375         int opcode,
    376         String owner,
    377         String name,
    378         String desc)
    379     {
    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(int opcode, int operand) {
    415         mv.visitIntInsn(opcode, operand);
    416 
    417         if (constructor) {
    418             switch (opcode) {
    419                 case BIPUSH:
    420                 case SIPUSH:
    421                     pushValue(OTHER);
    422             }
    423         }
    424     }
    425 
    426     public void visitLdcInsn(Object cst) {
    427         mv.visitLdcInsn(cst);
    428 
    429         if (constructor) {
    430             pushValue(OTHER);
    431             if (cst instanceof Double || cst instanceof Long) {
    432                 pushValue(OTHER);
    433             }
    434         }
    435     }
    436 
    437     public void visitMultiANewArrayInsn(String desc, int dims) {
    438         mv.visitMultiANewArrayInsn(desc, dims);
    439 
    440         if (constructor) {
    441             for (int i = 0; i < dims; i++) {
    442                 popValue();
    443             }
    444             pushValue(OTHER);
    445         }
    446     }
    447 
    448     public void visitTypeInsn(int opcode, String name) {
    449         mv.visitTypeInsn(opcode, name);
    450 
    451         // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
    452         if (constructor && opcode == NEW) {
    453             pushValue(OTHER);
    454         }
    455     }
    456 
    457     public void visitMethodInsn(
    458         int opcode,
    459         String owner,
    460         String name,
    461         String desc)
    462     {
    463         mv.visitMethodInsn(opcode, owner, name, desc);
    464 
    465         if (constructor) {
    466             Type[] types = Type.getArgumentTypes(desc);
    467             for (int i = 0; i < types.length; i++) {
    468                 popValue();
    469                 if (types[i].getSize() == 2) {
    470                     popValue();
    471                 }
    472             }
    473             switch (opcode) {
    474                 // case INVOKESTATIC:
    475                 // break;
    476 
    477                 case INVOKEINTERFACE:
    478                 case INVOKEVIRTUAL:
    479                     popValue(); // objectref
    480                     break;
    481 
    482                 case INVOKESPECIAL:
    483                     Object type = popValue(); // objectref
    484                     if (type == THIS && !superInitialized) {
    485                         onMethodEnter();
    486                         superInitialized = true;
    487                         // once super has been initialized it is no longer
    488                         // necessary to keep track of stack state
    489                         constructor = false;
    490                     }
    491                     break;
    492             }
    493 
    494             Type returnType = Type.getReturnType(desc);
    495             if (returnType != Type.VOID_TYPE) {
    496                 pushValue(OTHER);
    497                 if (returnType.getSize() == 2) {
    498                     pushValue(OTHER);
    499                 }
    500             }
    501         }
    502     }
    503 
    504     public void visitJumpInsn(int opcode, Label label) {
    505         mv.visitJumpInsn(opcode, label);
    506 
    507         if (constructor) {
    508             switch (opcode) {
    509                 case IFEQ:
    510                 case IFNE:
    511                 case IFLT:
    512                 case IFGE:
    513                 case IFGT:
    514                 case IFLE:
    515                 case IFNULL:
    516                 case IFNONNULL:
    517                     popValue();
    518                     break;
    519 
    520                 case IF_ICMPEQ:
    521                 case IF_ICMPNE:
    522                 case IF_ICMPLT:
    523                 case IF_ICMPGE:
    524                 case IF_ICMPGT:
    525                 case IF_ICMPLE:
    526                 case IF_ACMPEQ:
    527                 case IF_ACMPNE:
    528                     popValue();
    529                     popValue();
    530                     break;
    531 
    532                 case JSR:
    533                     pushValue(OTHER);
    534                     break;
    535             }
    536             addBranch(label);
    537         }
    538     }
    539 
    540     public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
    541         mv.visitLookupSwitchInsn(dflt, keys, labels);
    542 
    543         if (constructor) {
    544             popValue();
    545             addBranches(dflt, labels);
    546         }
    547     }
    548 
    549     public void visitTableSwitchInsn(
    550         int min,
    551         int max,
    552         Label dflt,
    553         Label[] labels)
    554     {
    555         mv.visitTableSwitchInsn(min, max, dflt, labels);
    556 
    557         if (constructor) {
    558             popValue();
    559             addBranches(dflt, labels);
    560         }
    561     }
    562 
    563     private void addBranches(Label dflt, Label[] labels) {
    564         addBranch(dflt);
    565         for (int i = 0; i < labels.length; i++) {
    566             addBranch(labels[i]);
    567         }
    568     }
    569 
    570     private void addBranch(Label label) {
    571         if (branches.containsKey(label)) {
    572             return;
    573         }
    574         ArrayList frame = new ArrayList();
    575         frame.addAll(stackFrame);
    576         branches.put(label, frame);
    577     }
    578 
    579     private Object popValue() {
    580         return stackFrame.remove(stackFrame.size()-1);
    581     }
    582 
    583     private Object peekValue() {
    584         return stackFrame.get(stackFrame.size()-1);
    585     }
    586 
    587     private void pushValue(Object o) {
    588         stackFrame.add(o);
    589     }
    590 
    591     /**
    592      * Called at the beginning of the method or after super
    593      * class class call in the constructor.
    594      * <br><br>
    595      *
    596      * <i>Custom code can use or change all the local variables,
    597      * but should not change state of the stack.</i>
    598      */
    599     protected abstract void onMethodEnter();
    600 
    601     /**
    602      * Called before explicit exit from the method using either
    603      * return or throw. Top element on the stack contains the
    604      * return value or exception instance. For example:
    605      *
    606      * <pre>
    607      *   public void onMethodExit(int opcode) {
    608      *     if(opcode==RETURN) {
    609      *         visitInsn(ACONST_NULL);
    610      *     } else if(opcode==ARETURN || opcode==ATHROW) {
    611      *         dup();
    612      *     } else {
    613      *         if(opcode==LRETURN || opcode==DRETURN) {
    614      *             dup2();
    615      *         } else {
    616      *             dup();
    617      *         }
    618      *         box(Type.getReturnType(this.methodDesc));
    619      *     }
    620      *     visitIntInsn(SIPUSH, opcode);
    621      *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
    622      *   }
    623      *
    624      *   // an actual call back method
    625      *   public static void onExit(int opcode, Object param) {
    626      *     ...
    627      * </pre>
    628      *
    629      * <br><br>
    630      *
    631      * <i>Custom code can use or change all the local variables,
    632      * but should not change state of the stack.</i>
    633      *
    634      * @param opcode one of the RETURN, IRETURN, FRETURN,
    635      *   ARETURN, LRETURN, DRETURN or ATHROW
    636      *
    637      */
    638     protected abstract void onMethodExit(int opcode);
    639 
    640     // TODO onException, onMethodCall
    641 
    642 }
    643 
    644