1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 /*
  26  * This file is available under and governed by the GNU General Public
  27  * License version 2 only, as published by the Free Software Foundation.
  28  * However, the following notice accompanied the original version of this
  29  * file:
  30  *
  31  * ASM: a very small and fast Java bytecode manipulation framework
  32  * Copyright (c) 2000-2011 INRIA, France Telecom
  33  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.util;
  60 
  61 import java.io.FileInputStream;
  62 import java.io.PrintWriter;
  63 import java.util.ArrayList;
  64 import java.util.HashMap;
  65 import java.util.Iterator;
  66 import java.util.List;
  67 import java.util.Map;
  68 
  69 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
  70 import jdk.internal.org.objectweb.asm.Attribute;
  71 import jdk.internal.org.objectweb.asm.ClassReader;
  72 import jdk.internal.org.objectweb.asm.ClassVisitor;
  73 import jdk.internal.org.objectweb.asm.FieldVisitor;
  74 import jdk.internal.org.objectweb.asm.Label;
  75 import jdk.internal.org.objectweb.asm.MethodVisitor;
  76 import jdk.internal.org.objectweb.asm.Opcodes;
  77 import jdk.internal.org.objectweb.asm.Type;
  78 import jdk.internal.org.objectweb.asm.tree.ClassNode;
  79 import jdk.internal.org.objectweb.asm.tree.MethodNode;
  80 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;
  81 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue;
  82 import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
  83 import jdk.internal.org.objectweb.asm.tree.analysis.SimpleVerifier;
  84 
  85 /**
  86  * A {@link ClassVisitor} that checks that its methods are properly used. More
  87  * precisely this class adapter checks each method call individually, based
  88  * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
  89  * of method calls. For example, the invalid sequence
  90  * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
  91  * "i", "D", null)</tt>
  92  * will <i>not</i> be detected by this class adapter.
  93  *
  94  * <p><code>CheckClassAdapter</code> can be also used to verify bytecode
  95  * transformations in order to make sure transformed bytecode is sane. For
  96  * example:
  97  *
  98  * <pre>
  99  *   InputStream is = ...; // get bytes for the source class
 100  *   ClassReader cr = new ClassReader(is);
 101  *   ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
 102  *   ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw));
 103  *   cr.accept(cv, 0);
 104  *
 105  *   StringWriter sw = new StringWriter();
 106  *   PrintWriter pw = new PrintWriter(sw);
 107  *   CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
 108  *   assertTrue(sw.toString(), sw.toString().length()==0);
 109  * </pre>
 110  *
 111  * Above code runs transformed bytecode trough the
 112  * <code>CheckClassAdapter</code>. It won't be exactly the same verification
 113  * as JVM does, but it run data flow analysis for the code of each method and
 114  * checks that expectations are met for each method instruction.
 115  *
 116  * <p>If method bytecode has errors, assertion text will show the erroneous
 117  * instruction number and dump of the failed method with information about
 118  * locals and stack slot for each instruction. For example (format is -
 119  * insnNumber locals : stack):
 120  *
 121  * <pre>
 122  * jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
 123  *   at jdk.internal.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
 124  *   at jdk.internal.org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
 125  * ...
 126  * remove()V
 127  * 00000 LinkedBlockingQueue$Itr . . . . . . . .  :
 128  *   ICONST_0
 129  * 00001 LinkedBlockingQueue$Itr . . . . . . . .  : I
 130  *   ISTORE 2
 131  * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . .  :
 132  * ...
 133  *
 134  * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . .  :
 135  *   ILOAD 1
 136  * 00072 <b>?</b>
 137  *   INVOKESPECIAL java/lang/Integer.<init> (I)V
 138  * ...
 139  * </pre>
 140  *
 141  * In the above output you can see that variable 1 loaded by
 142  * <code>ILOAD 1</code> instruction at position <code>00071</code> is not
 143  * initialized. You can also see that at the beginning of the method (code
 144  * inserted by the transformation) variable 2 is initialized.
 145  *
 146  * <p>Note that when used like that, <code>CheckClassAdapter.verify()</code>
 147  * can trigger additional class loading, because it is using
 148  * <code>SimpleVerifier</code>.
 149  *
 150  * @author Eric Bruneton
 151  */
 152 public class CheckClassAdapter extends ClassVisitor {
 153 
 154     /**
 155      * The class version number.
 156      */
 157     private int version;
 158 
 159     /**
 160      * <tt>true</tt> if the visit method has been called.
 161      */
 162     private boolean start;
 163 
 164     /**
 165      * <tt>true</tt> if the visitSource method has been called.
 166      */
 167     private boolean source;
 168 
 169     /**
 170      * <tt>true</tt> if the visitOuterClass method has been called.
 171      */
 172     private boolean outer;
 173 
 174     /**
 175      * <tt>true</tt> if the visitEnd method has been called.
 176      */
 177     private boolean end;
 178 
 179     /**
 180      * The already visited labels. This map associate Integer values to Label
 181      * keys.
 182      */
 183     private Map<Label, Integer> labels;
 184 
 185     /**
 186      * <tt>true</tt> if the method code must be checked with a BasicVerifier.
 187      */
 188     private boolean checkDataFlow;
 189 
 190     /**
 191      * Checks a given class. <p> Usage: CheckClassAdapter &lt;binary
 192      * class name or class file name&gt;
 193      *
 194      * @param args the command line arguments.
 195      *
 196      * @throws Exception if the class cannot be found, or if an IO exception
 197      *         occurs.
 198      */
 199     public static void main(final String[] args) throws Exception {
 200         if (args.length != 1) {
 201             System.err.println("Verifies the given class.");
 202             System.err.println("Usage: CheckClassAdapter "
 203                     + "<fully qualified class name or class file name>");
 204             return;
 205         }
 206         ClassReader cr;
 207         if (args[0].endsWith(".class")) {
 208             cr = new ClassReader(new FileInputStream(args[0]));
 209         } else {
 210             cr = new ClassReader(args[0]);
 211         }
 212 
 213         verify(cr, false, new PrintWriter(System.err));
 214     }
 215 
 216     /**
 217      * Checks a given class.
 218      *
 219      * @param cr a <code>ClassReader</code> that contains bytecode for the
 220      *        analysis.
 221      * @param loader a <code>ClassLoader</code> which will be used to load
 222      *        referenced classes. This is useful if you are verifiying multiple
 223      *        interdependent classes.
 224      * @param dump true if bytecode should be printed out not only when errors
 225      *        are found.
 226      * @param pw write where results going to be printed
 227      */
 228     public static void verify(
 229         final ClassReader cr,
 230         final ClassLoader loader,
 231         final boolean dump,
 232         final PrintWriter pw)
 233     {
 234         ClassNode cn = new ClassNode();
 235         cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);
 236 
 237         Type syperType = cn.superName == null
 238                 ? null
 239                 : Type.getObjectType(cn.superName);
 240         List<MethodNode> methods = cn.methods;
 241 
 242         List<Type> interfaces = new ArrayList<Type>();
 243         for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) {
 244             interfaces.add(Type.getObjectType(i.next().toString()));
 245         }
 246 
 247         for (int i = 0; i < methods.size(); ++i) {
 248             MethodNode method = methods.get(i);
 249             SimpleVerifier verifier = new SimpleVerifier(Type.getObjectType(cn.name),
 250                     syperType,
 251                     interfaces,
 252                     (cn.access & Opcodes.ACC_INTERFACE) != 0);
 253             Analyzer<BasicValue> a = new Analyzer<BasicValue>(verifier);
 254             if (loader != null) {
 255                 verifier.setClassLoader(loader);
 256             }
 257             try {
 258                 a.analyze(cn.name, method);
 259                 if (!dump) {
 260                     continue;
 261                 }
 262             } catch (Exception e) {
 263                 e.printStackTrace(pw);
 264             }
 265             printAnalyzerResult(method, a, pw);
 266         }
 267         pw.flush();
 268     }
 269 
 270     /**
 271      * Checks a given class
 272      *
 273      * @param cr a <code>ClassReader</code> that contains bytecode for the
 274      *        analysis.
 275      * @param dump true if bytecode should be printed out not only when errors
 276      *        are found.
 277      * @param pw write where results going to be printed
 278      */
 279     public static void verify(
 280         final ClassReader cr,
 281         final boolean dump,
 282         final PrintWriter pw)
 283     {
 284         verify(cr, null, dump, pw);
 285     }
 286 
 287     static void printAnalyzerResult(
 288         MethodNode method,
 289         Analyzer<BasicValue> a,
 290         final PrintWriter pw)
 291     {
 292         Frame<BasicValue>[] frames = a.getFrames();
 293         Textifier t = new Textifier();
 294         TraceMethodVisitor mv = new TraceMethodVisitor(t);
 295 
 296         pw.println(method.name + method.desc);
 297         for (int j = 0; j < method.instructions.size(); ++j) {
 298             method.instructions.get(j).accept(mv);
 299 
 300             StringBuffer s = new StringBuffer();
 301             Frame<BasicValue> f = frames[j];
 302             if (f == null) {
 303                 s.append('?');
 304             } else {
 305                 for (int k = 0; k < f.getLocals(); ++k) {
 306                     s.append(getShortName(f.getLocal(k).toString()))
 307                             .append(' ');
 308                 }
 309                 s.append(" : ");
 310                 for (int k = 0; k < f.getStackSize(); ++k) {
 311                     s.append(getShortName(f.getStack(k).toString()))
 312                             .append(' ');
 313                 }
 314             }
 315             while (s.length() < method.maxStack + method.maxLocals + 1) {
 316                 s.append(' ');
 317             }
 318             pw.print(Integer.toString(j + 100000).substring(1));
 319             pw.print(" " + s + " : " + t.text.get(t.text.size() - 1));
 320         }
 321         for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
 322             method.tryCatchBlocks.get(j).accept(mv);
 323             pw.print(" " + t.text.get(t.text.size() - 1));
 324         }
 325         pw.println();
 326     }
 327 
 328     private static String getShortName(final String name) {
 329         int n = name.lastIndexOf('/');
 330         int k = name.length();
 331         if (name.charAt(k - 1) == ';') {
 332             k--;
 333         }
 334         return n == -1 ? name : name.substring(n + 1, k);
 335     }
 336 
 337     /**
 338      * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
 339      * this constructor</i>. Instead, they must use the
 340      * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
 341      *
 342      * @param cv the class visitor to which this adapter must delegate calls.
 343      */
 344     public CheckClassAdapter(final ClassVisitor cv) {
 345         this(cv, true);
 346     }
 347 
 348     /**
 349      * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
 350      * this constructor</i>. Instead, they must use the
 351      * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
 352      *
 353      * @param cv the class visitor to which this adapter must delegate calls.
 354      * @param checkDataFlow <tt>true</tt> to perform basic data flow checks, or
 355      *        <tt>false</tt> to not perform any data flow check (see
 356      *        {@link CheckMethodAdapter}). This option requires valid maxLocals
 357      *        and maxStack values.
 358      */
 359     public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow)
 360     {
 361         this(Opcodes.ASM4, cv, checkDataFlow);
 362     }
 363 
 364     /**
 365      * Constructs a new {@link CheckClassAdapter}.
 366      *
 367      * @param api the ASM API version implemented by this visitor. Must be one
 368      *        of {@link Opcodes#ASM4}.
 369      * @param cv the class visitor to which this adapter must delegate calls.
 370      * @param checkDataFlow <tt>true</tt> to perform basic data flow checks, or
 371      *        <tt>false</tt> to not perform any data flow check (see
 372      *        {@link CheckMethodAdapter}). This option requires valid maxLocals
 373      *        and maxStack values.
 374      */
 375     protected CheckClassAdapter(
 376         final int api,
 377         final ClassVisitor cv,
 378         final boolean checkDataFlow)
 379     {
 380         super(api, cv);
 381         this.labels = new HashMap<Label, Integer>();
 382         this.checkDataFlow = checkDataFlow;
 383     }
 384 
 385     // ------------------------------------------------------------------------
 386     // Implementation of the ClassVisitor interface
 387     // ------------------------------------------------------------------------
 388 
 389     @Override
 390     public void visit(
 391         final int version,
 392         final int access,
 393         final String name,
 394         final String signature,
 395         final String superName,
 396         final String[] interfaces)
 397     {
 398         if (start) {
 399             throw new IllegalStateException("visit must be called only once");
 400         }
 401         start = true;
 402         checkState();
 403         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
 404                 + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
 405                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
 406                 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
 407                 + Opcodes.ACC_DEPRECATED
 408                 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
 409         if (name == null || !name.endsWith("package-info")) {
 410             CheckMethodAdapter.checkInternalName(name, "class name");
 411         }
 412         if ("java/lang/Object".equals(name)) {
 413             if (superName != null) {
 414                 throw new IllegalArgumentException("The super class name of the Object class must be 'null'");
 415             }
 416         } else {
 417             CheckMethodAdapter.checkInternalName(superName, "super class name");
 418         }
 419         if (signature != null) {
 420             CheckMethodAdapter.checkClassSignature(signature);
 421         }
 422         if ((access & Opcodes.ACC_INTERFACE) != 0) {
 423             if (!"java/lang/Object".equals(superName)) {
 424                 throw new IllegalArgumentException("The super class name of interfaces must be 'java/lang/Object'");
 425             }
 426         }
 427         if (interfaces != null) {
 428             for (int i = 0; i < interfaces.length; ++i) {
 429                 CheckMethodAdapter.checkInternalName(interfaces[i],
 430                         "interface name at index " + i);
 431             }
 432         }
 433         this.version = version;
 434         super.visit(version, access, name, signature, superName, interfaces);
 435     }
 436 
 437     @Override
 438     public void visitSource(final String file, final String debug) {
 439         checkState();
 440         if (source) {
 441             throw new IllegalStateException("visitSource can be called only once.");
 442         }
 443         source = true;
 444         super.visitSource(file, debug);
 445     }
 446 
 447     @Override
 448     public void visitOuterClass(
 449         final String owner,
 450         final String name,
 451         final String desc)
 452     {
 453         checkState();
 454         if (outer) {
 455             throw new IllegalStateException("visitOuterClass can be called only once.");
 456         }
 457         outer = true;
 458         if (owner == null) {
 459             throw new IllegalArgumentException("Illegal outer class owner");
 460         }
 461         if (desc != null) {
 462             CheckMethodAdapter.checkMethodDesc(desc);
 463         }
 464         super.visitOuterClass(owner, name, desc);
 465     }
 466 
 467     @Override
 468     public void visitInnerClass(
 469         final String name,
 470         final String outerName,
 471         final String innerName,
 472         final int access)
 473     {
 474         checkState();
 475         CheckMethodAdapter.checkInternalName(name, "class name");
 476         if (outerName != null) {
 477             CheckMethodAdapter.checkInternalName(outerName, "outer class name");
 478         }
 479         if (innerName != null) {
 480             CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
 481         }
 482         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
 483                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
 484                 + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
 485                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
 486                 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
 487         super.visitInnerClass(name, outerName, innerName, access);
 488     }
 489 
 490     @Override
 491     public FieldVisitor visitField(
 492         final int access,
 493         final String name,
 494         final String desc,
 495         final String signature,
 496         final Object value)
 497     {
 498         checkState();
 499         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
 500                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
 501                 + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
 502                 + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
 503                 + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED
 504                 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
 505         CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
 506         CheckMethodAdapter.checkDesc(desc, false);
 507         if (signature != null) {
 508             CheckMethodAdapter.checkFieldSignature(signature);
 509         }
 510         if (value != null) {
 511             CheckMethodAdapter.checkConstant(value);
 512         }
 513         FieldVisitor av = super.visitField(access, name, desc, signature, value);
 514         return new CheckFieldAdapter(av);
 515     }
 516 
 517     @Override
 518     public MethodVisitor visitMethod(
 519         final int access,
 520         final String name,
 521         final String desc,
 522         final String signature,
 523         final String[] exceptions)
 524     {
 525         checkState();
 526         checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
 527                 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
 528                 + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
 529                 + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
 530                 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
 531                 + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED
 532                 + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
 533         CheckMethodAdapter.checkMethodIdentifier(version, name, "method name");
 534         CheckMethodAdapter.checkMethodDesc(desc);
 535         if (signature != null) {
 536             CheckMethodAdapter.checkMethodSignature(signature);
 537         }
 538         if (exceptions != null) {
 539             for (int i = 0; i < exceptions.length; ++i) {
 540                 CheckMethodAdapter.checkInternalName(exceptions[i],
 541                         "exception name at index " + i);
 542             }
 543         }
 544         CheckMethodAdapter cma;
 545         if (checkDataFlow) {
 546             cma = new CheckMethodAdapter(access,
 547                     name,
 548                     desc,
 549                     super.visitMethod(access, name, desc, signature, exceptions),
 550                     labels);
 551         } else {
 552             cma = new CheckMethodAdapter(super.visitMethod(access,
 553                     name,
 554                     desc,
 555                     signature,
 556                     exceptions), labels);
 557         }
 558         cma.version = version;
 559         return cma;
 560     }
 561 
 562     @Override
 563     public AnnotationVisitor visitAnnotation(
 564         final String desc,
 565         final boolean visible)
 566     {
 567         checkState();
 568         CheckMethodAdapter.checkDesc(desc, false);
 569         return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
 570     }
 571 
 572     @Override
 573     public void visitAttribute(final Attribute attr) {
 574         checkState();
 575         if (attr == null) {
 576             throw new IllegalArgumentException("Invalid attribute (must not be null)");
 577         }
 578         super.visitAttribute(attr);
 579     }
 580 
 581     @Override
 582     public void visitEnd() {
 583         checkState();
 584         end = true;
 585         super.visitEnd();
 586     }
 587 
 588     // ------------------------------------------------------------------------
 589     // Utility methods
 590     // ------------------------------------------------------------------------
 591 
 592     /**
 593      * Checks that the visit method has been called and that visitEnd has not
 594      * been called.
 595      */
 596     private void checkState() {
 597         if (!start) {
 598             throw new IllegalStateException("Cannot visit member before visit has been called.");
 599         }
 600         if (end) {
 601             throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
 602         }
 603     }
 604 
 605     /**
 606      * Checks that the given access flags do not contain invalid flags. This
 607      * method also checks that mutually incompatible flags are not set
 608      * simultaneously.
 609      *
 610      * @param access the access flags to be checked
 611      * @param possibleAccess the valid access flags.
 612      */
 613     static void checkAccess(final int access, final int possibleAccess) {
 614         if ((access & ~possibleAccess) != 0) {
 615             throw new IllegalArgumentException("Invalid access flags: "
 616                     + access);
 617         }
 618         int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1;
 619         int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1;
 620         int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1;
 621         if (pub + pri + pro > 1) {
 622             throw new IllegalArgumentException("public private and protected are mutually exclusive: "
 623                     + access);
 624         }
 625         int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1;
 626         int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1;
 627         if (fin + abs > 1) {
 628             throw new IllegalArgumentException("final and abstract are mutually exclusive: "
 629                     + access);
 630         }
 631     }
 632 }