Home | History | Annotate | Download | only in convert
      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 package javassist.convert;
     16 
     17 import javassist.CannotCompileException;
     18 import javassist.ClassPool;
     19 import javassist.CtClass;
     20 import javassist.NotFoundException;
     21 import javassist.CodeConverter.ArrayAccessReplacementMethodNames;
     22 import javassist.bytecode.BadBytecode;
     23 import javassist.bytecode.CodeIterator;
     24 import javassist.bytecode.ConstPool;
     25 import javassist.bytecode.Descriptor;
     26 import javassist.bytecode.MethodInfo;
     27 import javassist.bytecode.analysis.Analyzer;
     28 import javassist.bytecode.analysis.Frame;
     29 
     30 /**
     31  * A transformer which replaces array access with static method invocations.
     32  *
     33  * @author <a href="kabir.khan (at) jboss.com">Kabir Khan</a>
     34  * @author Jason T. Greene
     35  * @version $Revision: 1.8 $
     36  */
     37 public final class TransformAccessArrayField extends Transformer {
     38     private final String methodClassname;
     39     private final ArrayAccessReplacementMethodNames names;
     40     private Frame[] frames;
     41     private int offset;
     42 
     43     public TransformAccessArrayField(Transformer next, String methodClassname,
     44             ArrayAccessReplacementMethodNames names) throws NotFoundException {
     45         super(next);
     46         this.methodClassname = methodClassname;
     47         this.names = names;
     48 
     49     }
     50 
     51     public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException {
     52         /*
     53          * This transformer must be isolated from other transformers, since some
     54          * of them affect the local variable and stack maximums without updating
     55          * the code attribute to reflect the changes. This screws up the
     56          * data-flow analyzer, since it relies on consistent code state. Even
     57          * if the attribute values were updated correctly, we would have to
     58          * detect it, and redo analysis, which is not cheap. Instead, we are
     59          * better off doing all changes in initialize() before everyone else has
     60          * a chance to muck things up.
     61          */
     62         CodeIterator iterator = minfo.getCodeAttribute().iterator();
     63         while (iterator.hasNext()) {
     64             try {
     65                 int pos = iterator.next();
     66                 int c = iterator.byteAt(pos);
     67 
     68                 if (c == AALOAD)
     69                     initFrames(clazz, minfo);
     70 
     71                 if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD
     72                         || c == FALOAD || c == IALOAD || c == LALOAD
     73                         || c == SALOAD) {
     74                     pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c));
     75                 } else if (c == AASTORE || c == BASTORE || c == CASTORE
     76                         || c == DASTORE || c == FASTORE || c == IASTORE
     77                         || c == LASTORE || c == SASTORE) {
     78                     pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c));
     79                 }
     80 
     81             } catch (Exception e) {
     82                 throw new CannotCompileException(e);
     83             }
     84         }
     85     }
     86 
     87     public void clean() {
     88         frames = null;
     89         offset = -1;
     90     }
     91 
     92     public int transform(CtClass tclazz, int pos, CodeIterator iterator,
     93             ConstPool cp) throws BadBytecode {
     94         // Do nothing, see above comment
     95         return pos;
     96     }
     97 
     98     private Frame getFrame(int pos) throws BadBytecode {
     99         return frames[pos - offset]; // Adjust pos
    100     }
    101 
    102     private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode {
    103         if (frames == null) {
    104             frames = ((new Analyzer())).analyze(clazz, minfo);
    105             offset = 0; // start tracking changes
    106         }
    107     }
    108 
    109     private int updatePos(int pos, int increment) {
    110         if (offset > -1)
    111             offset += increment;
    112 
    113         return pos + increment;
    114     }
    115 
    116     private String getTopType(int pos) throws BadBytecode {
    117         Frame frame = getFrame(pos);
    118         if (frame == null)
    119             return null;
    120 
    121         CtClass clazz = frame.peek().getCtClass();
    122         return clazz != null ? Descriptor.toJvmName(clazz) : null;
    123     }
    124 
    125     private int replace(ConstPool cp, CodeIterator iterator, int pos,
    126             int opcode, String signature) throws BadBytecode {
    127         String castType = null;
    128         String methodName = getMethodName(opcode);
    129         if (methodName != null) {
    130             // See if the object must be cast
    131             if (opcode == AALOAD) {
    132                 castType = getTopType(iterator.lookAhead());
    133                 // Do not replace an AALOAD instruction that we do not have a type for
    134                 // This happens when the state is guaranteed to be null (Type.UNINIT)
    135                 // So we don't really care about this case.
    136                 if (castType == null)
    137                     return pos;
    138                 if ("java/lang/Object".equals(castType))
    139                     castType = null;
    140             }
    141 
    142             // The gap may include extra padding
    143             // Write a nop in case the padding pushes the instruction forward
    144             iterator.writeByte(NOP, pos);
    145             CodeIterator.Gap gap
    146                 = iterator.insertGapAt(pos, castType != null ? 5 : 2, false);
    147             pos = gap.position;
    148             int mi = cp.addClassInfo(methodClassname);
    149             int methodref = cp.addMethodrefInfo(mi, methodName, signature);
    150             iterator.writeByte(INVOKESTATIC, pos);
    151             iterator.write16bit(methodref, pos + 1);
    152 
    153             if (castType != null) {
    154                 int index = cp.addClassInfo(castType);
    155                 iterator.writeByte(CHECKCAST, pos + 3);
    156                 iterator.write16bit(index, pos + 4);
    157             }
    158 
    159             pos = updatePos(pos, gap.length);
    160         }
    161 
    162         return pos;
    163     }
    164 
    165     private String getMethodName(int opcode) {
    166         String methodName = null;
    167         switch (opcode) {
    168         case AALOAD:
    169             methodName = names.objectRead();
    170             break;
    171         case BALOAD:
    172             methodName = names.byteOrBooleanRead();
    173             break;
    174         case CALOAD:
    175             methodName = names.charRead();
    176             break;
    177         case DALOAD:
    178             methodName = names.doubleRead();
    179             break;
    180         case FALOAD:
    181             methodName = names.floatRead();
    182             break;
    183         case IALOAD:
    184             methodName = names.intRead();
    185             break;
    186         case SALOAD:
    187             methodName = names.shortRead();
    188             break;
    189         case LALOAD:
    190             methodName = names.longRead();
    191             break;
    192         case AASTORE:
    193             methodName = names.objectWrite();
    194             break;
    195         case BASTORE:
    196             methodName = names.byteOrBooleanWrite();
    197             break;
    198         case CASTORE:
    199             methodName = names.charWrite();
    200             break;
    201         case DASTORE:
    202             methodName = names.doubleWrite();
    203             break;
    204         case FASTORE:
    205             methodName = names.floatWrite();
    206             break;
    207         case IASTORE:
    208             methodName = names.intWrite();
    209             break;
    210         case SASTORE:
    211             methodName = names.shortWrite();
    212             break;
    213         case LASTORE:
    214             methodName = names.longWrite();
    215             break;
    216         }
    217 
    218         if (methodName.equals(""))
    219             methodName = null;
    220 
    221         return methodName;
    222     }
    223 
    224     private String getLoadReplacementSignature(int opcode) throws BadBytecode {
    225         switch (opcode) {
    226         case AALOAD:
    227             return "(Ljava/lang/Object;I)Ljava/lang/Object;";
    228         case BALOAD:
    229             return "(Ljava/lang/Object;I)B";
    230         case CALOAD:
    231             return "(Ljava/lang/Object;I)C";
    232         case DALOAD:
    233             return "(Ljava/lang/Object;I)D";
    234         case FALOAD:
    235             return "(Ljava/lang/Object;I)F";
    236         case IALOAD:
    237             return "(Ljava/lang/Object;I)I";
    238         case SALOAD:
    239             return "(Ljava/lang/Object;I)S";
    240         case LALOAD:
    241             return "(Ljava/lang/Object;I)J";
    242         }
    243 
    244         throw new BadBytecode(opcode);
    245     }
    246 
    247     private String getStoreReplacementSignature(int opcode) throws BadBytecode {
    248         switch (opcode) {
    249         case AASTORE:
    250             return "(Ljava/lang/Object;ILjava/lang/Object;)V";
    251         case BASTORE:
    252             return "(Ljava/lang/Object;IB)V";
    253         case CASTORE:
    254             return "(Ljava/lang/Object;IC)V";
    255         case DASTORE:
    256             return "(Ljava/lang/Object;ID)V";
    257         case FASTORE:
    258             return "(Ljava/lang/Object;IF)V";
    259         case IASTORE:
    260             return "(Ljava/lang/Object;II)V";
    261         case SASTORE:
    262             return "(Ljava/lang/Object;IS)V";
    263         case LASTORE:
    264             return "(Ljava/lang/Object;IJ)V";
    265         }
    266 
    267         throw new BadBytecode(opcode);
    268     }
    269 }
    270