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 
  60 package jdk.internal.org.objectweb.asm.util;
  61 
  62 import java.io.PrintWriter;
  63 import java.io.StringWriter;
  64 import java.util.ArrayList;
  65 import java.util.Collections;
  66 import java.util.HashMap;
  67 import java.util.HashSet;
  68 import java.util.List;
  69 import java.util.Map;
  70 import java.util.Set;
  71 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  72 import jdk.internal.org.objectweb.asm.Attribute;
  73 import jdk.internal.org.objectweb.asm.ClassWriter;
  74 import jdk.internal.org.objectweb.asm.ConstantDynamic;
  75 import jdk.internal.org.objectweb.asm.Handle;
  76 import jdk.internal.org.objectweb.asm.Label;
  77 import jdk.internal.org.objectweb.asm.MethodVisitor;
  78 import jdk.internal.org.objectweb.asm.Opcodes;
  79 import jdk.internal.org.objectweb.asm.Type;
  80 import jdk.internal.org.objectweb.asm.TypePath;
  81 import jdk.internal.org.objectweb.asm.TypeReference;
  82 import jdk.internal.org.objectweb.asm.tree.MethodNode;
  83 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
  84 import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
  85 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
  86 import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier;
  87 
  88 /**
  89  * A {@link MethodVisitor} that checks that its methods are properly used. More precisely this
  90  * method adapter checks each instruction individually, i.e., each visit method checks some
  91  * preconditions based <i>only</i> on its arguments - such as the fact that the given opcode is
  92  * correct for a given visit method. This adapter can also perform some basic data flow checks (more
  93  * precisely those that can be performed without the full class hierarchy - see {@link
  94  * jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a method whose signature is
  95  * {@code void m ()}, the invalid instruction IRETURN, or the invalid sequence IADD L2I will be
  96  * detected if the data flow checks are enabled. These checks are enabled by using the {@link
  97  * #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. They are not performed if
  98  * any other constructor is used.
  99  *
 100  * @author Eric Bruneton
 101  */
 102 public class CheckMethodAdapter extends MethodVisitor {
 103 
 104     /** The 'generic' instruction visit methods (i.e. those that take an opcode argument). */
 105     private enum Method {
 106         VISIT_INSN,
 107         VISIT_INT_INSN,
 108         VISIT_VAR_INSN,
 109         VISIT_TYPE_INSN,
 110         VISIT_FIELD_INSN,
 111         VISIT_METHOD_INSN,
 112         VISIT_JUMP_INSN
 113     }
 114 
 115     /** The method to use to visit each instruction. Only generic methods are represented here. */
 116     private static final Method[] OPCODE_METHODS = {
 117         Method.VISIT_INSN, // NOP
 118         Method.VISIT_INSN, // ACONST_NULL
 119         Method.VISIT_INSN, // ICONST_M1
 120         Method.VISIT_INSN, // ICONST_0
 121         Method.VISIT_INSN, // ICONST_1
 122         Method.VISIT_INSN, // ICONST_2
 123         Method.VISIT_INSN, // ICONST_3
 124         Method.VISIT_INSN, // ICONST_4
 125         Method.VISIT_INSN, // ICONST_5
 126         Method.VISIT_INSN, // LCONST_0
 127         Method.VISIT_INSN, // LCONST_1
 128         Method.VISIT_INSN, // FCONST_0
 129         Method.VISIT_INSN, // FCONST_1
 130         Method.VISIT_INSN, // FCONST_2
 131         Method.VISIT_INSN, // DCONST_0
 132         Method.VISIT_INSN, // DCONST_1
 133         Method.VISIT_INT_INSN, // BIPUSH
 134         Method.VISIT_INT_INSN, // SIPUSH
 135         null, // LDC
 136         null, // LDC_W
 137         null, // LDC2_W
 138         Method.VISIT_VAR_INSN, // ILOAD
 139         Method.VISIT_VAR_INSN, // LLOAD
 140         Method.VISIT_VAR_INSN, // FLOAD
 141         Method.VISIT_VAR_INSN, // DLOAD
 142         Method.VISIT_VAR_INSN, // ALOAD
 143         null, // ILOAD_0
 144         null, // ILOAD_1
 145         null, // ILOAD_2
 146         null, // ILOAD_3
 147         null, // LLOAD_0
 148         null, // LLOAD_1
 149         null, // LLOAD_2
 150         null, // LLOAD_3
 151         null, // FLOAD_0
 152         null, // FLOAD_1
 153         null, // FLOAD_2
 154         null, // FLOAD_3
 155         null, // DLOAD_0
 156         null, // DLOAD_1
 157         null, // DLOAD_2
 158         null, // DLOAD_3
 159         null, // ALOAD_0
 160         null, // ALOAD_1
 161         null, // ALOAD_2
 162         null, // ALOAD_3
 163         Method.VISIT_INSN, // IALOAD
 164         Method.VISIT_INSN, // LALOAD
 165         Method.VISIT_INSN, // FALOAD
 166         Method.VISIT_INSN, // DALOAD
 167         Method.VISIT_INSN, // AALOAD
 168         Method.VISIT_INSN, // BALOAD
 169         Method.VISIT_INSN, // CALOAD
 170         Method.VISIT_INSN, // SALOAD
 171         Method.VISIT_VAR_INSN, // ISTORE
 172         Method.VISIT_VAR_INSN, // LSTORE
 173         Method.VISIT_VAR_INSN, // FSTORE
 174         Method.VISIT_VAR_INSN, // DSTORE
 175         Method.VISIT_VAR_INSN, // ASTORE
 176         null, // ISTORE_0
 177         null, // ISTORE_1
 178         null, // ISTORE_2
 179         null, // ISTORE_3
 180         null, // LSTORE_0
 181         null, // LSTORE_1
 182         null, // LSTORE_2
 183         null, // LSTORE_3
 184         null, // FSTORE_0
 185         null, // FSTORE_1
 186         null, // FSTORE_2
 187         null, // FSTORE_3
 188         null, // DSTORE_0
 189         null, // DSTORE_1
 190         null, // DSTORE_2
 191         null, // DSTORE_3
 192         null, // ASTORE_0
 193         null, // ASTORE_1
 194         null, // ASTORE_2
 195         null, // ASTORE_3
 196         Method.VISIT_INSN, // IASTORE
 197         Method.VISIT_INSN, // LASTORE
 198         Method.VISIT_INSN, // FASTORE
 199         Method.VISIT_INSN, // DASTORE
 200         Method.VISIT_INSN, // AASTORE
 201         Method.VISIT_INSN, // BASTORE
 202         Method.VISIT_INSN, // CASTORE
 203         Method.VISIT_INSN, // SASTORE
 204         Method.VISIT_INSN, // POP
 205         Method.VISIT_INSN, // POP2
 206         Method.VISIT_INSN, // DUP
 207         Method.VISIT_INSN, // DUP_X1
 208         Method.VISIT_INSN, // DUP_X2
 209         Method.VISIT_INSN, // DUP2
 210         Method.VISIT_INSN, // DUP2_X1
 211         Method.VISIT_INSN, // DUP2_X2
 212         Method.VISIT_INSN, // SWAP
 213         Method.VISIT_INSN, // IADD
 214         Method.VISIT_INSN, // LADD
 215         Method.VISIT_INSN, // FADD
 216         Method.VISIT_INSN, // DADD
 217         Method.VISIT_INSN, // ISUB
 218         Method.VISIT_INSN, // LSUB
 219         Method.VISIT_INSN, // FSUB
 220         Method.VISIT_INSN, // DSUB
 221         Method.VISIT_INSN, // IMUL
 222         Method.VISIT_INSN, // LMUL
 223         Method.VISIT_INSN, // FMUL
 224         Method.VISIT_INSN, // DMUL
 225         Method.VISIT_INSN, // IDIV
 226         Method.VISIT_INSN, // LDIV
 227         Method.VISIT_INSN, // FDIV
 228         Method.VISIT_INSN, // DDIV
 229         Method.VISIT_INSN, // IREM
 230         Method.VISIT_INSN, // LREM
 231         Method.VISIT_INSN, // FREM
 232         Method.VISIT_INSN, // DREM
 233         Method.VISIT_INSN, // INEG
 234         Method.VISIT_INSN, // LNEG
 235         Method.VISIT_INSN, // FNEG
 236         Method.VISIT_INSN, // DNEG
 237         Method.VISIT_INSN, // ISHL
 238         Method.VISIT_INSN, // LSHL
 239         Method.VISIT_INSN, // ISHR
 240         Method.VISIT_INSN, // LSHR
 241         Method.VISIT_INSN, // IUSHR
 242         Method.VISIT_INSN, // LUSHR
 243         Method.VISIT_INSN, // IAND
 244         Method.VISIT_INSN, // LAND
 245         Method.VISIT_INSN, // IOR
 246         Method.VISIT_INSN, // LOR
 247         Method.VISIT_INSN, // IXOR
 248         Method.VISIT_INSN, // LXOR
 249         null, // IINC
 250         Method.VISIT_INSN, // I2L
 251         Method.VISIT_INSN, // I2F
 252         Method.VISIT_INSN, // I2D
 253         Method.VISIT_INSN, // L2I
 254         Method.VISIT_INSN, // L2F
 255         Method.VISIT_INSN, // L2D
 256         Method.VISIT_INSN, // F2I
 257         Method.VISIT_INSN, // F2L
 258         Method.VISIT_INSN, // F2D
 259         Method.VISIT_INSN, // D2I
 260         Method.VISIT_INSN, // D2L
 261         Method.VISIT_INSN, // D2F
 262         Method.VISIT_INSN, // I2B
 263         Method.VISIT_INSN, // I2C
 264         Method.VISIT_INSN, // I2S
 265         Method.VISIT_INSN, // LCMP
 266         Method.VISIT_INSN, // FCMPL
 267         Method.VISIT_INSN, // FCMPG
 268         Method.VISIT_INSN, // DCMPL
 269         Method.VISIT_INSN, // DCMPG
 270         Method.VISIT_JUMP_INSN, // IFEQ
 271         Method.VISIT_JUMP_INSN, // IFNE
 272         Method.VISIT_JUMP_INSN, // IFLT
 273         Method.VISIT_JUMP_INSN, // IFGE
 274         Method.VISIT_JUMP_INSN, // IFGT
 275         Method.VISIT_JUMP_INSN, // IFLE
 276         Method.VISIT_JUMP_INSN, // IF_ICMPEQ
 277         Method.VISIT_JUMP_INSN, // IF_ICMPNE
 278         Method.VISIT_JUMP_INSN, // IF_ICMPLT
 279         Method.VISIT_JUMP_INSN, // IF_ICMPGE
 280         Method.VISIT_JUMP_INSN, // IF_ICMPGT
 281         Method.VISIT_JUMP_INSN, // IF_ICMPLE
 282         Method.VISIT_JUMP_INSN, // IF_ACMPEQ
 283         Method.VISIT_JUMP_INSN, // IF_ACMPNE
 284         Method.VISIT_JUMP_INSN, // GOTO
 285         Method.VISIT_JUMP_INSN, // JSR
 286         Method.VISIT_VAR_INSN, // RET
 287         null, // TABLESWITCH
 288         null, // LOOKUPSWITCH
 289         Method.VISIT_INSN, // IRETURN
 290         Method.VISIT_INSN, // LRETURN
 291         Method.VISIT_INSN, // FRETURN
 292         Method.VISIT_INSN, // DRETURN
 293         Method.VISIT_INSN, // ARETURN
 294         Method.VISIT_INSN, // RETURN
 295         Method.VISIT_FIELD_INSN, // GETSTATIC
 296         Method.VISIT_FIELD_INSN, // PUTSTATIC
 297         Method.VISIT_FIELD_INSN, // GETFIELD
 298         Method.VISIT_FIELD_INSN, // PUTFIELD
 299         Method.VISIT_METHOD_INSN, // INVOKEVIRTUAL
 300         Method.VISIT_METHOD_INSN, // INVOKESPECIAL
 301         Method.VISIT_METHOD_INSN, // INVOKESTATIC
 302         Method.VISIT_METHOD_INSN, // INVOKEINTERFACE
 303         null, // INVOKEDYNAMIC
 304         Method.VISIT_TYPE_INSN, // NEW
 305         Method.VISIT_INT_INSN, // NEWARRAY
 306         Method.VISIT_TYPE_INSN, // ANEWARRAY
 307         Method.VISIT_INSN, // ARRAYLENGTH
 308         Method.VISIT_INSN, // ATHROW
 309         Method.VISIT_TYPE_INSN, // CHECKCAST
 310         Method.VISIT_TYPE_INSN, // INSTANCEOF
 311         Method.VISIT_INSN, // MONITORENTER
 312         Method.VISIT_INSN, // MONITOREXIT
 313         null, // WIDE
 314         null, // MULTIANEWARRAY
 315         Method.VISIT_JUMP_INSN, // IFNULL
 316         Method.VISIT_JUMP_INSN // IFNONNULL
 317     };
 318 
 319     private static final String INVALID = "Invalid ";
 320     private static final String INVALID_DESCRIPTOR = "Invalid descriptor: ";
 321     private static final String INVALID_TYPE_REFERENCE = "Invalid type reference sort 0x";
 322     private static final String INVALID_LOCAL_VARIABLE_INDEX = "Invalid local variable index";
 323     private static final String MUST_NOT_BE_NULL_OR_EMPTY = " (must not be null or empty)";
 324     private static final String START_LABEL = "start label";
 325     private static final String END_LABEL = "end label";
 326 
 327     /** The class version number. */
 328     public int version;
 329 
 330     /** The access flags of the visited method. */
 331     private int access;
 332 
 333     /**
 334       * The number of method parameters that can have runtime visible annotations. 0 means that all the
 335       * parameters from the method descriptor can have annotations.
 336       */
 337     private int visibleAnnotableParameterCount;
 338 
 339     /**
 340       * The number of method parameters that can have runtime invisible annotations. 0 means that all
 341       * the parameters from the method descriptor can have annotations.
 342       */
 343     private int invisibleAnnotableParameterCount;
 344 
 345     /** Whether the {@link #visitCode} method has been called. */
 346     private boolean visitCodeCalled;
 347 
 348     /** Whether the {@link #visitMaxs} method has been called. */
 349     private boolean visitMaxCalled;
 350 
 351     /** Whether the {@link #visitEnd} method has been called. */
 352     private boolean visitEndCalled;
 353 
 354     /** The number of visited instructions so far. */
 355     private int insnCount;
 356 
 357     /** The index of the instruction designated by each visited label. */
 358     private final Map<Label, Integer> labelInsnIndices;
 359 
 360     /** The labels referenced by the visited method. */
 361     private Set<Label> referencedLabels;
 362 
 363     /** The index of the instruction corresponding to the last visited stack map frame. */
 364     private int lastFrameInsnIndex = -1;
 365 
 366     /** The number of visited frames in expanded form. */
 367     private int numExpandedFrames;
 368 
 369     /** The number of visited frames in compressed form. */
 370     private int numCompressedFrames;
 371 
 372     /**
 373       * The exception handler ranges. Each pair of list element contains the start and end labels of an
 374       * exception handler block.
 375       */
 376     private List<Label> handlers;
 377 
 378     /**
 379       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
 380       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
 381       * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link
 382       * #CheckMethodAdapter(int, MethodVisitor, Map)} version.
 383       *
 384       * @param methodvisitor the method visitor to which this adapter must delegate calls.
 385       */
 386     public CheckMethodAdapter(final MethodVisitor methodvisitor) {
 387         this(methodvisitor, new HashMap<>());
 388     }
 389 
 390     /**
 391       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
 392       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
 393       * <i>Subclasses must not use this constructor</i>. Instead, they must use the {@link
 394       * #CheckMethodAdapter(int, MethodVisitor, Map)} version.
 395       *
 396       * @param methodVisitor the method visitor to which this adapter must delegate calls.
 397       * @param labelInsnIndices the index of the instruction designated by each visited label so far
 398       *     (in other methods). This map is updated with the labels from the visited method.
 399       * @throws IllegalStateException If a subclass calls this constructor.
 400       */
 401     public CheckMethodAdapter(
 402             final MethodVisitor methodVisitor, final Map<Label, Integer> labelInsnIndices) {
 403         this(/* latest api = */ Opcodes.ASM9, methodVisitor, labelInsnIndices);
 404         if (getClass() != CheckMethodAdapter.class) {
 405             throw new IllegalStateException();
 406         }
 407     }
 408 
 409     /**
 410       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will not perform any
 411       * data flow check (see {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}).
 412       *
 413       * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of the
 414       *     {@code ASM}<i>x</i> values in {@link Opcodes}.
 415       * @param methodVisitor the method visitor to which this adapter must delegate calls.
 416       * @param labelInsnIndices the index of the instruction designated by each visited label so far
 417       *     (in other methods). This map is updated with the labels from the visited method.
 418       */
 419     protected CheckMethodAdapter(
 420             final int api,
 421             final MethodVisitor methodVisitor,
 422             final Map<Label, Integer> labelInsnIndices) {
 423         super(api, methodVisitor);
 424         this.labelInsnIndices = labelInsnIndices;
 425         this.referencedLabels = new HashSet<>();
 426         this.handlers = new ArrayList<>();
 427     }
 428 
 429     /**
 430       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data
 431       * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid
 432       * instruction IRETURN, or the invalid sequence IADD L2I will be detected. <i>Subclasses must not
 433       * use this constructor</i>. Instead, they must use the {@link
 434       * #CheckMethodAdapter(int,int,String,String,MethodVisitor,Map)} version.
 435       *
 436       * @param access the method's access flags.
 437       * @param name the method's name.
 438       * @param descriptor the method's descriptor (see {@link Type}).
 439       * @param methodVisitor the method visitor to which this adapter must delegate calls.
 440       * @param labelInsnIndices the index of the instruction designated by each visited label so far
 441       *     (in other methods). This map is updated with the labels from the visited method.
 442       */
 443     public CheckMethodAdapter(
 444             final int access,
 445             final String name,
 446             final String descriptor,
 447             final MethodVisitor methodVisitor,
 448             final Map<Label, Integer> labelInsnIndices) {
 449         this(
 450                 /* latest api = */ Opcodes.ASM9, access, name, descriptor, methodVisitor, labelInsnIndices);
 451         if (getClass() != CheckMethodAdapter.class) {
 452             throw new IllegalStateException();
 453         }
 454     }
 455 
 456     /**
 457       * Constructs a new {@link CheckMethodAdapter} object. This method adapter will perform basic data
 458       * flow checks. For instance in a method whose signature is {@code void m ()}, the invalid
 459       * instruction IRETURN, or the invalid sequence IADD L2I will be detected.
 460       *
 461       * @param api the ASM API version implemented by this CheckMethodAdapter. Must be one of the
 462       *     {@code ASM}<i>x</i> values in {@link Opcodes}.
 463       * @param access the method's access flags.
 464       * @param name the method's name.
 465       * @param descriptor the method's descriptor (see {@link Type}).
 466       * @param methodVisitor the method visitor to which this adapter must delegate calls.
 467       * @param labelInsnIndices the index of the instruction designated by each visited label so far
 468       *     (in other methods). This map is updated with the labels from the visited method.
 469       */
 470     protected CheckMethodAdapter(
 471             final int api,
 472             final int access,
 473             final String name,
 474             final String descriptor,
 475             final MethodVisitor methodVisitor,
 476             final Map<Label, Integer> labelInsnIndices) {
 477         this(
 478                 api,
 479                 new MethodNode(api, access, name, descriptor, null, null) {
 480                     @Override
 481                     public void visitEnd() {
 482                         Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicVerifier());
 483                         try {
 484                             // If 'methodVisitor' is a MethodWriter of a ClassWriter with no flags to compute the
 485                             // max stack and locals nor the stack map frames, we know that valid max stack and
 486                             // locals must be provided. Otherwise we assume they are not needed at this stage.
 487                             // TODO(ebruneton): similarly, check that valid stack map frames are provided if the
 488                             // class writer has no flags to compute them, and the class version is V1_7 or more.
 489                             boolean checkMaxStackAndLocals =
 490                                     (methodVisitor instanceof MethodWriterWrapper)
 491                                             && !((MethodWriterWrapper) methodVisitor).computesMaxs();
 492                             if (checkMaxStackAndLocals) {
 493                                 analyzer.analyze("dummy", this);
 494                             } else {
 495                                 analyzer.analyzeAndComputeMaxs("dummy", this);
 496                             }
 497                         } catch (IndexOutOfBoundsException | AnalyzerException e) {
 498                             throwError(analyzer, e);
 499                         }
 500                         if (methodVisitor != null) {
 501                             accept(methodVisitor);
 502                         }
 503                     }
 504 
 505                     private void throwError(final Analyzer<BasicValue> analyzer, final Exception e) {
 506                         StringWriter stringWriter = new StringWriter();
 507                         PrintWriter printWriter = new PrintWriter(stringWriter, true);
 508                         CheckClassAdapter.printAnalyzerResult(this, analyzer, printWriter);
 509                         printWriter.close();
 510                         throw new IllegalArgumentException(e.getMessage() + ' ' + stringWriter.toString(), e);
 511                     }
 512                 },
 513                 labelInsnIndices);
 514         this.access = access;
 515     }
 516 
 517     @Override
 518     public void visitParameter(final String name, final int access) {
 519         if (name != null) {
 520             checkUnqualifiedName(version, name, "name");
 521         }
 522         CheckClassAdapter.checkAccess(
 523                 access, Opcodes.ACC_FINAL | Opcodes.ACC_MANDATED | Opcodes.ACC_SYNTHETIC);
 524         super.visitParameter(name, access);
 525     }
 526 
 527     @Override
 528     public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
 529         checkVisitEndNotCalled();
 530         checkDescriptor(version, descriptor, false);
 531         return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
 532     }
 533 
 534     @Override
 535     public AnnotationVisitor visitTypeAnnotation(
 536             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
 537         checkVisitEndNotCalled();
 538         int sort = new TypeReference(typeRef).getSort();
 539         if (sort != TypeReference.METHOD_TYPE_PARAMETER
 540                 && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND
 541                 && sort != TypeReference.METHOD_RETURN
 542                 && sort != TypeReference.METHOD_RECEIVER
 543                 && sort != TypeReference.METHOD_FORMAL_PARAMETER
 544                 && sort != TypeReference.THROWS) {
 545             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
 546         }
 547         CheckClassAdapter.checkTypeRef(typeRef);
 548         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
 549         return new CheckAnnotationAdapter(
 550                 super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
 551     }
 552 
 553     @Override
 554     public AnnotationVisitor visitAnnotationDefault() {
 555         checkVisitEndNotCalled();
 556         return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false);
 557     }
 558 
 559     @Override
 560     public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) {
 561         checkVisitEndNotCalled();
 562         if (visible) {
 563             visibleAnnotableParameterCount = parameterCount;
 564         } else {
 565             invisibleAnnotableParameterCount = parameterCount;
 566         }
 567         super.visitAnnotableParameterCount(parameterCount, visible);
 568     }
 569 
 570     @Override
 571     public AnnotationVisitor visitParameterAnnotation(
 572             final int parameter, final String descriptor, final boolean visible) {
 573         checkVisitEndNotCalled();
 574         if ((visible
 575                         && visibleAnnotableParameterCount > 0
 576                         && parameter >= visibleAnnotableParameterCount)
 577                 || (!visible
 578                         && invisibleAnnotableParameterCount > 0
 579                         && parameter >= invisibleAnnotableParameterCount)) {
 580             throw new IllegalArgumentException("Invalid parameter index");
 581         }
 582         checkDescriptor(version, descriptor, false);
 583         return new CheckAnnotationAdapter(
 584                 super.visitParameterAnnotation(parameter, descriptor, visible));
 585     }
 586 
 587     @Override
 588     public void visitAttribute(final Attribute attribute) {
 589         checkVisitEndNotCalled();
 590         if (attribute == null) {
 591             throw new IllegalArgumentException("Invalid attribute (must not be null)");
 592         }
 593         super.visitAttribute(attribute);
 594     }
 595 
 596     @Override
 597     public void visitCode() {
 598         if ((access & Opcodes.ACC_ABSTRACT) != 0) {
 599             throw new UnsupportedOperationException("Abstract methods cannot have code");
 600         }
 601         visitCodeCalled = true;
 602         super.visitCode();
 603     }
 604 
 605     @Override
 606     public void visitFrame(
 607             final int type,
 608             final int numLocal,
 609             final Object[] local,
 610             final int numStack,
 611             final Object[] stack) {
 612         if (insnCount == lastFrameInsnIndex) {
 613             throw new IllegalStateException("At most one frame can be visited at a given code location.");
 614         }
 615         lastFrameInsnIndex = insnCount;
 616         int maxNumLocal;
 617         int maxNumStack;
 618         switch (type) {
 619             case Opcodes.F_NEW:
 620             case Opcodes.F_FULL:
 621                 maxNumLocal = Integer.MAX_VALUE;
 622                 maxNumStack = Integer.MAX_VALUE;
 623                 break;
 624 
 625             case Opcodes.F_SAME:
 626                 maxNumLocal = 0;
 627                 maxNumStack = 0;
 628                 break;
 629 
 630             case Opcodes.F_SAME1:
 631                 maxNumLocal = 0;
 632                 maxNumStack = 1;
 633                 break;
 634 
 635             case Opcodes.F_APPEND:
 636             case Opcodes.F_CHOP:
 637                 maxNumLocal = 3;
 638                 maxNumStack = 0;
 639                 break;
 640 
 641             default:
 642                 throw new IllegalArgumentException("Invalid frame type " + type);
 643         }
 644 
 645         if (numLocal > maxNumLocal) {
 646             throw new IllegalArgumentException(
 647                     "Invalid numLocal=" + numLocal + " for frame type " + type);
 648         }
 649         if (numStack > maxNumStack) {
 650             throw new IllegalArgumentException(
 651                     "Invalid numStack=" + numStack + " for frame type " + type);
 652         }
 653 
 654         if (type != Opcodes.F_CHOP) {
 655             if (numLocal > 0 && (local == null || local.length < numLocal)) {
 656                 throw new IllegalArgumentException("Array local[] is shorter than numLocal");
 657             }
 658             for (int i = 0; i < numLocal; ++i) {
 659                 checkFrameValue(local[i]);
 660             }
 661         }
 662         if (numStack > 0 && (stack == null || stack.length < numStack)) {
 663             throw new IllegalArgumentException("Array stack[] is shorter than numStack");
 664         }
 665         for (int i = 0; i < numStack; ++i) {
 666             checkFrameValue(stack[i]);
 667         }
 668         if (type == Opcodes.F_NEW) {
 669             ++numExpandedFrames;
 670         } else {
 671             ++numCompressedFrames;
 672         }
 673         if (numExpandedFrames > 0 && numCompressedFrames > 0) {
 674             throw new IllegalArgumentException("Expanded and compressed frames must not be mixed.");
 675         }
 676         super.visitFrame(type, numLocal, local, numStack, stack);
 677     }
 678 
 679     @Override
 680     public void visitInsn(final int opcode) {
 681         checkVisitCodeCalled();
 682         checkVisitMaxsNotCalled();
 683         checkOpcodeMethod(opcode, Method.VISIT_INSN);
 684         super.visitInsn(opcode);
 685         ++insnCount;
 686     }
 687 
 688     @Override
 689     public void visitIntInsn(final int opcode, final int operand) {
 690         checkVisitCodeCalled();
 691         checkVisitMaxsNotCalled();
 692         checkOpcodeMethod(opcode, Method.VISIT_INT_INSN);
 693         switch (opcode) {
 694             case Opcodes.BIPUSH:
 695                 checkSignedByte(operand, "Invalid operand");
 696                 break;
 697             case Opcodes.SIPUSH:
 698                 checkSignedShort(operand, "Invalid operand");
 699                 break;
 700             case Opcodes.NEWARRAY:
 701                 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
 702                     throw new IllegalArgumentException(
 703                             "Invalid operand (must be an array type code T_...): " + operand);
 704                 }
 705                 break;
 706             default:
 707                 throw new AssertionError();
 708         }
 709         super.visitIntInsn(opcode, operand);
 710         ++insnCount;
 711     }
 712 
 713     @Override
 714     public void visitVarInsn(final int opcode, final int varIndex) {
 715         checkVisitCodeCalled();
 716         checkVisitMaxsNotCalled();
 717         checkOpcodeMethod(opcode, Method.VISIT_VAR_INSN);
 718         checkUnsignedShort(varIndex, INVALID_LOCAL_VARIABLE_INDEX);
 719         super.visitVarInsn(opcode, varIndex);
 720         ++insnCount;
 721     }
 722 
 723     @Override
 724     public void visitTypeInsn(final int opcode, final String type) {
 725         checkVisitCodeCalled();
 726         checkVisitMaxsNotCalled();
 727         checkOpcodeMethod(opcode, Method.VISIT_TYPE_INSN);
 728         checkInternalName(version, type, "type");
 729         if (opcode == Opcodes.NEW && type.charAt(0) == '[') {
 730             throw new IllegalArgumentException("NEW cannot be used to create arrays: " + type);
 731         }
 732         super.visitTypeInsn(opcode, type);
 733         ++insnCount;
 734     }
 735 
 736     @Override
 737     public void visitFieldInsn(
 738             final int opcode, final String owner, final String name, final String descriptor) {
 739         checkVisitCodeCalled();
 740         checkVisitMaxsNotCalled();
 741         checkOpcodeMethod(opcode, Method.VISIT_FIELD_INSN);
 742         checkInternalName(version, owner, "owner");
 743         checkUnqualifiedName(version, name, "name");
 744         checkDescriptor(version, descriptor, false);
 745         super.visitFieldInsn(opcode, owner, name, descriptor);
 746         ++insnCount;
 747     }
 748 
 749     @Override
 750     public void visitMethodInsn(
 751             final int opcodeAndSource,
 752             final String owner,
 753             final String name,
 754             final String descriptor,
 755             final boolean isInterface) {
 756         if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
 757             // Redirect the call to the deprecated version of this method.
 758             super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
 759             return;
 760         }
 761         int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
 762 
 763         checkVisitCodeCalled();
 764         checkVisitMaxsNotCalled();
 765         checkOpcodeMethod(opcode, Method.VISIT_METHOD_INSN);
 766         if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) {
 767             checkMethodIdentifier(version, name, "name");
 768         }
 769         checkInternalName(version, owner, "owner");
 770         checkMethodDescriptor(version, descriptor);
 771         if (opcode == Opcodes.INVOKEVIRTUAL && isInterface) {
 772             throw new IllegalArgumentException("INVOKEVIRTUAL can't be used with interfaces");
 773         }
 774         if (opcode == Opcodes.INVOKEINTERFACE && !isInterface) {
 775             throw new IllegalArgumentException("INVOKEINTERFACE can't be used with classes");
 776         }
 777         if (opcode == Opcodes.INVOKESPECIAL && isInterface && (version & 0xFFFF) < Opcodes.V1_8) {
 778             throw new IllegalArgumentException(
 779                     "INVOKESPECIAL can't be used with interfaces prior to Java 8");
 780         }
 781         super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
 782         ++insnCount;
 783     }
 784 
 785     @Override
 786     public void visitInvokeDynamicInsn(
 787             final String name,
 788             final String descriptor,
 789             final Handle bootstrapMethodHandle,
 790             final Object... bootstrapMethodArguments) {
 791         checkVisitCodeCalled();
 792         checkVisitMaxsNotCalled();
 793         checkMethodIdentifier(version, name, "name");
 794         checkMethodDescriptor(version, descriptor);
 795         if (bootstrapMethodHandle.getTag() != Opcodes.H_INVOKESTATIC
 796                 && bootstrapMethodHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
 797             throw new IllegalArgumentException("invalid handle tag " + bootstrapMethodHandle.getTag());
 798         }
 799         for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
 800             checkLdcConstant(bootstrapMethodArgument);
 801         }
 802         super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
 803         ++insnCount;
 804     }
 805 
 806     @Override
 807     public void visitJumpInsn(final int opcode, final Label label) {
 808         checkVisitCodeCalled();
 809         checkVisitMaxsNotCalled();
 810         checkOpcodeMethod(opcode, Method.VISIT_JUMP_INSN);
 811         checkLabel(label, false, "label");
 812         super.visitJumpInsn(opcode, label);
 813         referencedLabels.add(label);
 814         ++insnCount;
 815     }
 816 
 817     @Override
 818     public void visitLabel(final Label label) {
 819         checkVisitCodeCalled();
 820         checkVisitMaxsNotCalled();
 821         checkLabel(label, false, "label");
 822         if (labelInsnIndices.get(label) != null) {
 823             throw new IllegalStateException("Already visited label");
 824         }
 825         labelInsnIndices.put(label, insnCount);
 826         super.visitLabel(label);
 827     }
 828 
 829     @Override
 830     public void visitLdcInsn(final Object value) {
 831         checkVisitCodeCalled();
 832         checkVisitMaxsNotCalled();
 833         checkLdcConstant(value);
 834         super.visitLdcInsn(value);
 835         ++insnCount;
 836     }
 837 
 838     @Override
 839     public void visitIincInsn(final int varIndex, final int increment) {
 840         checkVisitCodeCalled();
 841         checkVisitMaxsNotCalled();
 842         checkUnsignedShort(varIndex, INVALID_LOCAL_VARIABLE_INDEX);
 843         checkSignedShort(increment, "Invalid increment");
 844         super.visitIincInsn(varIndex, increment);
 845         ++insnCount;
 846     }
 847 
 848     @Override
 849     public void visitTableSwitchInsn(
 850             final int min, final int max, final Label dflt, final Label... labels) {
 851         checkVisitCodeCalled();
 852         checkVisitMaxsNotCalled();
 853         if (max < min) {
 854             throw new IllegalArgumentException(
 855                     "Max = " + max + " must be greater than or equal to min = " + min);
 856         }
 857         checkLabel(dflt, false, "default label");
 858         if (labels == null || labels.length != max - min + 1) {
 859             throw new IllegalArgumentException("There must be max - min + 1 labels");
 860         }
 861         for (int i = 0; i < labels.length; ++i) {
 862             checkLabel(labels[i], false, "label at index " + i);
 863         }
 864         super.visitTableSwitchInsn(min, max, dflt, labels);
 865         Collections.addAll(referencedLabels, labels);
 866         ++insnCount;
 867     }
 868 
 869     @Override
 870     public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
 871         checkVisitMaxsNotCalled();
 872         checkVisitCodeCalled();
 873         checkLabel(dflt, false, "default label");
 874         if (keys == null || labels == null || keys.length != labels.length) {
 875             throw new IllegalArgumentException("There must be the same number of keys and labels");
 876         }
 877         for (int i = 0; i < labels.length; ++i) {
 878             checkLabel(labels[i], false, "label at index " + i);
 879         }
 880         super.visitLookupSwitchInsn(dflt, keys, labels);
 881         referencedLabels.add(dflt);
 882         Collections.addAll(referencedLabels, labels);
 883         ++insnCount;
 884     }
 885 
 886     @Override
 887     public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) {
 888         checkVisitCodeCalled();
 889         checkVisitMaxsNotCalled();
 890         checkDescriptor(version, descriptor, false);
 891         if (descriptor.charAt(0) != '[') {
 892             throw new IllegalArgumentException(
 893                     "Invalid descriptor (must be an array type descriptor): " + descriptor);
 894         }
 895         if (numDimensions < 1) {
 896             throw new IllegalArgumentException(
 897                     "Invalid dimensions (must be greater than 0): " + numDimensions);
 898         }
 899         if (numDimensions > descriptor.lastIndexOf('[') + 1) {
 900             throw new IllegalArgumentException(
 901                     "Invalid dimensions (must not be greater than numDimensions(descriptor)): "
 902                             + numDimensions);
 903         }
 904         super.visitMultiANewArrayInsn(descriptor, numDimensions);
 905         ++insnCount;
 906     }
 907 
 908     @Override
 909     public AnnotationVisitor visitInsnAnnotation(
 910             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
 911         checkVisitCodeCalled();
 912         checkVisitMaxsNotCalled();
 913         int sort = new TypeReference(typeRef).getSort();
 914         if (sort != TypeReference.INSTANCEOF
 915                 && sort != TypeReference.NEW
 916                 && sort != TypeReference.CONSTRUCTOR_REFERENCE
 917                 && sort != TypeReference.METHOD_REFERENCE
 918                 && sort != TypeReference.CAST
 919                 && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
 920                 && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT
 921                 && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
 922                 && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) {
 923             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
 924         }
 925         CheckClassAdapter.checkTypeRef(typeRef);
 926         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
 927         return new CheckAnnotationAdapter(
 928                 super.visitInsnAnnotation(typeRef, typePath, descriptor, visible));
 929     }
 930 
 931     @Override
 932     public void visitTryCatchBlock(
 933             final Label start, final Label end, final Label handler, final String type) {
 934         checkVisitCodeCalled();
 935         checkVisitMaxsNotCalled();
 936         checkLabel(start, false, START_LABEL);
 937         checkLabel(end, false, END_LABEL);
 938         checkLabel(handler, false, "handler label");
 939         if (labelInsnIndices.get(start) != null
 940                 || labelInsnIndices.get(end) != null
 941                 || labelInsnIndices.get(handler) != null) {
 942             throw new IllegalStateException("Try catch blocks must be visited before their labels");
 943         }
 944         if (type != null) {
 945             checkInternalName(version, type, "type");
 946         }
 947         super.visitTryCatchBlock(start, end, handler, type);
 948         handlers.add(start);
 949         handlers.add(end);
 950     }
 951 
 952     @Override
 953     public AnnotationVisitor visitTryCatchAnnotation(
 954             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
 955         checkVisitCodeCalled();
 956         checkVisitMaxsNotCalled();
 957         int sort = new TypeReference(typeRef).getSort();
 958         if (sort != TypeReference.EXCEPTION_PARAMETER) {
 959             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
 960         }
 961         CheckClassAdapter.checkTypeRef(typeRef);
 962         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
 963         return new CheckAnnotationAdapter(
 964                 super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible));
 965     }
 966 
 967     @Override
 968     public void visitLocalVariable(
 969             final String name,
 970             final String descriptor,
 971             final String signature,
 972             final Label start,
 973             final Label end,
 974             final int index) {
 975         checkVisitCodeCalled();
 976         checkVisitMaxsNotCalled();
 977         checkUnqualifiedName(version, name, "name");
 978         checkDescriptor(version, descriptor, false);
 979         if (signature != null) {
 980             CheckClassAdapter.checkFieldSignature(signature);
 981         }
 982         checkLabel(start, true, START_LABEL);
 983         checkLabel(end, true, END_LABEL);
 984         checkUnsignedShort(index, INVALID_LOCAL_VARIABLE_INDEX);
 985         int startInsnIndex = labelInsnIndices.get(start).intValue();
 986         int endInsnIndex = labelInsnIndices.get(end).intValue();
 987         if (endInsnIndex < startInsnIndex) {
 988             throw new IllegalArgumentException(
 989                     "Invalid start and end labels (end must be greater than start)");
 990         }
 991         super.visitLocalVariable(name, descriptor, signature, start, end, index);
 992     }
 993 
 994     @Override
 995     public AnnotationVisitor visitLocalVariableAnnotation(
 996             final int typeRef,
 997             final TypePath typePath,
 998             final Label[] start,
 999             final Label[] end,
1000             final int[] index,
1001             final String descriptor,
1002             final boolean visible) {
1003         checkVisitCodeCalled();
1004         checkVisitMaxsNotCalled();
1005         int sort = new TypeReference(typeRef).getSort();
1006         if (sort != TypeReference.LOCAL_VARIABLE && sort != TypeReference.RESOURCE_VARIABLE) {
1007             throw new IllegalArgumentException(INVALID_TYPE_REFERENCE + Integer.toHexString(sort));
1008         }
1009         CheckClassAdapter.checkTypeRef(typeRef);
1010         checkDescriptor(version, descriptor, false);
1011         if (start == null
1012                 || end == null
1013                 || index == null
1014                 || end.length != start.length
1015                 || index.length != start.length) {
1016             throw new IllegalArgumentException(
1017                     "Invalid start, end and index arrays (must be non null and of identical length");
1018         }
1019         for (int i = 0; i < start.length; ++i) {
1020             checkLabel(start[i], true, START_LABEL);
1021             checkLabel(end[i], true, END_LABEL);
1022             checkUnsignedShort(index[i], INVALID_LOCAL_VARIABLE_INDEX);
1023             int startInsnIndex = labelInsnIndices.get(start[i]).intValue();
1024             int endInsnIndex = labelInsnIndices.get(end[i]).intValue();
1025             if (endInsnIndex < startInsnIndex) {
1026                 throw new IllegalArgumentException(
1027                         "Invalid start and end labels (end must be greater than start)");
1028             }
1029         }
1030         return super.visitLocalVariableAnnotation(
1031                 typeRef, typePath, start, end, index, descriptor, visible);
1032     }
1033 
1034     @Override
1035     public void visitLineNumber(final int line, final Label start) {
1036         checkVisitCodeCalled();
1037         checkVisitMaxsNotCalled();
1038         checkUnsignedShort(line, "Invalid line number");
1039         checkLabel(start, true, START_LABEL);
1040         super.visitLineNumber(line, start);
1041     }
1042 
1043     @Override
1044     public void visitMaxs(final int maxStack, final int maxLocals) {
1045         checkVisitCodeCalled();
1046         checkVisitMaxsNotCalled();
1047         visitMaxCalled = true;
1048         for (Label l : referencedLabels) {
1049             if (labelInsnIndices.get(l) == null) {
1050                 throw new IllegalStateException("Undefined label used");
1051             }
1052         }
1053         for (int i = 0; i < handlers.size(); i += 2) {
1054             Integer startInsnIndex = labelInsnIndices.get(handlers.get(i));
1055             Integer endInsnIndex = labelInsnIndices.get(handlers.get(i + 1));
1056             if (startInsnIndex == null || endInsnIndex == null) {
1057                 throw new IllegalStateException("Undefined try catch block labels");
1058             }
1059             if (endInsnIndex.intValue() <= startInsnIndex.intValue()) {
1060                 throw new IllegalStateException("Emty try catch block handler range");
1061             }
1062         }
1063         checkUnsignedShort(maxStack, "Invalid max stack");
1064         checkUnsignedShort(maxLocals, "Invalid max locals");
1065         super.visitMaxs(maxStack, maxLocals);
1066     }
1067 
1068     @Override
1069     public void visitEnd() {
1070         checkVisitEndNotCalled();
1071         visitEndCalled = true;
1072         super.visitEnd();
1073     }
1074 
1075     // -----------------------------------------------------------------------------------------------
1076     // Utility methods
1077     // -----------------------------------------------------------------------------------------------
1078 
1079     /** Checks that the {@link #visitCode} method has been called. */
1080     private void checkVisitCodeCalled() {
1081         if (!visitCodeCalled) {
1082             throw new IllegalStateException(
1083                     "Cannot visit instructions before visitCode has been called.");
1084         }
1085     }
1086 
1087     /** Checks that the {@link #visitMaxs} method has not been called. */
1088     private void checkVisitMaxsNotCalled() {
1089         if (visitMaxCalled) {
1090             throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
1091         }
1092     }
1093 
1094     /** Checks that the {@link #visitEnd} method has not been called. */
1095     private void checkVisitEndNotCalled() {
1096         if (visitEndCalled) {
1097             throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
1098         }
1099     }
1100 
1101     /**
1102       * Checks a stack frame value.
1103       *
1104       * @param value the value to be checked.
1105       */
1106     private void checkFrameValue(final Object value) {
1107         if (value == Opcodes.TOP
1108                 || value == Opcodes.INTEGER
1109                 || value == Opcodes.FLOAT
1110                 || value == Opcodes.LONG
1111                 || value == Opcodes.DOUBLE
1112                 || value == Opcodes.NULL
1113                 || value == Opcodes.UNINITIALIZED_THIS) {
1114             return;
1115         }
1116         if (value instanceof String) {
1117             checkInternalName(version, (String) value, "Invalid stack frame value");
1118         } else if (value instanceof Label) {
1119             referencedLabels.add((Label) value);
1120         } else {
1121             throw new IllegalArgumentException("Invalid stack frame value: " + value);
1122         }
1123     }
1124 
1125     /**
1126       * Checks that the method to visit the given opcode is equal to the given method.
1127       *
1128       * @param opcode the opcode to be checked.
1129       * @param method the expected visit method.
1130       */
1131     private static void checkOpcodeMethod(final int opcode, final Method method) {
1132         if (opcode < Opcodes.NOP || opcode > Opcodes.IFNONNULL || OPCODE_METHODS[opcode] != method) {
1133             throw new IllegalArgumentException("Invalid opcode: " + opcode);
1134         }
1135     }
1136 
1137     /**
1138       * Checks that the given value is a signed byte.
1139       *
1140       * @param value the value to be checked.
1141       * @param message the message to use in case of error.
1142       */
1143     private static void checkSignedByte(final int value, final String message) {
1144         if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
1145             throw new IllegalArgumentException(message + " (must be a signed byte): " + value);
1146         }
1147     }
1148 
1149     /**
1150       * Checks that the given value is a signed short.
1151       *
1152       * @param value the value to be checked.
1153       * @param message the message to use in case of error.
1154       */
1155     private static void checkSignedShort(final int value, final String message) {
1156         if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
1157             throw new IllegalArgumentException(message + " (must be a signed short): " + value);
1158         }
1159     }
1160 
1161     /**
1162       * Checks that the given value is an unsigned short.
1163       *
1164       * @param value the value to be checked.
1165       * @param message the message to use in case of error.
1166       */
1167     private static void checkUnsignedShort(final int value, final String message) {
1168         if (value < 0 || value > 65535) {
1169             throw new IllegalArgumentException(message + " (must be an unsigned short): " + value);
1170         }
1171     }
1172 
1173     /**
1174       * Checks that the given value is an {@link Integer}, {@link Float}, {@link Long}, {@link Double}
1175       * or {@link String} value.
1176       *
1177       * @param value the value to be checked.
1178       */
1179     static void checkConstant(final Object value) {
1180         if (!(value instanceof Integer)
1181                 && !(value instanceof Float)
1182                 && !(value instanceof Long)
1183                 && !(value instanceof Double)
1184                 && !(value instanceof String)) {
1185             throw new IllegalArgumentException("Invalid constant: " + value);
1186         }
1187     }
1188 
1189     /**
1190       * Checks that the given value is a valid operand for the LDC instruction.
1191       *
1192       * @param value the value to be checked.
1193       */
1194     private void checkLdcConstant(final Object value) {
1195         if (value instanceof Type) {
1196             int sort = ((Type) value).getSort();
1197             if (sort != Type.OBJECT && sort != Type.ARRAY && sort != Type.METHOD) {
1198                 throw new IllegalArgumentException("Illegal LDC constant value");
1199             }
1200             if (sort != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) {
1201                 throw new IllegalArgumentException("ldc of a constant class requires at least version 1.5");
1202             }
1203             if (sort == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) {
1204                 throw new IllegalArgumentException("ldc of a method type requires at least version 1.7");
1205             }
1206         } else if (value instanceof Handle) {
1207             if ((version & 0xFFFF) < Opcodes.V1_7) {
1208                 throw new IllegalArgumentException("ldc of a Handle requires at least version 1.7");
1209             }
1210             Handle handle = (Handle) value;
1211             int tag = handle.getTag();
1212             if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) {
1213                 throw new IllegalArgumentException("invalid handle tag " + tag);
1214             }
1215             checkInternalName(this.version, handle.getOwner(), "handle owner");
1216             if (tag <= Opcodes.H_PUTSTATIC) {
1217                 checkDescriptor(this.version, handle.getDesc(), false);
1218             } else {
1219                 checkMethodDescriptor(this.version, handle.getDesc());
1220             }
1221             String handleName = handle.getName();
1222             if (!("<init>".equals(handleName) && tag == Opcodes.H_NEWINVOKESPECIAL)) {
1223                 checkMethodIdentifier(this.version, handleName, "handle name");
1224             }
1225         } else if (value instanceof ConstantDynamic) {
1226             if ((version & 0xFFFF) < Opcodes.V11) {
1227                 throw new IllegalArgumentException("ldc of a ConstantDynamic requires at least version 11");
1228             }
1229             ConstantDynamic constantDynamic = (ConstantDynamic) value;
1230             checkMethodIdentifier(this.version, constantDynamic.getName(), "constant dynamic name");
1231             checkDescriptor(this.version, constantDynamic.getDescriptor(), false);
1232             checkLdcConstant(constantDynamic.getBootstrapMethod());
1233             int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
1234             for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
1235                 checkLdcConstant(constantDynamic.getBootstrapMethodArgument(i));
1236             }
1237         } else {
1238             checkConstant(value);
1239         }
1240     }
1241 
1242     /**
1243       * Checks that the given string is a valid unqualified name.
1244       *
1245       * @param version the class version.
1246       * @param name the string to be checked.
1247       * @param message the message to use in case of error.
1248       */
1249     static void checkUnqualifiedName(final int version, final String name, final String message) {
1250         checkIdentifier(version, name, 0, -1, message);
1251     }
1252 
1253     /**
1254       * Checks that the given substring is a valid Java identifier.
1255       *
1256       * @param version the class version.
1257       * @param name the string to be checked.
1258       * @param startPos the index of the first character of the identifier (inclusive).
1259       * @param endPos the index of the last character of the identifier (exclusive). -1 is equivalent
1260       *     to {@code name.length()} if name is not {@literal null}.
1261       * @param message the message to use in case of error.
1262       */
1263     static void checkIdentifier(
1264             final int version,
1265             final String name,
1266             final int startPos,
1267             final int endPos,
1268             final String message) {
1269         if (name == null || (endPos == -1 ? name.length() <= startPos : endPos <= startPos)) {
1270             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1271         }
1272         int max = endPos == -1 ? name.length() : endPos;
1273         if ((version & 0xFFFF) >= Opcodes.V1_5) {
1274             for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) {
1275                 if (".;[/".indexOf(name.codePointAt(i)) != -1) {
1276                     throw new IllegalArgumentException(
1277                             INVALID + message + " (must not contain . ; [ or /): " + name);
1278                 }
1279             }
1280             return;
1281         }
1282         for (int i = startPos; i < max; i = name.offsetByCodePoints(i, 1)) {
1283             if (i == startPos
1284                     ? !Character.isJavaIdentifierStart(name.codePointAt(i))
1285                     : !Character.isJavaIdentifierPart(name.codePointAt(i))) {
1286                 throw new IllegalArgumentException(
1287                         INVALID + message + " (must be a valid Java identifier): " + name);
1288             }
1289         }
1290     }
1291 
1292     /**
1293       * Checks that the given string is a valid Java identifier.
1294       *
1295       * @param version the class version.
1296       * @param name the string to be checked.
1297       * @param message the message to use in case of error.
1298       */
1299     static void checkMethodIdentifier(final int version, final String name, final String message) {
1300         if (name == null || name.length() == 0) {
1301             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1302         }
1303         if ((version & 0xFFFF) >= Opcodes.V1_5) {
1304             for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) {
1305                 if (".;[/<>".indexOf(name.codePointAt(i)) != -1) {
1306                     throw new IllegalArgumentException(
1307                             INVALID + message + " (must be a valid unqualified name): " + name);
1308                 }
1309             }
1310             return;
1311         }
1312         for (int i = 0; i < name.length(); i = name.offsetByCodePoints(i, 1)) {
1313             if (i == 0
1314                     ? !Character.isJavaIdentifierStart(name.codePointAt(i))
1315                     : !Character.isJavaIdentifierPart(name.codePointAt(i))) {
1316                 throw new IllegalArgumentException(
1317                         INVALID
1318                                 + message
1319                                 + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
1320                                 + name);
1321             }
1322         }
1323     }
1324 
1325     /**
1326       * Checks that the given string is a valid internal class name or array type descriptor.
1327       *
1328       * @param version the class version.
1329       * @param name the string to be checked.
1330       * @param message the message to use in case of error.
1331       */
1332     static void checkInternalName(final int version, final String name, final String message) {
1333         if (name == null || name.length() == 0) {
1334             throw new IllegalArgumentException(INVALID + message + MUST_NOT_BE_NULL_OR_EMPTY);
1335         }
1336         if (name.charAt(0) == '[') {
1337             checkDescriptor(version, name, false);
1338         } else {
1339             checkInternalClassName(version, name, message);
1340         }
1341     }
1342 
1343     /**
1344       * Checks that the given string is a valid internal class name.
1345       *
1346       * @param version the class version.
1347       * @param name the string to be checked.
1348       * @param message the message to use in case of error.
1349       */
1350     private static void checkInternalClassName(
1351             final int version, final String name, final String message) {
1352         try {
1353             int startIndex = 0;
1354             int slashIndex;
1355             while ((slashIndex = name.indexOf('/', startIndex + 1)) != -1) {
1356                 checkIdentifier(version, name, startIndex, slashIndex, null);
1357                 startIndex = slashIndex + 1;
1358             }
1359             checkIdentifier(version, name, startIndex, name.length(), null);
1360         } catch (IllegalArgumentException e) {
1361             throw new IllegalArgumentException(
1362                     INVALID + message + " (must be an internal class name): " + name, e);
1363         }
1364     }
1365 
1366     /**
1367       * Checks that the given string is a valid type descriptor.
1368       *
1369       * @param version the class version.
1370       * @param descriptor the string to be checked.
1371       * @param canBeVoid {@literal true} if {@code V} can be considered valid.
1372       */
1373     static void checkDescriptor(final int version, final String descriptor, final boolean canBeVoid) {
1374         int endPos = checkDescriptor(version, descriptor, 0, canBeVoid);
1375         if (endPos != descriptor.length()) {
1376             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1377         }
1378     }
1379 
1380     /**
1381       * Checks that the given substring is a valid type descriptor.
1382       *
1383       * @param version the class version.
1384       * @param descriptor the string to be checked.
1385       * @param startPos the index of the first character of the type descriptor (inclusive).
1386       * @param canBeVoid whether {@code V} can be considered valid.
1387       * @return the index of the last character of the type descriptor, plus one.
1388       */
1389     private static int checkDescriptor(
1390             final int version, final String descriptor, final int startPos, final boolean canBeVoid) {
1391         if (descriptor == null || startPos >= descriptor.length()) {
1392             throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
1393         }
1394         switch (descriptor.charAt(startPos)) {
1395             case 'V':
1396                 if (canBeVoid) {
1397                     return startPos + 1;
1398                 } else {
1399                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1400                 }
1401             case 'Z':
1402             case 'C':
1403             case 'B':
1404             case 'S':
1405             case 'I':
1406             case 'F':
1407             case 'J':
1408             case 'D':
1409                 return startPos + 1;
1410             case '[':
1411                 int pos = startPos + 1;
1412                 while (pos < descriptor.length() && descriptor.charAt(pos) == '[') {
1413                     ++pos;
1414                 }
1415                 if (pos < descriptor.length()) {
1416                     return checkDescriptor(version, descriptor, pos, false);
1417                 } else {
1418                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1419                 }
1420             case 'L':
1421             case 'Q':
1422                 int endPos = descriptor.indexOf(';', startPos);
1423                 if (startPos == -1 || endPos - startPos < 2) {
1424                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1425                 }
1426                 try {
1427                     checkInternalClassName(version, descriptor.substring(startPos + 1, endPos), null);
1428                 } catch (IllegalArgumentException e) {
1429                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor, e);
1430                 }
1431                 return endPos + 1;
1432             default:
1433                 throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1434         }
1435     }
1436 
1437     /**
1438       * Checks that the given string is a valid method descriptor.
1439       *
1440       * @param version the class version.
1441       * @param descriptor the string to be checked.
1442       */
1443     static void checkMethodDescriptor(final int version, final String descriptor) {
1444         if (descriptor == null || descriptor.length() == 0) {
1445             throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
1446         }
1447         if (descriptor.charAt(0) != '(' || descriptor.length() < 3) {
1448             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1449         }
1450         int pos = 1;
1451         if (descriptor.charAt(pos) != ')') {
1452             do {
1453                 if (descriptor.charAt(pos) == 'V') {
1454                     throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1455                 }
1456                 pos = checkDescriptor(version, descriptor, pos, false);
1457             } while (pos < descriptor.length() && descriptor.charAt(pos) != ')');
1458         }
1459         pos = checkDescriptor(version, descriptor, pos + 1, true);
1460         if (pos != descriptor.length()) {
1461             throw new IllegalArgumentException(INVALID_DESCRIPTOR + descriptor);
1462         }
1463     }
1464 
1465     /**
1466       * Checks that the given label is not null. This method can also check that the label has been
1467       * visited.
1468       *
1469       * @param label the label to be checked.
1470       * @param checkVisited whether to check that the label has been visited.
1471       * @param message the message to use in case of error.
1472       */
1473     private void checkLabel(final Label label, final boolean checkVisited, final String message) {
1474         if (label == null) {
1475             throw new IllegalArgumentException(INVALID + message + " (must not be null)");
1476         }
1477         if (checkVisited && labelInsnIndices.get(label) == null) {
1478             throw new IllegalArgumentException(INVALID + message + " (must be visited first)");
1479         }
1480     }
1481 
1482     static class MethodWriterWrapper extends MethodVisitor {
1483 
1484         private final ClassWriter owner;
1485 
1486         MethodWriterWrapper(final int api, final ClassWriter owner, final MethodVisitor methodWriter) {
1487             super(api, methodWriter);
1488             this.owner = owner;
1489         }
1490 
1491         boolean computesMaxs() {
1492             return owner.hasFlags(ClassWriter.COMPUTE_MAXS) || owner.hasFlags(ClassWriter.COMPUTE_FRAMES);
1493         }
1494     }
1495 }
1496