1/***
2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2007 INRIA, France Telecom
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 *    contributors may be used to endorse or promote products derived from
16 *    this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
29 */
30package org.mockito.asm.util;
31
32import org.mockito.asm.AnnotationVisitor;
33import org.mockito.asm.Attribute;
34import org.mockito.asm.Label;
35import org.mockito.asm.MethodAdapter;
36import org.mockito.asm.MethodVisitor;
37import org.mockito.asm.Opcodes;
38import org.mockito.asm.Type;
39
40import java.util.HashMap;
41import java.util.Map;
42
43/**
44 * A {@link MethodAdapter} that checks that its methods are properly used. More
45 * precisely this code adapter checks each instruction individually (i.e., each
46 * visit method checks some preconditions based <i>only</i> on its arguments -
47 * such as the fact that the given opcode is correct for a given visit method),
48 * but does <i>not</i> check the <i>sequence</i> of instructions. For example,
49 * in a method whose signature is <tt>void m ()</tt>, the invalid instruction
50 * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by
51 * this code adapter.
52 *
53 * @author Eric Bruneton
54 */
55public class CheckMethodAdapter extends MethodAdapter {
56
57    /**
58     * <tt>true</tt> if the visitCode method has been called.
59     */
60    private boolean startCode;
61
62    /**
63     * <tt>true</tt> if the visitMaxs method has been called.
64     */
65    private boolean endCode;
66
67    /**
68     * <tt>true</tt> if the visitEnd method has been called.
69     */
70    private boolean endMethod;
71
72    /**
73     * The already visited labels. This map associate Integer values to Label
74     * keys.
75     */
76    private final Map labels;
77
78    /**
79     * Code of the visit method to be used for each opcode.
80     */
81    private static final int[] TYPE;
82
83    static {
84        String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
85                + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
86                + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
87                + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
88        TYPE = new int[s.length()];
89        for (int i = 0; i < TYPE.length; ++i) {
90            TYPE[i] = s.charAt(i) - 'A' - 1;
91        }
92    }
93
94    // code to generate the above string
95    // public static void main (String[] args) {
96    // int[] TYPE = new int[] {
97    // 0, //NOP
98    // 0, //ACONST_NULL
99    // 0, //ICONST_M1
100    // 0, //ICONST_0
101    // 0, //ICONST_1
102    // 0, //ICONST_2
103    // 0, //ICONST_3
104    // 0, //ICONST_4
105    // 0, //ICONST_5
106    // 0, //LCONST_0
107    // 0, //LCONST_1
108    // 0, //FCONST_0
109    // 0, //FCONST_1
110    // 0, //FCONST_2
111    // 0, //DCONST_0
112    // 0, //DCONST_1
113    // 1, //BIPUSH
114    // 1, //SIPUSH
115    // 7, //LDC
116    // -1, //LDC_W
117    // -1, //LDC2_W
118    // 2, //ILOAD
119    // 2, //LLOAD
120    // 2, //FLOAD
121    // 2, //DLOAD
122    // 2, //ALOAD
123    // -1, //ILOAD_0
124    // -1, //ILOAD_1
125    // -1, //ILOAD_2
126    // -1, //ILOAD_3
127    // -1, //LLOAD_0
128    // -1, //LLOAD_1
129    // -1, //LLOAD_2
130    // -1, //LLOAD_3
131    // -1, //FLOAD_0
132    // -1, //FLOAD_1
133    // -1, //FLOAD_2
134    // -1, //FLOAD_3
135    // -1, //DLOAD_0
136    // -1, //DLOAD_1
137    // -1, //DLOAD_2
138    // -1, //DLOAD_3
139    // -1, //ALOAD_0
140    // -1, //ALOAD_1
141    // -1, //ALOAD_2
142    // -1, //ALOAD_3
143    // 0, //IALOAD
144    // 0, //LALOAD
145    // 0, //FALOAD
146    // 0, //DALOAD
147    // 0, //AALOAD
148    // 0, //BALOAD
149    // 0, //CALOAD
150    // 0, //SALOAD
151    // 2, //ISTORE
152    // 2, //LSTORE
153    // 2, //FSTORE
154    // 2, //DSTORE
155    // 2, //ASTORE
156    // -1, //ISTORE_0
157    // -1, //ISTORE_1
158    // -1, //ISTORE_2
159    // -1, //ISTORE_3
160    // -1, //LSTORE_0
161    // -1, //LSTORE_1
162    // -1, //LSTORE_2
163    // -1, //LSTORE_3
164    // -1, //FSTORE_0
165    // -1, //FSTORE_1
166    // -1, //FSTORE_2
167    // -1, //FSTORE_3
168    // -1, //DSTORE_0
169    // -1, //DSTORE_1
170    // -1, //DSTORE_2
171    // -1, //DSTORE_3
172    // -1, //ASTORE_0
173    // -1, //ASTORE_1
174    // -1, //ASTORE_2
175    // -1, //ASTORE_3
176    // 0, //IASTORE
177    // 0, //LASTORE
178    // 0, //FASTORE
179    // 0, //DASTORE
180    // 0, //AASTORE
181    // 0, //BASTORE
182    // 0, //CASTORE
183    // 0, //SASTORE
184    // 0, //POP
185    // 0, //POP2
186    // 0, //DUP
187    // 0, //DUP_X1
188    // 0, //DUP_X2
189    // 0, //DUP2
190    // 0, //DUP2_X1
191    // 0, //DUP2_X2
192    // 0, //SWAP
193    // 0, //IADD
194    // 0, //LADD
195    // 0, //FADD
196    // 0, //DADD
197    // 0, //ISUB
198    // 0, //LSUB
199    // 0, //FSUB
200    // 0, //DSUB
201    // 0, //IMUL
202    // 0, //LMUL
203    // 0, //FMUL
204    // 0, //DMUL
205    // 0, //IDIV
206    // 0, //LDIV
207    // 0, //FDIV
208    // 0, //DDIV
209    // 0, //IREM
210    // 0, //LREM
211    // 0, //FREM
212    // 0, //DREM
213    // 0, //INEG
214    // 0, //LNEG
215    // 0, //FNEG
216    // 0, //DNEG
217    // 0, //ISHL
218    // 0, //LSHL
219    // 0, //ISHR
220    // 0, //LSHR
221    // 0, //IUSHR
222    // 0, //LUSHR
223    // 0, //IAND
224    // 0, //LAND
225    // 0, //IOR
226    // 0, //LOR
227    // 0, //IXOR
228    // 0, //LXOR
229    // 8, //IINC
230    // 0, //I2L
231    // 0, //I2F
232    // 0, //I2D
233    // 0, //L2I
234    // 0, //L2F
235    // 0, //L2D
236    // 0, //F2I
237    // 0, //F2L
238    // 0, //F2D
239    // 0, //D2I
240    // 0, //D2L
241    // 0, //D2F
242    // 0, //I2B
243    // 0, //I2C
244    // 0, //I2S
245    // 0, //LCMP
246    // 0, //FCMPL
247    // 0, //FCMPG
248    // 0, //DCMPL
249    // 0, //DCMPG
250    // 6, //IFEQ
251    // 6, //IFNE
252    // 6, //IFLT
253    // 6, //IFGE
254    // 6, //IFGT
255    // 6, //IFLE
256    // 6, //IF_ICMPEQ
257    // 6, //IF_ICMPNE
258    // 6, //IF_ICMPLT
259    // 6, //IF_ICMPGE
260    // 6, //IF_ICMPGT
261    // 6, //IF_ICMPLE
262    // 6, //IF_ACMPEQ
263    // 6, //IF_ACMPNE
264    // 6, //GOTO
265    // 6, //JSR
266    // 2, //RET
267    // 9, //TABLESWITCH
268    // 10, //LOOKUPSWITCH
269    // 0, //IRETURN
270    // 0, //LRETURN
271    // 0, //FRETURN
272    // 0, //DRETURN
273    // 0, //ARETURN
274    // 0, //RETURN
275    // 4, //GETSTATIC
276    // 4, //PUTSTATIC
277    // 4, //GETFIELD
278    // 4, //PUTFIELD
279    // 5, //INVOKEVIRTUAL
280    // 5, //INVOKESPECIAL
281    // 5, //INVOKESTATIC
282    // 5, //INVOKEINTERFACE
283    // -1, //UNUSED
284    // 3, //NEW
285    // 1, //NEWARRAY
286    // 3, //ANEWARRAY
287    // 0, //ARRAYLENGTH
288    // 0, //ATHROW
289    // 3, //CHECKCAST
290    // 3, //INSTANCEOF
291    // 0, //MONITORENTER
292    // 0, //MONITOREXIT
293    // -1, //WIDE
294    // 11, //MULTIANEWARRAY
295    // 6, //IFNULL
296    // 6, //IFNONNULL
297    // -1, //GOTO_W
298    // -1 //JSR_W
299    // };
300    // for (int i = 0; i < TYPE.length; ++i) {
301    // System.out.print((char)(TYPE[i] + 1 + 'A'));
302    // }
303    // System.out.println();
304    // }
305
306    /**
307     * Constructs a new {@link CheckMethodAdapter} object.
308     *
309     * @param cv the code visitor to which this adapter must delegate calls.
310     */
311    public CheckMethodAdapter(final MethodVisitor cv) {
312        super(cv);
313        this.labels = new HashMap();
314    }
315
316    public AnnotationVisitor visitAnnotation(
317        final String desc,
318        final boolean visible)
319    {
320        checkEndMethod();
321        checkDesc(desc, false);
322        return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible));
323    }
324
325    public AnnotationVisitor visitAnnotationDefault() {
326        checkEndMethod();
327        return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false);
328    }
329
330    public AnnotationVisitor visitParameterAnnotation(
331        final int parameter,
332        final String desc,
333        final boolean visible)
334    {
335        checkEndMethod();
336        checkDesc(desc, false);
337        return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter,
338                desc,
339                visible));
340    }
341
342    public void visitAttribute(final Attribute attr) {
343        checkEndMethod();
344        if (attr == null) {
345            throw new IllegalArgumentException("Invalid attribute (must not be null)");
346        }
347        mv.visitAttribute(attr);
348    }
349
350    public void visitCode() {
351        startCode = true;
352        mv.visitCode();
353    }
354
355    public void visitFrame(
356        final int type,
357        final int nLocal,
358        final Object[] local,
359        final int nStack,
360        final Object[] stack)
361    {
362        int mLocal;
363        int mStack;
364        switch (type) {
365            case Opcodes.F_NEW:
366            case Opcodes.F_FULL:
367                mLocal = Integer.MAX_VALUE;
368                mStack = Integer.MAX_VALUE;
369                break;
370
371            case Opcodes.F_SAME:
372                mLocal = 0;
373                mStack = 0;
374                break;
375
376            case Opcodes.F_SAME1:
377                mLocal = 0;
378                mStack = 1;
379                break;
380
381            case Opcodes.F_APPEND:
382            case Opcodes.F_CHOP:
383                mLocal = 3;
384                mStack = 0;
385                break;
386
387            default:
388                throw new IllegalArgumentException("Invalid frame type " + type);
389        }
390
391        if (nLocal > mLocal) {
392            throw new IllegalArgumentException("Invalid nLocal=" + nLocal
393                    + " for frame type " + type);
394        }
395        if (nStack > mStack) {
396            throw new IllegalArgumentException("Invalid nStack=" + nStack
397                    + " for frame type " + type);
398        }
399
400        if (type != Opcodes.F_CHOP) {
401            if (nLocal > 0 && (local == null || local.length < nLocal)) {
402                throw new IllegalArgumentException("Array local[] is shorter than nLocal");
403            }
404            for (int i = 0; i < nLocal; ++i) {
405                checkFrameValue(local[i]);
406            }
407        }
408        if (nStack > 0 && (stack == null || stack.length < nStack)) {
409            throw new IllegalArgumentException("Array stack[] is shorter than nStack");
410        }
411        for (int i = 0; i < nStack; ++i) {
412            checkFrameValue(stack[i]);
413        }
414
415        mv.visitFrame(type, nLocal, local, nStack, stack);
416    }
417
418    public void visitInsn(final int opcode) {
419        checkStartCode();
420        checkEndCode();
421        checkOpcode(opcode, 0);
422        mv.visitInsn(opcode);
423    }
424
425    public void visitIntInsn(final int opcode, final int operand) {
426        checkStartCode();
427        checkEndCode();
428        checkOpcode(opcode, 1);
429        switch (opcode) {
430            case Opcodes.BIPUSH:
431                checkSignedByte(operand, "Invalid operand");
432                break;
433            case Opcodes.SIPUSH:
434                checkSignedShort(operand, "Invalid operand");
435                break;
436            // case Constants.NEWARRAY:
437            default:
438                if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
439                    throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): "
440                            + operand);
441                }
442        }
443        mv.visitIntInsn(opcode, operand);
444    }
445
446    public void visitVarInsn(final int opcode, final int var) {
447        checkStartCode();
448        checkEndCode();
449        checkOpcode(opcode, 2);
450        checkUnsignedShort(var, "Invalid variable index");
451        mv.visitVarInsn(opcode, var);
452    }
453
454    public void visitTypeInsn(final int opcode, final String type) {
455        checkStartCode();
456        checkEndCode();
457        checkOpcode(opcode, 3);
458        checkInternalName(type, "type");
459        if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
460            throw new IllegalArgumentException("NEW cannot be used to create arrays: "
461                    + type);
462        }
463        mv.visitTypeInsn(opcode, type);
464    }
465
466    public void visitFieldInsn(
467        final int opcode,
468        final String owner,
469        final String name,
470        final String desc)
471    {
472        checkStartCode();
473        checkEndCode();
474        checkOpcode(opcode, 4);
475        checkInternalName(owner, "owner");
476        checkIdentifier(name, "name");
477        checkDesc(desc, false);
478        mv.visitFieldInsn(opcode, owner, name, desc);
479    }
480
481    public void visitMethodInsn(
482        final int opcode,
483        final String owner,
484        final String name,
485        final String desc)
486    {
487        checkStartCode();
488        checkEndCode();
489        checkOpcode(opcode, 5);
490        checkMethodIdentifier(name, "name");
491        checkInternalName(owner, "owner");
492        checkMethodDesc(desc);
493        mv.visitMethodInsn(opcode, owner, name, desc);
494    }
495
496    public void visitJumpInsn(final int opcode, final Label label) {
497        checkStartCode();
498        checkEndCode();
499        checkOpcode(opcode, 6);
500        checkLabel(label, false, "label");
501        mv.visitJumpInsn(opcode, label);
502    }
503
504    public void visitLabel(final Label label) {
505        checkStartCode();
506        checkEndCode();
507        checkLabel(label, false, "label");
508        if (labels.get(label) != null) {
509            throw new IllegalArgumentException("Already visited label");
510        }
511        labels.put(label, new Integer(labels.size()));
512        mv.visitLabel(label);
513    }
514
515    public void visitLdcInsn(final Object cst) {
516        checkStartCode();
517        checkEndCode();
518        if (!(cst instanceof Type)) {
519            checkConstant(cst);
520        }
521        mv.visitLdcInsn(cst);
522    }
523
524    public void visitIincInsn(final int var, final int increment) {
525        checkStartCode();
526        checkEndCode();
527        checkUnsignedShort(var, "Invalid variable index");
528        checkSignedShort(increment, "Invalid increment");
529        mv.visitIincInsn(var, increment);
530    }
531
532    public void visitTableSwitchInsn(
533        final int min,
534        final int max,
535        final Label dflt,
536        final Label[] labels)
537    {
538        checkStartCode();
539        checkEndCode();
540        if (max < min) {
541            throw new IllegalArgumentException("Max = " + max
542                    + " must be greater than or equal to min = " + min);
543        }
544        checkLabel(dflt, false, "default label");
545        if (labels == null || labels.length != max - min + 1) {
546            throw new IllegalArgumentException("There must be max - min + 1 labels");
547        }
548        for (int i = 0; i < labels.length; ++i) {
549            checkLabel(labels[i], false, "label at index " + i);
550        }
551        mv.visitTableSwitchInsn(min, max, dflt, labels);
552    }
553
554    public void visitLookupSwitchInsn(
555        final Label dflt,
556        final int[] keys,
557        final Label[] labels)
558    {
559        checkEndCode();
560        checkStartCode();
561        checkLabel(dflt, false, "default label");
562        if (keys == null || labels == null || keys.length != labels.length) {
563            throw new IllegalArgumentException("There must be the same number of keys and labels");
564        }
565        for (int i = 0; i < labels.length; ++i) {
566            checkLabel(labels[i], false, "label at index " + i);
567        }
568        mv.visitLookupSwitchInsn(dflt, keys, labels);
569    }
570
571    public void visitMultiANewArrayInsn(final String desc, final int dims) {
572        checkStartCode();
573        checkEndCode();
574        checkDesc(desc, false);
575        if (desc.charAt(0) != '[') {
576            throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): "
577                    + desc);
578        }
579        if (dims < 1) {
580            throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): "
581                    + dims);
582        }
583        if (dims > desc.lastIndexOf('[') + 1) {
584            throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): "
585                    + dims);
586        }
587        mv.visitMultiANewArrayInsn(desc, dims);
588    }
589
590    public void visitTryCatchBlock(
591        final Label start,
592        final Label end,
593        final Label handler,
594        final String type)
595    {
596        checkStartCode();
597        checkEndCode();
598        if (type != null) {
599            checkInternalName(type, "type");
600        }
601        mv.visitTryCatchBlock(start, end, handler, type);
602    }
603
604    public void visitLocalVariable(
605        final String name,
606        final String desc,
607        final String signature,
608        final Label start,
609        final Label end,
610        final int index)
611    {
612        checkStartCode();
613        checkEndCode();
614        checkIdentifier(name, "name");
615        checkDesc(desc, false);
616        checkLabel(start, true, "start label");
617        checkLabel(end, true, "end label");
618        checkUnsignedShort(index, "Invalid variable index");
619        int s = ((Integer) labels.get(start)).intValue();
620        int e = ((Integer) labels.get(end)).intValue();
621        if (e < s) {
622            throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)");
623        }
624        mv.visitLocalVariable(name, desc, signature, start, end, index);
625    }
626
627    public void visitLineNumber(final int line, final Label start) {
628        checkStartCode();
629        checkEndCode();
630        checkUnsignedShort(line, "Invalid line number");
631        checkLabel(start, true, "start label");
632        mv.visitLineNumber(line, start);
633    }
634
635    public void visitMaxs(final int maxStack, final int maxLocals) {
636        checkStartCode();
637        checkEndCode();
638        endCode = true;
639        checkUnsignedShort(maxStack, "Invalid max stack");
640        checkUnsignedShort(maxLocals, "Invalid max locals");
641        mv.visitMaxs(maxStack, maxLocals);
642    }
643
644    public void visitEnd() {
645        checkEndMethod();
646        endMethod = true;
647        mv.visitEnd();
648    }
649
650    // -------------------------------------------------------------------------
651
652    /**
653     * Checks that the visitCode method has been called.
654     */
655    void checkStartCode() {
656        if (!startCode) {
657            throw new IllegalStateException("Cannot visit instructions before visitCode has been called.");
658        }
659    }
660
661    /**
662     * Checks that the visitMaxs method has not been called.
663     */
664    void checkEndCode() {
665        if (endCode) {
666            throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
667        }
668    }
669
670    /**
671     * Checks that the visitEnd method has not been called.
672     */
673    void checkEndMethod() {
674        if (endMethod) {
675            throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
676        }
677    }
678
679    /**
680     * Checks a stack frame value.
681     *
682     * @param value the value to be checked.
683     */
684    static void checkFrameValue(final Object value) {
685        if (value == Opcodes.TOP || value == Opcodes.INTEGER
686                || value == Opcodes.FLOAT || value == Opcodes.LONG
687                || value == Opcodes.DOUBLE || value == Opcodes.NULL
688                || value == Opcodes.UNINITIALIZED_THIS)
689        {
690            return;
691        }
692        if (value instanceof String) {
693            checkInternalName((String) value, "Invalid stack frame value");
694            return;
695        }
696        if (!(value instanceof Label)) {
697            throw new IllegalArgumentException("Invalid stack frame value: "
698                    + value);
699        }
700    }
701
702    /**
703     * Checks that the type of the given opcode is equal to the given type.
704     *
705     * @param opcode the opcode to be checked.
706     * @param type the expected opcode type.
707     */
708    static void checkOpcode(final int opcode, final int type) {
709        if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
710            throw new IllegalArgumentException("Invalid opcode: " + opcode);
711        }
712    }
713
714    /**
715     * Checks that the given value is a signed byte.
716     *
717     * @param value the value to be checked.
718     * @param msg an message to be used in case of error.
719     */
720    static void checkSignedByte(final int value, final String msg) {
721        if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
722            throw new IllegalArgumentException(msg
723                    + " (must be a signed byte): " + value);
724        }
725    }
726
727    /**
728     * Checks that the given value is a signed short.
729     *
730     * @param value the value to be checked.
731     * @param msg an message to be used in case of error.
732     */
733    static void checkSignedShort(final int value, final String msg) {
734        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
735            throw new IllegalArgumentException(msg
736                    + " (must be a signed short): " + value);
737        }
738    }
739
740    /**
741     * Checks that the given value is an unsigned short.
742     *
743     * @param value the value to be checked.
744     * @param msg an message to be used in case of error.
745     */
746    static void checkUnsignedShort(final int value, final String msg) {
747        if (value < 0 || value > 65535) {
748            throw new IllegalArgumentException(msg
749                    + " (must be an unsigned short): " + value);
750        }
751    }
752
753    /**
754     * Checks that the given value is an {@link Integer}, a{@link Float}, a
755     * {@link Long}, a {@link Double} or a {@link String}.
756     *
757     * @param cst the value to be checked.
758     */
759    static void checkConstant(final Object cst) {
760        if (!(cst instanceof Integer) && !(cst instanceof Float)
761                && !(cst instanceof Long) && !(cst instanceof Double)
762                && !(cst instanceof String))
763        {
764            throw new IllegalArgumentException("Invalid constant: " + cst);
765        }
766    }
767
768    /**
769     * Checks that the given string is a valid Java identifier.
770     *
771     * @param name the string to be checked.
772     * @param msg a message to be used in case of error.
773     */
774    static void checkIdentifier(final String name, final String msg) {
775        checkIdentifier(name, 0, -1, msg);
776    }
777
778    /**
779     * Checks that the given substring is a valid Java identifier.
780     *
781     * @param name the string to be checked.
782     * @param start index of the first character of the identifier (inclusive).
783     * @param end index of the last character of the identifier (exclusive). -1
784     *        is equivalent to <tt>name.length()</tt> if name is not
785     *        <tt>null</tt>.
786     * @param msg a message to be used in case of error.
787     */
788    static void checkIdentifier(
789        final String name,
790        final int start,
791        final int end,
792        final String msg)
793    {
794        if (name == null || (end == -1 ? name.length() <= start : end <= start))
795        {
796            throw new IllegalArgumentException("Invalid " + msg
797                    + " (must not be null or empty)");
798        }
799        if (!Character.isJavaIdentifierStart(name.charAt(start))) {
800            throw new IllegalArgumentException("Invalid " + msg
801                    + " (must be a valid Java identifier): " + name);
802        }
803        int max = end == -1 ? name.length() : end;
804        for (int i = start + 1; i < max; ++i) {
805            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
806                throw new IllegalArgumentException("Invalid " + msg
807                        + " (must be a valid Java identifier): " + name);
808            }
809        }
810    }
811
812    /**
813     * Checks that the given string is a valid Java identifier or is equal to
814     * '&lt;init&gt;' or '&lt;clinit&gt;'.
815     *
816     * @param name the string to be checked.
817     * @param msg a message to be used in case of error.
818     */
819    static void checkMethodIdentifier(final String name, final String msg) {
820        if (name == null || name.length() == 0) {
821            throw new IllegalArgumentException("Invalid " + msg
822                    + " (must not be null or empty)");
823        }
824        if ("<init>".equals(name) || "<clinit>".equals(name)) {
825            return;
826        }
827        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
828            throw new IllegalArgumentException("Invalid "
829                    + msg
830                    + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
831                    + name);
832        }
833        for (int i = 1; i < name.length(); ++i) {
834            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
835                throw new IllegalArgumentException("Invalid "
836                        + msg
837                        + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
838                        + name);
839            }
840        }
841    }
842
843    /**
844     * Checks that the given string is a valid internal class name.
845     *
846     * @param name the string to be checked.
847     * @param msg a message to be used in case of error.
848     */
849    static void checkInternalName(final String name, final String msg) {
850        if (name == null || name.length() == 0) {
851            throw new IllegalArgumentException("Invalid " + msg
852                    + " (must not be null or empty)");
853        }
854        if (name.charAt(0) == '[') {
855            checkDesc(name, false);
856        } else {
857            checkInternalName(name, 0, -1, msg);
858        }
859    }
860
861    /**
862     * Checks that the given substring is a valid internal class name.
863     *
864     * @param name the string to be checked.
865     * @param start index of the first character of the identifier (inclusive).
866     * @param end index of the last character of the identifier (exclusive). -1
867     *        is equivalent to <tt>name.length()</tt> if name is not
868     *        <tt>null</tt>.
869     * @param msg a message to be used in case of error.
870     */
871    static void checkInternalName(
872        final String name,
873        final int start,
874        final int end,
875        final String msg)
876    {
877        int max = end == -1 ? name.length() : end;
878        try {
879            int begin = start;
880            int slash;
881            do {
882                slash = name.indexOf('/', begin + 1);
883                if (slash == -1 || slash > max) {
884                    slash = max;
885                }
886                checkIdentifier(name, begin, slash, null);
887                begin = slash + 1;
888            } while (slash != max);
889        } catch (IllegalArgumentException _) {
890            throw new IllegalArgumentException("Invalid "
891                    + msg
892                    + " (must be a fully qualified class name in internal form): "
893                    + name);
894        }
895    }
896
897    /**
898     * Checks that the given string is a valid type descriptor.
899     *
900     * @param desc the string to be checked.
901     * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
902     */
903    static void checkDesc(final String desc, final boolean canBeVoid) {
904        int end = checkDesc(desc, 0, canBeVoid);
905        if (end != desc.length()) {
906            throw new IllegalArgumentException("Invalid descriptor: " + desc);
907        }
908    }
909
910    /**
911     * Checks that a the given substring is a valid type descriptor.
912     *
913     * @param desc the string to be checked.
914     * @param start index of the first character of the identifier (inclusive).
915     * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
916     * @return the index of the last character of the type decriptor, plus one.
917     */
918    static int checkDesc(
919        final String desc,
920        final int start,
921        final boolean canBeVoid)
922    {
923        if (desc == null || start >= desc.length()) {
924            throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
925        }
926        int index;
927        switch (desc.charAt(start)) {
928            case 'V':
929                if (canBeVoid) {
930                    return start + 1;
931                } else {
932                    throw new IllegalArgumentException("Invalid descriptor: "
933                            + desc);
934                }
935            case 'Z':
936            case 'C':
937            case 'B':
938            case 'S':
939            case 'I':
940            case 'F':
941            case 'J':
942            case 'D':
943                return start + 1;
944            case '[':
945                index = start + 1;
946                while (index < desc.length() && desc.charAt(index) == '[') {
947                    ++index;
948                }
949                if (index < desc.length()) {
950                    return checkDesc(desc, index, false);
951                } else {
952                    throw new IllegalArgumentException("Invalid descriptor: "
953                            + desc);
954                }
955            case 'L':
956                index = desc.indexOf(';', start);
957                if (index == -1 || index - start < 2) {
958                    throw new IllegalArgumentException("Invalid descriptor: "
959                            + desc);
960                }
961                try {
962                    checkInternalName(desc, start + 1, index, null);
963                } catch (IllegalArgumentException _) {
964                    throw new IllegalArgumentException("Invalid descriptor: "
965                            + desc);
966                }
967                return index + 1;
968            default:
969                throw new IllegalArgumentException("Invalid descriptor: "
970                        + desc);
971        }
972    }
973
974    /**
975     * Checks that the given string is a valid method descriptor.
976     *
977     * @param desc the string to be checked.
978     */
979    static void checkMethodDesc(final String desc) {
980        if (desc == null || desc.length() == 0) {
981            throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
982        }
983        if (desc.charAt(0) != '(' || desc.length() < 3) {
984            throw new IllegalArgumentException("Invalid descriptor: " + desc);
985        }
986        int start = 1;
987        if (desc.charAt(start) != ')') {
988            do {
989                if (desc.charAt(start) == 'V') {
990                    throw new IllegalArgumentException("Invalid descriptor: "
991                            + desc);
992                }
993                start = checkDesc(desc, start, false);
994            } while (start < desc.length() && desc.charAt(start) != ')');
995        }
996        start = checkDesc(desc, start + 1, true);
997        if (start != desc.length()) {
998            throw new IllegalArgumentException("Invalid descriptor: " + desc);
999        }
1000    }
1001
1002    /**
1003     * Checks a class signature.
1004     *
1005     * @param signature a string containing the signature that must be checked.
1006     */
1007    static void checkClassSignature(final String signature) {
1008        // ClassSignature:
1009        // FormalTypeParameters? ClassTypeSignature ClassTypeSignature*
1010
1011        int pos = 0;
1012        if (getChar(signature, 0) == '<') {
1013            pos = checkFormalTypeParameters(signature, pos);
1014        }
1015        pos = checkClassTypeSignature(signature, pos);
1016        while (getChar(signature, pos) == 'L') {
1017            pos = checkClassTypeSignature(signature, pos);
1018        }
1019        if (pos != signature.length()) {
1020            throw new IllegalArgumentException(signature + ": error at index "
1021                    + pos);
1022        }
1023    }
1024
1025    /**
1026     * Checks a method signature.
1027     *
1028     * @param signature a string containing the signature that must be checked.
1029     */
1030    static void checkMethodSignature(final String signature) {
1031        // MethodTypeSignature:
1032        // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) (
1033        // ^ClassTypeSignature | ^TypeVariableSignature )*
1034
1035        int pos = 0;
1036        if (getChar(signature, 0) == '<') {
1037            pos = checkFormalTypeParameters(signature, pos);
1038        }
1039        pos = checkChar('(', signature, pos);
1040        while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
1041            pos = checkTypeSignature(signature, pos);
1042        }
1043        pos = checkChar(')', signature, pos);
1044        if (getChar(signature, pos) == 'V') {
1045            ++pos;
1046        } else {
1047            pos = checkTypeSignature(signature, pos);
1048        }
1049        while (getChar(signature, pos) == '^') {
1050            ++pos;
1051            if (getChar(signature, pos) == 'L') {
1052                pos = checkClassTypeSignature(signature, pos);
1053            } else {
1054                pos = checkTypeVariableSignature(signature, pos);
1055            }
1056        }
1057        if (pos != signature.length()) {
1058            throw new IllegalArgumentException(signature + ": error at index "
1059                    + pos);
1060        }
1061    }
1062
1063    /**
1064     * Checks a field signature.
1065     *
1066     * @param signature a string containing the signature that must be checked.
1067     */
1068    static void checkFieldSignature(final String signature) {
1069        int pos = checkFieldTypeSignature(signature, 0);
1070        if (pos != signature.length()) {
1071            throw new IllegalArgumentException(signature + ": error at index "
1072                    + pos);
1073        }
1074    }
1075
1076    /**
1077     * Checks the formal type parameters of a class or method signature.
1078     *
1079     * @param signature a string containing the signature that must be checked.
1080     * @param pos index of first character to be checked.
1081     * @return the index of the first character after the checked part.
1082     */
1083    private static int checkFormalTypeParameters(final String signature, int pos)
1084    {
1085        // FormalTypeParameters:
1086        // < FormalTypeParameter+ >
1087
1088        pos = checkChar('<', signature, pos);
1089        pos = checkFormalTypeParameter(signature, pos);
1090        while (getChar(signature, pos) != '>') {
1091            pos = checkFormalTypeParameter(signature, pos);
1092        }
1093        return pos + 1;
1094    }
1095
1096    /**
1097     * Checks a formal type parameter of a class or method signature.
1098     *
1099     * @param signature a string containing the signature that must be checked.
1100     * @param pos index of first character to be checked.
1101     * @return the index of the first character after the checked part.
1102     */
1103    private static int checkFormalTypeParameter(final String signature, int pos)
1104    {
1105        // FormalTypeParameter:
1106        // Identifier : FieldTypeSignature? (: FieldTypeSignature)*
1107
1108        pos = checkIdentifier(signature, pos);
1109        pos = checkChar(':', signature, pos);
1110        if ("L[T".indexOf(getChar(signature, pos)) != -1) {
1111            pos = checkFieldTypeSignature(signature, pos);
1112        }
1113        while (getChar(signature, pos) == ':') {
1114            pos = checkFieldTypeSignature(signature, pos + 1);
1115        }
1116        return pos;
1117    }
1118
1119    /**
1120     * Checks a field type signature.
1121     *
1122     * @param signature a string containing the signature that must be checked.
1123     * @param pos index of first character to be checked.
1124     * @return the index of the first character after the checked part.
1125     */
1126    private static int checkFieldTypeSignature(final String signature, int pos)
1127    {
1128        // FieldTypeSignature:
1129        // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature
1130        //
1131        // ArrayTypeSignature:
1132        // [ TypeSignature
1133
1134        switch (getChar(signature, pos)) {
1135            case 'L':
1136                return checkClassTypeSignature(signature, pos);
1137            case '[':
1138                return checkTypeSignature(signature, pos + 1);
1139            default:
1140                return checkTypeVariableSignature(signature, pos);
1141        }
1142    }
1143
1144    /**
1145     * Checks a class type signature.
1146     *
1147     * @param signature a string containing the signature that must be checked.
1148     * @param pos index of first character to be checked.
1149     * @return the index of the first character after the checked part.
1150     */
1151    private static int checkClassTypeSignature(final String signature, int pos)
1152    {
1153        // ClassTypeSignature:
1154        // L Identifier ( / Identifier )* TypeArguments? ( . Identifier
1155        // TypeArguments? )* ;
1156
1157        pos = checkChar('L', signature, pos);
1158        pos = checkIdentifier(signature, pos);
1159        while (getChar(signature, pos) == '/') {
1160            pos = checkIdentifier(signature, pos + 1);
1161        }
1162        if (getChar(signature, pos) == '<') {
1163            pos = checkTypeArguments(signature, pos);
1164        }
1165        while (getChar(signature, pos) == '.') {
1166            pos = checkIdentifier(signature, pos + 1);
1167            if (getChar(signature, pos) == '<') {
1168                pos = checkTypeArguments(signature, pos);
1169            }
1170        }
1171        return checkChar(';', signature, pos);
1172    }
1173
1174    /**
1175     * Checks the type arguments in a class type signature.
1176     *
1177     * @param signature a string containing the signature that must be checked.
1178     * @param pos index of first character to be checked.
1179     * @return the index of the first character after the checked part.
1180     */
1181    private static int checkTypeArguments(final String signature, int pos) {
1182        // TypeArguments:
1183        // < TypeArgument+ >
1184
1185        pos = checkChar('<', signature, pos);
1186        pos = checkTypeArgument(signature, pos);
1187        while (getChar(signature, pos) != '>') {
1188            pos = checkTypeArgument(signature, pos);
1189        }
1190        return pos + 1;
1191    }
1192
1193    /**
1194     * Checks a type argument in a class type signature.
1195     *
1196     * @param signature a string containing the signature that must be checked.
1197     * @param pos index of first character to be checked.
1198     * @return the index of the first character after the checked part.
1199     */
1200    private static int checkTypeArgument(final String signature, int pos) {
1201        // TypeArgument:
1202        // * | ( ( + | - )? FieldTypeSignature )
1203
1204        char c = getChar(signature, pos);
1205        if (c == '*') {
1206            return pos + 1;
1207        } else if (c == '+' || c == '-') {
1208            pos++;
1209        }
1210        return checkFieldTypeSignature(signature, pos);
1211    }
1212
1213    /**
1214     * Checks a type variable signature.
1215     *
1216     * @param signature a string containing the signature that must be checked.
1217     * @param pos index of first character to be checked.
1218     * @return the index of the first character after the checked part.
1219     */
1220    private static int checkTypeVariableSignature(
1221        final String signature,
1222        int pos)
1223    {
1224        // TypeVariableSignature:
1225        // T Identifier ;
1226
1227        pos = checkChar('T', signature, pos);
1228        pos = checkIdentifier(signature, pos);
1229        return checkChar(';', signature, pos);
1230    }
1231
1232    /**
1233     * Checks a type signature.
1234     *
1235     * @param signature a string containing the signature that must be checked.
1236     * @param pos index of first character to be checked.
1237     * @return the index of the first character after the checked part.
1238     */
1239    private static int checkTypeSignature(final String signature, int pos) {
1240        // TypeSignature:
1241        // Z | C | B | S | I | F | J | D | FieldTypeSignature
1242
1243        switch (getChar(signature, pos)) {
1244            case 'Z':
1245            case 'C':
1246            case 'B':
1247            case 'S':
1248            case 'I':
1249            case 'F':
1250            case 'J':
1251            case 'D':
1252                return pos + 1;
1253            default:
1254                return checkFieldTypeSignature(signature, pos);
1255        }
1256    }
1257
1258    /**
1259     * Checks an identifier.
1260     *
1261     * @param signature a string containing the signature that must be checked.
1262     * @param pos index of first character to be checked.
1263     * @return the index of the first character after the checked part.
1264     */
1265    private static int checkIdentifier(final String signature, int pos) {
1266        if (!Character.isJavaIdentifierStart(getChar(signature, pos))) {
1267            throw new IllegalArgumentException(signature
1268                    + ": identifier expected at index " + pos);
1269        }
1270        ++pos;
1271        while (Character.isJavaIdentifierPart(getChar(signature, pos))) {
1272            ++pos;
1273        }
1274        return pos;
1275    }
1276
1277    /**
1278     * Checks a single character.
1279     *
1280     * @param signature a string containing the signature that must be checked.
1281     * @param pos index of first character to be checked.
1282     * @return the index of the first character after the checked part.
1283     */
1284    private static int checkChar(final char c, final String signature, int pos)
1285    {
1286        if (getChar(signature, pos) == c) {
1287            return pos + 1;
1288        }
1289        throw new IllegalArgumentException(signature + ": '" + c
1290                + "' expected at index " + pos);
1291    }
1292
1293    /**
1294     * Returns the signature car at the given index.
1295     *
1296     * @param signature a signature.
1297     * @param pos an index in signature.
1298     * @return the character at the given index, or 0 if there is no such
1299     *         character.
1300     */
1301    private static char getChar(final String signature, int pos) {
1302        return pos < signature.length() ? signature.charAt(pos) : (char) 0;
1303    }
1304
1305    /**
1306     * Checks that the given label is not null. This method can also check that
1307     * the label has been visited.
1308     *
1309     * @param label the label to be checked.
1310     * @param checkVisited <tt>true</tt> to check that the label has been
1311     *        visited.
1312     * @param msg a message to be used in case of error.
1313     */
1314    void checkLabel(
1315        final Label label,
1316        final boolean checkVisited,
1317        final String msg)
1318    {
1319        if (label == null) {
1320            throw new IllegalArgumentException("Invalid " + msg
1321                    + " (must not be null)");
1322        }
1323        if (checkVisited && labels.get(label) == null) {
1324            throw new IllegalArgumentException("Invalid " + msg
1325                    + " (must be visited first)");
1326        }
1327    }
1328}
1329