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  */
  59 package jdk.internal.org.objectweb.asm.util;
  60 
  61 import java.io.PrintWriter;
  62 import java.io.StringWriter;
  63 import java.lang.reflect.Field;
  64 import java.util.ArrayList;
  65 import java.util.HashMap;
  66 import java.util.HashSet;
  67 import java.util.List;
  68 import java.util.Map;
  69 import java.util.Set;
  70 
  71 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  72 import jdk.internal.org.objectweb.asm.Attribute;
  73 import jdk.internal.org.objectweb.asm.Handle;
  74 import jdk.internal.org.objectweb.asm.Label;
  75 import jdk.internal.org.objectweb.asm.MethodVisitor;
  76 import jdk.internal.org.objectweb.asm.Opcodes;
  77 import jdk.internal.org.objectweb.asm.Type;
  78 import jdk.internal.org.objectweb.asm.TypePath;
  79 import jdk.internal.org.objectweb.asm.TypeReference;
  80 import jdk.internal.org.objectweb.asm.tree.MethodNode;
  81 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
  82 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
  83 import 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  */
 101 public 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      * If an explicit first frame has been visited before the first instruction.
 147      */
 148     private boolean hasExplicitFirstFrame;
 149 
 150     /**
 151      * Number of visited frames in expanded form.
 152      */
 153     private int expandedFrames;
 154 
 155     /**
 156      * Number of visited frames in compressed form.
 157      */
 158     private int compressedFrames;
 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      */
 425     public CheckMethodAdapter(final MethodVisitor mv,
 426             final Map<Label, Integer> labels) {
 427         this(Opcodes.ASM5, mv, labels);
 428     }
 429 
 430     /**
 431      * Constructs a new {@link CheckMethodAdapter} object. This method adapter
 432      * will not perform any data flow check (see
 433      * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
 434      *
 435      * @param mv
 436      *            the method visitor to which this adapter must delegate calls.
 437      * @param labels
 438      *            a map of already visited labels (in other methods).
 439      */
 440     protected CheckMethodAdapter(final int api, final MethodVisitor mv,
 441             final Map<Label, Integer> labels) {
 442         super(api, mv);
 443         this.labels = labels;
 444         this.usedLabels = new HashSet<Label>();
 445         this.handlers = new ArrayList<Label>();
 446     }
 447 
 448     /**
 449      * Constructs a new {@link CheckMethodAdapter} object. This method adapter
 450      * will perform basic data flow checks. For instance in a method whose
 451      * signature is <tt>void m ()</tt>, the invalid instruction IRETURN, or the
 452      * invalid sequence IADD L2I will be detected.
 453      *
 454      * @param access
 455      *            the method's access flags.
 456      * @param name
 457      *            the method's name.
 458      * @param desc
 459      *            the method's descriptor (see {@link Type Type}).
 460      * @param cmv
 461      *            the method visitor to which this adapter must delegate calls.
 462      * @param labels
 463      *            a map of already visited labels (in other methods).
 464      */
 465     public CheckMethodAdapter(final int access, final String name,
 466             final String desc, final MethodVisitor cmv,
 467             final Map<Label, Integer> labels) {
 468         this(new MethodNode(access, name, desc, null, null) {
 469             @Override
 470             public void visitEnd() {
 471                 Analyzer<BasicValue> a = new Analyzer<BasicValue>(
 472                         new BasicVerifier());
 473                 try {
 474                     a.analyze("dummy", this);
 475                 } catch (Exception e) {
 476                     if (e instanceof IndexOutOfBoundsException
 477                             && maxLocals == 0 && maxStack == 0) {
 478                         throw new RuntimeException(
 479                                 "Data flow checking option requires valid, non zero maxLocals and maxStack values.");
 480                     }
 481                     e.printStackTrace();
 482                     StringWriter sw = new StringWriter();
 483                     PrintWriter pw = new PrintWriter(sw, true);
 484                     CheckClassAdapter.printAnalyzerResult(this, a, pw);
 485                     pw.close();
 486                     throw new RuntimeException(e.getMessage() + ' '
 487                             + sw.toString());
 488                 }
 489                 accept(cmv);
 490             }
 491         }, labels);
 492         this.access = access;
 493     }
 494 
 495     @Override
 496     public void visitParameter(String name, int access) {
 497         if (name != null) {
 498             checkUnqualifiedName(version, name, "name");
 499         }
 500         CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL
 501                 + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC);
 502     }
 503 
 504     @Override
 505     public AnnotationVisitor visitAnnotation(final String desc,
 506             final boolean visible) {
 507         checkEndMethod();
 508         checkDesc(desc, false);
 509         return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
 510     }
 511 
 512     @Override
 513     public AnnotationVisitor visitTypeAnnotation(final int typeRef,
 514             final TypePath typePath, final String desc, final boolean visible) {
 515         checkEndMethod();
 516         int sort = typeRef >>> 24;
 517         if (sort != TypeReference.METHOD_TYPE_PARAMETER
 518                 && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND
 519                 && sort != TypeReference.METHOD_RETURN
 520                 && sort != TypeReference.METHOD_RECEIVER
 521                 && sort != TypeReference.METHOD_FORMAL_PARAMETER
 522                 && sort != TypeReference.THROWS) {
 523             throw new IllegalArgumentException("Invalid type reference sort 0x"
 524                     + Integer.toHexString(sort));
 525         }
 526         CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
 527         CheckMethodAdapter.checkDesc(desc, false);
 528         return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef,
 529                 typePath, desc, visible));
 530     }
 531 
 532     @Override
 533     public AnnotationVisitor visitAnnotationDefault() {
 534         checkEndMethod();
 535         return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false);
 536     }
 537 
 538     @Override
 539     public AnnotationVisitor visitParameterAnnotation(final int parameter,
 540             final String desc, final boolean visible) {
 541         checkEndMethod();
 542         checkDesc(desc, false);
 543         return new CheckAnnotationAdapter(super.visitParameterAnnotation(
 544                 parameter, desc, visible));
 545     }
 546 
 547     @Override
 548     public void visitAttribute(final Attribute attr) {
 549         checkEndMethod();
 550         if (attr == null) {
 551             throw new IllegalArgumentException(
 552                     "Invalid attribute (must not be null)");
 553         }
 554         super.visitAttribute(attr);
 555     }
 556 
 557     @Override
 558     public void visitCode() {
 559         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
 560             throw new RuntimeException("Abstract methods cannot have code");
 561         }
 562         startCode = true;
 563         super.visitCode();
 564     }
 565 
 566     @Override
 567     public void visitFrame(final int type, final int nLocal,
 568             final Object[] local, final int nStack, final Object[] stack) {
 569         int mLocal;
 570         int mStack;
 571         switch (type) {
 572         case Opcodes.F_NEW:
 573         case Opcodes.F_FULL:
 574             mLocal = Integer.MAX_VALUE;
 575             mStack = Integer.MAX_VALUE;
 576             break;
 577 
 578         case Opcodes.F_SAME:
 579             mLocal = 0;
 580             mStack = 0;
 581             break;
 582 
 583         case Opcodes.F_SAME1:
 584             mLocal = 0;
 585             mStack = 1;
 586             break;
 587 
 588         case Opcodes.F_APPEND:
 589         case Opcodes.F_CHOP:
 590             mLocal = 3;
 591             mStack = 0;
 592             break;
 593 
 594         default:
 595             throw new IllegalArgumentException("Invalid frame type " + type);
 596         }
 597 
 598         if (nLocal > mLocal) {
 599             throw new IllegalArgumentException("Invalid nLocal=" + nLocal
 600                     + " for frame type " + type);
 601         }
 602         if (nStack > mStack) {
 603             throw new IllegalArgumentException("Invalid nStack=" + nStack
 604                     + " for frame type " + type);
 605         }
 606 
 607         if (type != Opcodes.F_CHOP) {
 608             if (nLocal > 0 && (local == null || local.length < nLocal)) {
 609                 throw new IllegalArgumentException(
 610                         "Array local[] is shorter than nLocal");
 611             }
 612             for (int i = 0; i < nLocal; ++i) {
 613                 checkFrameValue(local[i]);
 614             }
 615         }
 616         if (nStack > 0 && (stack == null || stack.length < nStack)) {
 617             throw new IllegalArgumentException(
 618                     "Array stack[] is shorter than nStack");
 619         }
 620         for (int i = 0; i < nStack; ++i) {
 621             checkFrameValue(stack[i]);
 622         }
 623         if (type == Opcodes.F_NEW) {
 624             if (insnCount == 0) {
 625                 hasExplicitFirstFrame = true;
 626             } else if (!hasExplicitFirstFrame) {
 627                 throw new RuntimeException(
 628                         "In expanded form, a first frame must be explicitly "
 629                                 + "visited before the first instruction.");
 630             }
 631             ++expandedFrames;
 632         } else {
 633             ++compressedFrames;
 634         }
 635         if (expandedFrames > 0 && compressedFrames > 0) {
 636             throw new RuntimeException(
 637                     "Expanded and compressed frames must not be mixed.");
 638         }
 639         super.visitFrame(type, nLocal, local, nStack, stack);
 640     }
 641 
 642     @Override
 643     public void visitInsn(final int opcode) {
 644         checkStartCode();
 645         checkEndCode();
 646         checkOpcode(opcode, 0);
 647         super.visitInsn(opcode);
 648         ++insnCount;
 649     }
 650 
 651     @Override
 652     public void visitIntInsn(final int opcode, final int operand) {
 653         checkStartCode();
 654         checkEndCode();
 655         checkOpcode(opcode, 1);
 656         switch (opcode) {
 657         case Opcodes.BIPUSH:
 658             checkSignedByte(operand, "Invalid operand");
 659             break;
 660         case Opcodes.SIPUSH:
 661             checkSignedShort(operand, "Invalid operand");
 662             break;
 663         // case Constants.NEWARRAY:
 664         default:
 665             if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
 666                 throw new IllegalArgumentException(
 667                         "Invalid operand (must be an array type code T_...): "
 668                                 + operand);
 669             }
 670         }
 671         super.visitIntInsn(opcode, operand);
 672         ++insnCount;
 673     }
 674 
 675     @Override
 676     public void visitVarInsn(final int opcode, final int var) {
 677         checkStartCode();
 678         checkEndCode();
 679         checkOpcode(opcode, 2);
 680         checkUnsignedShort(var, "Invalid variable index");
 681         super.visitVarInsn(opcode, var);
 682         ++insnCount;
 683     }
 684 
 685     @Override
 686     public void visitTypeInsn(final int opcode, final String type) {
 687         checkStartCode();
 688         checkEndCode();
 689         checkOpcode(opcode, 3);
 690         checkInternalName(type, "type");
 691         if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
 692             throw new IllegalArgumentException(
 693                     "NEW cannot be used to create arrays: " + type);
 694         }
 695         super.visitTypeInsn(opcode, type);
 696         ++insnCount;
 697     }
 698 
 699     @Override
 700     public void visitFieldInsn(final int opcode, final String owner,
 701             final String name, final String desc) {
 702         checkStartCode();
 703         checkEndCode();
 704         checkOpcode(opcode, 4);
 705         checkInternalName(owner, "owner");
 706         checkUnqualifiedName(version, name, "name");
 707         checkDesc(desc, false);
 708         super.visitFieldInsn(opcode, owner, name, desc);
 709         ++insnCount;
 710     }
 711 
 712     @Override
 713     public void visitMethodInsn(final int opcode, final String owner,
 714             final String name, final String desc) {
 715         checkStartCode();
 716         checkEndCode();
 717         checkOpcode(opcode, 5);
 718         if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
 719             checkMethodIdentifier(version, name, "name");
 720         }
 721         checkInternalName(owner, "owner");
 722         checkMethodDesc(desc);
 723         super.visitMethodInsn(opcode, owner, name, desc);
 724         ++insnCount;
 725     }
 726 
 727     @Override
 728     public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
 729             Object... bsmArgs) {
 730         checkStartCode();
 731         checkEndCode();
 732         checkMethodIdentifier(version, name, "name");
 733         checkMethodDesc(desc);
 734         if (bsm.getTag() != Opcodes.H_INVOKESTATIC
 735                 && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
 736             throw new IllegalArgumentException("invalid handle tag "
 737                     + bsm.getTag());
 738         }
 739         for (int i = 0; i < bsmArgs.length; i++) {
 740             checkLDCConstant(bsmArgs[i]);
 741         }
 742         super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
 743         ++insnCount;
 744     }
 745 
 746     @Override
 747     public void visitJumpInsn(final int opcode, final Label label) {
 748         checkStartCode();
 749         checkEndCode();
 750         checkOpcode(opcode, 6);
 751         checkLabel(label, false, "label");
 752         checkNonDebugLabel(label);
 753         super.visitJumpInsn(opcode, label);
 754         usedLabels.add(label);
 755         ++insnCount;
 756     }
 757 
 758     @Override
 759     public void visitLabel(final Label label) {
 760         checkStartCode();
 761         checkEndCode();
 762         checkLabel(label, false, "label");
 763         if (labels.get(label) != null) {
 764             throw new IllegalArgumentException("Already visited label");
 765         }
 766         labels.put(label, new Integer(insnCount));
 767         super.visitLabel(label);
 768     }
 769 
 770     @Override
 771     public void visitLdcInsn(final Object cst) {
 772         checkStartCode();
 773         checkEndCode();
 774         checkLDCConstant(cst);
 775         super.visitLdcInsn(cst);
 776         ++insnCount;
 777     }
 778 
 779     @Override
 780     public void visitIincInsn(final int var, final int increment) {
 781         checkStartCode();
 782         checkEndCode();
 783         checkUnsignedShort(var, "Invalid variable index");
 784         checkSignedShort(increment, "Invalid increment");
 785         super.visitIincInsn(var, increment);
 786         ++insnCount;
 787     }
 788 
 789     @Override
 790     public void visitTableSwitchInsn(final int min, final int max,
 791             final Label dflt, final Label... labels) {
 792         checkStartCode();
 793         checkEndCode();
 794         if (max < min) {
 795             throw new IllegalArgumentException("Max = " + max
 796                     + " must be greater than or equal to min = " + min);
 797         }
 798         checkLabel(dflt, false, "default label");
 799         checkNonDebugLabel(dflt);
 800         if (labels == null || labels.length != max - min + 1) {
 801             throw new IllegalArgumentException(
 802                     "There must be max - min + 1 labels");
 803         }
 804         for (int i = 0; i < labels.length; ++i) {
 805             checkLabel(labels[i], false, "label at index " + i);
 806             checkNonDebugLabel(labels[i]);
 807         }
 808         super.visitTableSwitchInsn(min, max, dflt, labels);
 809         for (int i = 0; i < labels.length; ++i) {
 810             usedLabels.add(labels[i]);
 811         }
 812         ++insnCount;
 813     }
 814 
 815     @Override
 816     public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
 817             final Label[] labels) {
 818         checkEndCode();
 819         checkStartCode();
 820         checkLabel(dflt, false, "default label");
 821         checkNonDebugLabel(dflt);
 822         if (keys == null || labels == null || keys.length != labels.length) {
 823             throw new IllegalArgumentException(
 824                     "There must be the same number of keys and labels");
 825         }
 826         for (int i = 0; i < labels.length; ++i) {
 827             checkLabel(labels[i], false, "label at index " + i);
 828             checkNonDebugLabel(labels[i]);
 829         }
 830         super.visitLookupSwitchInsn(dflt, keys, labels);
 831         usedLabels.add(dflt);
 832         for (int i = 0; i < labels.length; ++i) {
 833             usedLabels.add(labels[i]);
 834         }
 835         ++insnCount;
 836     }
 837 
 838     @Override
 839     public void visitMultiANewArrayInsn(final String desc, final int dims) {
 840         checkStartCode();
 841         checkEndCode();
 842         checkDesc(desc, false);
 843         if (desc.charAt(0) != '[') {
 844             throw new IllegalArgumentException(
 845                     "Invalid descriptor (must be an array type descriptor): "
 846                             + desc);
 847         }
 848         if (dims < 1) {
 849             throw new IllegalArgumentException(
 850                     "Invalid dimensions (must be greater than 0): " + dims);
 851         }
 852         if (dims > desc.lastIndexOf('[') + 1) {
 853             throw new IllegalArgumentException(
 854                     "Invalid dimensions (must not be greater than dims(desc)): "
 855                             + dims);
 856         }
 857         super.visitMultiANewArrayInsn(desc, dims);
 858         ++insnCount;
 859     }
 860 
 861     @Override
 862     public AnnotationVisitor visitInsnAnnotation(final int typeRef,
 863             final TypePath typePath, final String desc, final boolean visible) {
 864         checkStartCode();
 865         checkEndCode();
 866         int sort = typeRef >>> 24;
 867         if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW
 868                 && sort != TypeReference.CONSTRUCTOR_REFERENCE
 869                 && sort != TypeReference.METHOD_REFERENCE
 870                 && sort != TypeReference.CAST
 871                 && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
 872                 && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT
 873                 && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
 874                 && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) {
 875             throw new IllegalArgumentException("Invalid type reference sort 0x"
 876                     + Integer.toHexString(sort));
 877         }
 878         CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
 879         CheckMethodAdapter.checkDesc(desc, false);
 880         return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef,
 881                 typePath, desc, visible));
 882     }
 883 
 884     @Override
 885     public void visitTryCatchBlock(final Label start, final Label end,
 886             final Label handler, final String type) {
 887         checkStartCode();
 888         checkEndCode();
 889         checkLabel(start, false, "start label");
 890         checkLabel(end, false, "end label");
 891         checkLabel(handler, false, "handler label");
 892         checkNonDebugLabel(start);
 893         checkNonDebugLabel(end);
 894         checkNonDebugLabel(handler);
 895         if (labels.get(start) != null || labels.get(end) != null
 896                 || labels.get(handler) != null) {
 897             throw new IllegalStateException(
 898                     "Try catch blocks must be visited before their labels");
 899         }
 900         if (type != null) {
 901             checkInternalName(type, "type");
 902         }
 903         super.visitTryCatchBlock(start, end, handler, type);
 904         handlers.add(start);
 905         handlers.add(end);
 906     }
 907 
 908     @Override
 909     public AnnotationVisitor visitTryCatchAnnotation(final int typeRef,
 910             final TypePath typePath, final String desc, final boolean visible) {
 911         checkStartCode();
 912         checkEndCode();
 913         int sort = typeRef >>> 24;
 914         if (sort != TypeReference.EXCEPTION_PARAMETER) {
 915             throw new IllegalArgumentException("Invalid type reference sort 0x"
 916                     + Integer.toHexString(sort));
 917         }
 918         CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
 919         CheckMethodAdapter.checkDesc(desc, false);
 920         return new CheckAnnotationAdapter(super.visitTryCatchAnnotation(
 921                 typeRef, typePath, desc, visible));
 922     }
 923 
 924     @Override
 925     public void visitLocalVariable(final String name, final String desc,
 926             final String signature, final Label start, final Label end,
 927             final int index) {
 928         checkStartCode();
 929         checkEndCode();
 930         checkUnqualifiedName(version, name, "name");
 931         checkDesc(desc, false);
 932         checkLabel(start, true, "start label");
 933         checkLabel(end, true, "end label");
 934         checkUnsignedShort(index, "Invalid variable index");
 935         int s = labels.get(start).intValue();
 936         int e = labels.get(end).intValue();
 937         if (e < s) {
 938             throw new IllegalArgumentException(
 939                     "Invalid start and end labels (end must be greater than start)");
 940         }
 941         super.visitLocalVariable(name, desc, signature, start, end, index);
 942     }
 943 
 944     @Override
 945     public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
 946             TypePath typePath, Label[] start, Label[] end, int[] index,
 947             String desc, boolean visible) {
 948         checkStartCode();
 949         checkEndCode();
 950         int sort = typeRef >>> 24;
 951         if (sort != TypeReference.LOCAL_VARIABLE
 952                 && sort != TypeReference.RESOURCE_VARIABLE) {
 953             throw new IllegalArgumentException("Invalid type reference sort 0x"
 954                     + Integer.toHexString(sort));
 955         }
 956         CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
 957         checkDesc(desc, false);
 958         if (start == null || end == null || index == null
 959                 || end.length != start.length || index.length != start.length) {
 960             throw new IllegalArgumentException(
 961                     "Invalid start, end and index arrays (must be non null and of identical length");
 962         }
 963         for (int i = 0; i < start.length; ++i) {
 964             checkLabel(start[i], true, "start label");
 965             checkLabel(end[i], true, "end label");
 966             checkUnsignedShort(index[i], "Invalid variable index");
 967             int s = labels.get(start[i]).intValue();
 968             int e = labels.get(end[i]).intValue();
 969             if (e < s) {
 970                 throw new IllegalArgumentException(
 971                         "Invalid start and end labels (end must be greater than start)");
 972             }
 973         }
 974         return super.visitLocalVariableAnnotation(typeRef, typePath, start,
 975                 end, index, desc, visible);
 976     }
 977 
 978     @Override
 979     public void visitLineNumber(final int line, final Label start) {
 980         checkStartCode();
 981         checkEndCode();
 982         checkUnsignedShort(line, "Invalid line number");
 983         checkLabel(start, true, "start label");
 984         super.visitLineNumber(line, start);
 985     }
 986 
 987     @Override
 988     public void visitMaxs(final int maxStack, final int maxLocals) {
 989         checkStartCode();
 990         checkEndCode();
 991         endCode = true;
 992         for (Label l : usedLabels) {
 993             if (labels.get(l) == null) {
 994                 throw new IllegalStateException("Undefined label used");
 995             }
 996         }
 997         for (int i = 0; i < handlers.size();) {
 998             Integer start = labels.get(handlers.get(i++));
 999             Integer end = labels.get(handlers.get(i++));
1000             if (start == null || end == null) {
1001                 throw new IllegalStateException(
1002                         "Undefined try catch block labels");
1003             }
1004             if (end.intValue() <= start.intValue()) {
1005                 throw new IllegalStateException(
1006                         "Emty try catch block handler range");
1007             }
1008         }
1009         checkUnsignedShort(maxStack, "Invalid max stack");
1010         checkUnsignedShort(maxLocals, "Invalid max locals");
1011         super.visitMaxs(maxStack, maxLocals);
1012     }
1013 
1014     @Override
1015     public void visitEnd() {
1016         checkEndMethod();
1017         endMethod = true;
1018         super.visitEnd();
1019     }
1020 
1021     // -------------------------------------------------------------------------
1022 
1023     /**
1024      * Checks that the visitCode method has been called.
1025      */
1026     void checkStartCode() {
1027         if (!startCode) {
1028             throw new IllegalStateException(
1029                     "Cannot visit instructions before visitCode has been called.");
1030         }
1031     }
1032 
1033     /**
1034      * Checks that the visitMaxs method has not been called.
1035      */
1036     void checkEndCode() {
1037         if (endCode) {
1038             throw new IllegalStateException(
1039                     "Cannot visit instructions after visitMaxs has been called.");
1040         }
1041     }
1042 
1043     /**
1044      * Checks that the visitEnd method has not been called.
1045      */
1046     void checkEndMethod() {
1047         if (endMethod) {
1048             throw new IllegalStateException(
1049                     "Cannot visit elements after visitEnd has been called.");
1050         }
1051     }
1052 
1053     /**
1054      * Checks a stack frame value.
1055      *
1056      * @param value
1057      *            the value to be checked.
1058      */
1059     void checkFrameValue(final Object value) {
1060         if (value == Opcodes.TOP || value == Opcodes.INTEGER
1061                 || value == Opcodes.FLOAT || value == Opcodes.LONG
1062                 || value == Opcodes.DOUBLE || value == Opcodes.NULL
1063                 || value == Opcodes.UNINITIALIZED_THIS) {
1064             return;
1065         }
1066         if (value instanceof String) {
1067             checkInternalName((String) value, "Invalid stack frame value");
1068             return;
1069         }
1070         if (!(value instanceof Label)) {
1071             throw new IllegalArgumentException("Invalid stack frame value: "
1072                     + value);
1073         } else {
1074             usedLabels.add((Label) value);
1075         }
1076     }
1077 
1078     /**
1079      * Checks that the type of the given opcode is equal to the given type.
1080      *
1081      * @param opcode
1082      *            the opcode to be checked.
1083      * @param type
1084      *            the expected opcode type.
1085      */
1086     static void checkOpcode(final int opcode, final int type) {
1087         if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
1088             throw new IllegalArgumentException("Invalid opcode: " + opcode);
1089         }
1090     }
1091 
1092     /**
1093      * Checks that the given value is a signed byte.
1094      *
1095      * @param value
1096      *            the value to be checked.
1097      * @param msg
1098      *            an message to be used in case of error.
1099      */
1100     static void checkSignedByte(final int value, final String msg) {
1101         if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
1102             throw new IllegalArgumentException(msg
1103                     + " (must be a signed byte): " + value);
1104         }
1105     }
1106 
1107     /**
1108      * Checks that the given value is a signed short.
1109      *
1110      * @param value
1111      *            the value to be checked.
1112      * @param msg
1113      *            an message to be used in case of error.
1114      */
1115     static void checkSignedShort(final int value, final String msg) {
1116         if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
1117             throw new IllegalArgumentException(msg
1118                     + " (must be a signed short): " + value);
1119         }
1120     }
1121 
1122     /**
1123      * Checks that the given value is an unsigned short.
1124      *
1125      * @param value
1126      *            the value to be checked.
1127      * @param msg
1128      *            an message to be used in case of error.
1129      */
1130     static void checkUnsignedShort(final int value, final String msg) {
1131         if (value < 0 || value > 65535) {
1132             throw new IllegalArgumentException(msg
1133                     + " (must be an unsigned short): " + value);
1134         }
1135     }
1136 
1137     /**
1138      * Checks that the given value is an {@link Integer}, a{@link Float}, a
1139      * {@link Long}, a {@link Double} or a {@link String}.
1140      *
1141      * @param cst
1142      *            the value to be checked.
1143      */
1144     static void checkConstant(final Object cst) {
1145         if (!(cst instanceof Integer) && !(cst instanceof Float)
1146                 && !(cst instanceof Long) && !(cst instanceof Double)
1147                 && !(cst instanceof String)) {
1148             throw new IllegalArgumentException("Invalid constant: " + cst);
1149         }
1150     }
1151 
1152     void checkLDCConstant(final Object cst) {
1153         if (cst instanceof Type) {
1154             int s = ((Type) cst).getSort();
1155             if (s != Type.OBJECT && s != Type.ARRAY && s != Type.METHOD) {
1156                 throw new IllegalArgumentException("Illegal LDC constant value");
1157             }
1158             if (s != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) {
1159                 throw new IllegalArgumentException(
1160                         "ldc of a constant class requires at least version 1.5");
1161             }
1162             if (s == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) {
1163                 throw new IllegalArgumentException(
1164                         "ldc of a method type requires at least version 1.7");
1165             }
1166         } else if (cst instanceof Handle) {
1167             if ((version & 0xFFFF) < Opcodes.V1_7) {
1168                 throw new IllegalArgumentException(
1169                         "ldc of a handle requires at least version 1.7");
1170             }
1171             int tag = ((Handle) cst).getTag();
1172             if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) {
1173                 throw new IllegalArgumentException("invalid handle tag " + tag);
1174             }
1175         } else {
1176             checkConstant(cst);
1177         }
1178     }
1179 
1180     /**
1181      * Checks that the given string is a valid unqualified name.
1182      *
1183      * @param version
1184      *            the class version.
1185      * @param name
1186      *            the string to be checked.
1187      * @param msg
1188      *            a message to be used in case of error.
1189      */
1190     static void checkUnqualifiedName(int version, final String name,
1191             final String msg) {
1192         if ((version & 0xFFFF) < Opcodes.V1_5) {
1193             checkIdentifier(name, msg);
1194         } else {
1195             for (int i = 0; i < name.length(); ++i) {
1196                 if (".;[/".indexOf(name.charAt(i)) != -1) {
1197                     throw new IllegalArgumentException("Invalid " + msg
1198                             + " (must be a valid unqualified name): " + name);
1199                 }
1200             }
1201         }
1202     }
1203 
1204     /**
1205      * Checks that the given string is a valid Java identifier.
1206      *
1207      * @param name
1208      *            the string to be checked.
1209      * @param msg
1210      *            a message to be used in case of error.
1211      */
1212     static void checkIdentifier(final String name, final String msg) {
1213         checkIdentifier(name, 0, -1, msg);
1214     }
1215 
1216     /**
1217      * Checks that the given substring is a valid Java identifier.
1218      *
1219      * @param name
1220      *            the string to be checked.
1221      * @param start
1222      *            index of the first character of the identifier (inclusive).
1223      * @param end
1224      *            index of the last character of the identifier (exclusive). -1
1225      *            is equivalent to <tt>name.length()</tt> if name is not
1226      *            <tt>null</tt>.
1227      * @param msg
1228      *            a message to be used in case of error.
1229      */
1230     static void checkIdentifier(final String name, final int start,
1231             final int end, final String msg) {
1232         if (name == null || (end == -1 ? name.length() <= start : end <= start)) {
1233             throw new IllegalArgumentException("Invalid " + msg
1234                     + " (must not be null or empty)");
1235         }
1236         if (!Character.isJavaIdentifierStart(name.charAt(start))) {
1237             throw new IllegalArgumentException("Invalid " + msg
1238                     + " (must be a valid Java identifier): " + name);
1239         }
1240         int max = end == -1 ? name.length() : end;
1241         for (int i = start + 1; i < max; ++i) {
1242             if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1243                 throw new IllegalArgumentException("Invalid " + msg
1244                         + " (must be a valid Java identifier): " + name);
1245             }
1246         }
1247     }
1248 
1249     /**
1250      * Checks that the given string is a valid Java identifier.
1251      *
1252      * @param version
1253      *            the class version.
1254      * @param name
1255      *            the string to be checked.
1256      * @param msg
1257      *            a message to be used in case of error.
1258      */
1259     static void checkMethodIdentifier(int version, final String name,
1260             final String msg) {
1261         if (name == null || name.length() == 0) {
1262             throw new IllegalArgumentException("Invalid " + msg
1263                     + " (must not be null or empty)");
1264         }
1265         if ((version & 0xFFFF) >= Opcodes.V1_5) {
1266             for (int i = 0; i < name.length(); ++i) {
1267                 if (".;[/<>".indexOf(name.charAt(i)) != -1) {
1268                     throw new IllegalArgumentException("Invalid " + msg
1269                             + " (must be a valid unqualified name): " + name);
1270                 }
1271             }
1272             return;
1273         }
1274         if (!Character.isJavaIdentifierStart(name.charAt(0))) {
1275             throw new IllegalArgumentException(
1276                     "Invalid "
1277                             + msg
1278                             + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
1279                             + name);
1280         }
1281         for (int i = 1; i < name.length(); ++i) {
1282             if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1283                 throw new IllegalArgumentException(
1284                         "Invalid "
1285                                 + msg
1286                                 + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
1287                                 + name);
1288             }
1289         }
1290     }
1291 
1292     /**
1293      * Checks that the given string is a valid internal class name.
1294      *
1295      * @param name
1296      *            the string to be checked.
1297      * @param msg
1298      *            a message to be used in case of error.
1299      */
1300     static void checkInternalName(final String name, final String msg) {
1301         if (name == null || name.length() == 0) {
1302             throw new IllegalArgumentException("Invalid " + msg
1303                     + " (must not be null or empty)");
1304         }
1305         if (name.charAt(0) == '[') {
1306             checkDesc(name, false);
1307         } else {
1308             checkInternalName(name, 0, -1, msg);
1309         }
1310     }
1311 
1312     /**
1313      * Checks that the given substring is a valid internal class name.
1314      *
1315      * @param name
1316      *            the string to be checked.
1317      * @param start
1318      *            index of the first character of the identifier (inclusive).
1319      * @param end
1320      *            index of the last character of the identifier (exclusive). -1
1321      *            is equivalent to <tt>name.length()</tt> if name is not
1322      *            <tt>null</tt>.
1323      * @param msg
1324      *            a message to be used in case of error.
1325      */
1326     static void checkInternalName(final String name, final int start,
1327             final int end, final String msg) {
1328         int max = end == -1 ? name.length() : end;
1329         try {
1330             int begin = start;
1331             int slash;
1332             do {
1333                 slash = name.indexOf('/', begin + 1);
1334                 if (slash == -1 || slash > max) {
1335                     slash = max;
1336                 }
1337                 checkIdentifier(name, begin, slash, null);
1338                 begin = slash + 1;
1339             } while (slash != max);
1340         } catch (IllegalArgumentException unused) {
1341             throw new IllegalArgumentException(
1342                     "Invalid "
1343                             + msg
1344                             + " (must be a fully qualified class name in internal form): "
1345                             + name);
1346         }
1347     }
1348 
1349     /**
1350      * Checks that the given string is a valid type descriptor.
1351      *
1352      * @param desc
1353      *            the string to be checked.
1354      * @param canBeVoid
1355      *            <tt>true</tt> if <tt>V</tt> can be considered valid.
1356      */
1357     static void checkDesc(final String desc, final boolean canBeVoid) {
1358         int end = checkDesc(desc, 0, canBeVoid);
1359         if (end != desc.length()) {
1360             throw new IllegalArgumentException("Invalid descriptor: " + desc);
1361         }
1362     }
1363 
1364     /**
1365      * Checks that a the given substring is a valid type descriptor.
1366      *
1367      * @param desc
1368      *            the string to be checked.
1369      * @param start
1370      *            index of the first character of the identifier (inclusive).
1371      * @param canBeVoid
1372      *            <tt>true</tt> if <tt>V</tt> can be considered valid.
1373      * @return the index of the last character of the type decriptor, plus one.
1374      */
1375     static int checkDesc(final String desc, final int start,
1376             final boolean canBeVoid) {
1377         if (desc == null || start >= desc.length()) {
1378             throw new IllegalArgumentException(
1379                     "Invalid type descriptor (must not be null or empty)");
1380         }
1381         int index;
1382         switch (desc.charAt(start)) {
1383         case 'V':
1384             if (canBeVoid) {
1385                 return start + 1;
1386             } else {
1387                 throw new IllegalArgumentException("Invalid descriptor: "
1388                         + desc);
1389             }
1390         case 'Z':
1391         case 'C':
1392         case 'B':
1393         case 'S':
1394         case 'I':
1395         case 'F':
1396         case 'J':
1397         case 'D':
1398             return start + 1;
1399         case '[':
1400             index = start + 1;
1401             while (index < desc.length() && desc.charAt(index) == '[') {
1402                 ++index;
1403             }
1404             if (index < desc.length()) {
1405                 return checkDesc(desc, index, false);
1406             } else {
1407                 throw new IllegalArgumentException("Invalid descriptor: "
1408                         + desc);
1409             }
1410         case 'L':
1411             index = desc.indexOf(';', start);
1412             if (index == -1 || index - start < 2) {
1413                 throw new IllegalArgumentException("Invalid descriptor: "
1414                         + desc);
1415             }
1416             try {
1417                 checkInternalName(desc, start + 1, index, null);
1418             } catch (IllegalArgumentException unused) {
1419                 throw new IllegalArgumentException("Invalid descriptor: "
1420                         + desc);
1421             }
1422             return index + 1;
1423         default:
1424             throw new IllegalArgumentException("Invalid descriptor: " + desc);
1425         }
1426     }
1427 
1428     /**
1429      * Checks that the given string is a valid method descriptor.
1430      *
1431      * @param desc
1432      *            the string to be checked.
1433      */
1434     static void checkMethodDesc(final String desc) {
1435         if (desc == null || desc.length() == 0) {
1436             throw new IllegalArgumentException(
1437                     "Invalid method descriptor (must not be null or empty)");
1438         }
1439         if (desc.charAt(0) != '(' || desc.length() < 3) {
1440             throw new IllegalArgumentException("Invalid descriptor: " + desc);
1441         }
1442         int start = 1;
1443         if (desc.charAt(start) != ')') {
1444             do {
1445                 if (desc.charAt(start) == 'V') {
1446                     throw new IllegalArgumentException("Invalid descriptor: "
1447                             + desc);
1448                 }
1449                 start = checkDesc(desc, start, false);
1450             } while (start < desc.length() && desc.charAt(start) != ')');
1451         }
1452         start = checkDesc(desc, start + 1, true);
1453         if (start != desc.length()) {
1454             throw new IllegalArgumentException("Invalid descriptor: " + desc);
1455         }
1456     }
1457 
1458     /**
1459      * Checks that the given label is not null. This method can also check that
1460      * the label has been visited.
1461      *
1462      * @param label
1463      *            the label to be checked.
1464      * @param checkVisited
1465      *            <tt>true</tt> to check that the label has been visited.
1466      * @param msg
1467      *            a message to be used in case of error.
1468      */
1469     void checkLabel(final Label label, final boolean checkVisited,
1470             final String msg) {
1471         if (label == null) {
1472             throw new IllegalArgumentException("Invalid " + msg
1473                     + " (must not be null)");
1474         }
1475         if (checkVisited && labels.get(label) == null) {
1476             throw new IllegalArgumentException("Invalid " + msg
1477                     + " (must be visited first)");
1478         }
1479     }
1480 
1481     /**
1482      * Checks that the given label is not a label used only for debug purposes.
1483      *
1484      * @param label
1485      *            the label to be checked.
1486      */
1487     private static void checkNonDebugLabel(final Label label) {
1488         Field f = getLabelStatusField();
1489         int status = 0;
1490         try {
1491             status = f == null ? 0 : ((Integer) f.get(label)).intValue();
1492         } catch (IllegalAccessException e) {
1493             throw new Error("Internal error");
1494         }
1495         if ((status & 0x01) != 0) {
1496             throw new IllegalArgumentException(
1497                     "Labels used for debug info cannot be reused for control flow");
1498         }
1499     }
1500 
1501     /**
1502      * Returns the Field object corresponding to the Label.status field.
1503      *
1504      * @return the Field object corresponding to the Label.status field.
1505      */
1506     private static Field getLabelStatusField() {
1507         if (labelStatusField == null) {
1508             labelStatusField = getLabelField("a");
1509             if (labelStatusField == null) {
1510                 labelStatusField = getLabelField("status");
1511             }
1512         }
1513         return labelStatusField;
1514     }
1515 
1516     /**
1517      * Returns the field of the Label class whose name is given.
1518      *
1519      * @param name
1520      *            a field name.
1521      * @return the field of the Label class whose name is given, or null.
1522      */
1523     private static Field getLabelField(final String name) {
1524         try {
1525             Field f = Label.class.getDeclaredField(name);
1526             f.setAccessible(true);
1527             return f;
1528         } catch (NoSuchFieldException e) {
1529             return null;
1530         }
1531     }
1532 }