1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25/*
26 * This file is available under and governed by the GNU General Public
27 * License version 2 only, as published by the Free Software Foundation.
28 * However, the following notice accompanied the original version of this
29 * file:
30 *
31 * ASM: a very small and fast Java bytecode manipulation framework
32 * Copyright (c) 2000-2011 INRIA, France Telecom
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. Neither the name of the copyright holders nor the names of its
44 *    contributors may be used to endorse or promote products derived from
45 *    this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59package jdk.internal.org.objectweb.asm.util;
60
61import java.io.PrintWriter;
62import java.io.StringWriter;
63import java.lang.reflect.Field;
64import java.util.ArrayList;
65import java.util.HashMap;
66import java.util.HashSet;
67import java.util.List;
68import java.util.Map;
69import java.util.Set;
70
71import jdk.internal.org.objectweb.asm.AnnotationVisitor;
72import jdk.internal.org.objectweb.asm.Attribute;
73import jdk.internal.org.objectweb.asm.Handle;
74import jdk.internal.org.objectweb.asm.Label;
75import jdk.internal.org.objectweb.asm.MethodVisitor;
76import jdk.internal.org.objectweb.asm.Opcodes;
77import jdk.internal.org.objectweb.asm.Type;
78import jdk.internal.org.objectweb.asm.TypePath;
79import jdk.internal.org.objectweb.asm.TypeReference;
80import jdk.internal.org.objectweb.asm.tree.MethodNode;
81import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
82import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
83import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier;
84
85/**
86 * A {@link MethodVisitor} that checks that its methods are properly used. More
87 * precisely this method adapter checks each instruction individually, i.e.,
88 * each visit method checks some preconditions based <i>only</i> on its
89 * arguments - such as the fact that the given opcode is correct for a given
90 * visit method. This adapter can also perform some basic data flow checks (more
91 * precisely those that can be performed without the full class hierarchy - see
92 * {@link jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a
93 * method whose signature is <tt>void m ()</tt>, the invalid instruction
94 * IRETURN, or the invalid sequence IADD L2I will be detected if the data flow
95 * checks are enabled. These checks are enabled by using the
96 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor.
97 * They are not performed if any other constructor is used.
98 *
99 * @author Eric Bruneton
100 */
101public class CheckMethodAdapter extends MethodVisitor {
102
103    /**
104     * The class version number.
105     */
106    public int version;
107
108    /**
109     * The access flags of the method.
110     */
111    private int access;
112
113    /**
114     * <tt>true</tt> if the visitCode method has been called.
115     */
116    private boolean startCode;
117
118    /**
119     * <tt>true</tt> if the visitMaxs method has been called.
120     */
121    private boolean endCode;
122
123    /**
124     * <tt>true</tt> if the visitEnd method has been called.
125     */
126    private boolean endMethod;
127
128    /**
129     * Number of visited instructions.
130     */
131    private int insnCount;
132
133    /**
134     * The already visited labels. This map associate Integer values to pseudo
135     * code offsets.
136     */
137    private final Map<Label, Integer> labels;
138
139    /**
140     * The labels used in this method. Every used label must be visited with
141     * visitLabel before the end of the method (i.e. should be in #labels).
142     */
143    private Set<Label> usedLabels;
144
145    /**
146     * Number of visited frames in expanded form.
147     */
148    private int expandedFrames;
149
150    /**
151     * Number of visited frames in compressed form.
152     */
153    private int compressedFrames;
154
155    /**
156     * Number of instructions before the last visited frame.
157     */
158    private int lastFrame = -1;
159
160    /**
161     * The exception handler ranges. Each pair of list element contains the
162     * start and end labels of an exception handler block.
163     */
164    private List<Label> handlers;
165
166    /**
167     * Code of the visit method to be used for each opcode.
168     */
169    private static final int[] TYPE;
170
171    /**
172     * The Label.status field.
173     */
174    private static Field labelStatusField;
175
176    static {
177        String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
178                + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
179                + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
180                + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
181        TYPE = new int[s.length()];
182        for (int i = 0; i < TYPE.length; ++i) {
183            TYPE[i] = s.charAt(i) - 'A' - 1;
184        }
185    }
186
187    // code to generate the above string
188    // public static void main (String[] args) {
189    // int[] TYPE = new int[] {
190    // 0, //NOP
191    // 0, //ACONST_NULL
192    // 0, //ICONST_M1
193    // 0, //ICONST_0
194    // 0, //ICONST_1
195    // 0, //ICONST_2
196    // 0, //ICONST_3
197    // 0, //ICONST_4
198    // 0, //ICONST_5
199    // 0, //LCONST_0
200    // 0, //LCONST_1
201    // 0, //FCONST_0
202    // 0, //FCONST_1
203    // 0, //FCONST_2
204    // 0, //DCONST_0
205    // 0, //DCONST_1
206    // 1, //BIPUSH
207    // 1, //SIPUSH
208    // 7, //LDC
209    // -1, //LDC_W
210    // -1, //LDC2_W
211    // 2, //ILOAD
212    // 2, //LLOAD
213    // 2, //FLOAD
214    // 2, //DLOAD
215    // 2, //ALOAD
216    // -1, //ILOAD_0
217    // -1, //ILOAD_1
218    // -1, //ILOAD_2
219    // -1, //ILOAD_3
220    // -1, //LLOAD_0
221    // -1, //LLOAD_1
222    // -1, //LLOAD_2
223    // -1, //LLOAD_3
224    // -1, //FLOAD_0
225    // -1, //FLOAD_1
226    // -1, //FLOAD_2
227    // -1, //FLOAD_3
228    // -1, //DLOAD_0
229    // -1, //DLOAD_1
230    // -1, //DLOAD_2
231    // -1, //DLOAD_3
232    // -1, //ALOAD_0
233    // -1, //ALOAD_1
234    // -1, //ALOAD_2
235    // -1, //ALOAD_3
236    // 0, //IALOAD
237    // 0, //LALOAD
238    // 0, //FALOAD
239    // 0, //DALOAD
240    // 0, //AALOAD
241    // 0, //BALOAD
242    // 0, //CALOAD
243    // 0, //SALOAD
244    // 2, //ISTORE
245    // 2, //LSTORE
246    // 2, //FSTORE
247    // 2, //DSTORE
248    // 2, //ASTORE
249    // -1, //ISTORE_0
250    // -1, //ISTORE_1
251    // -1, //ISTORE_2
252    // -1, //ISTORE_3
253    // -1, //LSTORE_0
254    // -1, //LSTORE_1
255    // -1, //LSTORE_2
256    // -1, //LSTORE_3
257    // -1, //FSTORE_0
258    // -1, //FSTORE_1
259    // -1, //FSTORE_2
260    // -1, //FSTORE_3
261    // -1, //DSTORE_0
262    // -1, //DSTORE_1
263    // -1, //DSTORE_2
264    // -1, //DSTORE_3
265    // -1, //ASTORE_0
266    // -1, //ASTORE_1
267    // -1, //ASTORE_2
268    // -1, //ASTORE_3
269    // 0, //IASTORE
270    // 0, //LASTORE
271    // 0, //FASTORE
272    // 0, //DASTORE
273    // 0, //AASTORE
274    // 0, //BASTORE
275    // 0, //CASTORE
276    // 0, //SASTORE
277    // 0, //POP
278    // 0, //POP2
279    // 0, //DUP
280    // 0, //DUP_X1
281    // 0, //DUP_X2
282    // 0, //DUP2
283    // 0, //DUP2_X1
284    // 0, //DUP2_X2
285    // 0, //SWAP
286    // 0, //IADD
287    // 0, //LADD
288    // 0, //FADD
289    // 0, //DADD
290    // 0, //ISUB
291    // 0, //LSUB
292    // 0, //FSUB
293    // 0, //DSUB
294    // 0, //IMUL
295    // 0, //LMUL
296    // 0, //FMUL
297    // 0, //DMUL
298    // 0, //IDIV
299    // 0, //LDIV
300    // 0, //FDIV
301    // 0, //DDIV
302    // 0, //IREM
303    // 0, //LREM
304    // 0, //FREM
305    // 0, //DREM
306    // 0, //INEG
307    // 0, //LNEG
308    // 0, //FNEG
309    // 0, //DNEG
310    // 0, //ISHL
311    // 0, //LSHL
312    // 0, //ISHR
313    // 0, //LSHR
314    // 0, //IUSHR
315    // 0, //LUSHR
316    // 0, //IAND
317    // 0, //LAND
318    // 0, //IOR
319    // 0, //LOR
320    // 0, //IXOR
321    // 0, //LXOR
322    // 8, //IINC
323    // 0, //I2L
324    // 0, //I2F
325    // 0, //I2D
326    // 0, //L2I
327    // 0, //L2F
328    // 0, //L2D
329    // 0, //F2I
330    // 0, //F2L
331    // 0, //F2D
332    // 0, //D2I
333    // 0, //D2L
334    // 0, //D2F
335    // 0, //I2B
336    // 0, //I2C
337    // 0, //I2S
338    // 0, //LCMP
339    // 0, //FCMPL
340    // 0, //FCMPG
341    // 0, //DCMPL
342    // 0, //DCMPG
343    // 6, //IFEQ
344    // 6, //IFNE
345    // 6, //IFLT
346    // 6, //IFGE
347    // 6, //IFGT
348    // 6, //IFLE
349    // 6, //IF_ICMPEQ
350    // 6, //IF_ICMPNE
351    // 6, //IF_ICMPLT
352    // 6, //IF_ICMPGE
353    // 6, //IF_ICMPGT
354    // 6, //IF_ICMPLE
355    // 6, //IF_ACMPEQ
356    // 6, //IF_ACMPNE
357    // 6, //GOTO
358    // 6, //JSR
359    // 2, //RET
360    // 9, //TABLESWITCH
361    // 10, //LOOKUPSWITCH
362    // 0, //IRETURN
363    // 0, //LRETURN
364    // 0, //FRETURN
365    // 0, //DRETURN
366    // 0, //ARETURN
367    // 0, //RETURN
368    // 4, //GETSTATIC
369    // 4, //PUTSTATIC
370    // 4, //GETFIELD
371    // 4, //PUTFIELD
372    // 5, //INVOKEVIRTUAL
373    // 5, //INVOKESPECIAL
374    // 5, //INVOKESTATIC
375    // 5, //INVOKEINTERFACE
376    // -1, //INVOKEDYNAMIC
377    // 3, //NEW
378    // 1, //NEWARRAY
379    // 3, //ANEWARRAY
380    // 0, //ARRAYLENGTH
381    // 0, //ATHROW
382    // 3, //CHECKCAST
383    // 3, //INSTANCEOF
384    // 0, //MONITORENTER
385    // 0, //MONITOREXIT
386    // -1, //WIDE
387    // 11, //MULTIANEWARRAY
388    // 6, //IFNULL
389    // 6, //IFNONNULL
390    // -1, //GOTO_W
391    // -1 //JSR_W
392    // };
393    // for (int i = 0; i < TYPE.length; ++i) {
394    // System.out.print((char)(TYPE[i] + 1 + 'A'));
395    // }
396    // System.out.println();
397    // }
398
399    /**
400     * Constructs a new {@link CheckMethodAdapter} object. This method adapter
401     * will not perform any data flow check (see
402     * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
403     * <i>Subclasses must not use this constructor</i>. Instead, they must use
404     * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version.
405     *
406     * @param mv
407     *            the method visitor to which this adapter must delegate calls.
408     */
409    public CheckMethodAdapter(final MethodVisitor mv) {
410        this(mv, new HashMap<Label, Integer>());
411    }
412
413    /**
414     * Constructs a new {@link CheckMethodAdapter} object. This method adapter
415     * will not perform any data flow check (see
416     * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
417     * <i>Subclasses must not use this constructor</i>. Instead, they must use
418     * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version.
419     *
420     * @param mv
421     *            the method visitor to which this adapter must delegate calls.
422     * @param labels
423     *            a map of already visited labels (in other methods).
424     * @throws IllegalStateException
425     *             If a subclass calls this constructor.
426     */
427    public CheckMethodAdapter(final MethodVisitor mv,
428            final Map<Label, Integer> labels) {
429        this(Opcodes.ASM5, mv, labels);
430        if (getClass() != CheckMethodAdapter.class) {
431            throw new IllegalStateException();
432        }
433    }
434
435    /**
436     * Constructs a new {@link CheckMethodAdapter} object. This method adapter
437     * will not perform any data flow check (see
438     * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
439     *
440     * @param api
441     *            the ASM API version implemented by this CheckMethodAdapter.
442     *            Must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
443     * @param mv
444     *            the method visitor to which this adapter must delegate calls.
445     * @param labels
446     *            a map of already visited labels (in other methods).
447     */
448    protected CheckMethodAdapter(final int api, final MethodVisitor mv,
449            final Map<Label, Integer> labels) {
450        super(api, mv);
451        this.labels = labels;
452        this.usedLabels = new HashSet<Label>();
453        this.handlers = new ArrayList<Label>();
454    }
455
456    /**
457     * Constructs a new {@link CheckMethodAdapter} object. This method adapter
458     * will perform basic data flow checks. For instance in a method whose
459     * signature is <tt>void m ()</tt>, the invalid instruction IRETURN, or the
460     * invalid sequence IADD L2I will be detected.
461     *
462     * @param access
463     *            the method's access flags.
464     * @param name
465     *            the method's name.
466     * @param desc
467     *            the method's descriptor (see {@link Type Type}).
468     * @param cmv
469     *            the method visitor to which this adapter must delegate calls.
470     * @param labels
471     *            a map of already visited labels (in other methods).
472     */
473    public CheckMethodAdapter(final int access, final String name,
474            final String desc, final MethodVisitor cmv,
475            final Map<Label, Integer> labels) {
476        this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) {
477            @Override
478            public void visitEnd() {
479                Analyzer<BasicValue> a = new Analyzer<BasicValue>(
480                        new BasicVerifier());
481                try {
482                    a.analyze("dummy", this);
483                } catch (Exception e) {
484                    if (e instanceof IndexOutOfBoundsException
485                            && maxLocals == 0 && maxStack == 0) {
486                        throw new RuntimeException(
487                                "Data flow checking option requires valid, non zero maxLocals and maxStack values.");
488                    }
489                    e.printStackTrace();
490                    StringWriter sw = new StringWriter();
491                    PrintWriter pw = new PrintWriter(sw, true);
492                    CheckClassAdapter.printAnalyzerResult(this, a, pw);
493                    pw.close();
494                    throw new RuntimeException(e.getMessage() + ' '
495                            + sw.toString());
496                }
497                accept(cmv);
498            }
499        }, labels);
500        this.access = access;
501    }
502
503    @Override
504    public void visitParameter(String name, int access) {
505        if (name != null) {
506            checkUnqualifiedName(version, name, "name");
507        }
508        CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL
509                + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC);
510        super.visitParameter(name, access);
511    }
512
513    @Override
514    public AnnotationVisitor visitAnnotation(final String desc,
515            final boolean visible) {
516        checkEndMethod();
517        checkDesc(desc, false);
518        return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
519    }
520
521    @Override
522    public AnnotationVisitor visitTypeAnnotation(final int typeRef,
523            final TypePath typePath, final String desc, final boolean visible) {
524        checkEndMethod();
525        int sort = typeRef >>> 24;
526        if (sort != TypeReference.METHOD_TYPE_PARAMETER
527                && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND
528                && sort != TypeReference.METHOD_RETURN
529                && sort != TypeReference.METHOD_RECEIVER
530                && sort != TypeReference.METHOD_FORMAL_PARAMETER
531                && sort != TypeReference.THROWS) {
532            throw new IllegalArgumentException("Invalid type reference sort 0x"
533                    + Integer.toHexString(sort));
534        }
535        CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
536        CheckMethodAdapter.checkDesc(desc, false);
537        return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef,
538                typePath, desc, visible));
539    }
540
541    @Override
542    public AnnotationVisitor visitAnnotationDefault() {
543        checkEndMethod();
544        return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false);
545    }
546
547    @Override
548    public AnnotationVisitor visitParameterAnnotation(final int parameter,
549            final String desc, final boolean visible) {
550        checkEndMethod();
551        checkDesc(desc, false);
552        return new CheckAnnotationAdapter(super.visitParameterAnnotation(
553                parameter, desc, visible));
554    }
555
556    @Override
557    public void visitAttribute(final Attribute attr) {
558        checkEndMethod();
559        if (attr == null) {
560            throw new IllegalArgumentException(
561                    "Invalid attribute (must not be null)");
562        }
563        super.visitAttribute(attr);
564    }
565
566    @Override
567    public void visitCode() {
568        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
569            throw new RuntimeException("Abstract methods cannot have code");
570        }
571        startCode = true;
572        super.visitCode();
573    }
574
575    @Override
576    public void visitFrame(final int type, final int nLocal,
577            final Object[] local, final int nStack, final Object[] stack) {
578        if (insnCount == lastFrame) {
579            throw new IllegalStateException(
580                    "At most one frame can be visited at a given code location.");
581        }
582        lastFrame = insnCount;
583        int mLocal;
584        int mStack;
585        switch (type) {
586        case Opcodes.F_NEW:
587        case Opcodes.F_FULL:
588            mLocal = Integer.MAX_VALUE;
589            mStack = Integer.MAX_VALUE;
590            break;
591
592        case Opcodes.F_SAME:
593            mLocal = 0;
594            mStack = 0;
595            break;
596
597        case Opcodes.F_SAME1:
598            mLocal = 0;
599            mStack = 1;
600            break;
601
602        case Opcodes.F_APPEND:
603        case Opcodes.F_CHOP:
604            mLocal = 3;
605            mStack = 0;
606            break;
607
608        default:
609            throw new IllegalArgumentException("Invalid frame type " + type);
610        }
611
612        if (nLocal > mLocal) {
613            throw new IllegalArgumentException("Invalid nLocal=" + nLocal
614                    + " for frame type " + type);
615        }
616        if (nStack > mStack) {
617            throw new IllegalArgumentException("Invalid nStack=" + nStack
618                    + " for frame type " + type);
619        }
620
621        if (type != Opcodes.F_CHOP) {
622            if (nLocal > 0 && (local == null || local.length < nLocal)) {
623                throw new IllegalArgumentException(
624                        "Array local[] is shorter than nLocal");
625            }
626            for (int i = 0; i < nLocal; ++i) {
627                checkFrameValue(local[i]);
628            }
629        }
630        if (nStack > 0 && (stack == null || stack.length < nStack)) {
631            throw new IllegalArgumentException(
632                    "Array stack[] is shorter than nStack");
633        }
634        for (int i = 0; i < nStack; ++i) {
635            checkFrameValue(stack[i]);
636        }
637        if (type == Opcodes.F_NEW) {
638            ++expandedFrames;
639        } else {
640            ++compressedFrames;
641        }
642        if (expandedFrames > 0 && compressedFrames > 0) {
643            throw new RuntimeException(
644                    "Expanded and compressed frames must not be mixed.");
645        }
646        super.visitFrame(type, nLocal, local, nStack, stack);
647    }
648
649    @Override
650    public void visitInsn(final int opcode) {
651        checkStartCode();
652        checkEndCode();
653        checkOpcode(opcode, 0);
654        super.visitInsn(opcode);
655        ++insnCount;
656    }
657
658    @Override
659    public void visitIntInsn(final int opcode, final int operand) {
660        checkStartCode();
661        checkEndCode();
662        checkOpcode(opcode, 1);
663        switch (opcode) {
664        case Opcodes.BIPUSH:
665            checkSignedByte(operand, "Invalid operand");
666            break;
667        case Opcodes.SIPUSH:
668            checkSignedShort(operand, "Invalid operand");
669            break;
670        // case Constants.NEWARRAY:
671        default:
672            if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
673                throw new IllegalArgumentException(
674                        "Invalid operand (must be an array type code T_...): "
675                                + operand);
676            }
677        }
678        super.visitIntInsn(opcode, operand);
679        ++insnCount;
680    }
681
682    @Override
683    public void visitVarInsn(final int opcode, final int var) {
684        checkStartCode();
685        checkEndCode();
686        checkOpcode(opcode, 2);
687        checkUnsignedShort(var, "Invalid variable index");
688        super.visitVarInsn(opcode, var);
689        ++insnCount;
690    }
691
692    @Override
693    public void visitTypeInsn(final int opcode, final String type) {
694        checkStartCode();
695        checkEndCode();
696        checkOpcode(opcode, 3);
697        checkInternalName(type, "type");
698        if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
699            throw new IllegalArgumentException(
700                    "NEW cannot be used to create arrays: " + type);
701        }
702        super.visitTypeInsn(opcode, type);
703        ++insnCount;
704    }
705
706    @Override
707    public void visitFieldInsn(final int opcode, final String owner,
708            final String name, final String desc) {
709        checkStartCode();
710        checkEndCode();
711        checkOpcode(opcode, 4);
712        checkInternalName(owner, "owner");
713        checkUnqualifiedName(version, name, "name");
714        checkDesc(desc, false);
715        super.visitFieldInsn(opcode, owner, name, desc);
716        ++insnCount;
717    }
718
719    @Deprecated
720    @Override
721    public void visitMethodInsn(int opcode, String owner, String name,
722            String desc) {
723        if (api >= Opcodes.ASM5) {
724            super.visitMethodInsn(opcode, owner, name, desc);
725            return;
726        }
727        doVisitMethodInsn(opcode, owner, name, desc,
728                opcode == Opcodes.INVOKEINTERFACE);
729    }
730
731    @Override
732    public void visitMethodInsn(int opcode, String owner, String name,
733            String desc, boolean itf) {
734        if (api < Opcodes.ASM5) {
735            super.visitMethodInsn(opcode, owner, name, desc, itf);
736            return;
737        }
738        doVisitMethodInsn(opcode, owner, name, desc, itf);
739    }
740
741    private void doVisitMethodInsn(int opcode, final String owner,
742            final String name, final String desc, final boolean itf) {
743        checkStartCode();
744        checkEndCode();
745        checkOpcode(opcode, 5);
746        if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
747            checkMethodIdentifier(version, name, "name");
748        }
749        checkInternalName(owner, "owner");
750        checkMethodDesc(desc);
751        if (opcode == Opcodes.INVOKEVIRTUAL && itf) {
752            throw new IllegalArgumentException(
753                    "INVOKEVIRTUAL can't be used with interfaces");
754        }
755        if (opcode == Opcodes.INVOKEINTERFACE && !itf) {
756            throw new IllegalArgumentException(
757                    "INVOKEINTERFACE can't be used with classes");
758        }
759        // Calling super.visitMethodInsn requires to call the correct version
760        // depending on this.api (otherwise infinite loops can occur). To
761        // simplify and to make it easier to automatically remove the backward
762        // compatibility code, we inline the code of the overridden method here.
763        if (mv != null) {
764            mv.visitMethodInsn(opcode, owner, name, desc, itf);
765        }
766        ++insnCount;
767    }
768
769    @Override
770    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
771            Object... bsmArgs) {
772        checkStartCode();
773        checkEndCode();
774        checkMethodIdentifier(version, name, "name");
775        checkMethodDesc(desc);
776        if (bsm.getTag() != Opcodes.H_INVOKESTATIC
777                && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
778            throw new IllegalArgumentException("invalid handle tag "
779                    + bsm.getTag());
780        }
781        for (int i = 0; i < bsmArgs.length; i++) {
782            checkLDCConstant(bsmArgs[i]);
783        }
784        super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
785        ++insnCount;
786    }
787
788    @Override
789    public void visitJumpInsn(final int opcode, final Label label) {
790        checkStartCode();
791        checkEndCode();
792        checkOpcode(opcode, 6);
793        checkLabel(label, false, "label");
794        checkNonDebugLabel(label);
795        super.visitJumpInsn(opcode, label);
796        usedLabels.add(label);
797        ++insnCount;
798    }
799
800    @Override
801    public void visitLabel(final Label label) {
802        checkStartCode();
803        checkEndCode();
804        checkLabel(label, false, "label");
805        if (labels.get(label) != null) {
806            throw new IllegalArgumentException("Already visited label");
807        }
808        labels.put(label, insnCount);
809        super.visitLabel(label);
810    }
811
812    @Override
813    public void visitLdcInsn(final Object cst) {
814        checkStartCode();
815        checkEndCode();
816        checkLDCConstant(cst);
817        super.visitLdcInsn(cst);
818        ++insnCount;
819    }
820
821    @Override
822    public void visitIincInsn(final int var, final int increment) {
823        checkStartCode();
824        checkEndCode();
825        checkUnsignedShort(var, "Invalid variable index");
826        checkSignedShort(increment, "Invalid increment");
827        super.visitIincInsn(var, increment);
828        ++insnCount;
829    }
830
831    @Override
832    public void visitTableSwitchInsn(final int min, final int max,
833            final Label dflt, final Label... labels) {
834        checkStartCode();
835        checkEndCode();
836        if (max < min) {
837            throw new IllegalArgumentException("Max = " + max
838                    + " must be greater than or equal to min = " + min);
839        }
840        checkLabel(dflt, false, "default label");
841        checkNonDebugLabel(dflt);
842        if (labels == null || labels.length != max - min + 1) {
843            throw new IllegalArgumentException(
844                    "There must be max - min + 1 labels");
845        }
846        for (int i = 0; i < labels.length; ++i) {
847            checkLabel(labels[i], false, "label at index " + i);
848            checkNonDebugLabel(labels[i]);
849        }
850        super.visitTableSwitchInsn(min, max, dflt, labels);
851        for (int i = 0; i < labels.length; ++i) {
852            usedLabels.add(labels[i]);
853        }
854        ++insnCount;
855    }
856
857    @Override
858    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
859            final Label[] labels) {
860        checkEndCode();
861        checkStartCode();
862        checkLabel(dflt, false, "default label");
863        checkNonDebugLabel(dflt);
864        if (keys == null || labels == null || keys.length != labels.length) {
865            throw new IllegalArgumentException(
866                    "There must be the same number of keys and labels");
867        }
868        for (int i = 0; i < labels.length; ++i) {
869            checkLabel(labels[i], false, "label at index " + i);
870            checkNonDebugLabel(labels[i]);
871        }
872        super.visitLookupSwitchInsn(dflt, keys, labels);
873        usedLabels.add(dflt);
874        for (int i = 0; i < labels.length; ++i) {
875            usedLabels.add(labels[i]);
876        }
877        ++insnCount;
878    }
879
880    @Override
881    public void visitMultiANewArrayInsn(final String desc, final int dims) {
882        checkStartCode();
883        checkEndCode();
884        checkDesc(desc, false);
885        if (desc.charAt(0) != '[') {
886            throw new IllegalArgumentException(
887                    "Invalid descriptor (must be an array type descriptor): "
888                            + desc);
889        }
890        if (dims < 1) {
891            throw new IllegalArgumentException(
892                    "Invalid dimensions (must be greater than 0): " + dims);
893        }
894        if (dims > desc.lastIndexOf('[') + 1) {
895            throw new IllegalArgumentException(
896                    "Invalid dimensions (must not be greater than dims(desc)): "
897                            + dims);
898        }
899        super.visitMultiANewArrayInsn(desc, dims);
900        ++insnCount;
901    }
902
903    @Override
904    public AnnotationVisitor visitInsnAnnotation(final int typeRef,
905            final TypePath typePath, final String desc, final boolean visible) {
906        checkStartCode();
907        checkEndCode();
908        int sort = typeRef >>> 24;
909        if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW
910                && sort != TypeReference.CONSTRUCTOR_REFERENCE
911                && sort != TypeReference.METHOD_REFERENCE
912                && sort != TypeReference.CAST
913                && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
914                && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT
915                && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
916                && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) {
917            throw new IllegalArgumentException("Invalid type reference sort 0x"
918                    + Integer.toHexString(sort));
919        }
920        CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
921        CheckMethodAdapter.checkDesc(desc, false);
922        return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef,
923                typePath, desc, visible));
924    }
925
926    @Override
927    public void visitTryCatchBlock(final Label start, final Label end,
928            final Label handler, final String type) {
929        checkStartCode();
930        checkEndCode();
931        checkLabel(start, false, "start label");
932        checkLabel(end, false, "end label");
933        checkLabel(handler, false, "handler label");
934        checkNonDebugLabel(start);
935        checkNonDebugLabel(end);
936        checkNonDebugLabel(handler);
937        if (labels.get(start) != null || labels.get(end) != null
938                || labels.get(handler) != null) {
939            throw new IllegalStateException(
940                    "Try catch blocks must be visited before their labels");
941        }
942        if (type != null) {
943            checkInternalName(type, "type");
944        }
945        super.visitTryCatchBlock(start, end, handler, type);
946        handlers.add(start);
947        handlers.add(end);
948    }
949
950    @Override
951    public AnnotationVisitor visitTryCatchAnnotation(final int typeRef,
952            final TypePath typePath, final String desc, final boolean visible) {
953        checkStartCode();
954        checkEndCode();
955        int sort = typeRef >>> 24;
956        if (sort != TypeReference.EXCEPTION_PARAMETER) {
957            throw new IllegalArgumentException("Invalid type reference sort 0x"
958                    + Integer.toHexString(sort));
959        }
960        CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
961        CheckMethodAdapter.checkDesc(desc, false);
962        return new CheckAnnotationAdapter(super.visitTryCatchAnnotation(
963                typeRef, typePath, desc, visible));
964    }
965
966    @Override
967    public void visitLocalVariable(final String name, final String desc,
968            final String signature, final Label start, final Label end,
969            final int index) {
970        checkStartCode();
971        checkEndCode();
972        checkUnqualifiedName(version, name, "name");
973        checkDesc(desc, false);
974        checkLabel(start, true, "start label");
975        checkLabel(end, true, "end label");
976        checkUnsignedShort(index, "Invalid variable index");
977        int s = labels.get(start).intValue();
978        int e = labels.get(end).intValue();
979        if (e < s) {
980            throw new IllegalArgumentException(
981                    "Invalid start and end labels (end must be greater than start)");
982        }
983        super.visitLocalVariable(name, desc, signature, start, end, index);
984    }
985
986    @Override
987    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
988            TypePath typePath, Label[] start, Label[] end, int[] index,
989            String desc, boolean visible) {
990        checkStartCode();
991        checkEndCode();
992        int sort = typeRef >>> 24;
993        if (sort != TypeReference.LOCAL_VARIABLE
994                && sort != TypeReference.RESOURCE_VARIABLE) {
995            throw new IllegalArgumentException("Invalid type reference sort 0x"
996                    + Integer.toHexString(sort));
997        }
998        CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
999        checkDesc(desc, false);
1000        if (start == null || end == null || index == null
1001                || end.length != start.length || index.length != start.length) {
1002            throw new IllegalArgumentException(
1003                    "Invalid start, end and index arrays (must be non null and of identical length");
1004        }
1005        for (int i = 0; i < start.length; ++i) {
1006            checkLabel(start[i], true, "start label");
1007            checkLabel(end[i], true, "end label");
1008            checkUnsignedShort(index[i], "Invalid variable index");
1009            int s = labels.get(start[i]).intValue();
1010            int e = labels.get(end[i]).intValue();
1011            if (e < s) {
1012                throw new IllegalArgumentException(
1013                        "Invalid start and end labels (end must be greater than start)");
1014            }
1015        }
1016        return super.visitLocalVariableAnnotation(typeRef, typePath, start,
1017                end, index, desc, visible);
1018    }
1019
1020    @Override
1021    public void visitLineNumber(final int line, final Label start) {
1022        checkStartCode();
1023        checkEndCode();
1024        checkUnsignedShort(line, "Invalid line number");
1025        checkLabel(start, true, "start label");
1026        super.visitLineNumber(line, start);
1027    }
1028
1029    @Override
1030    public void visitMaxs(final int maxStack, final int maxLocals) {
1031        checkStartCode();
1032        checkEndCode();
1033        endCode = true;
1034        for (Label l : usedLabels) {
1035            if (labels.get(l) == null) {
1036                throw new IllegalStateException("Undefined label used");
1037            }
1038        }
1039        for (int i = 0; i < handlers.size();) {
1040            Integer start = labels.get(handlers.get(i++));
1041            Integer end = labels.get(handlers.get(i++));
1042            if (start == null || end == null) {
1043                throw new IllegalStateException(
1044                        "Undefined try catch block labels");
1045            }
1046            if (end.intValue() <= start.intValue()) {
1047                throw new IllegalStateException(
1048                        "Emty try catch block handler range");
1049            }
1050        }
1051        checkUnsignedShort(maxStack, "Invalid max stack");
1052        checkUnsignedShort(maxLocals, "Invalid max locals");
1053        super.visitMaxs(maxStack, maxLocals);
1054    }
1055
1056    @Override
1057    public void visitEnd() {
1058        checkEndMethod();
1059        endMethod = true;
1060        super.visitEnd();
1061    }
1062
1063    // -------------------------------------------------------------------------
1064
1065    /**
1066     * Checks that the visitCode method has been called.
1067     */
1068    void checkStartCode() {
1069        if (!startCode) {
1070            throw new IllegalStateException(
1071                    "Cannot visit instructions before visitCode has been called.");
1072        }
1073    }
1074
1075    /**
1076     * Checks that the visitMaxs method has not been called.
1077     */
1078    void checkEndCode() {
1079        if (endCode) {
1080            throw new IllegalStateException(
1081                    "Cannot visit instructions after visitMaxs has been called.");
1082        }
1083    }
1084
1085    /**
1086     * Checks that the visitEnd method has not been called.
1087     */
1088    void checkEndMethod() {
1089        if (endMethod) {
1090            throw new IllegalStateException(
1091                    "Cannot visit elements after visitEnd has been called.");
1092        }
1093    }
1094
1095    /**
1096     * Checks a stack frame value.
1097     *
1098     * @param value
1099     *            the value to be checked.
1100     */
1101    void checkFrameValue(final Object value) {
1102        if (value == Opcodes.TOP || value == Opcodes.INTEGER
1103                || value == Opcodes.FLOAT || value == Opcodes.LONG
1104                || value == Opcodes.DOUBLE || value == Opcodes.NULL
1105                || value == Opcodes.UNINITIALIZED_THIS) {
1106            return;
1107        }
1108        if (value instanceof String) {
1109            checkInternalName((String) value, "Invalid stack frame value");
1110            return;
1111        }
1112        if (!(value instanceof Label)) {
1113            throw new IllegalArgumentException("Invalid stack frame value: "
1114                    + value);
1115        } else {
1116            usedLabels.add((Label) value);
1117        }
1118    }
1119
1120    /**
1121     * Checks that the type of the given opcode is equal to the given type.
1122     *
1123     * @param opcode
1124     *            the opcode to be checked.
1125     * @param type
1126     *            the expected opcode type.
1127     */
1128    static void checkOpcode(final int opcode, final int type) {
1129        if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
1130            throw new IllegalArgumentException("Invalid opcode: " + opcode);
1131        }
1132    }
1133
1134    /**
1135     * Checks that the given value is a signed byte.
1136     *
1137     * @param value
1138     *            the value to be checked.
1139     * @param msg
1140     *            an message to be used in case of error.
1141     */
1142    static void checkSignedByte(final int value, final String msg) {
1143        if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
1144            throw new IllegalArgumentException(msg
1145                    + " (must be a signed byte): " + value);
1146        }
1147    }
1148
1149    /**
1150     * Checks that the given value is a signed short.
1151     *
1152     * @param value
1153     *            the value to be checked.
1154     * @param msg
1155     *            an message to be used in case of error.
1156     */
1157    static void checkSignedShort(final int value, final String msg) {
1158        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
1159            throw new IllegalArgumentException(msg
1160                    + " (must be a signed short): " + value);
1161        }
1162    }
1163
1164    /**
1165     * Checks that the given value is an unsigned short.
1166     *
1167     * @param value
1168     *            the value to be checked.
1169     * @param msg
1170     *            an message to be used in case of error.
1171     */
1172    static void checkUnsignedShort(final int value, final String msg) {
1173        if (value < 0 || value > 65535) {
1174            throw new IllegalArgumentException(msg
1175                    + " (must be an unsigned short): " + value);
1176        }
1177    }
1178
1179    /**
1180     * Checks that the given value is an {@link Integer}, a{@link Float}, a
1181     * {@link Long}, a {@link Double} or a {@link String}.
1182     *
1183     * @param cst
1184     *            the value to be checked.
1185     */
1186    static void checkConstant(final Object cst) {
1187        if (!(cst instanceof Integer) && !(cst instanceof Float)
1188                && !(cst instanceof Long) && !(cst instanceof Double)
1189                && !(cst instanceof String)) {
1190            throw new IllegalArgumentException("Invalid constant: " + cst);
1191        }
1192    }
1193
1194    void checkLDCConstant(final Object cst) {
1195        if (cst instanceof Type) {
1196            int s = ((Type) cst).getSort();
1197            if (s != Type.OBJECT && s != Type.ARRAY && s != Type.METHOD) {
1198                throw new IllegalArgumentException("Illegal LDC constant value");
1199            }
1200            if (s != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) {
1201                throw new IllegalArgumentException(
1202                        "ldc of a constant class requires at least version 1.5");
1203            }
1204            if (s == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) {
1205                throw new IllegalArgumentException(
1206                        "ldc of a method type requires at least version 1.7");
1207            }
1208        } else if (cst instanceof Handle) {
1209            if ((version & 0xFFFF) < Opcodes.V1_7) {
1210                throw new IllegalArgumentException(
1211                        "ldc of a handle requires at least version 1.7");
1212            }
1213            int tag = ((Handle) cst).getTag();
1214            if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) {
1215                throw new IllegalArgumentException("invalid handle tag " + tag);
1216            }
1217        } else {
1218            checkConstant(cst);
1219        }
1220    }
1221
1222    /**
1223     * Checks that the given string is a valid unqualified name.
1224     *
1225     * @param version
1226     *            the class version.
1227     * @param name
1228     *            the string to be checked.
1229     * @param msg
1230     *            a message to be used in case of error.
1231     */
1232    static void checkUnqualifiedName(int version, final String name,
1233            final String msg) {
1234        if ((version & 0xFFFF) < Opcodes.V1_5) {
1235            checkIdentifier(name, msg);
1236        } else {
1237            for (int i = 0; i < name.length(); ++i) {
1238                if (".;[/".indexOf(name.charAt(i)) != -1) {
1239                    throw new IllegalArgumentException("Invalid " + msg
1240                            + " (must be a valid unqualified name): " + name);
1241                }
1242            }
1243        }
1244    }
1245
1246    /**
1247     * Checks that the given string is a valid Java identifier.
1248     *
1249     * @param name
1250     *            the string to be checked.
1251     * @param msg
1252     *            a message to be used in case of error.
1253     */
1254    static void checkIdentifier(final String name, final String msg) {
1255        checkIdentifier(name, 0, -1, msg);
1256    }
1257
1258    /**
1259     * Checks that the given substring is a valid Java identifier.
1260     *
1261     * @param name
1262     *            the string to be checked.
1263     * @param start
1264     *            index of the first character of the identifier (inclusive).
1265     * @param end
1266     *            index of the last character of the identifier (exclusive). -1
1267     *            is equivalent to <tt>name.length()</tt> if name is not
1268     *            <tt>null</tt>.
1269     * @param msg
1270     *            a message to be used in case of error.
1271     */
1272    static void checkIdentifier(final String name, final int start,
1273            final int end, final String msg) {
1274        if (name == null || (end == -1 ? name.length() <= start : end <= start)) {
1275            throw new IllegalArgumentException("Invalid " + msg
1276                    + " (must not be null or empty)");
1277        }
1278        if (!Character.isJavaIdentifierStart(name.charAt(start))) {
1279            throw new IllegalArgumentException("Invalid " + msg
1280                    + " (must be a valid Java identifier): " + name);
1281        }
1282        int max = end == -1 ? name.length() : end;
1283        for (int i = start + 1; i < max; ++i) {
1284            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1285                throw new IllegalArgumentException("Invalid " + msg
1286                        + " (must be a valid Java identifier): " + name);
1287            }
1288        }
1289    }
1290
1291    /**
1292     * Checks that the given string is a valid Java identifier.
1293     *
1294     * @param version
1295     *            the class version.
1296     * @param name
1297     *            the string to be checked.
1298     * @param msg
1299     *            a message to be used in case of error.
1300     */
1301    static void checkMethodIdentifier(int version, final String name,
1302            final String msg) {
1303        if (name == null || name.length() == 0) {
1304            throw new IllegalArgumentException("Invalid " + msg
1305                    + " (must not be null or empty)");
1306        }
1307        if ((version & 0xFFFF) >= Opcodes.V1_5) {
1308            for (int i = 0; i < name.length(); ++i) {
1309                if (".;[/<>".indexOf(name.charAt(i)) != -1) {
1310                    throw new IllegalArgumentException("Invalid " + msg
1311                            + " (must be a valid unqualified name): " + name);
1312                }
1313            }
1314            return;
1315        }
1316        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1317            throw new IllegalArgumentException(
1318                    "Invalid "
1319                            + msg
1320                            + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
1321                            + name);
1322        }
1323        for (int i = 1; i < name.length(); ++i) {
1324            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1325                throw new IllegalArgumentException(
1326                        "Invalid "
1327                                + msg
1328                                + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
1329                                + name);
1330            }
1331        }
1332    }
1333
1334    /**
1335     * Checks that the given string is a valid internal class name.
1336     *
1337     * @param name
1338     *            the string to be checked.
1339     * @param msg
1340     *            a message to be used in case of error.
1341     */
1342    static void checkInternalName(final String name, final String msg) {
1343        if (name == null || name.length() == 0) {
1344            throw new IllegalArgumentException("Invalid " + msg
1345                    + " (must not be null or empty)");
1346        }
1347        if (name.charAt(0) == '[') {
1348            checkDesc(name, false);
1349        } else {
1350            checkInternalName(name, 0, -1, msg);
1351        }
1352    }
1353
1354    /**
1355     * Checks that the given substring is a valid internal class name.
1356     *
1357     * @param name
1358     *            the string to be checked.
1359     * @param start
1360     *            index of the first character of the identifier (inclusive).
1361     * @param end
1362     *            index of the last character of the identifier (exclusive). -1
1363     *            is equivalent to <tt>name.length()</tt> if name is not
1364     *            <tt>null</tt>.
1365     * @param msg
1366     *            a message to be used in case of error.
1367     */
1368    static void checkInternalName(final String name, final int start,
1369            final int end, final String msg) {
1370        int max = end == -1 ? name.length() : end;
1371        try {
1372            int begin = start;
1373            int slash;
1374            do {
1375                slash = name.indexOf('/', begin + 1);
1376                if (slash == -1 || slash > max) {
1377                    slash = max;
1378                }
1379                checkIdentifier(name, begin, slash, null);
1380                begin = slash + 1;
1381            } while (slash != max);
1382        } catch (IllegalArgumentException unused) {
1383            throw new IllegalArgumentException(
1384                    "Invalid "
1385                            + msg
1386                            + " (must be a fully qualified class name in internal form): "
1387                            + name);
1388        }
1389    }
1390
1391    /**
1392     * Checks that the given string is a valid type descriptor.
1393     *
1394     * @param desc
1395     *            the string to be checked.
1396     * @param canBeVoid
1397     *            <tt>true</tt> if <tt>V</tt> can be considered valid.
1398     */
1399    static void checkDesc(final String desc, final boolean canBeVoid) {
1400        int end = checkDesc(desc, 0, canBeVoid);
1401        if (end != desc.length()) {
1402            throw new IllegalArgumentException("Invalid descriptor: " + desc);
1403        }
1404    }
1405
1406    /**
1407     * Checks that a the given substring is a valid type descriptor.
1408     *
1409     * @param desc
1410     *            the string to be checked.
1411     * @param start
1412     *            index of the first character of the identifier (inclusive).
1413     * @param canBeVoid
1414     *            <tt>true</tt> if <tt>V</tt> can be considered valid.
1415     * @return the index of the last character of the type decriptor, plus one.
1416     */
1417    static int checkDesc(final String desc, final int start,
1418            final boolean canBeVoid) {
1419        if (desc == null || start >= desc.length()) {
1420            throw new IllegalArgumentException(
1421                    "Invalid type descriptor (must not be null or empty)");
1422        }
1423        int index;
1424        switch (desc.charAt(start)) {
1425        case 'V':
1426            if (canBeVoid) {
1427                return start + 1;
1428            } else {
1429                throw new IllegalArgumentException("Invalid descriptor: "
1430                        + desc);
1431            }
1432        case 'Z':
1433        case 'C':
1434        case 'B':
1435        case 'S':
1436        case 'I':
1437        case 'F':
1438        case 'J':
1439        case 'D':
1440            return start + 1;
1441        case '[':
1442            index = start + 1;
1443            while (index < desc.length() && desc.charAt(index) == '[') {
1444                ++index;
1445            }
1446            if (index < desc.length()) {
1447                return checkDesc(desc, index, false);
1448            } else {
1449                throw new IllegalArgumentException("Invalid descriptor: "
1450                        + desc);
1451            }
1452        case 'L':
1453            index = desc.indexOf(';', start);
1454            if (index == -1 || index - start < 2) {
1455                throw new IllegalArgumentException("Invalid descriptor: "
1456                        + desc);
1457            }
1458            try {
1459                checkInternalName(desc, start + 1, index, null);
1460            } catch (IllegalArgumentException unused) {
1461                throw new IllegalArgumentException("Invalid descriptor: "
1462                        + desc);
1463            }
1464            return index + 1;
1465        default:
1466            throw new IllegalArgumentException("Invalid descriptor: " + desc);
1467        }
1468    }
1469
1470    /**
1471     * Checks that the given string is a valid method descriptor.
1472     *
1473     * @param desc
1474     *            the string to be checked.
1475     */
1476    static void checkMethodDesc(final String desc) {
1477        if (desc == null || desc.length() == 0) {
1478            throw new IllegalArgumentException(
1479                    "Invalid method descriptor (must not be null or empty)");
1480        }
1481        if (desc.charAt(0) != '(' || desc.length() < 3) {
1482            throw new IllegalArgumentException("Invalid descriptor: " + desc);
1483        }
1484        int start = 1;
1485        if (desc.charAt(start) != ')') {
1486            do {
1487                if (desc.charAt(start) == 'V') {
1488                    throw new IllegalArgumentException("Invalid descriptor: "
1489                            + desc);
1490                }
1491                start = checkDesc(desc, start, false);
1492            } while (start < desc.length() && desc.charAt(start) != ')');
1493        }
1494        start = checkDesc(desc, start + 1, true);
1495        if (start != desc.length()) {
1496            throw new IllegalArgumentException("Invalid descriptor: " + desc);
1497        }
1498    }
1499
1500    /**
1501     * Checks that the given label is not null. This method can also check that
1502     * the label has been visited.
1503     *
1504     * @param label
1505     *            the label to be checked.
1506     * @param checkVisited
1507     *            <tt>true</tt> to check that the label has been visited.
1508     * @param msg
1509     *            a message to be used in case of error.
1510     */
1511    void checkLabel(final Label label, final boolean checkVisited,
1512            final String msg) {
1513        if (label == null) {
1514            throw new IllegalArgumentException("Invalid " + msg
1515                    + " (must not be null)");
1516        }
1517        if (checkVisited && labels.get(label) == null) {
1518            throw new IllegalArgumentException("Invalid " + msg
1519                    + " (must be visited first)");
1520        }
1521    }
1522
1523    /**
1524     * Checks that the given label is not a label used only for debug purposes.
1525     *
1526     * @param label
1527     *            the label to be checked.
1528     */
1529    private static void checkNonDebugLabel(final Label label) {
1530        Field f = getLabelStatusField();
1531        int status = 0;
1532        try {
1533            status = f == null ? 0 : ((Integer) f.get(label)).intValue();
1534        } catch (IllegalAccessException e) {
1535            throw new Error("Internal error");
1536        }
1537        if ((status & 0x01) != 0) {
1538            throw new IllegalArgumentException(
1539                    "Labels used for debug info cannot be reused for control flow");
1540        }
1541    }
1542
1543    /**
1544     * Returns the Field object corresponding to the Label.status field.
1545     *
1546     * @return the Field object corresponding to the Label.status field.
1547     */
1548    private static Field getLabelStatusField() {
1549        if (labelStatusField == null) {
1550            labelStatusField = getLabelField("a");
1551            if (labelStatusField == null) {
1552                labelStatusField = getLabelField("status");
1553            }
1554        }
1555        return labelStatusField;
1556    }
1557
1558    /**
1559     * Returns the field of the Label class whose name is given.
1560     *
1561     * @param name
1562     *            a field name.
1563     * @return the field of the Label class whose name is given, or null.
1564     */
1565    private static Field getLabelField(final String name) {
1566        try {
1567            Field f = Label.class.getDeclaredField(name);
1568            f.setAccessible(true);
1569            return f;
1570        } catch (NoSuchFieldException e) {
1571            return null;
1572        }
1573    }
1574}
1575