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
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.DataOutputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.PrintWriter;
22import java.io.IOException;
23import java.util.Map;
24import 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 */
36public 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