Home | History | Annotate | Download | only in bytecode
      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.bytecode;
     17 
     18 import java.io.DataInputStream;
     19 import java.io.DataOutputStream;
     20 import java.io.IOException;
     21 import java.util.List;
     22 import java.util.ArrayList;
     23 import java.util.Iterator;
     24 import java.util.Map;
     25 
     26 /**
     27  * <code>Code_attribute</code>.
     28  *
     29  * <p>To browse the <code>code</code> field of
     30  * a <code>Code_attribute</code> structure,
     31  * use <code>CodeIterator</code>.
     32  *
     33  * @see CodeIterator
     34  */
     35 public class CodeAttribute extends AttributeInfo implements Opcode {
     36     /**
     37      * The name of this attribute <code>"Code"</code>.
     38      */
     39     public static final String tag = "Code";
     40 
     41     // code[] is stored in AttributeInfo.info.
     42 
     43     private int maxStack;
     44     private int maxLocals;
     45     private ExceptionTable exceptions;
     46     private ArrayList attributes;
     47 
     48     /**
     49      * Constructs a <code>Code_attribute</code>.
     50      *
     51      * @param cp        constant pool table
     52      * @param stack     <code>max_stack</code>
     53      * @param locals    <code>max_locals</code>
     54      * @param code      <code>code[]</code>
     55      * @param etable    <code>exception_table[]</code>
     56      */
     57     public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code,
     58                          ExceptionTable etable)
     59     {
     60         super(cp, tag);
     61         maxStack = stack;
     62         maxLocals = locals;
     63         info = code;
     64         exceptions = etable;
     65         attributes = new ArrayList();
     66     }
     67 
     68     /**
     69      * Constructs a copy of <code>Code_attribute</code>.
     70      * Specified class names are replaced during the copy.
     71      *
     72      * @param cp                constant pool table.
     73      * @param src               source Code attribute.
     74      * @param classnames        pairs of replaced and substituted
     75      *                          class names.
     76      */
     77     private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames)
     78         throws BadBytecode
     79     {
     80         super(cp, tag);
     81 
     82         maxStack = src.getMaxStack();
     83         maxLocals = src.getMaxLocals();
     84         exceptions = src.getExceptionTable().copy(cp, classnames);
     85         attributes = new ArrayList();
     86         List src_attr = src.getAttributes();
     87         int num = src_attr.size();
     88         for (int i = 0; i < num; ++i) {
     89             AttributeInfo ai = (AttributeInfo)src_attr.get(i);
     90             attributes.add(ai.copy(cp, classnames));
     91         }
     92 
     93         info = src.copyCode(cp, classnames, exceptions, this);
     94     }
     95 
     96     CodeAttribute(ConstPool cp, int name_id, DataInputStream in)
     97         throws IOException
     98     {
     99         super(cp, name_id, (byte[])null);
    100         int attr_len = in.readInt();
    101 
    102         maxStack = in.readUnsignedShort();
    103         maxLocals = in.readUnsignedShort();
    104 
    105         int code_len = in.readInt();
    106         info = new byte[code_len];
    107         in.readFully(info);
    108 
    109         exceptions = new ExceptionTable(cp, in);
    110 
    111         attributes = new ArrayList();
    112         int num = in.readUnsignedShort();
    113         for (int i = 0; i < num; ++i)
    114             attributes.add(AttributeInfo.read(cp, in));
    115     }
    116 
    117     /**
    118      * Makes a copy.  Class names are replaced according to the
    119      * given <code>Map</code> object.
    120      *
    121      * @param newCp     the constant pool table used by the new copy.
    122      * @param classnames        pairs of replaced and substituted
    123      *                          class names.
    124      * @exception RuntimeCopyException  if a <code>BadBytecode</code>
    125      *                          exception is thrown, it is
    126      *                          converted into
    127      *                          <code>RuntimeCopyException</code>.
    128      *
    129      * @return <code>CodeAttribute</code> object.
    130      */
    131     public AttributeInfo copy(ConstPool newCp, Map classnames)
    132         throws RuntimeCopyException
    133     {
    134         try {
    135             return new CodeAttribute(newCp, this, classnames);
    136         }
    137         catch (BadBytecode e) {
    138             throw new RuntimeCopyException("bad bytecode. fatal?");
    139         }
    140     }
    141 
    142     /**
    143      * An exception that may be thrown by <code>copy()</code>
    144      * in <code>CodeAttribute</code>.
    145      */
    146     public static class RuntimeCopyException extends RuntimeException {
    147         /**
    148          * Constructs an exception.
    149          */
    150         public RuntimeCopyException(String s) {
    151             super(s);
    152         }
    153     }
    154 
    155     /**
    156      * Returns the length of this <code>attribute_info</code>
    157      * structure.
    158      * The returned value is <code>attribute_length + 6</code>.
    159      */
    160     public int length() {
    161         return 18 + info.length + exceptions.size() * 8
    162                + AttributeInfo.getLength(attributes);
    163     }
    164 
    165     void write(DataOutputStream out) throws IOException {
    166         out.writeShort(name);           // attribute_name_index
    167         out.writeInt(length() - 6);     // attribute_length
    168         out.writeShort(maxStack);       // max_stack
    169         out.writeShort(maxLocals);      // max_locals
    170         out.writeInt(info.length);      // code_length
    171         out.write(info);                // code
    172         exceptions.write(out);
    173         out.writeShort(attributes.size());      // attributes_count
    174         AttributeInfo.writeAll(attributes, out);        // attributes
    175     }
    176 
    177     /**
    178      * This method is not available.
    179      *
    180      * @throws java.lang.UnsupportedOperationException  always thrown.
    181      */
    182     public byte[] get() {
    183         throw new UnsupportedOperationException("CodeAttribute.get()");
    184     }
    185 
    186     /**
    187      * This method is not available.
    188      *
    189      * @throws java.lang.UnsupportedOperationException  always thrown.
    190      */
    191     public void set(byte[] newinfo) {
    192         throw new UnsupportedOperationException("CodeAttribute.set()");
    193     }
    194 
    195     void renameClass(String oldname, String newname) {
    196         AttributeInfo.renameClass(attributes, oldname, newname);
    197     }
    198 
    199     void renameClass(Map classnames) {
    200         AttributeInfo.renameClass(attributes, classnames);
    201     }
    202 
    203     void getRefClasses(Map classnames) {
    204         AttributeInfo.getRefClasses(attributes, classnames);
    205     }
    206 
    207     /**
    208      * Returns the name of the class declaring the method including
    209      * this code attribute.
    210      */
    211     public String getDeclaringClass() {
    212         ConstPool cp = getConstPool();
    213         return cp.getClassName();
    214     }
    215 
    216     /**
    217      * Returns <code>max_stack</code>.
    218      */
    219     public int getMaxStack() {
    220         return maxStack;
    221     }
    222 
    223     /**
    224      * Sets <code>max_stack</code>.
    225      */
    226     public void setMaxStack(int value) {
    227         maxStack = value;
    228     }
    229 
    230     /**
    231      * Computes the maximum stack size and sets <code>max_stack</code>
    232      * to the computed size.
    233      *
    234      * @throws BadBytecode      if this method fails in computing.
    235      * @return the newly computed value of <code>max_stack</code>
    236      */
    237     public int computeMaxStack() throws BadBytecode {
    238         maxStack = new CodeAnalyzer(this).computeMaxStack();
    239         return maxStack;
    240     }
    241 
    242     /**
    243      * Returns <code>max_locals</code>.
    244      */
    245     public int getMaxLocals() {
    246         return maxLocals;
    247     }
    248 
    249     /**
    250      * Sets <code>max_locals</code>.
    251      */
    252     public void setMaxLocals(int value) {
    253         maxLocals = value;
    254     }
    255 
    256     /**
    257      * Returns <code>code_length</code>.
    258      */
    259     public int getCodeLength() {
    260         return info.length;
    261     }
    262 
    263     /**
    264      * Returns <code>code[]</code>.
    265      */
    266     public byte[] getCode() {
    267         return info;
    268     }
    269 
    270     /**
    271      * Sets <code>code[]</code>.
    272      */
    273     void setCode(byte[] newinfo) { super.set(newinfo); }
    274 
    275     /**
    276      * Makes a new iterator for reading this code attribute.
    277      */
    278     public CodeIterator iterator() {
    279         return new CodeIterator(this);
    280     }
    281 
    282     /**
    283      * Returns <code>exception_table[]</code>.
    284      */
    285     public ExceptionTable getExceptionTable() { return exceptions; }
    286 
    287     /**
    288      * Returns <code>attributes[]</code>.
    289      * It returns a list of <code>AttributeInfo</code>.
    290      * A new element can be added to the returned list
    291      * and an existing element can be removed from the list.
    292      *
    293      * @see AttributeInfo
    294      */
    295     public List getAttributes() { return attributes; }
    296 
    297     /**
    298      * Returns the attribute with the specified name.
    299      * If it is not found, this method returns null.
    300      *
    301      * @param name      attribute name
    302      * @return          an <code>AttributeInfo</code> object or null.
    303      */
    304     public AttributeInfo getAttribute(String name) {
    305         return AttributeInfo.lookup(attributes, name);
    306     }
    307 
    308     /**
    309      * Adds a stack map table.  If another copy of stack map table
    310      * is already contained, the old one is removed.
    311      *
    312      * @param smt       the stack map table added to this code attribute.
    313      *                  If it is null, a new stack map is not added.
    314      *                  Only the old stack map is removed.
    315      */
    316     public void setAttribute(StackMapTable smt) {
    317         AttributeInfo.remove(attributes, StackMapTable.tag);
    318         if (smt != null)
    319             attributes.add(smt);
    320     }
    321 
    322     /**
    323      * Adds a stack map table for J2ME (CLDC).  If another copy of stack map table
    324      * is already contained, the old one is removed.
    325      *
    326      * @param sm        the stack map table added to this code attribute.
    327      *                  If it is null, a new stack map is not added.
    328      *                  Only the old stack map is removed.
    329      * @since 3.12
    330      */
    331     public void setAttribute(StackMap sm) {
    332         AttributeInfo.remove(attributes, StackMap.tag);
    333         if (sm != null)
    334             attributes.add(sm);
    335     }
    336 
    337     /**
    338      * Copies code.
    339      */
    340     private byte[] copyCode(ConstPool destCp, Map classnames,
    341                             ExceptionTable etable, CodeAttribute destCa)
    342         throws BadBytecode
    343     {
    344         int len = getCodeLength();
    345         byte[] newCode = new byte[len];
    346         destCa.info = newCode;
    347         LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(),
    348                                 newCode, destCp, classnames);
    349         return LdcEntry.doit(newCode, ldc, etable, destCa);
    350     }
    351 
    352     private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
    353                                      ConstPool srcCp, byte[] newcode,
    354                                      ConstPool destCp, Map classnameMap)
    355         throws BadBytecode
    356     {
    357         int i2, index;
    358         LdcEntry ldcEntry = null;
    359 
    360         for (int i = beginPos; i < endPos; i = i2) {
    361             i2 = CodeIterator.nextOpcode(code, i);
    362             byte c = code[i];
    363             newcode[i] = c;
    364             switch (c & 0xff) {
    365             case LDC_W :
    366             case LDC2_W :
    367             case GETSTATIC :
    368             case PUTSTATIC :
    369             case GETFIELD :
    370             case PUTFIELD :
    371             case INVOKEVIRTUAL :
    372             case INVOKESPECIAL :
    373             case INVOKESTATIC :
    374             case NEW :
    375             case ANEWARRAY :
    376             case CHECKCAST :
    377             case INSTANCEOF :
    378                 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
    379                                   classnameMap);
    380                 break;
    381             case LDC :
    382                 index = code[i + 1] & 0xff;
    383                 index = srcCp.copy(index, destCp, classnameMap);
    384                 if (index < 0x100)
    385                     newcode[i + 1] = (byte)index;
    386                 else {
    387                     newcode[i] = NOP;
    388                     newcode[i + 1] = NOP;
    389                     LdcEntry ldc = new LdcEntry();
    390                     ldc.where = i;
    391                     ldc.index = index;
    392                     ldc.next = ldcEntry;
    393                     ldcEntry = ldc;
    394                 }
    395                 break;
    396             case INVOKEINTERFACE :
    397                 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
    398                                   classnameMap);
    399                 newcode[i + 3] = code[i + 3];
    400                 newcode[i + 4] = code[i + 4];
    401                 break;
    402             case MULTIANEWARRAY :
    403                 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
    404                                   classnameMap);
    405                 newcode[i + 3] = code[i + 3];
    406                 break;
    407             default :
    408                 while (++i < i2)
    409                     newcode[i] = code[i];
    410 
    411                 break;
    412             }
    413         }
    414 
    415         return ldcEntry;
    416     }
    417 
    418     private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
    419                                           byte[] newcode, ConstPool destCp,
    420                                           Map classnameMap) {
    421         int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff);
    422         index = srcCp.copy(index, destCp, classnameMap);
    423         newcode[i] = (byte)(index >> 8);
    424         newcode[i + 1] = (byte)index;
    425     }
    426 
    427     static class LdcEntry {
    428         LdcEntry next;
    429         int where;
    430         int index;
    431 
    432         static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable,
    433                            CodeAttribute ca)
    434             throws BadBytecode
    435         {
    436             if (ldc != null)
    437                 code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc);
    438 
    439             /* The original code was the following:
    440 
    441                while (ldc != null) {
    442                  int where = ldc.where;
    443                  code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca);
    444                  code[where] = (byte)Opcode.LDC_W;
    445                  ByteArray.write16bit(ldc.index, code, where + 1);
    446                  ldc = ldc.next;
    447                }
    448 
    449                But this code does not support a large method > 32KB.
    450             */
    451 
    452             return code;
    453         }
    454     }
    455 
    456     /**
    457      * Changes the index numbers of the local variables
    458      * to append a new parameter.
    459      * This method does not update <code>LocalVariableAttribute</code>,
    460      * <code>StackMapTable</code>, or <code>StackMap</code>.
    461      * These attributes must be explicitly updated.
    462      *
    463      * @param where         the index of the new parameter.
    464      * @param size         the type size of the new parameter (1 or 2).
    465      *
    466      * @see LocalVariableAttribute#shiftIndex(int, int)
    467      * @see StackMapTable#insertLocal(int, int, int)
    468      * @see StackMap#insertLocal(int, int, int)
    469      */
    470     public void insertLocalVar(int where, int size) throws BadBytecode {
    471         CodeIterator ci = iterator();
    472         while (ci.hasNext())
    473             shiftIndex(ci, where, size);
    474 
    475         setMaxLocals(getMaxLocals() + size);
    476     }
    477 
    478     /**
    479      * @param lessThan      If the index of the local variable is
    480      *                      less than this value, it does not change.
    481      *                      Otherwise, the index is increased.
    482      * @param delta         the indexes of the local variables are
    483      *                      increased by this value.
    484      */
    485     private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode {
    486         int index = ci.next();
    487         int opcode = ci.byteAt(index);
    488         if (opcode < ILOAD)
    489             return;
    490         else if (opcode < IASTORE) {
    491             if (opcode < ILOAD_0) {
    492                 // iload, lload, fload, dload, aload
    493                 shiftIndex8(ci, index, opcode, lessThan, delta);
    494             }
    495             else if (opcode < IALOAD) {
    496                 // iload_0, ..., aload_3
    497                 shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD);
    498             }
    499             else if (opcode < ISTORE)
    500                 return;
    501             else if (opcode < ISTORE_0) {
    502                 // istore, lstore, ...
    503                 shiftIndex8(ci, index, opcode, lessThan, delta);
    504             }
    505             else {
    506                 // istore_0, ..., astore_3
    507                 shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE);
    508             }
    509         }
    510         else if (opcode == IINC) {
    511             int var = ci.byteAt(index + 1);
    512             if (var < lessThan)
    513                 return;
    514 
    515             var += delta;
    516             if (var < 0x100)
    517                 ci.writeByte(var, index + 1);
    518             else {
    519                 int plus = (byte)ci.byteAt(index + 2);
    520                 int pos = ci.insertExGap(3);
    521                 ci.writeByte(WIDE, pos - 3);
    522                 ci.writeByte(IINC, pos - 2);
    523                 ci.write16bit(var, pos - 1);
    524                 ci.write16bit(plus, pos + 1);
    525             }
    526         }
    527         else if (opcode == RET)
    528             shiftIndex8(ci, index, opcode, lessThan, delta);
    529         else if (opcode == WIDE) {
    530             int var = ci.u16bitAt(index + 2);
    531             if (var < lessThan)
    532                 return;
    533 
    534             var += delta;
    535             ci.write16bit(var, index + 2);
    536         }
    537     }
    538 
    539     private static void shiftIndex8(CodeIterator ci, int index, int opcode,
    540                                     int lessThan, int delta)
    541          throws BadBytecode
    542     {
    543         int var = ci.byteAt(index + 1);
    544         if (var < lessThan)
    545             return;
    546 
    547         var += delta;
    548         if (var < 0x100)
    549             ci.writeByte(var, index + 1);
    550         else {
    551             int pos = ci.insertExGap(2);
    552             ci.writeByte(WIDE, pos - 2);
    553             ci.writeByte(opcode, pos - 1);
    554             ci.write16bit(var, pos);
    555         }
    556     }
    557 
    558     private static void shiftIndex0(CodeIterator ci, int index, int opcode,
    559                                     int lessThan, int delta,
    560                                     int opcode_i_0, int opcode_i)
    561         throws BadBytecode
    562     {
    563         int var = (opcode - opcode_i_0) % 4;
    564         if (var < lessThan)
    565             return;
    566 
    567         var += delta;
    568         if (var < 4)
    569             ci.writeByte(opcode + delta, index);
    570         else {
    571             opcode = (opcode - opcode_i_0) / 4 + opcode_i;
    572             if (var < 0x100) {
    573                 int pos = ci.insertExGap(1);
    574                 ci.writeByte(opcode, pos - 1);
    575                 ci.writeByte(var, pos);
    576             }
    577             else {
    578                 int pos = ci.insertExGap(3);
    579                 ci.writeByte(WIDE, pos - 1);
    580                 ci.writeByte(opcode, pos);
    581                 ci.write16bit(var, pos + 1);
    582             }
    583         }
    584     }
    585 }
    586