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