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