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.ByteArrayOutputStream;
     21 import java.io.PrintWriter;
     22 import java.io.IOException;
     23 import java.util.Map;
     24 import javassist.CannotCompileException;
     25 
     26 /**
     27  * <code>stack_map</code> attribute.
     28  *
     29  * <p>This is an entry in the attributes table of a Code attribute.
     30  * It was introduced by J2SE 6 for the process of verification by
     31  * typechecking.
     32  *
     33  * @see StackMap
     34  * @since 3.4
     35  */
     36 public class StackMapTable extends AttributeInfo {
     37     /**
     38      * The name of this attribute <code>"StackMapTable"</code>.
     39      */
     40     public static final String tag = "StackMapTable";
     41 
     42     /**
     43      * Constructs a <code>stack_map</code> attribute.
     44      */
     45     StackMapTable(ConstPool cp, byte[] newInfo) {
     46         super(cp, tag, newInfo);
     47     }
     48 
     49     StackMapTable(ConstPool cp, int name_id, DataInputStream in)
     50         throws IOException
     51     {
     52         super(cp, name_id, in);
     53     }
     54 
     55     /**
     56      * Makes a copy.
     57      *
     58      * @exception RuntimeCopyException  if a <code>BadBytecode</code>
     59      *                          exception is thrown while copying,
     60      *                          it is converted into
     61      *                          <code>RuntimeCopyException</code>.
     62      *
     63      */
     64     public AttributeInfo copy(ConstPool newCp, Map classnames)
     65         throws RuntimeCopyException
     66     {
     67         try {
     68             return new StackMapTable(newCp,
     69                             new Copier(this.constPool, info, newCp).doit());
     70         }
     71         catch (BadBytecode e) {
     72             throw new RuntimeCopyException("bad bytecode. fatal?");
     73         }
     74     }
     75 
     76     /**
     77      * An exception that may be thrown by <code>copy()</code>
     78      * in <code>StackMapTable</code>.
     79      */
     80     public static class RuntimeCopyException extends RuntimeException {
     81         /**
     82          * Constructs an exception.
     83          */
     84         public RuntimeCopyException(String s) {
     85             super(s);
     86         }
     87     }
     88 
     89     void write(DataOutputStream out) throws IOException {
     90         super.write(out);
     91     }
     92 
     93     /**
     94      * <code>Top_variable_info.tag</code>.
     95      */
     96     public static final int TOP = 0;
     97 
     98     /**
     99      * <code>Integer_variable_info.tag</code>.
    100      */
    101     public static final int INTEGER = 1;
    102 
    103     /**
    104      * <code>Float_variable_info.tag</code>.
    105      */
    106     public static final int FLOAT = 2;
    107 
    108     /**
    109      * <code>Double_variable_info.tag</code>.
    110      */
    111     public static final int DOUBLE = 3;
    112 
    113     /**
    114      * <code>Long_variable_info.tag</code>.
    115      */
    116     public static final int LONG = 4;
    117 
    118     /**
    119      * <code>Null_variable_info.tag</code>.
    120      */
    121     public static final int NULL = 5;
    122 
    123     /**
    124      * <code>UninitializedThis_variable_info.tag</code>.
    125      */
    126     public static final int THIS = 6;
    127 
    128     /**
    129      * <code>Object_variable_info.tag</code>.
    130      */
    131     public static final int OBJECT = 7;
    132 
    133     /**
    134      * <code>Uninitialized_variable_info.tag</code>.
    135      */
    136     public static final int UNINIT = 8;
    137 
    138     /**
    139      * A code walker for a StackMapTable attribute.
    140      */
    141     public static class Walker {
    142         byte[] info;
    143         int numOfEntries;
    144 
    145         /**
    146          * Constructs a walker.
    147          *
    148          * @param smt       the StackMapTable that this walker
    149          *                  walks around.
    150          */
    151         public Walker(StackMapTable smt) {
    152             this(smt.get());
    153         }
    154 
    155         /**
    156          * Constructs a walker.
    157          *
    158          * @param data      the <code>info</code> field of the
    159          *                  <code>attribute_info</code> structure.
    160          *                  It can be obtained by <code>get()</code>
    161          *                  in the <code>AttributeInfo</code> class.
    162          */
    163         public Walker(byte[] data) {
    164             info = data;
    165             numOfEntries = ByteArray.readU16bit(data, 0);
    166         }
    167 
    168         /**
    169          * Returns the number of the entries.
    170          */
    171         public final int size() { return numOfEntries; }
    172 
    173         /**
    174          * Visits each entry of the stack map frames.
    175          */
    176         public void parse() throws BadBytecode {
    177             int n = numOfEntries;
    178             int pos = 2;
    179             for (int i = 0; i < n; i++)
    180                 pos = stackMapFrames(pos, i);
    181         }
    182 
    183         /**
    184          * Invoked when the next entry of the stack map frames is visited.
    185          *
    186          * @param pos       the position of the frame in the <code>info</code>
    187          *                  field of <code>attribute_info</code> structure.
    188          * @param nth       the frame is the N-th
    189          *                  (0, 1st, 2nd, 3rd, 4th, ...) entry.
    190          * @return          the position of the next frame.
    191          */
    192         int stackMapFrames(int pos, int nth) throws BadBytecode {
    193             int type = info[pos] & 0xff;
    194             if (type < 64) {
    195                 sameFrame(pos, type);
    196                 pos++;
    197             }
    198             else if (type < 128)
    199                 pos = sameLocals(pos, type);
    200             else if (type < 247)
    201                 throw new BadBytecode("bad frame_type in StackMapTable");
    202             else if (type == 247)   // SAME_LOCALS_1_STACK_ITEM_EXTENDED
    203                 pos = sameLocals(pos, type);
    204             else if (type < 251) {
    205                 int offset = ByteArray.readU16bit(info, pos + 1);
    206                 chopFrame(pos, offset, 251 - type);
    207                 pos += 3;
    208             }
    209             else if (type == 251) { // SAME_FRAME_EXTENDED
    210                 int offset = ByteArray.readU16bit(info, pos + 1);
    211                 sameFrame(pos, offset);
    212                 pos += 3;
    213             }
    214             else if (type < 255)
    215                 pos = appendFrame(pos, type);
    216             else    // FULL_FRAME
    217                 pos = fullFrame(pos);
    218 
    219             return pos;
    220         }
    221 
    222         /**
    223          * Invoked if the visited frame is a <code>same_frame</code> or
    224          * a <code>same_frame_extended</code>.
    225          *
    226          * @param pos       the position of this frame in the <code>info</code>
    227          *                  field of <code>attribute_info</code> structure.
    228          * @param offsetDelta
    229          */
    230         public void sameFrame(int pos, int offsetDelta) throws BadBytecode {}
    231 
    232         private int sameLocals(int pos, int type) throws BadBytecode {
    233             int top = pos;
    234             int offset;
    235             if (type < 128)
    236                 offset = type - 64;
    237             else { // type == 247
    238                 offset = ByteArray.readU16bit(info, pos + 1);
    239                 pos += 2;
    240             }
    241 
    242             int tag = info[pos + 1] & 0xff;
    243             int data = 0;
    244             if (tag == OBJECT || tag == UNINIT) {
    245                 data = ByteArray.readU16bit(info, pos + 2);
    246                 pos += 2;
    247             }
    248 
    249             sameLocals(top, offset, tag, data);
    250             return pos + 2;
    251         }
    252 
    253         /**
    254          * Invoked if the visited frame is a <code>same_locals_1_stack_item_frame</code>
    255          * or a <code>same_locals_1_stack_item_frame_extended</code>.
    256          *
    257          * @param pos               the position.
    258          * @param offsetDelta
    259          * @param stackTag          <code>stack[0].tag</code>.
    260          * @param stackData         <code>stack[0].cpool_index</code>
    261          *                          if the tag is <code>OBJECT</code>,
    262          *                          or <code>stack[0].offset</code>
    263          *                          if the tag is <code>UNINIT</code>.
    264          */
    265         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData)
    266             throws BadBytecode {}
    267 
    268         /**
    269          * Invoked if the visited frame is a <code>chop_frame</code>.
    270          *
    271          * @param pos               the position.
    272          * @param offsetDelta
    273          * @param k                 the <cod>k</code> last locals are absent.
    274          */
    275         public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {}
    276 
    277         private int appendFrame(int pos, int type) throws BadBytecode {
    278             int k = type - 251;
    279             int offset = ByteArray.readU16bit(info, pos + 1);
    280             int[] tags = new int[k];
    281             int[] data = new int[k];
    282             int p = pos + 3;
    283             for (int i = 0; i < k; i++) {
    284                 int tag = info[p] & 0xff;
    285                 tags[i] = tag;
    286                 if (tag == OBJECT || tag == UNINIT) {
    287                     data[i] = ByteArray.readU16bit(info, p + 1);
    288                     p += 3;
    289                 }
    290                 else {
    291                     data[i] = 0;
    292                     p++;
    293                 }
    294             }
    295 
    296             appendFrame(pos, offset, tags, data);
    297             return p;
    298         }
    299 
    300         /**
    301          * Invoked if the visited frame is a <code>append_frame</code>.
    302          *
    303          * @param pos           the position.
    304          * @param offsetDelta
    305          * @param tags          <code>locals[i].tag</code>.
    306          * @param data          <code>locals[i].cpool_index</code>
    307          *                      or <cod>locals[i].offset</code>.
    308          */
    309         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data)
    310             throws BadBytecode {}
    311 
    312         private int fullFrame(int pos) throws BadBytecode {
    313             int offset = ByteArray.readU16bit(info, pos + 1);
    314             int numOfLocals = ByteArray.readU16bit(info, pos + 3);
    315             int[] localsTags = new int[numOfLocals];
    316             int[] localsData = new int[numOfLocals];
    317             int p = verifyTypeInfo(pos + 5, numOfLocals, localsTags, localsData);
    318             int numOfItems = ByteArray.readU16bit(info, p);
    319             int[] itemsTags = new int[numOfItems];
    320             int[] itemsData = new int[numOfItems];
    321             p = verifyTypeInfo(p + 2, numOfItems, itemsTags, itemsData);
    322             fullFrame(pos, offset, localsTags, localsData, itemsTags, itemsData);
    323             return p;
    324         }
    325 
    326         /**
    327          * Invoked if the visited frame is <code>full_frame</code>.
    328          *
    329          * @param pos               the position.
    330          * @param offsetDelta
    331          * @param localTags         <code>locals[i].tag</code>
    332          * @param localData         <code>locals[i].cpool_index</code>
    333          *                          or <code>locals[i].offset</code>
    334          * @param stackTags         <code>stack[i].tag</code>
    335          * @param stackData         <code>stack[i].cpool_index</code>
    336          *                          or <code>stack[i].offset</code>
    337          */
    338         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    339                               int[] stackTags, int[] stackData)
    340             throws BadBytecode {}
    341 
    342         private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) {
    343             for (int i = 0; i < n; i++) {
    344                 int tag = info[pos++] & 0xff;
    345                 tags[i] = tag;
    346                 if (tag == OBJECT || tag == UNINIT) {
    347                     data[i] = ByteArray.readU16bit(info, pos);
    348                     pos += 2;
    349                 }
    350             }
    351 
    352             return pos;
    353         }
    354     }
    355 
    356     static class SimpleCopy extends Walker {
    357         private Writer writer;
    358 
    359         public SimpleCopy(byte[] data) {
    360             super(data);
    361             writer = new Writer(data.length);
    362         }
    363 
    364         public byte[] doit() throws BadBytecode {
    365             parse();
    366             return writer.toByteArray();
    367         }
    368 
    369         public void sameFrame(int pos, int offsetDelta) {
    370             writer.sameFrame(offsetDelta);
    371         }
    372 
    373         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
    374             writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData));
    375         }
    376 
    377         public void chopFrame(int pos, int offsetDelta, int k) {
    378             writer.chopFrame(offsetDelta, k);
    379         }
    380 
    381         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
    382             writer.appendFrame(offsetDelta, tags, copyData(tags, data));
    383         }
    384 
    385         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    386                               int[] stackTags, int[] stackData) {
    387             writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData),
    388                              stackTags, copyData(stackTags, stackData));
    389         }
    390 
    391         protected int copyData(int tag, int data) {
    392             return data;
    393         }
    394 
    395         protected int[] copyData(int[] tags, int[] data) {
    396             return data;
    397         }
    398     }
    399 
    400     static class Copier extends SimpleCopy {
    401         private ConstPool srcPool, destPool;
    402 
    403         public Copier(ConstPool src, byte[] data, ConstPool dest) {
    404             super(data);
    405             srcPool = src;
    406             destPool = dest;
    407         }
    408 
    409         protected int copyData(int tag, int data) {
    410             if (tag == OBJECT)
    411                 return srcPool.copy(data, destPool, null);
    412             else
    413                 return data;
    414         }
    415 
    416         protected int[] copyData(int[] tags, int[] data) {
    417             int[] newData = new int[data.length];
    418             for (int i = 0; i < data.length; i++)
    419                 if (tags[i] == OBJECT)
    420                     newData[i] = srcPool.copy(data[i], destPool, null);
    421                 else
    422                     newData[i] = data[i];
    423 
    424             return newData;
    425         }
    426     }
    427 
    428     /**
    429      * Updates this stack map table when a new local variable is inserted
    430      * for a new parameter.
    431      *
    432      * @param index          the index of the added local variable.
    433      * @param tag            the type tag of that local variable.
    434      * @param classInfo      the index of the <code>CONSTANT_Class_info</code> structure
    435      *                       in a constant pool table.  This should be zero unless the tag
    436      *                       is <code>ITEM_Object</code>.
    437      *
    438      * @see javassist.CtBehavior#addParameter(javassist.CtClass)
    439      * @see #typeTagOf(char)
    440      * @see ConstPool
    441      */
    442     public void insertLocal(int index, int tag, int classInfo)
    443         throws BadBytecode
    444     {
    445         byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit();
    446         this.set(data);
    447     }
    448 
    449     /**
    450      * Returns the tag of the type specified by the
    451      * descriptor.  This method returns <code>INTEGER</code>
    452      * unless the descriptor is either D (double), F (float),
    453      * J (long), L (class type), or [ (array).
    454      *
    455      * @param descriptor        the type descriptor.
    456      * @see Descriptor
    457      */
    458     public static int typeTagOf(char descriptor) {
    459         switch (descriptor) {
    460         case 'D' :
    461             return DOUBLE;
    462         case 'F' :
    463             return FLOAT;
    464         case 'J' :
    465             return LONG;
    466         case 'L' :
    467         case '[' :
    468             return OBJECT;
    469         // case 'V' :
    470         default :
    471             return INTEGER;
    472         }
    473     }
    474 
    475     /* This implementation assumes that a local variable initially
    476      * holding a parameter value is never changed to be a different
    477      * type.
    478      *
    479      */
    480     static class InsertLocal extends SimpleCopy {
    481         private int varIndex;
    482         private int varTag, varData;
    483 
    484         public InsertLocal(byte[] data, int varIndex, int varTag, int varData) {
    485             super(data);
    486             this.varIndex = varIndex;
    487             this.varTag = varTag;
    488             this.varData = varData;
    489         }
    490 
    491         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    492                               int[] stackTags, int[] stackData) {
    493             int len = localTags.length;
    494             if (len < varIndex) {
    495                 super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData);
    496                 return;
    497             }
    498 
    499             int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1;
    500             int[] localTags2 = new int[len + typeSize];
    501             int[] localData2 = new int[len + typeSize];
    502             int index = varIndex;
    503             int j = 0;
    504             for (int i = 0; i < len; i++) {
    505                 if (j == index)
    506                     j += typeSize;
    507 
    508                 localTags2[j] = localTags[i];
    509                 localData2[j++] = localData[i];
    510             }
    511 
    512             localTags2[index] = varTag;
    513             localData2[index] = varData;
    514             if (typeSize > 1) {
    515                 localTags2[index + 1] = TOP;
    516                 localData2[index + 1] = 0;
    517             }
    518 
    519             super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData);
    520         }
    521     }
    522 
    523     /**
    524      * A writer of stack map tables.
    525      */
    526     public static class Writer {
    527         ByteArrayOutputStream output;
    528         int numOfEntries;
    529 
    530         /**
    531          * Constructs a writer.
    532          * @param size      the initial buffer size.
    533          */
    534         public Writer(int size) {
    535             output = new ByteArrayOutputStream(size);
    536             numOfEntries = 0;
    537             output.write(0);        // u2 number_of_entries
    538             output.write(0);
    539         }
    540 
    541         /**
    542          * Returns the stack map table written out.
    543          */
    544         public byte[] toByteArray() {
    545             byte[] b = output.toByteArray();
    546             ByteArray.write16bit(numOfEntries, b, 0);
    547             return b;
    548         }
    549 
    550         /**
    551          * Constructs and a return a stack map table containing
    552          * the written stack map entries.
    553          *
    554          * @param cp        the constant pool used to write
    555          *                  the stack map entries.
    556          */
    557         public StackMapTable toStackMapTable(ConstPool cp) {
    558             return new StackMapTable(cp, toByteArray());
    559         }
    560 
    561         /**
    562          * Writes a <code>same_frame</code> or a <code>same_frame_extended</code>.
    563          */
    564         public void sameFrame(int offsetDelta) {
    565             numOfEntries++;
    566             if (offsetDelta < 64)
    567                 output.write(offsetDelta);
    568             else {
    569                 output.write(251);  // SAME_FRAME_EXTENDED
    570                 write16(offsetDelta);
    571             }
    572         }
    573 
    574         /**
    575          * Writes a <code>same_locals_1_stack_item</code>
    576          * or a <code>same_locals_1_stack_item_extended</code>.
    577          *
    578          * @param tag           <code>stack[0].tag</code>.
    579          * @param data          <code>stack[0].cpool_index</code>
    580          *                      if the tag is <code>OBJECT</code>,
    581          *                      or <cod>stack[0].offset</code>
    582          *                      if the tag is <code>UNINIT</code>.
    583          *                      Otherwise, this parameter is not used.
    584          */
    585         public void sameLocals(int offsetDelta, int tag, int data) {
    586             numOfEntries++;
    587             if (offsetDelta < 64)
    588                 output.write(offsetDelta + 64);
    589             else {
    590                 output.write(247);  // SAME_LOCALS_1_STACK_ITEM_EXTENDED
    591                 write16(offsetDelta);
    592             }
    593 
    594             writeTypeInfo(tag, data);
    595         }
    596 
    597         /**
    598          * Writes a <code>chop_frame</code>.
    599          *
    600          * @param k                 the number of absent locals. 1, 2, or 3.
    601          */
    602         public void chopFrame(int offsetDelta, int k) {
    603             numOfEntries++;
    604             output.write(251 - k);
    605             write16(offsetDelta);
    606         }
    607 
    608         /**
    609          * Writes a <code>append_frame</code>.  The number of the appended
    610          * locals is specified by the length of <code>tags</code>.
    611          *
    612          * @param tags           <code>locals[].tag</code>.
    613          *                      The length of this array must be
    614          *                      either 1, 2, or 3.
    615          * @param data          <code>locals[].cpool_index</code>
    616          *                      if the tag is <code>OBJECT</code>,
    617          *                      or <cod>locals[].offset</code>
    618          *                      if the tag is <code>UNINIT</code>.
    619          *                      Otherwise, this parameter is not used.
    620          */
    621         public void appendFrame(int offsetDelta, int[] tags, int[] data) {
    622             numOfEntries++;
    623             int k = tags.length;    // k is 1, 2, or 3
    624             output.write(k + 251);
    625             write16(offsetDelta);
    626             for (int i = 0; i < k; i++)
    627                 writeTypeInfo(tags[i], data[i]);
    628         }
    629 
    630         /**
    631          * Writes a <code>full_frame</code>.
    632          * <code>number_of_locals</code> and <code>number_of_stack_items</code>
    633          * are specified by the the length of <code>localTags</code> and
    634          * <code>stackTags</code>.
    635          *
    636          * @param localTags     <code>locals[].tag</code>.
    637          * @param localData     <code>locals[].cpool_index</code>
    638          *                      if the tag is <code>OBJECT</code>,
    639          *                      or <cod>locals[].offset</code>
    640          *                      if the tag is <code>UNINIT</code>.
    641          *                      Otherwise, this parameter is not used.
    642          * @param stackTags     <code>stack[].tag</code>.
    643          * @param stackData     <code>stack[].cpool_index</code>
    644          *                      if the tag is <code>OBJECT</code>,
    645          *                      or <cod>stack[].offset</code>
    646          *                      if the tag is <code>UNINIT</code>.
    647          *                      Otherwise, this parameter is not used.
    648          */
    649         public void fullFrame(int offsetDelta, int[] localTags, int[] localData,
    650                               int[] stackTags, int[] stackData) {
    651             numOfEntries++;
    652             output.write(255);      // FULL_FRAME
    653             write16(offsetDelta);
    654             int n = localTags.length;
    655             write16(n);
    656             for (int i = 0; i < n; i++)
    657                 writeTypeInfo(localTags[i], localData[i]);
    658 
    659             n = stackTags.length;
    660             write16(n);
    661             for (int i = 0; i < n; i++)
    662                 writeTypeInfo(stackTags[i], stackData[i]);
    663         }
    664 
    665         private void writeTypeInfo(int tag, int data) {
    666             output.write(tag);
    667             if (tag == OBJECT || tag == UNINIT)
    668                 write16(data);
    669         }
    670 
    671         private void write16(int value) {
    672             output.write((value >>> 8) & 0xff);
    673             output.write(value & 0xff);
    674         }
    675     }
    676 
    677     /**
    678      * Prints the stack table map.
    679      */
    680     public void println(PrintWriter w) {
    681         Printer.print(this, w);
    682     }
    683 
    684     /**
    685      * Prints the stack table map.
    686      *
    687      * @param ps    a print stream such as <code>System.out</code>.
    688      */
    689     public void println(java.io.PrintStream ps) {
    690         Printer.print(this, new java.io.PrintWriter(ps, true));
    691     }
    692 
    693     static class Printer extends Walker {
    694         private PrintWriter writer;
    695         private int offset;
    696 
    697         /**
    698          * Prints the stack table map.
    699          */
    700         public static void print(StackMapTable smt, PrintWriter writer) {
    701             try {
    702                 new Printer(smt.get(), writer).parse();
    703             }
    704             catch (BadBytecode e) {
    705                 writer.println(e.getMessage());
    706             }
    707         }
    708 
    709         Printer(byte[] data, PrintWriter pw) {
    710             super(data);
    711             writer = pw;
    712             offset = -1;
    713         }
    714 
    715         public void sameFrame(int pos, int offsetDelta) {
    716             offset += offsetDelta + 1;
    717             writer.println(offset + " same frame: " + offsetDelta);
    718         }
    719 
    720         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
    721             offset += offsetDelta + 1;
    722             writer.println(offset + " same locals: " + offsetDelta);
    723             printTypeInfo(stackTag, stackData);
    724         }
    725 
    726         public void chopFrame(int pos, int offsetDelta, int k) {
    727             offset += offsetDelta + 1;
    728             writer.println(offset + " chop frame: " + offsetDelta + ",    " + k + " last locals");
    729         }
    730 
    731         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
    732             offset += offsetDelta + 1;
    733             writer.println(offset + " append frame: " + offsetDelta);
    734             for (int i = 0; i < tags.length; i++)
    735                 printTypeInfo(tags[i], data[i]);
    736         }
    737 
    738         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    739                               int[] stackTags, int[] stackData) {
    740             offset += offsetDelta + 1;
    741             writer.println(offset + " full frame: " + offsetDelta);
    742             writer.println("[locals]");
    743             for (int i = 0; i < localTags.length; i++)
    744                 printTypeInfo(localTags[i], localData[i]);
    745 
    746             writer.println("[stack]");
    747             for (int i = 0; i < stackTags.length; i++)
    748                 printTypeInfo(stackTags[i], stackData[i]);
    749         }
    750 
    751         private void printTypeInfo(int tag, int data) {
    752             String msg = null;
    753             switch (tag) {
    754             case TOP :
    755                 msg = "top";
    756                 break;
    757             case INTEGER :
    758                 msg = "integer";
    759                 break;
    760             case FLOAT :
    761                 msg = "float";
    762                 break;
    763             case DOUBLE :
    764                 msg = "double";
    765                 break;
    766             case LONG :
    767                 msg = "long";
    768                 break;
    769             case NULL :
    770                 msg = "null";
    771                 break;
    772             case THIS :
    773                 msg = "this";
    774                 break;
    775             case OBJECT :
    776                 msg = "object (cpool_index " + data + ")";
    777                 break;
    778             case UNINIT :
    779                 msg = "uninitialized (offset " + data + ")";
    780                 break;
    781             }
    782 
    783             writer.print("    ");
    784             writer.println(msg);
    785         }
    786     }
    787 
    788     void shiftPc(int where, int gapSize, boolean exclusive)
    789         throws BadBytecode
    790     {
    791         new Shifter(this, where, gapSize, exclusive).doit();
    792     }
    793 
    794     static class Shifter extends Walker {
    795         private StackMapTable stackMap;
    796         private int where, gap;
    797         private int position;
    798         private byte[] updatedInfo;
    799         private boolean exclusive;
    800 
    801         public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) {
    802             super(smt);
    803             stackMap = smt;
    804             this.where = where;
    805             this.gap = gap;
    806             this.position = 0;
    807             this.updatedInfo = null;
    808             this.exclusive = exclusive;
    809         }
    810 
    811         public void doit() throws BadBytecode {
    812             parse();
    813             if (updatedInfo != null)
    814                 stackMap.set(updatedInfo);
    815         }
    816 
    817         public void sameFrame(int pos, int offsetDelta) {
    818             update(pos, offsetDelta, 0, 251);
    819         }
    820 
    821         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
    822             update(pos, offsetDelta, 64, 247);
    823         }
    824 
    825         private void update(int pos, int offsetDelta, int base, int entry) {
    826             int oldPos = position;
    827             position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
    828             boolean match;
    829             if (exclusive)
    830                 match = oldPos < where  && where <= position;
    831             else
    832                 match = oldPos <= where  && where < position;
    833 
    834             if (match) {
    835                 int newDelta = offsetDelta + gap;
    836                 position += gap;
    837                 if (newDelta < 64)
    838                     info[pos] = (byte)(newDelta + base);
    839                 else if (offsetDelta < 64) {
    840                     byte[] newinfo = insertGap(info, pos, 2);
    841                     newinfo[pos] = (byte)entry;
    842                     ByteArray.write16bit(newDelta, newinfo, pos + 1);
    843                     updatedInfo = newinfo;
    844                 }
    845                 else
    846                     ByteArray.write16bit(newDelta, info, pos + 1);
    847             }
    848         }
    849 
    850         private static byte[] insertGap(byte[] info, int where, int gap) {
    851             int len = info.length;
    852             byte[] newinfo = new byte[len + gap];
    853             for (int i = 0; i < len; i++)
    854                 newinfo[i + (i < where ? 0 : gap)] = info[i];
    855 
    856             return newinfo;
    857         }
    858 
    859         public void chopFrame(int pos, int offsetDelta, int k) {
    860             update(pos, offsetDelta);
    861         }
    862 
    863         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
    864             update(pos, offsetDelta);
    865         }
    866 
    867         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    868                               int[] stackTags, int[] stackData) {
    869             update(pos, offsetDelta);
    870         }
    871 
    872         private void update(int pos, int offsetDelta) {
    873             int oldPos = position;
    874             position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1);
    875             boolean match;
    876             if (exclusive)
    877                 match = oldPos < where  && where <= position;
    878             else
    879                 match = oldPos <= where  && where < position;
    880 
    881             if (match) {
    882                 int newDelta = offsetDelta + gap;
    883                 ByteArray.write16bit(newDelta, info, pos + 1);
    884                 position += gap;
    885             }
    886         }
    887     }
    888 
    889     /**
    890      * Undocumented method.  Do not use; internal-use only.
    891      *
    892      * <p>This method is for javassist.convert.TransformNew.
    893      * It is called to update the stack map table when
    894      * the NEW opcode (and the following DUP) is removed.
    895      *
    896      * @param where     the position of the removed NEW opcode.
    897      */
    898      public void removeNew(int where) throws CannotCompileException {
    899         try {
    900             byte[] data = new NewRemover(this.get(), where).doit();
    901             this.set(data);
    902         }
    903         catch (BadBytecode e) {
    904             throw new CannotCompileException("bad stack map table", e);
    905         }
    906     }
    907 
    908     static class NewRemover extends SimpleCopy {
    909         int posOfNew;
    910 
    911         public NewRemover(byte[] data, int pos) {
    912             super(data);
    913             posOfNew = pos;
    914         }
    915 
    916         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
    917             if (stackTag == UNINIT && stackData == posOfNew)
    918                 super.sameFrame(pos, offsetDelta);
    919             else
    920                 super.sameLocals(pos, offsetDelta, stackTag, stackData);
    921         }
    922 
    923         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
    924                               int[] stackTags, int[] stackData) {
    925             int n = stackTags.length - 1;
    926             for (int i = 0; i < n; i++)
    927                 if (stackTags[i] == UNINIT && stackData[i] == posOfNew
    928                     && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) {
    929                     n++;
    930                     int[] stackTags2 = new int[n - 2];
    931                     int[] stackData2 = new int[n - 2];
    932                     int k = 0;
    933                     for (int j = 0; j < n; j++)
    934                         if (j == i)
    935                             j++;
    936                         else {
    937                             stackTags2[k] = stackTags[j];
    938                             stackData2[k++] = stackData[j];
    939                         }
    940 
    941                     stackTags = stackTags2;
    942                     stackData = stackData2;
    943                     break;
    944                 }
    945 
    946             super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData);
    947         }
    948     }
    949 }
    950