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