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.CannotCompileException;
     19 import javassist.ClassPool;
     20 import javassist.CtBehavior;
     21 import javassist.CtClass;
     22 import javassist.CtConstructor;
     23 import javassist.CtPrimitiveType;
     24 import javassist.NotFoundException;
     25 import javassist.bytecode.AccessFlag;
     26 import javassist.bytecode.BadBytecode;
     27 import javassist.bytecode.Bytecode;
     28 import javassist.bytecode.ClassFile;
     29 import javassist.bytecode.CodeAttribute;
     30 import javassist.bytecode.CodeIterator;
     31 import javassist.bytecode.ConstPool;
     32 import javassist.bytecode.ExceptionTable;
     33 import javassist.bytecode.ExceptionsAttribute;
     34 import javassist.bytecode.MethodInfo;
     35 import javassist.bytecode.Opcode;
     36 import javassist.compiler.Javac;
     37 
     38 import java.util.Iterator;
     39 import java.util.LinkedList;
     40 
     41 /**
     42  * Expression.
     43  */
     44 public abstract class Expr implements Opcode {
     45     int currentPos;
     46     CodeIterator iterator;
     47     CtClass thisClass;
     48     MethodInfo thisMethod;
     49     boolean edited;
     50     int maxLocals, maxStack;
     51 
     52     static final String javaLangObject = "java.lang.Object";
     53 
     54     /**
     55      * Undocumented constructor. Do not use; internal-use only.
     56      */
     57     protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) {
     58         currentPos = pos;
     59         iterator = i;
     60         thisClass = declaring;
     61         thisMethod = m;
     62     }
     63 
     64     /**
     65      * Returns the class that declares the method enclosing
     66      * this expression.
     67      *
     68      * @since 3.7
     69      */
     70     public CtClass getEnclosingClass() { return thisClass; }
     71 
     72     protected final ConstPool getConstPool() {
     73         return thisMethod.getConstPool();
     74     }
     75 
     76     protected final boolean edited() {
     77         return edited;
     78     }
     79 
     80     protected final int locals() {
     81         return maxLocals;
     82     }
     83 
     84     protected final int stack() {
     85         return maxStack;
     86     }
     87 
     88     /**
     89      * Returns true if this method is static.
     90      */
     91     protected final boolean withinStatic() {
     92         return (thisMethod.getAccessFlags() & AccessFlag.STATIC) != 0;
     93     }
     94 
     95     /**
     96      * Returns the constructor or method containing the expression.
     97      */
     98     public CtBehavior where() {
     99         MethodInfo mi = thisMethod;
    100         CtBehavior[] cb = thisClass.getDeclaredBehaviors();
    101         for (int i = cb.length - 1; i >= 0; --i)
    102             if (cb[i].getMethodInfo2() == mi)
    103                 return cb[i];
    104 
    105         CtConstructor init = thisClass.getClassInitializer();
    106         if (init != null && init.getMethodInfo2() == mi)
    107             return init;
    108 
    109         /* getDeclaredBehaviors() returns a list of methods/constructors.
    110          * Although the list is cached in a CtClass object, it might be
    111          * recreated for some reason.  Thus, the member name and the signature
    112          * must be also checked.
    113          */
    114         for (int i = cb.length - 1; i >= 0; --i) {
    115             if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName())
    116                 && thisMethod.getDescriptor()
    117                              .equals(cb[i].getMethodInfo2().getDescriptor())) {
    118                 return cb[i];
    119             }
    120         }
    121 
    122         throw new RuntimeException("fatal: not found");
    123     }
    124 
    125     /**
    126      * Returns the list of exceptions that the expression may throw. This list
    127      * includes both the exceptions that the try-catch statements including the
    128      * expression can catch and the exceptions that the throws declaration
    129      * allows the method to throw.
    130      */
    131     public CtClass[] mayThrow() {
    132         ClassPool pool = thisClass.getClassPool();
    133         ConstPool cp = thisMethod.getConstPool();
    134         LinkedList list = new LinkedList();
    135         try {
    136             CodeAttribute ca = thisMethod.getCodeAttribute();
    137             ExceptionTable et = ca.getExceptionTable();
    138             int pos = currentPos;
    139             int n = et.size();
    140             for (int i = 0; i < n; ++i)
    141                 if (et.startPc(i) <= pos && pos < et.endPc(i)) {
    142                     int t = et.catchType(i);
    143                     if (t > 0)
    144                         try {
    145                             addClass(list, pool.get(cp.getClassInfo(t)));
    146                         }
    147                         catch (NotFoundException e) {
    148                         }
    149                 }
    150         }
    151         catch (NullPointerException e) {
    152         }
    153 
    154         ExceptionsAttribute ea = thisMethod.getExceptionsAttribute();
    155         if (ea != null) {
    156             String[] exceptions = ea.getExceptions();
    157             if (exceptions != null) {
    158                 int n = exceptions.length;
    159                 for (int i = 0; i < n; ++i)
    160                     try {
    161                         addClass(list, pool.get(exceptions[i]));
    162                     }
    163                     catch (NotFoundException e) {
    164                     }
    165             }
    166         }
    167 
    168         return (CtClass[])list.toArray(new CtClass[list.size()]);
    169     }
    170 
    171     private static void addClass(LinkedList list, CtClass c) {
    172         Iterator it = list.iterator();
    173         while (it.hasNext())
    174             if (it.next() == c)
    175                 return;
    176 
    177         list.add(c);
    178     }
    179 
    180     /**
    181      * Returns the index of the bytecode corresponding to the expression. It is
    182      * the index into the byte array containing the Java bytecode that
    183      * implements the method.
    184      */
    185     public int indexOfBytecode() {
    186         return currentPos;
    187     }
    188 
    189     /**
    190      * Returns the line number of the source line containing the expression.
    191      *
    192      * @return -1 if this information is not available.
    193      */
    194     public int getLineNumber() {
    195         return thisMethod.getLineNumber(currentPos);
    196     }
    197 
    198     /**
    199      * Returns the source file containing the expression.
    200      *
    201      * @return null if this information is not available.
    202      */
    203     public String getFileName() {
    204         ClassFile cf = thisClass.getClassFile2();
    205         if (cf == null)
    206             return null;
    207         else
    208             return cf.getSourceFile();
    209     }
    210 
    211     static final boolean checkResultValue(CtClass retType, String prog)
    212             throws CannotCompileException {
    213         /*
    214          * Is $_ included in the source code?
    215          */
    216         boolean hasIt = (prog.indexOf(Javac.resultVarName) >= 0);
    217         if (!hasIt && retType != CtClass.voidType)
    218             throw new CannotCompileException(
    219                     "the resulting value is not stored in "
    220                             + Javac.resultVarName);
    221 
    222         return hasIt;
    223     }
    224 
    225     /*
    226      * If isStaticCall is true, null is assigned to $0. So $0 must be declared
    227      * by calling Javac.recordParams().
    228      *
    229      * After executing this method, the current stack depth might be less than
    230      * 0.
    231      */
    232     static final void storeStack(CtClass[] params, boolean isStaticCall,
    233             int regno, Bytecode bytecode) {
    234         storeStack0(0, params.length, params, regno + 1, bytecode);
    235         if (isStaticCall)
    236             bytecode.addOpcode(ACONST_NULL);
    237 
    238         bytecode.addAstore(regno);
    239     }
    240 
    241     private static void storeStack0(int i, int n, CtClass[] params, int regno,
    242             Bytecode bytecode) {
    243         if (i >= n)
    244             return;
    245         else {
    246             CtClass c = params[i];
    247             int size;
    248             if (c instanceof CtPrimitiveType)
    249                 size = ((CtPrimitiveType)c).getDataSize();
    250             else
    251                 size = 1;
    252 
    253             storeStack0(i + 1, n, params, regno + size, bytecode);
    254             bytecode.addStore(regno, c);
    255         }
    256     }
    257 
    258     // The implementation of replace() should call thisClass.checkModify()
    259     // so that isModify() will return true.  Otherwise, thisClass.classfile
    260     // might be released during compilation and the compiler might generate
    261     // bytecode with a wrong copy of ConstPool.
    262 
    263     /**
    264      * Replaces this expression with the bytecode derived from
    265      * the given source text.
    266      *
    267      * @param statement         a Java statement except try-catch.
    268      */
    269     public abstract void replace(String statement) throws CannotCompileException;
    270 
    271     /**
    272      * Replaces this expression with the bytecode derived from
    273      * the given source text and <code>ExprEditor</code>.
    274      *
    275      * @param statement         a Java statement except try-catch.
    276      * @param recursive         if not null, the substituted bytecode
    277      *                          is recursively processed by the given
    278      *                          <code>ExprEditor</code>.
    279      * @since 3.1
    280      */
    281     public void replace(String statement, ExprEditor recursive)
    282         throws CannotCompileException
    283     {
    284         replace(statement);
    285         if (recursive != null)
    286             runEditor(recursive, iterator);
    287     }
    288 
    289     protected void replace0(int pos, Bytecode bytecode, int size)
    290             throws BadBytecode {
    291         byte[] code = bytecode.get();
    292         edited = true;
    293         int gap = code.length - size;
    294         for (int i = 0; i < size; ++i)
    295             iterator.writeByte(NOP, pos + i);
    296 
    297         if (gap > 0)
    298             pos = iterator.insertGapAt(pos, gap, false).position;
    299 
    300         iterator.write(code, pos);
    301         iterator.insert(bytecode.getExceptionTable(), pos);
    302         maxLocals = bytecode.getMaxLocals();
    303         maxStack = bytecode.getMaxStack();
    304     }
    305 
    306     protected void runEditor(ExprEditor ed, CodeIterator oldIterator)
    307         throws CannotCompileException
    308     {
    309         CodeAttribute codeAttr = oldIterator.get();
    310         int orgLocals = codeAttr.getMaxLocals();
    311         int orgStack = codeAttr.getMaxStack();
    312         int newLocals = locals();
    313         codeAttr.setMaxStack(stack());
    314         codeAttr.setMaxLocals(newLocals);
    315         ExprEditor.LoopContext context
    316             = new ExprEditor.LoopContext(newLocals);
    317         int size = oldIterator.getCodeLength();
    318         int endPos = oldIterator.lookAhead();
    319         oldIterator.move(currentPos);
    320         if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos))
    321             edited = true;
    322 
    323         oldIterator.move(endPos + oldIterator.getCodeLength() - size);
    324         codeAttr.setMaxLocals(orgLocals);
    325         codeAttr.setMaxStack(orgStack);
    326         maxLocals = context.maxLocals;
    327         maxStack += context.maxStack;
    328     }
    329 }
    330