rev 47452 : imported patch jdk-new-asmv6.patch

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