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