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.FileInputStream;
  63 import java.io.IOException;
  64 import java.io.InputStream;
  65 import java.io.PrintWriter;
  66 import java.util.ArrayList;
  67 import java.util.HashMap;
  68 import java.util.List;
  69 import java.util.Map;
  70 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  71 import jdk.internal.org.objectweb.asm.Attribute;
  72 import jdk.internal.org.objectweb.asm.ClassReader;
  73 import jdk.internal.org.objectweb.asm.ClassVisitor;
  74 import jdk.internal.org.objectweb.asm.ClassWriter;
  75 import jdk.internal.org.objectweb.asm.FieldVisitor;
  76 import jdk.internal.org.objectweb.asm.Label;
  77 import jdk.internal.org.objectweb.asm.MethodVisitor;
  78 import jdk.internal.org.objectweb.asm.ModuleVisitor;
  79 import jdk.internal.org.objectweb.asm.Opcodes;
  80 import jdk.internal.org.objectweb.asm.RecordComponentVisitor;
  81 import jdk.internal.org.objectweb.asm.Type;
  82 import jdk.internal.org.objectweb.asm.TypePath;
  83 import jdk.internal.org.objectweb.asm.TypeReference;
  84 import jdk.internal.org.objectweb.asm.tree.ClassNode;
  85 import jdk.internal.org.objectweb.asm.tree.MethodNode;
  86 import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
  87 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
  88 import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException;
  89 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
  90 import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
  91 import jdk.internal.org.objectweb.asm.tree.analysis.SimpleVerifier;
  92 
  93 /**
  94  * A {@link ClassVisitor} that checks that its methods are properly used. More precisely this class
  95  * adapter checks each method call individually, based <i>only</i> on its arguments, but does
  96  * <i>not</i> check the <i>sequence</i> of method calls. For example, the invalid sequence {@code
  97  * visitField(ACC_PUBLIC, "i", "I", null)} {@code visitField(ACC_PUBLIC, "i", "D", null)} will
  98  * <i>not</i> be detected by this class adapter.
  99  *
 100  * <p><code>CheckClassAdapter</code> can be also used to verify bytecode transformations in order to
 101  * make sure that the transformed bytecode is sane. For example:
 102  *
 103  * <pre>
 104  * InputStream inputStream = ...; // get bytes for the source class
 105  * ClassReader classReader = new ClassReader(inputStream);
 106  * ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
 107  * ClassVisitor classVisitor = new <b>MyClassAdapter</b>(new CheckClassAdapter(classWriter, true));
 108  * classReader.accept(classVisitor, 0);
 109  *
 110  * StringWriter stringWriter = new StringWriter();
 111  * PrintWriter printWriter = new PrintWriter(stringWriter);
 112  * CheckClassAdapter.verify(new ClassReader(classWriter.toByteArray()), false, printWriter);
 113  * assertTrue(stringWriter.toString().isEmpty());
 114  * </pre>
 115  *
 116  * <p>The above code pass the transformed bytecode through a <code>CheckClassAdapter</code>, with
 117  * data flow checks enabled. These checks are not exactly the same as the JVM verification, but
 118  * provide some basic type checking for each method instruction. If the bytecode has errors, the
 119  * output text shows the erroneous instruction number, and a dump of the failed method with
 120  * information about the type of the local variables and of the operand stack slots for each
 121  * instruction. For example (format is - insnNumber locals : stack):
 122  *
 123  * <pre>
 124  * jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
 125  *   at jdk.internal.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:...)
 126  *   at jdk.internal.org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:...)
 127  * ...
 128  * remove()V
 129  * 00000 LinkedBlockingQueue$Itr . . . . . . . .  : ICONST_0
 130  * 00001 LinkedBlockingQueue$Itr . . . . . . . .  : I ISTORE 2
 131  * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . .  :
 132  * ...
 133  * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . .  : ILOAD 1
 134  * 00072 <b>?</b> INVOKESPECIAL java/lang/Integer.&lt;init&gt; (I)V
 135  * ...
 136  * </pre>
 137  *
 138  * <p>The above output shows that the local variable 1, loaded by the <code>ILOAD 1</code>
 139  * instruction at position <code>00071</code> is not initialized, whereas the local variable 2 is
 140  * initialized and contains an int value.
 141  *
 142  * @author Eric Bruneton
 143  */
 144 public class CheckClassAdapter extends ClassVisitor {
 145 
 146     /** The help message shown when command line arguments are incorrect. */
 147     private static final String USAGE =
 148             "Verifies the given class.\n"
 149                     + "Usage: CheckClassAdapter <fully qualified class name or class file name>";
 150 
 151     private static final String ERROR_AT = ": error at index ";
 152 
 153     /** Whether the bytecode must be checked with a BasicVerifier. */
 154     private boolean checkDataFlow;
 155 
 156     /** The class version number. */
 157     private int version;
 158 
 159     /** Whether the {@link #visit} method has been called. */
 160     private boolean visitCalled;
 161 
 162     /** Whether the {@link #visitModule} method has been called. */
 163     private boolean visitModuleCalled;
 164 
 165     /** Whether the {@link #visitSource} method has been called. */
 166     private boolean visitSourceCalled;
 167 
 168     /** Whether the {@link #visitOuterClass} method has been called. */
 169     private boolean visitOuterClassCalled;
 170 
 171     /** Whether the {@link #visitNestHost} method has been called. */
 172     private boolean visitNestHostCalled;
 173 
 174     /**
 175       * The common package of all the nest members. Not {@literal null} if the visitNestMember method
 176       * has been called.
 177       */
 178     private String nestMemberPackageName;
 179 
 180     /** Whether the {@link #visitEnd} method has been called. */
 181     private boolean visitEndCalled;
 182 
 183     /** The index of the instruction designated by each visited label so far. */
 184     private Map<Label, Integer> labelInsnIndices;
 185 
 186     // -----------------------------------------------------------------------------------------------
 187     // Constructors
 188     // -----------------------------------------------------------------------------------------------
 189 
 190     /**
 191       * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>.
 192       * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
 193       *
 194       * @param classVisitor the class visitor to which this adapter must delegate calls.
 195       */
 196     public CheckClassAdapter(final ClassVisitor classVisitor) {
 197         this(classVisitor, /* checkDataFlow = */ true);
 198     }
 199 
 200     /**
 201       * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>.
 202       * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
 203       *
 204       * @param classVisitor the class visitor to which this adapter must delegate calls.
 205       * @param checkDataFlow whether to perform basic data flow checks.
 206       * @throws IllegalStateException If a subclass calls this constructor.
 207       */
 208     public CheckClassAdapter(final ClassVisitor classVisitor, final boolean checkDataFlow) {
 209         this(/* latest api = */ Opcodes.ASM9, classVisitor, checkDataFlow);
 210         if (getClass() != CheckClassAdapter.class) {
 211             throw new IllegalStateException();
 212         }
 213     }
 214 
 215     /**
 216       * Constructs a new {@link CheckClassAdapter}.
 217       *
 218       * @param api the ASM API version implemented by this visitor. Must be one of the {@code
 219       *     ASM}<i>x</i> values in {@link Opcodes}.
 220       * @param classVisitor the class visitor to which this adapter must delegate calls.
 221       * @param checkDataFlow {@literal true} to perform basic data flow checks, or {@literal false} to
 222       *     not perform any data flow check (see {@link CheckMethodAdapter}).
 223       */
 224     protected CheckClassAdapter(
 225             final int api, final ClassVisitor classVisitor, final boolean checkDataFlow) {
 226         super(api, classVisitor);
 227         this.labelInsnIndices = new HashMap<>();
 228         this.checkDataFlow = checkDataFlow;
 229     }
 230 
 231     // -----------------------------------------------------------------------------------------------
 232     // Implementation of the ClassVisitor interface
 233     // -----------------------------------------------------------------------------------------------
 234 
 235     @Override
 236     public void visit(
 237             final int version,
 238             final int access,
 239             final String name,
 240             final String signature,
 241             final String superName,
 242             final String[] interfaces) {
 243         if (visitCalled) {
 244             throw new IllegalStateException("visit must be called only once");
 245         }
 246         visitCalled = true;
 247         checkState();
 248         checkAccess(
 249                 access,
 250                 Opcodes.ACC_PUBLIC
 251                         | Opcodes.ACC_FINAL
 252                         | Opcodes.ACC_SUPER
 253                         | Opcodes.ACC_INTERFACE
 254                         | Opcodes.ACC_ABSTRACT
 255                         | Opcodes.ACC_SYNTHETIC
 256                         | Opcodes.ACC_ANNOTATION
 257                         | Opcodes.ACC_ENUM
 258                         | Opcodes.ACC_DEPRECATED
 259                         | Opcodes.ACC_RECORD
 260                         | Opcodes.ACC_MODULE);
 261         if (name == null) {
 262             throw new IllegalArgumentException("Illegal class name (null)");
 263         }
 264         if (!name.endsWith("package-info") && !name.endsWith("module-info")) {
 265             CheckMethodAdapter.checkInternalName(version, name, "class name");
 266         }
 267         if ("java/lang/Object".equals(name)) {
 268             if (superName != null) {
 269                 throw new IllegalArgumentException(
 270                         "The super class name of the Object class must be 'null'");
 271             }
 272         } else if (name.endsWith("module-info")) {
 273             if (superName != null) {
 274                 throw new IllegalArgumentException(
 275                         "The super class name of a module-info class must be 'null'");
 276             }
 277         } else {
 278             CheckMethodAdapter.checkInternalName(version, superName, "super class name");
 279         }
 280         if (signature != null) {
 281             checkClassSignature(signature);
 282         }
 283         if ((access & Opcodes.ACC_INTERFACE) != 0 && !"java/lang/Object".equals(superName)) {
 284             throw new IllegalArgumentException(
 285                     "The super class name of interfaces must be 'java/lang/Object'");
 286         }
 287         if (interfaces != null) {
 288             for (int i = 0; i < interfaces.length; ++i) {
 289                 CheckMethodAdapter.checkInternalName(
 290                         version, interfaces[i], "interface name at index " + i);
 291             }
 292         }
 293         this.version = version;
 294         super.visit(version, access, name, signature, superName, interfaces);
 295     }
 296 
 297     @Override
 298     public void visitSource(final String file, final String debug) {
 299         checkState();
 300         if (visitSourceCalled) {
 301             throw new IllegalStateException("visitSource can be called only once.");
 302         }
 303         visitSourceCalled = true;
 304         super.visitSource(file, debug);
 305     }
 306 
 307     @Override
 308     public ModuleVisitor visitModule(final String name, final int access, final String version) {
 309         checkState();
 310         if (visitModuleCalled) {
 311             throw new IllegalStateException("visitModule can be called only once.");
 312         }
 313         visitModuleCalled = true;
 314         checkFullyQualifiedName(this.version, name, "module name");
 315         checkAccess(access, Opcodes.ACC_OPEN | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
 316         CheckModuleAdapter checkModuleAdapter =
 317                 new CheckModuleAdapter(
 318                         api, super.visitModule(name, access, version), (access & Opcodes.ACC_OPEN) != 0);
 319         checkModuleAdapter.classVersion = this.version;
 320         return checkModuleAdapter;
 321     }
 322 
 323     @Override
 324     public void visitNestHost(final String nestHost) {
 325         checkState();
 326         CheckMethodAdapter.checkInternalName(version, nestHost, "nestHost");
 327         if (visitNestHostCalled) {
 328             throw new IllegalStateException("visitNestHost can be called only once.");
 329         }
 330         if (nestMemberPackageName != null) {
 331             throw new IllegalStateException("visitNestHost and visitNestMember are mutually exclusive.");
 332         }
 333         visitNestHostCalled = true;
 334         super.visitNestHost(nestHost);
 335     }
 336 
 337     @Override
 338     public void visitNestMember(final String nestMember) {
 339         checkState();
 340         CheckMethodAdapter.checkInternalName(version, nestMember, "nestMember");
 341         if (visitNestHostCalled) {
 342             throw new IllegalStateException(
 343                     "visitMemberOfNest and visitNestHost are mutually exclusive.");
 344         }
 345         String packageName = packageName(nestMember);
 346         if (nestMemberPackageName == null) {
 347             nestMemberPackageName = packageName;
 348         } else if (!nestMemberPackageName.equals(packageName)) {
 349             throw new IllegalStateException(
 350                     "nest member " + nestMember + " should be in the package " + nestMemberPackageName);
 351         }
 352         super.visitNestMember(nestMember);
 353     }
 354 
 355     @Override
 356     public void visitPermittedSubclass(final String permittedSubclass) {
 357         checkState();
 358         CheckMethodAdapter.checkInternalName(version, permittedSubclass, "permittedSubclass");
 359         super.visitPermittedSubclass(permittedSubclass);
 360     }
 361 
 362     @Override
 363     public void visitOuterClass(final String owner, final String name, final String descriptor) {
 364         checkState();
 365         if (visitOuterClassCalled) {
 366             throw new IllegalStateException("visitOuterClass can be called only once.");
 367         }
 368         visitOuterClassCalled = true;
 369         if (owner == null) {
 370             throw new IllegalArgumentException("Illegal outer class owner");
 371         }
 372         if (descriptor != null) {
 373             CheckMethodAdapter.checkMethodDescriptor(version, descriptor);
 374         }
 375         super.visitOuterClass(owner, name, descriptor);
 376     }
 377 
 378     @Override
 379     public void visitInnerClass(
 380             final String name, final String outerName, final String innerName, final int access) {
 381         checkState();
 382         CheckMethodAdapter.checkInternalName(version, name, "class name");
 383         if (outerName != null) {
 384             CheckMethodAdapter.checkInternalName(version, outerName, "outer class name");
 385         }
 386         if (innerName != null) {
 387             int startIndex = 0;
 388             while (startIndex < innerName.length() && Character.isDigit(innerName.charAt(startIndex))) {
 389                 startIndex++;
 390             }
 391             if (startIndex == 0 || startIndex < innerName.length()) {
 392                 CheckMethodAdapter.checkIdentifier(version, innerName, startIndex, -1, "inner class name");
 393             }
 394         }
 395         checkAccess(
 396                 access,
 397                 Opcodes.ACC_PUBLIC
 398                         | Opcodes.ACC_PRIVATE
 399                         | Opcodes.ACC_PROTECTED
 400                         | Opcodes.ACC_STATIC
 401                         | Opcodes.ACC_FINAL
 402                         | Opcodes.ACC_INTERFACE
 403                         | Opcodes.ACC_ABSTRACT
 404                         | Opcodes.ACC_SYNTHETIC
 405                         | Opcodes.ACC_ANNOTATION
 406                         | Opcodes.ACC_ENUM);
 407         super.visitInnerClass(name, outerName, innerName, access);
 408     }
 409 
 410     @Override
 411     public RecordComponentVisitor visitRecordComponent(
 412             final String name, final String descriptor, final String signature) {
 413         checkState();
 414         CheckMethodAdapter.checkUnqualifiedName(version, name, "record component name");
 415         CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
 416         if (signature != null) {
 417             checkFieldSignature(signature);
 418         }
 419         return new CheckRecordComponentAdapter(
 420                 api, super.visitRecordComponent(name, descriptor, signature));
 421     }
 422 
 423     @Override
 424     public FieldVisitor visitField(
 425             final int access,
 426             final String name,
 427             final String descriptor,
 428             final String signature,
 429             final Object value) {
 430         checkState();
 431         checkAccess(
 432                 access,
 433                 Opcodes.ACC_PUBLIC
 434                         | Opcodes.ACC_PRIVATE
 435                         | Opcodes.ACC_PROTECTED
 436                         | Opcodes.ACC_STATIC
 437                         | Opcodes.ACC_FINAL
 438                         | Opcodes.ACC_VOLATILE
 439                         | Opcodes.ACC_TRANSIENT
 440                         | Opcodes.ACC_SYNTHETIC
 441                         | Opcodes.ACC_ENUM
 442                         | Opcodes.ACC_MANDATED
 443                         | Opcodes.ACC_DEPRECATED);
 444         CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
 445         CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
 446         if (signature != null) {
 447             checkFieldSignature(signature);
 448         }
 449         if (value != null) {
 450             CheckMethodAdapter.checkConstant(value);
 451         }
 452         return new CheckFieldAdapter(api, super.visitField(access, name, descriptor, signature, value));
 453     }
 454 
 455     @Override
 456     public MethodVisitor visitMethod(
 457             final int access,
 458             final String name,
 459             final String descriptor,
 460             final String signature,
 461             final String[] exceptions) {
 462         checkState();
 463         checkMethodAccess(
 464                 version,
 465                 access,
 466                 Opcodes.ACC_PUBLIC
 467                         | Opcodes.ACC_PRIVATE
 468                         | Opcodes.ACC_PROTECTED
 469                         | Opcodes.ACC_STATIC
 470                         | Opcodes.ACC_FINAL
 471                         | Opcodes.ACC_SYNCHRONIZED
 472                         | Opcodes.ACC_BRIDGE
 473                         | Opcodes.ACC_VARARGS
 474                         | Opcodes.ACC_NATIVE
 475                         | Opcodes.ACC_ABSTRACT
 476                         | Opcodes.ACC_STRICT
 477                         | Opcodes.ACC_SYNTHETIC
 478                         | Opcodes.ACC_MANDATED
 479                         | Opcodes.ACC_DEPRECATED);
 480         if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
 481             CheckMethodAdapter.checkMethodIdentifier(version, name, "method name");
 482         }
 483         CheckMethodAdapter.checkMethodDescriptor(version, descriptor);
 484         if (signature != null) {
 485             checkMethodSignature(signature);
 486         }
 487         if (exceptions != null) {
 488             for (int i = 0; i < exceptions.length; ++i) {
 489                 CheckMethodAdapter.checkInternalName(
 490                         version, exceptions[i], "exception name at index " + i);
 491             }
 492         }
 493         CheckMethodAdapter checkMethodAdapter;
 494         MethodVisitor methodVisitor =
 495                 super.visitMethod(access, name, descriptor, signature, exceptions);
 496         if (checkDataFlow) {
 497             if (cv instanceof ClassWriter) {
 498                 methodVisitor =
 499                         new CheckMethodAdapter.MethodWriterWrapper(api, (ClassWriter) cv, methodVisitor);
 500             }
 501             checkMethodAdapter =
 502                     new CheckMethodAdapter(api, access, name, descriptor, methodVisitor, labelInsnIndices);
 503         } else {
 504             checkMethodAdapter = new CheckMethodAdapter(api, methodVisitor, labelInsnIndices);
 505         }
 506         checkMethodAdapter.version = version;
 507         return checkMethodAdapter;
 508     }
 509 
 510     @Override
 511     public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
 512         checkState();
 513         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
 514         return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
 515     }
 516 
 517     @Override
 518     public AnnotationVisitor visitTypeAnnotation(
 519             final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
 520         checkState();
 521         int sort = new TypeReference(typeRef).getSort();
 522         if (sort != TypeReference.CLASS_TYPE_PARAMETER
 523                 && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND
 524                 && sort != TypeReference.CLASS_EXTENDS) {
 525             throw new IllegalArgumentException(
 526                     "Invalid type reference sort 0x" + Integer.toHexString(sort));
 527         }
 528         checkTypeRef(typeRef);
 529         CheckMethodAdapter.checkDescriptor(version, descriptor, false);
 530         return new CheckAnnotationAdapter(
 531                 super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
 532     }
 533 
 534     @Override
 535     public void visitAttribute(final Attribute attribute) {
 536         checkState();
 537         if (attribute == null) {
 538             throw new IllegalArgumentException("Invalid attribute (must not be null)");
 539         }
 540         super.visitAttribute(attribute);
 541     }
 542 
 543     @Override
 544     public void visitEnd() {
 545         checkState();
 546         visitEndCalled = true;
 547         super.visitEnd();
 548     }
 549 
 550     // -----------------------------------------------------------------------------------------------
 551     // Utility methods
 552     // -----------------------------------------------------------------------------------------------
 553 
 554     /** Checks that the visit method has been called and that visitEnd has not been called. */
 555     private void checkState() {
 556         if (!visitCalled) {
 557             throw new IllegalStateException("Cannot visit member before visit has been called.");
 558         }
 559         if (visitEndCalled) {
 560             throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
 561         }
 562     }
 563 
 564     /**
 565       * Checks that the given access flags do not contain invalid flags. This method also checks that
 566       * mutually incompatible flags are not set simultaneously.
 567       *
 568       * @param access the access flags to be checked.
 569       * @param possibleAccess the valid access flags.
 570       */
 571     static void checkAccess(final int access, final int possibleAccess) {
 572         if ((access & ~possibleAccess) != 0) {
 573             throw new IllegalArgumentException("Invalid access flags: " + access);
 574         }
 575         int publicProtectedPrivate = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE;
 576         if (Integer.bitCount(access & publicProtectedPrivate) > 1) {
 577             throw new IllegalArgumentException(
 578                     "public, protected and private are mutually exclusive: " + access);
 579         }
 580         if (Integer.bitCount(access & (Opcodes.ACC_FINAL | Opcodes.ACC_ABSTRACT)) > 1) {
 581             throw new IllegalArgumentException("final and abstract are mutually exclusive: " + access);
 582         }
 583     }
 584 
 585     /**
 586       * Checks that the given access flags do not contain invalid flags for a method. This method also
 587       * checks that mutually incompatible flags are not set simultaneously.
 588       *
 589       * @param version the class version.
 590       * @param access the method access flags to be checked.
 591       * @param possibleAccess the valid access flags.
 592       */
 593     private static void checkMethodAccess(
 594             final int version, final int access, final int possibleAccess) {
 595         checkAccess(access, possibleAccess);
 596         if ((version & 0xFFFF) < Opcodes.V17
 597                 && Integer.bitCount(access & (Opcodes.ACC_STRICT | Opcodes.ACC_ABSTRACT)) > 1) {
 598             throw new IllegalArgumentException("strictfp and abstract are mutually exclusive: " + access);
 599         }
 600     }
 601 
 602     /**
 603       * Checks that the given name is a fully qualified name, using dots.
 604       *
 605       * @param version the class version.
 606       * @param name the name to be checked.
 607       * @param source the source of 'name' (e.g 'module' for a module name).
 608       */
 609     static void checkFullyQualifiedName(final int version, final String name, final String source) {
 610         try {
 611             int startIndex = 0;
 612             int dotIndex;
 613             while ((dotIndex = name.indexOf('.', startIndex + 1)) != -1) {
 614                 CheckMethodAdapter.checkIdentifier(version, name, startIndex, dotIndex, null);
 615                 startIndex = dotIndex + 1;
 616             }
 617             CheckMethodAdapter.checkIdentifier(version, name, startIndex, name.length(), null);
 618         } catch (IllegalArgumentException e) {
 619             throw new IllegalArgumentException(
 620                     "Invalid " + source + " (must be a fully qualified name): " + name, e);
 621         }
 622     }
 623 
 624     /**
 625       * Checks a class signature.
 626       *
 627       * @param signature a string containing the signature that must be checked.
 628       */
 629     public static void checkClassSignature(final String signature) {
 630         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 631         // ClassSignature:
 632         //   [TypeParameters] SuperclassSignature SuperinterfaceSignature*
 633         // SuperclassSignature:
 634         //   ClassTypeSignature
 635         // SuperinterfaceSignature:
 636         //   ClassTypeSignature
 637         int pos = 0;
 638         if (getChar(signature, 0) == '<') {
 639             pos = checkTypeParameters(signature, pos);
 640         }
 641         pos = checkClassTypeSignature(signature, pos);
 642         while (getChar(signature, pos) == 'L' || getChar(signature, pos) == 'Q') {
 643             pos = checkClassTypeSignature(signature, pos);
 644         }
 645         if (pos != signature.length()) {
 646             throw new IllegalArgumentException(signature + ERROR_AT + pos);
 647         }
 648     }
 649 
 650     /**
 651       * Checks a method signature.
 652       *
 653       * @param signature a string containing the signature that must be checked.
 654       */
 655     public static void checkMethodSignature(final String signature) {
 656         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 657         // MethodSignature:
 658         //   [TypeParameters] ( JavaTypeSignature* ) Result ThrowsSignature*
 659         // Result:
 660         //   JavaTypeSignature
 661         //   VoidDescriptor
 662         // ThrowsSignature:
 663         //   ^ ClassTypeSignature
 664         //   ^ TypeVariableSignature
 665         int pos = 0;
 666         if (getChar(signature, 0) == '<') {
 667             pos = checkTypeParameters(signature, pos);
 668         }
 669         pos = checkChar('(', signature, pos);
 670         while ("ZCBSIFJDLQ[T".indexOf(getChar(signature, pos)) != -1) {
 671             pos = checkJavaTypeSignature(signature, pos);
 672         }
 673         pos = checkChar(')', signature, pos);
 674         if (getChar(signature, pos) == 'V') {
 675             ++pos;
 676         } else {
 677             pos = checkJavaTypeSignature(signature, pos);
 678         }
 679         while (getChar(signature, pos) == '^') {
 680             ++pos;
 681             if (getChar(signature, pos) == 'L' || getChar(signature, pos) == 'Q') {
 682                 pos = checkClassTypeSignature(signature, pos);
 683             } else {
 684                 pos = checkTypeVariableSignature(signature, pos);
 685             }
 686         }
 687         if (pos != signature.length()) {
 688             throw new IllegalArgumentException(signature + ERROR_AT + pos);
 689         }
 690     }
 691 
 692     /**
 693       * Checks a field signature.
 694       *
 695       * @param signature a string containing the signature that must be checked.
 696       */
 697     public static void checkFieldSignature(final String signature) {
 698         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 699         // FieldSignature:
 700         //   ReferenceTypeSignature
 701         int pos = checkReferenceTypeSignature(signature, 0);
 702         if (pos != signature.length()) {
 703             throw new IllegalArgumentException(signature + ERROR_AT + pos);
 704         }
 705     }
 706 
 707     /**
 708       * Checks the type parameters of a class or method signature.
 709       *
 710       * @param signature a string containing the signature that must be checked.
 711       * @param startPos index of first character to be checked.
 712       * @return the index of the first character after the checked part.
 713       */
 714     private static int checkTypeParameters(final String signature, final int startPos) {
 715         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 716         // TypeParameters:
 717         //   < TypeParameter TypeParameter* >
 718         int pos = startPos;
 719         pos = checkChar('<', signature, pos);
 720         pos = checkTypeParameter(signature, pos);
 721         while (getChar(signature, pos) != '>') {
 722             pos = checkTypeParameter(signature, pos);
 723         }
 724         return pos + 1;
 725     }
 726 
 727     /**
 728       * Checks a type parameter of a class or method signature.
 729       *
 730       * @param signature a string containing the signature that must be checked.
 731       * @param startPos index of first character to be checked.
 732       * @return the index of the first character after the checked part.
 733       */
 734     private static int checkTypeParameter(final String signature, final int startPos) {
 735         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 736         // TypeParameter:
 737         //   Identifier ClassBound InterfaceBound*
 738         // ClassBound:
 739         //   : [ReferenceTypeSignature]
 740         // InterfaceBound:
 741         //   : ReferenceTypeSignature
 742         int pos = startPos;
 743         pos = checkSignatureIdentifier(signature, pos);
 744         pos = checkChar(':', signature, pos);
 745         if ("L[T".indexOf(getChar(signature, pos)) != -1) {
 746             pos = checkReferenceTypeSignature(signature, pos);
 747         }
 748         while (getChar(signature, pos) == ':') {
 749             pos = checkReferenceTypeSignature(signature, pos + 1);
 750         }
 751         return pos;
 752     }
 753 
 754     /**
 755       * Checks a reference type signature.
 756       *
 757       * @param signature a string containing the signature that must be checked.
 758       * @param pos index of first character to be checked.
 759       * @return the index of the first character after the checked part.
 760       */
 761     private static int checkReferenceTypeSignature(final String signature, final int pos) {
 762         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 763         // ReferenceTypeSignature:
 764         //   ClassTypeSignature
 765         //   TypeVariableSignature
 766         //   ArrayTypeSignature
 767         // ArrayTypeSignature:
 768         //   [ JavaTypeSignature
 769         switch (getChar(signature, pos)) {
 770             case 'L':
 771                 return checkClassTypeSignature(signature, pos);
 772             case '[':
 773                 return checkJavaTypeSignature(signature, pos + 1);
 774             default:
 775                 return checkTypeVariableSignature(signature, pos);
 776         }
 777     }
 778 
 779     /**
 780       * Checks a class type signature.
 781       *
 782       * @param signature a string containing the signature that must be checked.
 783       * @param startPos index of first character to be checked.
 784       * @return the index of the first character after the checked part.
 785       */
 786     private static int checkClassTypeSignature(final String signature, final int startPos) {
 787         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 788         // ClassTypeSignature:
 789         //   L [PackageSpecifier] SimpleClassTypeSignature ClassTypeSignatureSuffix* ;
 790         // PackageSpecifier:
 791         //   Identifier / PackageSpecifier*
 792         // SimpleClassTypeSignature:
 793         //   Identifier [TypeArguments]
 794         // ClassTypeSignatureSuffix:
 795         //   . SimpleClassTypeSignature
 796         int pos = startPos;
 797         if (getChar(signature, pos) == 'L' || getChar(signature, pos) == 'Q') {
 798             pos = pos + 1;
 799         } else {
 800             throw new IllegalArgumentException(signature + ": 'L' or 'Q' expected at index " + pos);
 801         }
 802 
 803         pos = checkSignatureIdentifier(signature, pos);
 804         while (getChar(signature, pos) == '/') {
 805             pos = checkSignatureIdentifier(signature, pos + 1);
 806         }
 807         if (getChar(signature, pos) == '<') {
 808             pos = checkTypeArguments(signature, pos);
 809         }
 810         while (getChar(signature, pos) == '.') {
 811             pos = checkSignatureIdentifier(signature, pos + 1);
 812             if (getChar(signature, pos) == '<') {
 813                 pos = checkTypeArguments(signature, pos);
 814             }
 815         }
 816         return checkChar(';', signature, pos);
 817     }
 818 
 819     /**
 820       * Checks the type arguments in a class type signature.
 821       *
 822       * @param signature a string containing the signature that must be checked.
 823       * @param startPos index of first character to be checked.
 824       * @return the index of the first character after the checked part.
 825       */
 826     private static int checkTypeArguments(final String signature, final int startPos) {
 827         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 828         // TypeArguments:
 829         //   < TypeArgument TypeArgument* >
 830         int pos = startPos;
 831         pos = checkChar('<', signature, pos);
 832         pos = checkTypeArgument(signature, pos);
 833         while (getChar(signature, pos) != '>') {
 834             pos = checkTypeArgument(signature, pos);
 835         }
 836         return pos + 1;
 837     }
 838 
 839     /**
 840       * Checks a type argument in a class type signature.
 841       *
 842       * @param signature a string containing the signature that must be checked.
 843       * @param startPos index of first character to be checked.
 844       * @return the index of the first character after the checked part.
 845       */
 846     private static int checkTypeArgument(final String signature, final int startPos) {
 847         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 848         // TypeArgument:
 849         //   [WildcardIndicator] ReferenceTypeSignature
 850         //   *
 851         // WildcardIndicator:
 852         //   +
 853         //   -
 854         int pos = startPos;
 855         char c = getChar(signature, pos);
 856         if (c == '*') {
 857             return pos + 1;
 858         } else if (c == '+' || c == '-') {
 859             pos++;
 860         }
 861         return checkReferenceTypeSignature(signature, pos);
 862     }
 863 
 864     /**
 865       * Checks a type variable signature.
 866       *
 867       * @param signature a string containing the signature that must be checked.
 868       * @param startPos index of first character to be checked.
 869       * @return the index of the first character after the checked part.
 870       */
 871     private static int checkTypeVariableSignature(final String signature, final int startPos) {
 872         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 873         // TypeVariableSignature:
 874         //  T Identifier ;
 875         int pos = startPos;
 876         pos = checkChar('T', signature, pos);
 877         pos = checkSignatureIdentifier(signature, pos);
 878         return checkChar(';', signature, pos);
 879     }
 880 
 881     /**
 882       * Checks a Java type signature.
 883       *
 884       * @param signature a string containing the signature that must be checked.
 885       * @param startPos index of first character to be checked.
 886       * @return the index of the first character after the checked part.
 887       */
 888     private static int checkJavaTypeSignature(final String signature, final int startPos) {
 889         // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
 890         // JavaTypeSignature:
 891         //   ReferenceTypeSignature
 892         //   BaseType
 893         // BaseType:
 894         //   (one of)
 895         //   B C D F I J S Z
 896         int pos = startPos;
 897         switch (getChar(signature, pos)) {
 898             case 'B':
 899             case 'C':
 900             case 'D':
 901             case 'F':
 902             case 'I':
 903             case 'J':
 904             case 'S':
 905             case 'Z':
 906                 return pos + 1;
 907             default:
 908                 return checkReferenceTypeSignature(signature, pos);
 909         }
 910     }
 911 
 912     /**
 913       * Checks an identifier.
 914       *
 915       * @param signature a string containing the signature that must be checked.
 916       * @param startPos index of first character to be checked.
 917       * @return the index of the first character after the checked part.
 918       */
 919     private static int checkSignatureIdentifier(final String signature, final int startPos) {
 920         int pos = startPos;
 921         while (pos < signature.length() && ".;[/<>:".indexOf(signature.codePointAt(pos)) == -1) {
 922             pos = signature.offsetByCodePoints(pos, 1);
 923         }
 924         if (pos == startPos) {
 925             throw new IllegalArgumentException(signature + ": identifier expected at index " + startPos);
 926         }
 927         return pos;
 928     }
 929 
 930     /**
 931       * Checks a single character.
 932       *
 933       * @param c a character.
 934       * @param signature a string containing the signature that must be checked.
 935       * @param pos index of first character to be checked.
 936       * @return the index of the first character after the checked part.
 937       */
 938     private static int checkChar(final char c, final String signature, final int pos) {
 939         if (getChar(signature, pos) == c) {
 940             return pos + 1;
 941         }
 942         throw new IllegalArgumentException(signature + ": '" + c + "' expected at index " + pos);
 943     }
 944 
 945     /**
 946       * Returns the string character at the given index, or 0.
 947       *
 948       * @param string a string.
 949       * @param pos an index in 'string'.
 950       * @return the character at the given index, or 0 if there is no such character.
 951       */
 952     private static char getChar(final String string, final int pos) {
 953         return pos < string.length() ? string.charAt(pos) : (char) 0;
 954     }
 955 
 956     /**
 957       * Checks the reference to a type in a type annotation.
 958       *
 959       * @param typeRef a reference to an annotated type.
 960       */
 961     static void checkTypeRef(final int typeRef) {
 962         int mask = 0;
 963         switch (typeRef >>> 24) {
 964             case TypeReference.CLASS_TYPE_PARAMETER:
 965             case TypeReference.METHOD_TYPE_PARAMETER:
 966             case TypeReference.METHOD_FORMAL_PARAMETER:
 967                 mask = 0xFFFF0000;
 968                 break;
 969             case TypeReference.FIELD:
 970             case TypeReference.METHOD_RETURN:
 971             case TypeReference.METHOD_RECEIVER:
 972             case TypeReference.LOCAL_VARIABLE:
 973             case TypeReference.RESOURCE_VARIABLE:
 974             case TypeReference.INSTANCEOF:
 975             case TypeReference.NEW:
 976             case TypeReference.CONSTRUCTOR_REFERENCE:
 977             case TypeReference.METHOD_REFERENCE:
 978                 mask = 0xFF000000;
 979                 break;
 980             case TypeReference.CLASS_EXTENDS:
 981             case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
 982             case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
 983             case TypeReference.THROWS:
 984             case TypeReference.EXCEPTION_PARAMETER:
 985                 mask = 0xFFFFFF00;
 986                 break;
 987             case TypeReference.CAST:
 988             case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
 989             case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
 990             case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
 991             case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
 992                 mask = 0xFF0000FF;
 993                 break;
 994             default:
 995                 break;
 996         }
 997         if (mask == 0 || (typeRef & ~mask) != 0) {
 998             throw new IllegalArgumentException(
 999                     "Invalid type reference 0x" + Integer.toHexString(typeRef));
1000         }
1001     }
1002 
1003     /**
1004       * Returns the package name of an internal name.
1005       *
1006       * @param name an internal name.
1007       * @return the package name or "" if there is no package.
1008       */
1009     private static String packageName(final String name) {
1010         int index = name.lastIndexOf('/');
1011         if (index == -1) {
1012             return "";
1013         }
1014         return name.substring(0, index);
1015     }
1016 
1017     // -----------------------------------------------------------------------------------------------
1018     // Static verification methods
1019     // -----------------------------------------------------------------------------------------------
1020 
1021     /**
1022       * Checks the given class.
1023       *
1024       * <p>Usage: CheckClassAdapter &lt;binary class name or class file name&gt;
1025       *
1026       * @param args the command line arguments.
1027       * @throws IOException if the class cannot be found, or if an IO exception occurs.
1028       */
1029     public static void main(final String[] args) throws IOException {
1030         main(args, new PrintWriter(System.err, true));
1031     }
1032 
1033     /**
1034       * Checks the given class.
1035       *
1036       * @param args the command line arguments.
1037       * @param logger where to log errors.
1038       * @throws IOException if the class cannot be found, or if an IO exception occurs.
1039       */
1040     static void main(final String[] args, final PrintWriter logger) throws IOException {
1041         if (args.length != 1) {
1042             logger.println(USAGE);
1043             return;
1044         }
1045 
1046         ClassReader classReader;
1047         if (args[0].endsWith(".class")) {
1048             // Can't fix PMD warning for 1.5 compatibility.
1049             try (InputStream inputStream = new FileInputStream(args[0])) { // NOPMD(AvoidFileStream)
1050                 classReader = new ClassReader(inputStream);
1051             }
1052         } else {
1053             classReader = new ClassReader(args[0]);
1054         }
1055 
1056         verify(classReader, false, logger);
1057     }
1058 
1059     /**
1060       * Checks the given class.
1061       *
1062       * @param classReader the class to be checked.
1063       * @param printResults whether to print the results of the bytecode verification.
1064       * @param printWriter where the results (or the stack trace in case of error) must be printed.
1065       */
1066     public static void verify(
1067             final ClassReader classReader, final boolean printResults, final PrintWriter printWriter) {
1068         verify(classReader, null, printResults, printWriter);
1069     }
1070 
1071     /**
1072       * Checks the given class.
1073       *
1074       * @param classReader the class to be checked.
1075       * @param loader a <code>ClassLoader</code> which will be used to load referenced classes. May be
1076       *     {@literal null}.
1077       * @param printResults whether to print the results of the bytecode verification.
1078       * @param printWriter where the results (or the stack trace in case of error) must be printed.
1079       */
1080     public static void verify(
1081             final ClassReader classReader,
1082             final ClassLoader loader,
1083             final boolean printResults,
1084             final PrintWriter printWriter) {
1085         ClassNode classNode = new ClassNode();
1086         classReader.accept(
1087                 new CheckClassAdapter(/*latest*/ Opcodes.ASM9, classNode, false) {},
1088                 ClassReader.SKIP_DEBUG);
1089 
1090         Type syperType = classNode.superName == null ? null : Type.getObjectType(classNode.superName);
1091         List<MethodNode> methods = classNode.methods;
1092 
1093         List<Type> interfaces = new ArrayList<>();
1094         for (String interfaceName : classNode.interfaces) {
1095             interfaces.add(Type.getObjectType(interfaceName));
1096         }
1097 
1098         for (MethodNode method : methods) {
1099             SimpleVerifier verifier =
1100                     new SimpleVerifier(
1101                             Type.getObjectType(classNode.name),
1102                             syperType,
1103                             interfaces,
1104                             (classNode.access & Opcodes.ACC_INTERFACE) != 0);
1105             Analyzer<BasicValue> analyzer = new Analyzer<>(verifier);
1106             if (loader != null) {
1107                 verifier.setClassLoader(loader);
1108             }
1109             try {
1110                 analyzer.analyze(classNode.name, method);
1111             } catch (AnalyzerException e) {
1112                 e.printStackTrace(printWriter);
1113             }
1114             if (printResults) {
1115                 printAnalyzerResult(method, analyzer, printWriter);
1116             }
1117         }
1118         printWriter.flush();
1119     }
1120 
1121     static void printAnalyzerResult(
1122             final MethodNode method, final Analyzer<BasicValue> analyzer, final PrintWriter printWriter) {
1123         Textifier textifier = new Textifier();
1124         TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(textifier);
1125 
1126         printWriter.println(method.name + method.desc);
1127         for (int i = 0; i < method.instructions.size(); ++i) {
1128             method.instructions.get(i).accept(traceMethodVisitor);
1129 
1130             StringBuilder stringBuilder = new StringBuilder();
1131             Frame<BasicValue> frame = analyzer.getFrames()[i];
1132             if (frame == null) {
1133                 stringBuilder.append('?');
1134             } else {
1135                 for (int j = 0; j < frame.getLocals(); ++j) {
1136                     stringBuilder.append(getUnqualifiedName(frame.getLocal(j).toString())).append(' ');
1137                 }
1138                 stringBuilder.append(" : ");
1139                 for (int j = 0; j < frame.getStackSize(); ++j) {
1140                     stringBuilder.append(getUnqualifiedName(frame.getStack(j).toString())).append(' ');
1141                 }
1142             }
1143             while (stringBuilder.length() < method.maxStack + method.maxLocals + 1) {
1144                 stringBuilder.append(' ');
1145             }
1146             printWriter.print(Integer.toString(i + 100000).substring(1));
1147             printWriter.print(
1148                     " " + stringBuilder + " : " + textifier.text.get(textifier.text.size() - 1));
1149         }
1150         for (TryCatchBlockNode tryCatchBlock : method.tryCatchBlocks) {
1151             tryCatchBlock.accept(traceMethodVisitor);
1152             printWriter.print(" " + textifier.text.get(textifier.text.size() - 1));
1153         }
1154         printWriter.println();
1155     }
1156 
1157     private static String getUnqualifiedName(final String name) {
1158         int lastSlashIndex = name.lastIndexOf('/');
1159         if (lastSlashIndex == -1) {
1160             return name;
1161         } else {
1162             int endIndex = name.length();
1163             if (name.charAt(endIndex - 1) == ';') {
1164                 endIndex--;
1165             }
1166             int lastBracketIndex = name.lastIndexOf('[');
1167             if (lastBracketIndex == -1) {
1168                 return name.substring(lastSlashIndex + 1, endIndex);
1169             }
1170             return name.substring(0, lastBracketIndex + 1) + name.substring(lastSlashIndex + 1, endIndex);
1171         }
1172     }
1173 }
1174