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.ByteArrayOutputStream;
     19 import java.io.DataInputStream;
     20 import java.io.IOException;
     21 import java.util.Map;
     22 
     23 import javassist.CannotCompileException;
     24 import javassist.bytecode.StackMapTable.InsertLocal;
     25 import javassist.bytecode.StackMapTable.NewRemover;
     26 import javassist.bytecode.StackMapTable.Shifter;
     27 
     28 /**
     29  * Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
     30  *
     31  * <p>This is an entry in the attributes table of a Code attribute.
     32  * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification.
     33  *
     34  * <p>According to the CLDC specification, the sizes of some fields are not 16bit
     35  * but 32bit if the code size is more than 64K or the number of the local variables
     36  * is more than 64K.  However, for the J2ME CLDC technology, they are always 16bit.
     37  * The implementation of the StackMap class assumes they are 16bit.
     38  *
     39  * @see MethodInfo#doPreverify
     40  * @see StackMapTable
     41  * @since 3.12
     42  */
     43 public class StackMap extends AttributeInfo {
     44     /**
     45      * The name of this attribute <code>"StackMap"</code>.
     46      */
     47     public static final String tag = "StackMap";
     48 
     49 
     50     /**
     51      * Constructs a <code>stack_map</code> attribute.
     52      */
     53     StackMap(ConstPool cp, byte[] newInfo) {
     54         super(cp, tag, newInfo);
     55     }
     56 
     57     StackMap(ConstPool cp, int name_id, DataInputStream in)
     58         throws IOException
     59     {
     60         super(cp, name_id, in);
     61     }
     62 
     63     /**
     64      * Returns <code>number_of_entries</code>.
     65      */
     66     public int numOfEntries() {
     67     	return ByteArray.readU16bit(info, 0);
     68     }
     69 
     70     /**
     71      * <code>Top_variable_info.tag</code>.
     72      */
     73     public static final int TOP = 0;
     74 
     75     /**
     76      * <code>Integer_variable_info.tag</code>.
     77      */
     78     public static final int INTEGER = 1;
     79 
     80     /**
     81      * <code>Float_variable_info.tag</code>.
     82      */
     83     public static final int FLOAT = 2;
     84 
     85     /**
     86      * <code>Double_variable_info.tag</code>.
     87      */
     88     public static final int DOUBLE = 3;
     89 
     90     /**
     91      * <code>Long_variable_info.tag</code>.
     92      */
     93     public static final int LONG = 4;
     94 
     95     /**
     96      * <code>Null_variable_info.tag</code>.
     97      */
     98     public static final int NULL = 5;
     99 
    100     /**
    101      * <code>UninitializedThis_variable_info.tag</code>.
    102      */
    103     public static final int THIS = 6;
    104 
    105     /**
    106      * <code>Object_variable_info.tag</code>.
    107      */
    108     public static final int OBJECT = 7;
    109 
    110     /**
    111      * <code>Uninitialized_variable_info.tag</code>.
    112      */
    113     public static final int UNINIT = 8;
    114 
    115     /**
    116      * Makes a copy.
    117      */
    118     public AttributeInfo copy(ConstPool newCp, Map classnames) {
    119         Copier copier = new Copier(this, newCp, classnames);
    120         copier.visit();
    121         return copier.getStackMap();
    122     }
    123 
    124     /**
    125      * A code walker for a StackMap attribute.
    126      */
    127     public static class Walker {
    128         byte[] info;
    129 
    130         /**
    131          * Constructs a walker.
    132          */
    133         public Walker(StackMap sm) {
    134             info = sm.get();
    135         }
    136 
    137         /**
    138          * Visits each entry of the stack map frames.
    139          */
    140         public void visit() {
    141             int num = ByteArray.readU16bit(info, 0);
    142             int pos = 2;
    143             for (int i = 0; i < num; i++) {
    144                 int offset = ByteArray.readU16bit(info, pos);
    145                 int numLoc = ByteArray.readU16bit(info, pos + 2);
    146                 pos = locals(pos + 4, offset, numLoc);
    147                 int numStack = ByteArray.readU16bit(info, pos);
    148                 pos = stack(pos + 2, offset, numStack);
    149             }
    150         }
    151 
    152         /**
    153          * Invoked when <code>locals</code> of <code>stack_map_frame</code>
    154          * is visited.
    155          */
    156         public int locals(int pos, int offset, int num) {
    157             return typeInfoArray(pos, offset, num, true);
    158         }
    159 
    160         /**
    161          * Invoked when <code>stack</code> of <code>stack_map_frame</code>
    162          * is visited.
    163          */
    164         public int stack(int pos, int offset, int num) {
    165             return typeInfoArray(pos, offset, num, false);
    166         }
    167 
    168         /**
    169          * Invoked when an array of <code>verification_type_info</code> is
    170          * visited.
    171          *
    172          * @param num       the number of elements.
    173          * @param isLocals  true if this array is for <code>locals</code>.
    174          *                  false if it is for <code>stack</code>.
    175          */
    176         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
    177             for (int k = 0; k < num; k++)
    178                 pos = typeInfoArray2(k, pos);
    179 
    180             return pos;
    181         }
    182 
    183         int typeInfoArray2(int k, int pos) {
    184             byte tag = info[pos];
    185             if (tag == OBJECT) {
    186                 int clazz = ByteArray.readU16bit(info, pos + 1);
    187                 objectVariable(pos, clazz);
    188                 pos += 3;
    189             }
    190             else if (tag == UNINIT) {
    191                 int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
    192                 uninitialized(pos, offsetOfNew);
    193                 pos += 3;
    194             }
    195             else {
    196                 typeInfo(pos, tag);
    197                 pos++;
    198             }
    199 
    200             return pos;
    201         }
    202 
    203         /**
    204          * Invoked when an element of <code>verification_type_info</code>
    205          * (except <code>Object_variable_info</code> and
    206          * <code>Uninitialized_variable_info</code>) is visited.
    207          */
    208         public void typeInfo(int pos, byte tag) {}
    209 
    210         /**
    211          * Invoked when an element of type <code>Object_variable_info</code>
    212          * is visited.
    213          */
    214         public void objectVariable(int pos, int clazz) {}
    215 
    216         /**
    217          * Invoked when an element of type <code>Uninitialized_variable_info</code>
    218          * is visited.
    219          */
    220         public void uninitialized(int pos, int offset) {}
    221     }
    222 
    223     static class Copier extends Walker {
    224         byte[] dest;
    225         ConstPool srcCp, destCp;
    226         Map classnames;
    227 
    228         Copier(StackMap map, ConstPool newCp, Map classnames) {
    229             super(map);
    230             srcCp = map.getConstPool();
    231             dest = new byte[info.length];
    232             destCp = newCp;
    233             this.classnames = classnames;
    234         }
    235 
    236         public void visit() {
    237             int num = ByteArray.readU16bit(info, 0);
    238             ByteArray.write16bit(num, dest, 0);
    239             super.visit();
    240         }
    241 
    242         public int locals(int pos, int offset, int num) {
    243             ByteArray.write16bit(offset, dest, pos - 4);
    244             return super.locals(pos, offset, num);
    245         }
    246 
    247         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
    248             ByteArray.write16bit(num, dest, pos - 2);
    249             return super.typeInfoArray(pos, offset, num, isLocals);
    250         }
    251 
    252         public void typeInfo(int pos, byte tag) {
    253             dest[pos] = tag;
    254         }
    255 
    256         public void objectVariable(int pos, int clazz) {
    257             dest[pos] = OBJECT;
    258             int newClazz = srcCp.copy(clazz, destCp, classnames);
    259             ByteArray.write16bit(newClazz, dest, pos + 1);
    260         }
    261 
    262         public void uninitialized(int pos, int offset) {
    263             dest[pos] = UNINIT;
    264             ByteArray.write16bit(offset, dest, pos + 1);
    265         }
    266 
    267         public StackMap getStackMap() {
    268             return new StackMap(destCp, dest);
    269         }
    270     }
    271 
    272     /**
    273      * Updates this stack map table when a new local variable is inserted
    274      * for a new parameter.
    275      *
    276      * @param index          the index of the added local variable.
    277      * @param tag            the type tag of that local variable.
    278      *                       It is available by <code>StackMapTable.typeTagOf(char)</code>.
    279      * @param classInfo      the index of the <code>CONSTANT_Class_info</code> structure
    280      *                       in a constant pool table.  This should be zero unless the tag
    281      *                       is <code>ITEM_Object</code>.
    282      *
    283      * @see javassist.CtBehavior#addParameter(javassist.CtClass)
    284      * @see StackMapTable#typeTagOf(char)
    285      * @see ConstPool
    286      */
    287     public void insertLocal(int index, int tag, int classInfo)
    288         throws BadBytecode
    289     {
    290         byte[] data = new InsertLocal(this, index, tag, classInfo).doit();
    291         this.set(data);
    292     }
    293 
    294     static class SimpleCopy extends Walker {
    295         Writer writer;
    296 
    297         SimpleCopy(StackMap map) {
    298             super(map);
    299             writer = new Writer();
    300         }
    301 
    302         byte[] doit() {
    303             visit();
    304             return writer.toByteArray();
    305         }
    306 
    307         public void visit() {
    308             int num = ByteArray.readU16bit(info, 0);
    309             writer.write16bit(num);
    310             super.visit();
    311         }
    312 
    313         public int locals(int pos, int offset, int num) {
    314             writer.write16bit(offset);
    315             return super.locals(pos, offset, num);
    316         }
    317 
    318         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
    319             writer.write16bit(num);
    320             return super.typeInfoArray(pos, offset, num, isLocals);
    321         }
    322 
    323         public void typeInfo(int pos, byte tag) {
    324             writer.writeVerifyTypeInfo(tag, 0);
    325         }
    326 
    327         public void objectVariable(int pos, int clazz) {
    328             writer.writeVerifyTypeInfo(OBJECT, clazz);
    329         }
    330 
    331         public void uninitialized(int pos, int offset) {
    332             writer.writeVerifyTypeInfo(UNINIT, offset);
    333         }
    334     }
    335 
    336     static class InsertLocal extends SimpleCopy {
    337         private int varIndex;
    338         private int varTag, varData;
    339 
    340         InsertLocal(StackMap map, int varIndex, int varTag, int varData) {
    341             super(map);
    342             this.varIndex = varIndex;
    343             this.varTag = varTag;
    344             this.varData = varData;
    345         }
    346 
    347         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
    348             if (!isLocals || num < varIndex)
    349                 return super.typeInfoArray(pos, offset, num, isLocals);
    350 
    351             writer.write16bit(num + 1);
    352             for (int k = 0; k < num; k++) {
    353                 if (k == varIndex)
    354                     writeVarTypeInfo();
    355 
    356                 pos = typeInfoArray2(k, pos);
    357             }
    358 
    359             if (num == varIndex)
    360                 writeVarTypeInfo();
    361 
    362             return pos;
    363         }
    364 
    365         private void writeVarTypeInfo() {
    366             if (varTag == OBJECT)
    367                 writer.writeVerifyTypeInfo(OBJECT, varData);
    368             else if (varTag == UNINIT)
    369                 writer.writeVerifyTypeInfo(UNINIT, varData);
    370             else
    371                 writer.writeVerifyTypeInfo(varTag, 0);
    372         }
    373     }
    374 
    375     void shiftPc(int where, int gapSize, boolean exclusive)
    376         throws BadBytecode
    377     {
    378         new Shifter(this, where, gapSize, exclusive).visit();
    379     }
    380 
    381     static class Shifter extends Walker {
    382         private int where, gap;
    383         private boolean exclusive;
    384 
    385         public Shifter(StackMap smt, int where, int gap, boolean exclusive) {
    386             super(smt);
    387             this.where = where;
    388             this.gap = gap;
    389             this.exclusive = exclusive;
    390         }
    391 
    392         public int locals(int pos, int offset, int num) {
    393             if (exclusive ? where <= offset : where < offset)
    394                 ByteArray.write16bit(offset + gap, info, pos - 4);
    395 
    396             return super.locals(pos, offset, num);
    397         }
    398     }
    399 
    400     /**
    401      * Undocumented method.  Do not use; internal-use only.
    402      *
    403      * <p>This method is for javassist.convert.TransformNew.
    404      * It is called to update the stack map when
    405      * the NEW opcode (and the following DUP) is removed.
    406      *
    407      * @param where     the position of the removed NEW opcode.
    408      */
    409      public void removeNew(int where) throws CannotCompileException {
    410          byte[] data = new NewRemover(this, where).doit();
    411          this.set(data);
    412     }
    413 
    414     static class NewRemover extends SimpleCopy {
    415         int posOfNew;
    416 
    417         NewRemover(StackMap map, int where) {
    418             super(map);
    419             posOfNew = where;
    420         }
    421 
    422         public int stack(int pos, int offset, int num) {
    423             return stackTypeInfoArray(pos, offset, num);
    424         }
    425 
    426         private int stackTypeInfoArray(int pos, int offset, int num) {
    427             int p = pos;
    428             int count = 0;
    429             for (int k = 0; k < num; k++) {
    430                 byte tag = info[p];
    431                 if (tag == OBJECT)
    432                     p += 3;
    433                 else if (tag == UNINIT) {
    434                     int offsetOfNew = ByteArray.readU16bit(info, p + 1);
    435                     if (offsetOfNew == posOfNew)
    436                         count++;
    437 
    438                     p += 3;
    439                 }
    440                 else
    441                     p++;
    442             }
    443 
    444             writer.write16bit(num - count);
    445             for (int k = 0; k < num; k++) {
    446                 byte tag = info[pos];
    447                 if (tag == OBJECT) {
    448                     int clazz = ByteArray.readU16bit(info, pos + 1);
    449                     objectVariable(pos, clazz);
    450                     pos += 3;
    451                 }
    452                 else if (tag == UNINIT) {
    453                     int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
    454                     if (offsetOfNew != posOfNew)
    455                         uninitialized(pos, offsetOfNew);
    456 
    457                     pos += 3;
    458                 }
    459                 else {
    460                     typeInfo(pos, tag);
    461                     pos++;
    462                 }
    463             }
    464 
    465             return pos;
    466         }
    467     }
    468 
    469     /**
    470      * Prints this stack map.
    471      */
    472     public void print(java.io.PrintWriter out) {
    473         new Printer(this, out).print();
    474     }
    475 
    476     static class Printer extends Walker {
    477         private java.io.PrintWriter writer;
    478 
    479         public Printer(StackMap map, java.io.PrintWriter out) {
    480             super(map);
    481             writer = out;
    482         }
    483 
    484         public void print() {
    485             int num = ByteArray.readU16bit(info, 0);
    486             writer.println(num + " entries");
    487             visit();
    488         }
    489 
    490         public int locals(int pos, int offset, int num) {
    491             writer.println("  * offset " + offset);
    492             return super.locals(pos, offset, num);
    493         }
    494     }
    495 
    496     /**
    497      * Internal use only.
    498      */
    499     public static class Writer {
    500         // see javassist.bytecode.stackmap.MapMaker
    501 
    502         private ByteArrayOutputStream output;
    503 
    504         /**
    505          * Constructs a writer.
    506          */
    507         public Writer() {
    508             output = new ByteArrayOutputStream();
    509         }
    510 
    511         /**
    512          * Converts the written data into a byte array.
    513          */
    514         public byte[] toByteArray() {
    515             return output.toByteArray();
    516         }
    517 
    518         /**
    519          * Converts to a <code>StackMap</code> attribute.
    520          */
    521         public StackMap toStackMap(ConstPool cp) {
    522             return new StackMap(cp, output.toByteArray());
    523         }
    524 
    525         /**
    526          * Writes a <code>union verification_type_info</code> value.
    527          *
    528          * @param data      <code>cpool_index</code> or <code>offset</code>.
    529          */
    530         public void writeVerifyTypeInfo(int tag, int data) {
    531             output.write(tag);
    532             if (tag == StackMap.OBJECT || tag == StackMap.UNINIT)
    533                 write16bit(data);
    534         }
    535 
    536         /**
    537          * Writes a 16bit value.
    538          */
    539         public void write16bit(int value) {
    540             output.write((value >>> 8) & 0xff);
    541             output.write(value & 0xff);
    542         }
    543     }
    544 }
    545