• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
StackMapTable(ConstPool cp, byte[] newInfo)45     StackMapTable(ConstPool cp, byte[] newInfo) {
46         super(cp, tag, newInfo);
47     }
48 
StackMapTable(ConstPool cp, int name_id, DataInputStream in)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      */
copy(ConstPool newCp, Map classnames)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          */
RuntimeCopyException(String s)84         public RuntimeCopyException(String s) {
85             super(s);
86         }
87     }
88 
write(DataOutputStream out)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          */
Walker(StackMapTable smt)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          */
Walker(byte[] data)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          */
size()171         public final int size() { return numOfEntries; }
172 
173         /**
174          * Visits each entry of the stack map frames.
175          */
parse()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          */
stackMapFrames(int pos, int nth)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          */
sameFrame(int pos, int offsetDelta)230         public void sameFrame(int pos, int offsetDelta) throws BadBytecode {}
231 
sameLocals(int pos, int type)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          */
sameLocals(int pos, int offsetDelta, int stackTag, int stackData)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          */
chopFrame(int pos, int offsetDelta, int k)275         public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {}
276 
appendFrame(int pos, int type)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          */
appendFrame(int pos, int offsetDelta, int[] tags, int[] data)309         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data)
310             throws BadBytecode {}
311 
fullFrame(int pos)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          */
fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)338         public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData,
339                               int[] stackTags, int[] stackData)
340             throws BadBytecode {}
341 
verifyTypeInfo(int pos, int n, int[] tags, int[] data)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 
SimpleCopy(byte[] data)359         public SimpleCopy(byte[] data) {
360             super(data);
361             writer = new Writer(data.length);
362         }
363 
doit()364         public byte[] doit() throws BadBytecode {
365             parse();
366             return writer.toByteArray();
367         }
368 
sameFrame(int pos, int offsetDelta)369         public void sameFrame(int pos, int offsetDelta) {
370             writer.sameFrame(offsetDelta);
371         }
372 
sameLocals(int pos, int offsetDelta, int stackTag, int stackData)373         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
374             writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData));
375         }
376 
chopFrame(int pos, int offsetDelta, int k)377         public void chopFrame(int pos, int offsetDelta, int k) {
378             writer.chopFrame(offsetDelta, k);
379         }
380 
appendFrame(int pos, int offsetDelta, int[] tags, int[] data)381         public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) {
382             writer.appendFrame(offsetDelta, tags, copyData(tags, data));
383         }
384 
fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)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 
copyData(int tag, int data)391         protected int copyData(int tag, int data) {
392             return data;
393         }
394 
copyData(int[] tags, int[] data)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 
Copier(ConstPool src, byte[] data, ConstPool dest)403         public Copier(ConstPool src, byte[] data, ConstPool dest) {
404             super(data);
405             srcPool = src;
406             destPool = dest;
407         }
408 
copyData(int tag, int data)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 
copyData(int[] tags, int[] data)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      */
insertLocal(int index, int tag, int classInfo)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      */
typeTagOf(char descriptor)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 
InsertLocal(byte[] data, int varIndex, int varTag, int varData)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 
fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)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          */
Writer(int size)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          */
toByteArray()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          */
toStackMapTable(ConstPool cp)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          */
sameFrame(int offsetDelta)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          */
sameLocals(int offsetDelta, int tag, int data)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          */
chopFrame(int offsetDelta, int k)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          */
appendFrame(int offsetDelta, int[] tags, int[] data)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          */
fullFrame(int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)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 
writeTypeInfo(int tag, int data)665         private void writeTypeInfo(int tag, int data) {
666             output.write(tag);
667             if (tag == OBJECT || tag == UNINIT)
668                 write16(data);
669         }
670 
write16(int value)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      */
println(PrintWriter w)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      */
println(java.io.PrintStream ps)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          */
print(StackMapTable smt, PrintWriter writer)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 
Printer(byte[] data, PrintWriter pw)709         Printer(byte[] data, PrintWriter pw) {
710             super(data);
711             writer = pw;
712             offset = -1;
713         }
714 
sameFrame(int pos, int offsetDelta)715         public void sameFrame(int pos, int offsetDelta) {
716             offset += offsetDelta + 1;
717             writer.println(offset + " same frame: " + offsetDelta);
718         }
719 
sameLocals(int pos, int offsetDelta, int stackTag, int stackData)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 
chopFrame(int pos, int offsetDelta, int k)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 
appendFrame(int pos, int offsetDelta, int[] tags, int[] data)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 
fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)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 
printTypeInfo(int tag, int data)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 
shiftPc(int where, int gapSize, boolean exclusive)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 
Shifter(StackMapTable smt, int where, int gap, boolean exclusive)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 
doit()811         public void doit() throws BadBytecode {
812             parse();
813             if (updatedInfo != null)
814                 stackMap.set(updatedInfo);
815         }
816 
sameFrame(int pos, int offsetDelta)817         public void sameFrame(int pos, int offsetDelta) {
818             update(pos, offsetDelta, 0, 251);
819         }
820 
sameLocals(int pos, int offsetDelta, int stackTag, int stackData)821         public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) {
822             update(pos, offsetDelta, 64, 247);
823         }
824 
update(int pos, int offsetDelta, int base, int entry)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