Tracer.java revision 69e17611504376e4d4603925f8528dfc890fd2c6
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.stackmap;
17
18import javassist.bytecode.ByteArray;
19import javassist.bytecode.Opcode;
20import javassist.bytecode.ConstPool;
21import javassist.bytecode.Descriptor;
22import javassist.bytecode.BadBytecode;
23import javassist.ClassPool;
24
25/*
26 * A class for performing abstract interpretation.
27 * See also MapMaker class.
28 */
29
30public abstract class Tracer implements TypeTag {
31    protected ClassPool classPool;
32    protected ConstPool cpool;
33    protected String returnType;
34
35    protected int stackTop;
36    protected TypeData[] stackTypes;
37    protected TypeData[] localsTypes;
38
39    public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals,
40                  String retType) {
41        classPool = classes;
42        cpool = cp;
43        returnType = retType;
44        stackTop = 0;
45        stackTypes = new TypeData[maxStack];
46        localsTypes = new TypeData[maxLocals];
47    }
48
49    public Tracer(Tracer t, boolean copyStack) {
50        classPool = t.classPool;
51        cpool = t.cpool;
52        returnType = t.returnType;
53
54        stackTop = t.stackTop;
55        int size = t.stackTypes.length;
56        stackTypes = new TypeData[size];
57        if (copyStack)
58            copyFrom(t.stackTop, t.stackTypes, stackTypes);
59
60        int size2 = t.localsTypes.length;
61        localsTypes = new TypeData[size2];
62        copyFrom(size2, t.localsTypes, localsTypes);
63    }
64
65    protected static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) {
66        int k = -1;
67        for (int i = 0; i < n; i++) {
68            TypeData t = srcTypes[i];
69            destTypes[i] = t == TOP ? TOP : t.getSelf();
70            if (t != TOP)
71                if (t.is2WordType())
72                    k = i + 1;
73                else
74                    k = i;
75        }
76
77        return k + 1;
78    }
79
80    /**
81     * Does abstract interpretation on the given bytecode instruction.
82     * It records whether or not a local variable (i.e. register) is accessed.
83     * If the instruction requires that a local variable or
84     * a stack element has a more specific type, this method updates the
85     * type of it.
86     *
87     * @param pos         the position of the instruction.
88     * @return      the size of the instruction at POS.
89     */
90    protected int doOpcode(int pos, byte[] code) throws BadBytecode {
91        try {
92            int op = code[pos] & 0xff;
93            if (op < 96)
94                if (op < 54)
95                    return doOpcode0_53(pos, code, op);
96                else
97                    return doOpcode54_95(pos, code, op);
98            else
99                if (op < 148)
100                    return doOpcode96_147(pos, code, op);
101                else
102                    return doOpcode148_201(pos, code, op);
103        }
104        catch (ArrayIndexOutOfBoundsException e) {
105            throw new BadBytecode("inconsistent stack height " + e.getMessage());
106        }
107    }
108
109    protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {}
110    protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {}
111    protected void visitReturn(int pos, byte[] code) throws BadBytecode {}
112    protected void visitThrow(int pos, byte[] code) throws BadBytecode {}
113
114    /**
115     * @param pos           the position of TABLESWITCH
116     * @param code          bytecode
117     * @param n             the number of case labels
118     * @param offsetPos     the position of the branch-target table.
119     * @param defaultOffset     the offset to the default branch target.
120     */
121    protected void visitTableSwitch(int pos, byte[] code, int n,
122                int offsetPos, int defaultOffset) throws BadBytecode {}
123
124    /**
125     * @param pos           the position of LOOKUPSWITCH
126     * @param code          bytecode
127     * @param n             the number of case labels
128     * @param offsetPos     the position of the table of pairs of a value and a branch target.
129     * @param defaultOffset     the offset to the default branch target.
130     */
131    protected void visitLookupSwitch(int pos, byte[] code, int n,
132                int pairsPos, int defaultOffset) throws BadBytecode {}
133
134    /**
135     * Invoked when the visited instruction is jsr.
136     * Java6 or later does not allow using RET.
137     */
138    protected void visitJSR(int pos, byte[] code) throws BadBytecode {
139        /* Since JSR pushes a return address onto the operand stack,
140         * the stack map at the entry point of a subroutine is
141         * stackTypes resulting after executing the following code:
142         *
143         *     stackTypes[stackTop++] = TOP;
144         */
145    }
146
147    /**
148     * Invoked when the visited instruction is ret or wide ret.
149     * Java6 or later does not allow using RET.
150     */
151    protected void visitRET(int pos, byte[] code) throws BadBytecode {}
152
153    private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode {
154        int reg;
155        TypeData[] stackTypes = this.stackTypes;
156        switch (op) {
157        case Opcode.NOP :
158            break;
159        case Opcode.ACONST_NULL :
160            stackTypes[stackTop++] = new TypeData.NullType();
161            break;
162        case Opcode.ICONST_M1 :
163        case Opcode.ICONST_0 :
164        case Opcode.ICONST_1 :
165        case Opcode.ICONST_2 :
166        case Opcode.ICONST_3 :
167        case Opcode.ICONST_4 :
168        case Opcode.ICONST_5 :
169            stackTypes[stackTop++] = INTEGER;
170            break;
171        case Opcode.LCONST_0 :
172        case Opcode.LCONST_1 :
173            stackTypes[stackTop++] = LONG;
174            stackTypes[stackTop++] = TOP;
175            break;
176        case Opcode.FCONST_0 :
177        case Opcode.FCONST_1 :
178        case Opcode.FCONST_2 :
179            stackTypes[stackTop++] = FLOAT;
180            break;
181        case Opcode.DCONST_0 :
182        case Opcode.DCONST_1 :
183            stackTypes[stackTop++] = DOUBLE;
184            stackTypes[stackTop++] = TOP;
185            break;
186        case Opcode.BIPUSH :
187        case Opcode.SIPUSH :
188            stackTypes[stackTop++] = INTEGER;
189            return op == Opcode.SIPUSH ? 3 : 2;
190        case Opcode.LDC :
191            doLDC(code[pos + 1] & 0xff);
192            return 2;
193        case Opcode.LDC_W :
194        case Opcode.LDC2_W :
195            doLDC(ByteArray.readU16bit(code, pos + 1));
196            return 3;
197        case Opcode.ILOAD :
198            return doXLOAD(INTEGER, code, pos);
199        case Opcode.LLOAD :
200            return doXLOAD(LONG, code, pos);
201        case Opcode.FLOAD :
202            return doXLOAD(FLOAT, code, pos);
203        case Opcode.DLOAD :
204            return doXLOAD(DOUBLE, code, pos);
205        case Opcode.ALOAD :
206            return doALOAD(code[pos + 1] & 0xff);
207        case Opcode.ILOAD_0 :
208        case Opcode.ILOAD_1 :
209        case Opcode.ILOAD_2 :
210        case Opcode.ILOAD_3 :
211            stackTypes[stackTop++] = INTEGER;
212            break;
213        case Opcode.LLOAD_0 :
214        case Opcode.LLOAD_1 :
215        case Opcode.LLOAD_2 :
216        case Opcode.LLOAD_3 :
217            stackTypes[stackTop++] = LONG;
218            stackTypes[stackTop++] = TOP;
219            break;
220        case Opcode.FLOAD_0 :
221        case Opcode.FLOAD_1 :
222        case Opcode.FLOAD_2 :
223        case Opcode.FLOAD_3 :
224            stackTypes[stackTop++] = FLOAT;
225            break;
226        case Opcode.DLOAD_0 :
227        case Opcode.DLOAD_1 :
228        case Opcode.DLOAD_2 :
229        case Opcode.DLOAD_3 :
230            stackTypes[stackTop++] = DOUBLE;
231            stackTypes[stackTop++] = TOP;
232            break;
233        case Opcode.ALOAD_0 :
234        case Opcode.ALOAD_1 :
235        case Opcode.ALOAD_2 :
236        case Opcode.ALOAD_3 :
237            reg = op - Opcode.ALOAD_0;
238            stackTypes[stackTop++] = localsTypes[reg];
239            break;
240        case Opcode.IALOAD :
241            stackTypes[--stackTop - 1] = INTEGER;
242            break;
243        case Opcode.LALOAD :
244            stackTypes[stackTop - 2] = LONG;
245            stackTypes[stackTop - 1] = TOP;
246            break;
247        case Opcode.FALOAD :
248            stackTypes[--stackTop - 1] = FLOAT;
249            break;
250        case Opcode.DALOAD :
251            stackTypes[stackTop - 2] = DOUBLE;
252            stackTypes[stackTop - 1] = TOP;
253            break;
254        case Opcode.AALOAD : {
255            int s = --stackTop - 1;
256            TypeData data = stackTypes[s];
257            if (data == null || !data.isObjectType())
258                throw new BadBytecode("bad AALOAD");
259            else
260                stackTypes[s] = new TypeData.ArrayElement(data);
261
262            break; }
263        case Opcode.BALOAD :
264        case Opcode.CALOAD :
265        case Opcode.SALOAD :
266            stackTypes[--stackTop - 1] = INTEGER;
267            break;
268        default :
269            throw new RuntimeException("fatal");
270        }
271
272        return 1;
273    }
274
275    private void doLDC(int index) {
276        TypeData[] stackTypes = this.stackTypes;
277        int tag = cpool.getTag(index);
278        if (tag == ConstPool.CONST_String)
279            stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String");
280        else if (tag == ConstPool.CONST_Integer)
281            stackTypes[stackTop++] = INTEGER;
282        else if (tag == ConstPool.CONST_Float)
283            stackTypes[stackTop++] = FLOAT;
284        else if (tag == ConstPool.CONST_Long) {
285            stackTypes[stackTop++] = LONG;
286            stackTypes[stackTop++] = TOP;
287        }
288        else if (tag == ConstPool.CONST_Double) {
289            stackTypes[stackTop++] = DOUBLE;
290            stackTypes[stackTop++] = TOP;
291        }
292        else if (tag == ConstPool.CONST_Class)
293            stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class");
294        else
295            throw new RuntimeException("bad LDC: " + tag);
296    }
297
298    private int doXLOAD(TypeData type, byte[] code, int pos) {
299        int localVar = code[pos + 1] & 0xff;
300        return doXLOAD(localVar, type);
301    }
302
303    private int doXLOAD(int localVar, TypeData type) {
304        stackTypes[stackTop++] = type;
305        if (type.is2WordType())
306            stackTypes[stackTop++] = TOP;
307
308        return 2;
309    }
310
311    private int doALOAD(int localVar) { // int localVar, TypeData type) {
312        stackTypes[stackTop++] = localsTypes[localVar];
313        return 2;
314    }
315
316    private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode {
317        TypeData[] localsTypes = this.localsTypes;
318        TypeData[] stackTypes = this.stackTypes;
319        switch (op) {
320        case Opcode.ISTORE :
321            return doXSTORE(pos, code, INTEGER);
322        case Opcode.LSTORE :
323            return doXSTORE(pos, code, LONG);
324        case Opcode.FSTORE :
325            return doXSTORE(pos, code, FLOAT);
326        case Opcode.DSTORE :
327            return doXSTORE(pos, code, DOUBLE);
328        case Opcode.ASTORE :
329            return doASTORE(code[pos + 1] & 0xff);
330        case Opcode.ISTORE_0 :
331        case Opcode.ISTORE_1 :
332        case Opcode.ISTORE_2 :
333        case Opcode.ISTORE_3 :
334          { int var = op - Opcode.ISTORE_0;
335            localsTypes[var] = INTEGER;
336            stackTop--; }
337            break;
338        case Opcode.LSTORE_0 :
339        case Opcode.LSTORE_1 :
340        case Opcode.LSTORE_2 :
341        case Opcode.LSTORE_3 :
342          { int var = op - Opcode.LSTORE_0;
343            localsTypes[var] = LONG;
344            localsTypes[var + 1] = TOP;
345            stackTop -= 2; }
346            break;
347        case Opcode.FSTORE_0 :
348        case Opcode.FSTORE_1 :
349        case Opcode.FSTORE_2 :
350        case Opcode.FSTORE_3 :
351          { int var = op - Opcode.FSTORE_0;
352            localsTypes[var] = FLOAT;
353            stackTop--; }
354            break;
355        case Opcode.DSTORE_0 :
356        case Opcode.DSTORE_1 :
357        case Opcode.DSTORE_2 :
358        case Opcode.DSTORE_3 :
359          { int var = op - Opcode.DSTORE_0;
360            localsTypes[var] = DOUBLE;
361            localsTypes[var + 1] = TOP;
362            stackTop -= 2; }
363            break;
364        case Opcode.ASTORE_0 :
365        case Opcode.ASTORE_1 :
366        case Opcode.ASTORE_2 :
367        case Opcode.ASTORE_3 :
368          { int var = op - Opcode.ASTORE_0;
369            doASTORE(var);
370            break; }
371        case Opcode.IASTORE :
372        case Opcode.LASTORE :
373        case Opcode.FASTORE :
374        case Opcode.DASTORE :
375            stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3;
376            break;
377        case Opcode.AASTORE :
378            TypeData.setType(stackTypes[stackTop - 1],
379                             TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()),
380                             classPool);
381            stackTop -= 3;
382            break;
383        case Opcode.BASTORE :
384        case Opcode.CASTORE :
385        case Opcode.SASTORE :
386            stackTop -= 3;
387            break;
388        case Opcode.POP :
389            stackTop--;
390            break;
391        case Opcode.POP2 :
392            stackTop -= 2;
393            break;
394        case Opcode.DUP : {
395            int sp = stackTop;
396            stackTypes[sp] = stackTypes[sp - 1];
397            stackTop = sp + 1;
398            break; }
399        case Opcode.DUP_X1 :
400        case Opcode.DUP_X2 : {
401            int len = op - Opcode.DUP_X1 + 2;
402            doDUP_XX(1, len);
403            int sp = stackTop;
404            stackTypes[sp - len] = stackTypes[sp];
405            stackTop = sp + 1;
406            break; }
407        case Opcode.DUP2 :
408            doDUP_XX(2, 2);
409            stackTop += 2;
410            break;
411        case Opcode.DUP2_X1 :
412        case Opcode.DUP2_X2 : {
413            int len = op - Opcode.DUP2_X1 + 3;
414            doDUP_XX(2, len);
415            int sp = stackTop;
416            stackTypes[sp - len] = stackTypes[sp];
417            stackTypes[sp - len + 1] = stackTypes[sp + 1];
418            stackTop = sp + 2;
419            break; }
420        case Opcode.SWAP : {
421            int sp = stackTop - 1;
422            TypeData t = stackTypes[sp];
423            stackTypes[sp] = stackTypes[sp - 1];
424            stackTypes[sp - 1] = t;
425            break; }
426        default :
427            throw new RuntimeException("fatal");
428        }
429
430        return 1;
431    }
432
433    private int doXSTORE(int pos, byte[] code, TypeData type) {
434        int index = code[pos + 1] & 0xff;
435        return doXSTORE(index, type);
436    }
437
438    private int doXSTORE(int index, TypeData type) {
439        stackTop--;
440        localsTypes[index] = type;
441        if (type.is2WordType()) {
442            stackTop--;
443            localsTypes[index + 1] = TOP;
444        }
445
446        return 2;
447    }
448
449    private int doASTORE(int index) {
450        stackTop--;
451        // implicit upcast might be done.
452        localsTypes[index] = stackTypes[stackTop].copy();
453        return 2;
454    }
455
456    private void doDUP_XX(int delta, int len) {
457        TypeData types[] = stackTypes;
458        int sp = stackTop - 1;
459        int end = sp - len;
460        while (sp > end) {
461            types[sp + delta] = types[sp];
462            sp--;
463        }
464    }
465
466    private int doOpcode96_147(int pos, byte[] code, int op) {
467        if (op <= Opcode.LXOR) {    // IADD...LXOR
468            stackTop += Opcode.STACK_GROW[op];
469            return 1;
470        }
471
472        switch (op) {
473        case Opcode.IINC :
474            // this does not call writeLocal().
475            return 3;
476        case Opcode.I2L :
477            stackTypes[stackTop] = LONG;
478            stackTypes[stackTop - 1] = TOP;
479            stackTop++;
480            break;
481        case Opcode.I2F :
482            stackTypes[stackTop - 1] = FLOAT;
483            break;
484        case Opcode.I2D :
485            stackTypes[stackTop] = DOUBLE;
486            stackTypes[stackTop - 1] = TOP;
487            stackTop++;
488            break;
489        case Opcode.L2I :
490            stackTypes[--stackTop - 1] = INTEGER;
491            break;
492        case Opcode.L2F :
493            stackTypes[--stackTop - 1] = FLOAT;
494            break;
495        case Opcode.L2D :
496            stackTypes[stackTop - 1] = DOUBLE;
497            break;
498        case Opcode.F2I :
499            stackTypes[stackTop - 1] = INTEGER;
500            break;
501        case Opcode.F2L :
502            stackTypes[stackTop - 1] = TOP;
503            stackTypes[stackTop++] = LONG;
504            break;
505        case Opcode.F2D :
506            stackTypes[stackTop - 1] = TOP;
507            stackTypes[stackTop++] = DOUBLE;
508            break;
509        case Opcode.D2I :
510            stackTypes[--stackTop - 1] = INTEGER;
511            break;
512        case Opcode.D2L :
513            stackTypes[stackTop - 1] = LONG;
514            break;
515        case Opcode.D2F :
516            stackTypes[--stackTop - 1] = FLOAT;
517            break;
518        case Opcode.I2B :
519        case Opcode.I2C :
520        case Opcode.I2S :
521            break;
522        default :
523            throw new RuntimeException("fatal");
524        }
525
526        return 1;
527    }
528
529    private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode {
530        switch (op) {
531        case Opcode.LCMP :
532            stackTypes[stackTop - 4] = INTEGER;
533            stackTop -= 3;
534            break;
535        case Opcode.FCMPL :
536        case Opcode.FCMPG :
537            stackTypes[--stackTop - 1] = INTEGER;
538            break;
539        case Opcode.DCMPL :
540        case Opcode.DCMPG :
541            stackTypes[stackTop - 4] = INTEGER;
542            stackTop -= 3;
543            break;
544        case Opcode.IFEQ :
545        case Opcode.IFNE :
546        case Opcode.IFLT :
547        case Opcode.IFGE :
548        case Opcode.IFGT :
549        case Opcode.IFLE :
550            stackTop--;     // branch
551            visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
552            return 3;
553        case Opcode.IF_ICMPEQ :
554        case Opcode.IF_ICMPNE :
555        case Opcode.IF_ICMPLT :
556        case Opcode.IF_ICMPGE :
557        case Opcode.IF_ICMPGT :
558        case Opcode.IF_ICMPLE :
559        case Opcode.IF_ACMPEQ :
560        case Opcode.IF_ACMPNE :
561            stackTop -= 2;  // branch
562            visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
563            return 3;
564        case Opcode.GOTO :
565            visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1));
566            return 3;       // branch
567        case Opcode.JSR :
568            visitJSR(pos, code);
569            return 3;       // branch
570        case Opcode.RET :
571            visitRET(pos, code);
572            return 2;
573        case Opcode.TABLESWITCH : {
574            stackTop--;     // branch
575            int pos2 = (pos & ~3) + 8;
576            int low = ByteArray.read32bit(code, pos2);
577            int high = ByteArray.read32bit(code, pos2 + 4);
578            int n = high - low + 1;
579            visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4));
580            return n * 4 + 16 - (pos & 3); }
581        case Opcode.LOOKUPSWITCH : {
582            stackTop--;     // branch
583            int pos2 = (pos & ~3) + 8;
584            int n = ByteArray.read32bit(code, pos2);
585            visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4));
586            return n * 8 + 12 - (pos & 3); }
587        case Opcode.IRETURN :
588            stackTop--;
589            visitReturn(pos, code);
590            break;
591        case Opcode.LRETURN :
592            stackTop -= 2;
593            visitReturn(pos, code);
594            break;
595        case Opcode.FRETURN :
596            stackTop--;
597            visitReturn(pos, code);
598            break;
599        case Opcode.DRETURN :
600            stackTop -= 2;
601            visitReturn(pos, code);
602            break;
603        case Opcode.ARETURN :
604            TypeData.setType(stackTypes[--stackTop], returnType, classPool);
605            visitReturn(pos, code);
606            break;
607        case Opcode.RETURN :
608            visitReturn(pos, code);
609            break;
610        case Opcode.GETSTATIC :
611            return doGetField(pos, code, false);
612        case Opcode.PUTSTATIC :
613            return doPutField(pos, code, false);
614        case Opcode.GETFIELD :
615            return doGetField(pos, code, true);
616        case Opcode.PUTFIELD :
617            return doPutField(pos, code, true);
618        case Opcode.INVOKEVIRTUAL :
619        case Opcode.INVOKESPECIAL :
620            return doInvokeMethod(pos, code, true);
621        case Opcode.INVOKESTATIC :
622            return doInvokeMethod(pos, code, false);
623        case Opcode.INVOKEINTERFACE :
624            return doInvokeIntfMethod(pos, code);
625        case 186 :
626            throw new RuntimeException("bad opcode 186");
627        case Opcode.NEW : {
628            int i = ByteArray.readU16bit(code, pos + 1);
629            stackTypes[stackTop++]
630                      = new TypeData.UninitData(pos, cpool.getClassInfo(i));
631            return 3; }
632        case Opcode.NEWARRAY :
633            return doNEWARRAY(pos, code);
634        case Opcode.ANEWARRAY : {
635            int i = ByteArray.readU16bit(code, pos + 1);
636            String type = cpool.getClassInfo(i).replace('.', '/');
637            if (type.charAt(0) == '[')
638                type = "[" + type;
639            else
640                type = "[L" + type + ";";
641
642            stackTypes[stackTop - 1]
643                    = new TypeData.ClassName(type);
644            return 3; }
645        case Opcode.ARRAYLENGTH :
646            TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool);
647            stackTypes[stackTop - 1] = INTEGER;
648            break;
649        case Opcode.ATHROW :
650            TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool);
651            visitThrow(pos, code);
652            break;
653        case Opcode.CHECKCAST : {
654            // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
655            int i = ByteArray.readU16bit(code, pos + 1);
656            stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i));
657            return 3; }
658        case Opcode.INSTANCEOF :
659            // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool);
660            stackTypes[stackTop - 1] = INTEGER;
661            return 3;
662        case Opcode.MONITORENTER :
663        case Opcode.MONITOREXIT :
664            stackTop--;
665            // TypeData.setType(stackTypes[stackTop], "java.lang.Object", classPool);
666            break;
667        case Opcode.WIDE :
668            return doWIDE(pos, code);
669        case Opcode.MULTIANEWARRAY :
670            return doMultiANewArray(pos, code);
671        case Opcode.IFNULL :
672        case Opcode.IFNONNULL :
673            stackTop--;         // branch
674            visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1));
675            return 3;
676        case Opcode.GOTO_W :
677            visitGoto(pos, code, ByteArray.read32bit(code, pos + 1));
678            return 5;           // branch
679        case Opcode.JSR_W :
680            visitJSR(pos, code);
681            return 5;
682        }
683        return 1;
684    }
685
686    private int doWIDE(int pos, byte[] code) throws BadBytecode {
687        int op = code[pos + 1] & 0xff;
688        switch (op) {
689        case Opcode.ILOAD :
690            doWIDE_XLOAD(pos, code, INTEGER);
691            break;
692        case Opcode.LLOAD :
693            doWIDE_XLOAD(pos, code, LONG);
694            break;
695        case Opcode.FLOAD :
696            doWIDE_XLOAD(pos, code, FLOAT);
697            break;
698        case Opcode.DLOAD :
699            doWIDE_XLOAD(pos, code, DOUBLE);
700            break;
701        case Opcode.ALOAD : {
702            int index = ByteArray.readU16bit(code, pos + 2);
703            doALOAD(index);
704            break; }
705        case Opcode.ISTORE :
706            doWIDE_STORE(pos, code, INTEGER);
707            break;
708        case Opcode.LSTORE :
709            doWIDE_STORE(pos, code, LONG);
710            break;
711        case Opcode.FSTORE :
712            doWIDE_STORE(pos, code, FLOAT);
713            break;
714        case Opcode.DSTORE :
715            doWIDE_STORE(pos, code, DOUBLE);
716            break;
717        case Opcode.ASTORE : {
718            int index = ByteArray.readU16bit(code, pos + 2);
719            doASTORE(index);
720            break; }
721        case Opcode.IINC :
722            // this does not call writeLocal().
723            return 6;
724        case Opcode.RET :
725            visitRET(pos, code);
726            break;
727        default :
728            throw new RuntimeException("bad WIDE instruction: " + op);
729        }
730
731        return 4;
732    }
733
734    private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) {
735        int index = ByteArray.readU16bit(code, pos + 2);
736        doXLOAD(index, type);
737    }
738
739    private void doWIDE_STORE(int pos, byte[] code, TypeData type) {
740        int index = ByteArray.readU16bit(code, pos + 2);
741        doXSTORE(index, type);
742    }
743
744    private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
745        int index = ByteArray.readU16bit(code, pos + 1);
746        String desc = cpool.getFieldrefType(index);
747        stackTop -= Descriptor.dataSize(desc);
748        char c = desc.charAt(0);
749        if (c == 'L')
750            TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool);
751        else if (c == '[')
752            TypeData.setType(stackTypes[stackTop], desc, classPool);
753
754        setFieldTarget(notStatic, index);
755        return 3;
756    }
757
758    private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode {
759        int index = ByteArray.readU16bit(code, pos + 1);
760        setFieldTarget(notStatic, index);
761        String desc = cpool.getFieldrefType(index);
762        pushMemberType(desc);
763        return 3;
764    }
765
766    private void setFieldTarget(boolean notStatic, int index) throws BadBytecode {
767        if (notStatic) {
768            String className = cpool.getFieldrefClassName(index);
769            TypeData.setType(stackTypes[--stackTop], className, classPool);
770        }
771    }
772
773    private int doNEWARRAY(int pos, byte[] code) {
774        int s = stackTop - 1;
775        String type;
776        switch (code[pos + 1] & 0xff) {
777        case Opcode.T_BOOLEAN :
778            type = "[Z";
779            break;
780        case Opcode.T_CHAR :
781            type = "[C";
782            break;
783        case Opcode.T_FLOAT :
784            type = "[F";
785            break;
786        case Opcode.T_DOUBLE :
787            type = "[D";
788            break;
789        case Opcode.T_BYTE :
790            type = "[B";
791            break;
792        case Opcode.T_SHORT :
793            type = "[S";
794            break;
795        case Opcode.T_INT :
796            type = "[I";
797            break;
798        case Opcode.T_LONG :
799            type = "[J";
800            break;
801        default :
802            throw new RuntimeException("bad newarray");
803        }
804
805        stackTypes[s] = new TypeData.ClassName(type);
806        return 2;
807    }
808
809    private int doMultiANewArray(int pos, byte[] code) {
810        int i = ByteArray.readU16bit(code, pos + 1);
811        int dim = code[pos + 3] & 0xff;
812        stackTop -= dim - 1;
813
814        String type = cpool.getClassInfo(i).replace('.', '/');
815        stackTypes[stackTop - 1] = new TypeData.ClassName(type);
816        return 4;
817    }
818
819    private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode {
820        int i = ByteArray.readU16bit(code, pos + 1);
821        String desc = cpool.getMethodrefType(i);
822        checkParamTypes(desc, 1);
823        if (notStatic) {
824            String className = cpool.getMethodrefClassName(i);
825            TypeData.setType(stackTypes[--stackTop], className, classPool);
826        }
827
828        pushMemberType(desc);
829        return 3;
830    }
831
832    private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode {
833        int i = ByteArray.readU16bit(code, pos + 1);
834        String desc = cpool.getInterfaceMethodrefType(i);
835        checkParamTypes(desc, 1);
836        String className = cpool.getInterfaceMethodrefClassName(i);
837        TypeData.setType(stackTypes[--stackTop], className, classPool);
838        pushMemberType(desc);
839        return 5;
840    }
841
842    private void pushMemberType(String descriptor) {
843        int top = 0;
844        if (descriptor.charAt(0) == '(') {
845            top = descriptor.indexOf(')') + 1;
846            if (top < 1)
847                throw new IndexOutOfBoundsException("bad descriptor: "
848                                                    + descriptor);
849        }
850
851        TypeData[] types = stackTypes;
852        int index = stackTop;
853        switch (descriptor.charAt(top)) {
854        case '[' :
855            types[index] = new TypeData.ClassName(descriptor.substring(top));
856            break;
857        case 'L' :
858            types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top));
859            break;
860        case 'J' :
861            types[index] = LONG;
862            types[index + 1] = TOP;
863            stackTop += 2;
864            return;
865        case 'F' :
866            types[index] = FLOAT;
867            break;
868        case 'D' :
869            types[index] = DOUBLE;
870            types[index + 1] = TOP;
871            stackTop += 2;
872            return;
873        case 'V' :
874            return;
875        default : // C, B, S, I, Z
876            types[index] = INTEGER;
877            break;
878        }
879
880        stackTop++;
881    }
882
883    private static String getFieldClassName(String desc, int index) {
884        return desc.substring(index + 1, desc.length() - 1).replace('/', '.');
885    }
886
887    private void checkParamTypes(String desc, int i) throws BadBytecode {
888        char c = desc.charAt(i);
889        if (c == ')')
890            return;
891
892        int k = i;
893        boolean array = false;
894        while (c == '[') {
895            array = true;
896            c = desc.charAt(++k);
897        }
898
899        if (c == 'L') {
900            k = desc.indexOf(';', k) + 1;
901            if (k <= 0)
902                throw new IndexOutOfBoundsException("bad descriptor");
903        }
904        else
905            k++;
906
907        checkParamTypes(desc, k);
908        if (!array && (c == 'J' || c == 'D'))
909            stackTop -= 2;
910        else
911            stackTop--;
912
913        if (array)
914            TypeData.setType(stackTypes[stackTop],
915                             desc.substring(i, k), classPool);
916        else if (c == 'L')
917            TypeData.setType(stackTypes[stackTop],
918                             desc.substring(i + 1, k - 1).replace('/', '.'), classPool);
919    }
920}
921