Home | History | Annotate | Download | only in expr
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.expr;
     17 
     18 import javassist.bytecode.*;
     19 import javassist.CtClass;
     20 import javassist.CannotCompileException;
     21 
     22 /**
     23  * A translator of method bodies.
     24  *
     25  * <p>The users can define a subclass of this class to customize how to
     26  * modify a method body.  The overall architecture is similar to the
     27  * strategy pattern.
     28  *
     29  * <p>If <code>instrument()</code> is called in
     30  * <code>CtMethod</code>, the method body is scanned from the beginning
     31  * to the end.
     32  * Whenever an expression, such as a method call and a <tt>new</tt>
     33  * expression (object creation),
     34  * is found, <code>edit()</code> is called in <code>ExprEdit</code>.
     35  * <code>edit()</code> can inspect and modify the given expression.
     36  * The modification is reflected on the original method body.  If
     37  * <code>edit()</code> does nothing, the original method body is not
     38  * changed.
     39  *
     40  * <p>The following code is an example:
     41  *
     42  * <ul><pre>
     43  * CtMethod cm = ...;
     44  * cm.instrument(new ExprEditor() {
     45  *     public void edit(MethodCall m) throws CannotCompileException {
     46  *         if (m.getClassName().equals("Point")) {
     47  *             System.out.println(m.getMethodName() + " line: "
     48  *                                + m.getLineNumber());
     49  *     }
     50  * });
     51  * </pre></ul>
     52  *
     53  * <p>This code inspects all method calls appearing in the method represented
     54  * by <code>cm</code> and it prints the names and the line numbers of the
     55  * methods declared in class <code>Point</code>.  This code does not modify
     56  * the body of the method represented by <code>cm</code>.  If the method
     57  * body must be modified, call <code>replace()</code>
     58  * in <code>MethodCall</code>.
     59  *
     60  * @see javassist.CtClass#instrument(ExprEditor)
     61  * @see javassist.CtMethod#instrument(ExprEditor)
     62  * @see javassist.CtConstructor#instrument(ExprEditor)
     63  * @see MethodCall
     64  * @see NewExpr
     65  * @see FieldAccess
     66  *
     67  * @see javassist.CodeConverter
     68  */
     69 public class ExprEditor {
     70     /**
     71      * Default constructor.  It does nothing.
     72      */
     73     public ExprEditor() {}
     74 
     75     /**
     76      * Undocumented method.  Do not use; internal-use only.
     77      */
     78     public boolean doit(CtClass clazz, MethodInfo minfo)
     79         throws CannotCompileException
     80     {
     81         CodeAttribute codeAttr = minfo.getCodeAttribute();
     82         if (codeAttr == null)
     83             return false;
     84 
     85         CodeIterator iterator = codeAttr.iterator();
     86         boolean edited = false;
     87         LoopContext context = new LoopContext(codeAttr.getMaxLocals());
     88 
     89         while (iterator.hasNext())
     90             if (loopBody(iterator, clazz, minfo, context))
     91                 edited = true;
     92 
     93         ExceptionTable et = codeAttr.getExceptionTable();
     94         int n = et.size();
     95         for (int i = 0; i < n; ++i) {
     96             Handler h = new Handler(et, i, iterator, clazz, minfo);
     97             edit(h);
     98             if (h.edited()) {
     99                 edited = true;
    100                 context.updateMax(h.locals(), h.stack());
    101             }
    102         }
    103 
    104         // codeAttr might be modified by other partiess
    105         // so I check the current value of max-locals.
    106         if (codeAttr.getMaxLocals() < context.maxLocals)
    107             codeAttr.setMaxLocals(context.maxLocals);
    108 
    109         codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack);
    110         try {
    111             if (edited)
    112                 minfo.rebuildStackMapIf6(clazz.getClassPool(),
    113                                          clazz.getClassFile2());
    114         }
    115         catch (BadBytecode b) {
    116             throw new CannotCompileException(b.getMessage(), b);
    117         }
    118 
    119         return edited;
    120     }
    121 
    122     /**
    123      * Visits each bytecode in the given range.
    124      */
    125     boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context,
    126                  CodeIterator iterator, int endPos)
    127         throws CannotCompileException
    128     {
    129         boolean edited = false;
    130         while (iterator.hasNext() && iterator.lookAhead() < endPos) {
    131             int size = iterator.getCodeLength();
    132             if (loopBody(iterator, clazz, minfo, context)) {
    133                 edited = true;
    134                 int size2 = iterator.getCodeLength();
    135                 if (size != size2)  // the body was modified.
    136                     endPos += size2 - size;
    137             }
    138         }
    139 
    140         return edited;
    141     }
    142 
    143     final static class NewOp {
    144         NewOp next;
    145         int pos;
    146         String type;
    147 
    148         NewOp(NewOp n, int p, String t) {
    149             next = n;
    150             pos = p;
    151             type = t;
    152         }
    153     }
    154 
    155     final static class LoopContext {
    156         NewOp newList;
    157         int maxLocals;
    158         int maxStack;
    159 
    160         LoopContext(int locals) {
    161             maxLocals = locals;
    162             maxStack = 0;
    163             newList = null;
    164         }
    165 
    166         void updateMax(int locals, int stack) {
    167             if (maxLocals < locals)
    168                 maxLocals = locals;
    169 
    170             if (maxStack < stack)
    171                 maxStack = stack;
    172         }
    173     }
    174 
    175     final boolean loopBody(CodeIterator iterator, CtClass clazz,
    176                            MethodInfo minfo, LoopContext context)
    177         throws CannotCompileException
    178     {
    179         try {
    180             Expr expr = null;
    181             int pos = iterator.next();
    182             int c = iterator.byteAt(pos);
    183 
    184             if (c < Opcode.GETSTATIC)   // c < 178
    185                 /* skip */;
    186             else if (c < Opcode.NEWARRAY) { // c < 188
    187                 if (c == Opcode.INVOKESTATIC
    188                     || c == Opcode.INVOKEINTERFACE
    189                     || c == Opcode.INVOKEVIRTUAL) {
    190                     expr = new MethodCall(pos, iterator, clazz, minfo);
    191                     edit((MethodCall)expr);
    192                 }
    193                 else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
    194                          || c == Opcode.PUTFIELD
    195                          || c == Opcode.PUTSTATIC) {
    196                     expr = new FieldAccess(pos, iterator, clazz, minfo, c);
    197                     edit((FieldAccess)expr);
    198                 }
    199                 else if (c == Opcode.NEW) {
    200                     int index = iterator.u16bitAt(pos + 1);
    201                     context.newList = new NewOp(context.newList, pos,
    202                                         minfo.getConstPool().getClassInfo(index));
    203                 }
    204                 else if (c == Opcode.INVOKESPECIAL) {
    205                     NewOp newList = context.newList;
    206                     if (newList != null
    207                         && minfo.getConstPool().isConstructor(newList.type,
    208                                             iterator.u16bitAt(pos + 1)) > 0) {
    209                         expr = new NewExpr(pos, iterator, clazz, minfo,
    210                                            newList.type, newList.pos);
    211                         edit((NewExpr)expr);
    212                         context.newList = newList.next;
    213                     }
    214                     else {
    215                         MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo);
    216                         if (mcall.getMethodName().equals(MethodInfo.nameInit)) {
    217                             ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo);
    218                             expr = ccall;
    219                             edit(ccall);
    220                         }
    221                         else {
    222                             expr = mcall;
    223                             edit(mcall);
    224                         }
    225                     }
    226                 }
    227             }
    228             else {  // c >= 188
    229                 if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY
    230                     || c == Opcode.MULTIANEWARRAY) {
    231                     expr = new NewArray(pos, iterator, clazz, minfo, c);
    232                     edit((NewArray)expr);
    233                 }
    234                 else if (c == Opcode.INSTANCEOF) {
    235                     expr = new Instanceof(pos, iterator, clazz, minfo);
    236                     edit((Instanceof)expr);
    237                 }
    238                 else if (c == Opcode.CHECKCAST) {
    239                     expr = new Cast(pos, iterator, clazz, minfo);
    240                     edit((Cast)expr);
    241                 }
    242             }
    243 
    244             if (expr != null && expr.edited()) {
    245                 context.updateMax(expr.locals(), expr.stack());
    246                 return true;
    247             }
    248             else
    249                 return false;
    250         }
    251         catch (BadBytecode e) {
    252             throw new CannotCompileException(e);
    253         }
    254     }
    255 
    256     /**
    257      * Edits a <tt>new</tt> expression (overridable).
    258      * The default implementation performs nothing.
    259      *
    260      * @param e         the <tt>new</tt> expression creating an object.
    261      */
    262     public void edit(NewExpr e) throws CannotCompileException {}
    263 
    264     /**
    265      * Edits an expression for array creation (overridable).
    266      * The default implementation performs nothing.
    267      *
    268      * @param a         the <tt>new</tt> expression for creating an array.
    269      * @throws CannotCompileException
    270      */
    271     public void edit(NewArray a) throws CannotCompileException {}
    272 
    273     /**
    274      * Edits a method call (overridable).
    275      *
    276      * The default implementation performs nothing.
    277      */
    278     public void edit(MethodCall m) throws CannotCompileException {}
    279 
    280     /**
    281      * Edits a constructor call (overridable).
    282      * The constructor call is either
    283      * <code>super()</code> or <code>this()</code>
    284      * included in a constructor body.
    285      *
    286      * The default implementation performs nothing.
    287      *
    288      * @see #edit(NewExpr)
    289      */
    290     public void edit(ConstructorCall c) throws CannotCompileException {}
    291 
    292     /**
    293      * Edits a field-access expression (overridable).
    294      * Field access means both read and write.
    295      * The default implementation performs nothing.
    296      */
    297     public void edit(FieldAccess f) throws CannotCompileException {}
    298 
    299     /**
    300      * Edits an instanceof expression (overridable).
    301      * The default implementation performs nothing.
    302      */
    303     public void edit(Instanceof i) throws CannotCompileException {}
    304 
    305     /**
    306      * Edits an expression for explicit type casting (overridable).
    307      * The default implementation performs nothing.
    308      */
    309     public void edit(Cast c) throws CannotCompileException {}
    310 
    311     /**
    312      * Edits a catch clause (overridable).
    313      * The default implementation performs nothing.
    314      */
    315     public void edit(Handler h) throws CannotCompileException {}
    316 }
    317