/**
   * Override this method to create you own classes on the fly. The name contains the special token
   * $$BCEL$$. Everything before that token is consddered to be a package name. You can encode you
   * own arguments into the subsequent string. You must regard however not to use any "illegal"
   * characters, i.e., characters that may not appear in a Java class name too<br>
   * The default implementation interprets the string as a encoded compressed Java class, unpacks
   * and decodes it with the Utility.decode() method, and parses the resulting byte array and
   * returns the resulting JavaClass object.
   *
   * @param class_name compressed byte code with "$$BCEL$$" in it
   */
  protected JavaClass createClass(String class_name) {
    int index = class_name.indexOf("$$BCEL$$");
    String real_name = class_name.substring(index + 8);

    JavaClass clazz = null;
    try {
      byte[] bytes = Utility.decode(real_name, true);
      ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");

      clazz = parser.parse();
    } catch (Throwable e) {
      e.printStackTrace();
      return null;
    }

    // Adapt the class name to the passed value
    ConstantPool cp = clazz.getConstantPool();

    ConstantClass cl =
        (ConstantClass) cp.getConstant(clazz.getClassNameIndex(), Constants.CONSTANT_Class);
    ConstantUtf8 name = (ConstantUtf8) cp.getConstant(cl.getNameIndex(), Constants.CONSTANT_Utf8);
    name.setBytes(class_name.replace('.', '/'));

    return clazz;
  }
  @Test
  public void shouldReturnUnparsableClassIfErrorOccursWhileParsing() {
    ClassParser parser = mock(ClassParser.class);
    when(parser.getClass("MyClassName")).thenThrow(new RuntimeException(new NotFoundException("")));

    builder = new JavaClassBuilder(parser);

    Assert.assertThat(builder.createClass("MyClassName"), instanceOf(UnparsableClass.class));
  }
  private DbJVPackage importJarFile(
      File jarFile,
      DbJVPackage topMostPackage,
      Controller controller,
      int startJobDone,
      int endJobDone)
      throws DbException {
    DbJVClass dbClass;
    String filename = jarFile.getName();

    try {
      ZipFile zip = new ZipFile(jarFile);
      int i = 0, nb = zip.size();
      int span = endJobDone - startJobDone;

      for (Enumeration<?> e = zip.entries(); e.hasMoreElements(); ) {
        int jobDone = startJobDone + (i * span) / nb;

        ZipEntry entry = (ZipEntry) e.nextElement();
        String entryName = entry.getName();
        int idx = entryName.lastIndexOf('.');
        String ext = (idx == -1) ? null : entryName.substring(idx + 1);
        if ("class".equals(ext)) {
          InputStream is = zip.getInputStream(entry);

          ClassParser parser = new ClassParser(is, filename);
          JavaClass claz = parser.parse();
          dbClass = importClass(claz, controller);

          if (dbClass != null) {
            DbJVPackage pack = (DbJVPackage) dbClass.getCompositeOfType(DbJVPackage.metaClass);
            topMostPackage = findTopMostPackage(topMostPackage, pack);
            addToImportedPackage(pack);
          } // end if
        } // end if

        // check job done
        controller.checkPoint(jobDone);
        i++;

        // stop to reverse engineer if user has cancelled
        boolean finished = controller.isFinalState();
        if (finished) {
          break;
        }
      } // end for

      zip.close();

    } catch (IOException ex) {
      controller.println(ex.toString());
      dbClass = null;
    }

    return topMostPackage;
  } // end importClassFile()
  private DbJVClass importClassFile(String filename, Controller controller) throws DbException {
    DbJVClass dbClass;

    try {
      ClassParser parser = new ClassParser(filename);
      JavaClass claz = parser.parse();
      dbClass = importClass(claz, controller);

    } catch (IOException ex) {
      controller.println(ex.toString());
      dbClass = null;
    }

    return dbClass;
  } // end importClassFile()
 public static String getTableName(@NonNull Object o) {
   Container container = ClassParser.getContainer(o.getClass());
   String name = null;
   if (container != null) {
     name = getTableName(container);
   }
   return name;
 }
Exemple #6
0
  public static void main(String argv[]) {
    String[] file_name = new String[argv.length];
    int files = 0;
    ClassParser parser = null;
    JavaClass java_class = null;
    String zip_file = null;
    char sep = System.getProperty("file.separator").toCharArray()[0];
    String dir = "." + sep; // Where to store HTML files

    try {
      /* Parse command line arguments.
       */
      for (int i = 0; i < argv.length; i++) {
        if (argv[i].charAt(0) == '-') { // command line switch
          if (argv[i].equals("-d")) { // Specify target directory, default `.�
            dir = argv[++i];

            if (!dir.endsWith("" + sep)) dir = dir + sep;

            new File(dir).mkdirs(); // Create target directory if necessary
          } else if (argv[i].equals("-zip")) zip_file = argv[++i];
          else System.out.println("Unknown option " + argv[i]);
        } else // add file name to list */
        file_name[files++] = argv[i];
      }

      if (files == 0) System.err.println("Class2HTML: No input files specified.");
      else { // Loop through files ...
        for (int i = 0; i < files; i++) {
          System.out.print("Processing " + file_name[i] + "...");
          if (zip_file == null)
            parser = new ClassParser(file_name[i]); // Create parser object from file
          else
            parser = new ClassParser(zip_file, file_name[i]); // Create parser object from zip file

          java_class = parser.parse();
          new Class2HTML(java_class, dir);
          System.out.println("Done.");
        }
      }
    } catch (Exception e) {
      System.out.println(e);
      e.printStackTrace(System.out);
    }
  }
  /**
   * Given another class, return a transformed version of the class which replaces specified calls
   * with alternative static implementations
   */
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {

    // debug = className.equals ("chicory/Test");

    String fullClassName = className.replace("/", ".");

    debug_transform.log("In Transform: class = %s%n", className);

    // Don't instrument boot classes.  We only want to instrument
    // user classes classpath.
    // Most boot classes have the null loader,
    // but some generated classes (such as those in sun.reflect) will
    // have a non-null loader.  Some of these have a null parent loader,
    // but some do not.  The check for the sun.reflect package is a hack
    // to catch all of these.  A more consistent mechanism to determine
    // boot classes would be preferrable.
    if (loader == null) {
      debug_transform.log("ignoring system class %s, class loader == null", fullClassName);
      return (null);
    } else if (loader.getParent() == null) {
      debug_transform.log("ignoring system class %s, parent loader == null\n", fullClassName);
      return (null);
    } else if (fullClassName.startsWith("sun.reflect")) {
      debug_transform.log("ignoring system class %s, in sun.reflect package", fullClassName);
      return (null);
    } else if (fullClassName.startsWith("com.sun")) {
      System.out.printf("Class from com.sun package %s with nonnull loaders\n", fullClassName);
    }

    // Don't intrument our code
    if (className.startsWith("randoop.")) {
      debug_transform.log("Not considering randoop class %s%n", fullClassName);
      return (null);
    }

    // Look for match with specified regular expressions for class
    method_map = null;
    debug_class = false;
    for (MethodMapInfo mmi : map_list) {
      if (mmi.class_regex.matcher(className).matches()) {
        if (false && className.startsWith("RandoopTest")) debug_class = true;
        if (debug_class)
          System.out.printf("Classname %s matches re %s%n", className, mmi.class_regex);
        method_map = mmi.map;
        break;
      }
    }
    if (method_map == null) return null;

    debug_transform.log(
        "transforming class %s, loader %s - %s%n", className, loader, loader.getParent());

    // Parse the bytes of the classfile, die on any errors
    JavaClass c = null;
    ClassParser parser = new ClassParser(new ByteArrayInputStream(classfileBuffer), className);
    try {
      c = parser.parse();
    } catch (Exception e) {
      throw new RuntimeException("Unexpected error", e);
    }

    try {
      // Get the class information
      ClassGen cg = new ClassGen(c);
      ifact = new InstructionFactory(cg);

      map_calls(cg, className, loader);

      JavaClass njc = cg.getJavaClass();
      if (debug) njc.dump("/tmp/ret/" + njc.getClassName() + ".class");

      if (true) {
        return (cg.getJavaClass().getBytes());
      } else {
        debug_transform.log("not including class %s (filtered out)", className);
        return null;
      }

    } catch (Throwable e) {
      out.format("Unexpected error %s in transform", e);
      e.printStackTrace();
      return (null);
    }
  }
  /**
   * Adds all the constants found in the given class into the given ConstantSet, and returns it.
   *
   * @see #getConstants(String)
   */
  public static ConstantSet getConstants(String classname, ConstantSet result) {

    ClassParser cp;
    JavaClass jc;
    try {
      String classfileBase = classname.replace('.', '/');
      InputStream is = ClassPath.SYSTEM_CLASS_PATH.getInputStream(classfileBase, ".class");
      cp = new ClassParser(is, classname);
      jc = cp.parse();
    } catch (java.io.IOException e) {
      throw new Error("IOException while reading '" + classname + "': " + e.getMessage());
    }
    result.classname = jc.getClassName();

    // Get all of the constants from the pool
    ConstantPool constant_pool = jc.getConstantPool();
    for (Constant c : constant_pool.getConstantPool()) {
      // System.out.printf ("*Constant = %s%n", c);
      if (c == null
          || c instanceof ConstantClass
          || c instanceof ConstantFieldref
          || c instanceof ConstantInterfaceMethodref
          || c instanceof ConstantMethodref
          || c instanceof ConstantNameAndType
          || c instanceof ConstantUtf8) {
        continue;
      }
      if (c instanceof ConstantString) {
        result.strings.add((String) ((ConstantString) c).getConstantValue(constant_pool));
      } else if (c instanceof ConstantDouble) {
        result.doubles.add((Double) ((ConstantDouble) c).getConstantValue(constant_pool));
      } else if (c instanceof ConstantFloat) {
        result.floats.add((Float) ((ConstantFloat) c).getConstantValue(constant_pool));
      } else if (c instanceof ConstantInteger) {
        result.ints.add((Integer) ((ConstantInteger) c).getConstantValue(constant_pool));
      } else if (c instanceof ConstantLong) {
        result.longs.add((Long) ((ConstantLong) c).getConstantValue(constant_pool));
      } else {
        throw new RuntimeException("Unrecognized constant of type " + c.getClass() + ": " + c);
      }
    }

    ClassGen gen = new ClassGen(jc);
    ConstantPoolGen pool = gen.getConstantPool();

    // Process the code in each method looking for literals
    for (Method m : jc.getMethods()) {
      MethodGen mg = new MethodGen(m, jc.getClassName(), pool);
      InstructionList il = mg.getInstructionList();
      if (il == null) {
        // System.out.println("No instructions for " + mg);
      } else {
        for (Instruction inst : il.getInstructions()) {
          switch (inst.getOpcode()) {

              // Compare two objects, no literals
            case Constants.IF_ACMPEQ:
            case Constants.IF_ACMPNE:
              break;

              // These instructions compare the integer on the top of the stack
              // to zero.  There are no literals here (except 0)
            case Constants.IFEQ:
            case Constants.IFNE:
            case Constants.IFLT:
            case Constants.IFGE:
            case Constants.IFGT:
            case Constants.IFLE:
              {
                break;
              }

              // Instanceof pushes either 0 or 1 on the stack depending on whether
              // the object on top of stack is of the specified type.
              // If were interested in class literals, this would be interesting
            case Constants.INSTANCEOF:
              break;

              // Duplicates the item on the top of stack.  No literal.
            case Constants.DUP:
              {
                break;
              }

              // Duplicates the item on the top of the stack and inserts it 2
              // values down in the stack.  No literals
            case Constants.DUP_X1:
              {
                break;
              }

              // Duplicates either the top 2 category 1 values or a single
              // category 2 value and inserts it 2 or 3 values down on the
              // stack.
            case Constants.DUP2_X1:
              {
                break;
              }

              // Duplicate either one category 2 value or two category 1 values.
            case Constants.DUP2:
              {
                break;
              }

              // Dup the category 1 value on the top of the stack and insert it either
              // two or three values down on the stack.
            case Constants.DUP_X2:
              {
                break;
              }

            case Constants.DUP2_X2:
              {
                break;
              }

              // Pop instructions discard the top of the stack.
            case Constants.POP:
              {
                break;
              }

              // Pops either the top 2 category 1 values or a single category 2 value
              // from the top of the stack.
            case Constants.POP2:
              {
                break;
              }

              // Swaps the two category 1 types on the top of the stack.
            case Constants.SWAP:
              {
                break;
              }

              // Compares two integers on the stack
            case Constants.IF_ICMPEQ:
            case Constants.IF_ICMPGE:
            case Constants.IF_ICMPGT:
            case Constants.IF_ICMPLE:
            case Constants.IF_ICMPLT:
            case Constants.IF_ICMPNE:
              {
                break;
              }

              // Get the value of a field
            case Constants.GETFIELD:
              {
                break;
              }

              // stores the top of stack into a field
            case Constants.PUTFIELD:
              {
                break;
              }

              // Pushes the value of a static field on the stack
            case Constants.GETSTATIC:
              {
                break;
              }

              // Pops a value off of the stack into a static field
            case Constants.PUTSTATIC:
              {
                break;
              }

              // pushes a local onto the stack
            case Constants.DLOAD:
            case Constants.DLOAD_0:
            case Constants.DLOAD_1:
            case Constants.DLOAD_2:
            case Constants.DLOAD_3:
            case Constants.FLOAD:
            case Constants.FLOAD_0:
            case Constants.FLOAD_1:
            case Constants.FLOAD_2:
            case Constants.FLOAD_3:
            case Constants.ILOAD:
            case Constants.ILOAD_0:
            case Constants.ILOAD_1:
            case Constants.ILOAD_2:
            case Constants.ILOAD_3:
            case Constants.LLOAD:
            case Constants.LLOAD_0:
            case Constants.LLOAD_1:
            case Constants.LLOAD_2:
            case Constants.LLOAD_3:
              {
                break;
              }

              // Pops a value off of the stack into a local
            case Constants.DSTORE:
            case Constants.DSTORE_0:
            case Constants.DSTORE_1:
            case Constants.DSTORE_2:
            case Constants.DSTORE_3:
            case Constants.FSTORE:
            case Constants.FSTORE_0:
            case Constants.FSTORE_1:
            case Constants.FSTORE_2:
            case Constants.FSTORE_3:
            case Constants.ISTORE:
            case Constants.ISTORE_0:
            case Constants.ISTORE_1:
            case Constants.ISTORE_2:
            case Constants.ISTORE_3:
            case Constants.LSTORE:
            case Constants.LSTORE_0:
            case Constants.LSTORE_1:
            case Constants.LSTORE_2:
            case Constants.LSTORE_3:
              {
                break;
              }

              // Push a value from the runtime constant pool.  We'll get these
              // values when processing the constant pool itself
            case Constants.LDC:
            case Constants.LDC_W:
            case Constants.LDC2_W:
              {
                break;
              }

              // Push the length of an array on the stack
            case Constants.ARRAYLENGTH:
              {
                break;
              }

              // Push small constants (-1..5) on the stack.  These literals are
              // too common to bother mentioning
            case Constants.DCONST_0:
            case Constants.DCONST_1:
            case Constants.FCONST_0:
            case Constants.FCONST_1:
            case Constants.FCONST_2:
            case Constants.ICONST_0:
            case Constants.ICONST_1:
            case Constants.ICONST_2:
            case Constants.ICONST_3:
            case Constants.ICONST_4:
            case Constants.ICONST_5:
            case Constants.ICONST_M1:
            case Constants.LCONST_0:
            case Constants.LCONST_1:
              {
                break;
              }

            case Constants.BIPUSH:
            case Constants.SIPUSH:
              {
                ConstantPushInstruction cpi = (ConstantPushInstruction) inst;
                result.ints.add((Integer) cpi.getValue());
                break;
              }

              // Primitive Binary operators.
            case Constants.DADD:
            case Constants.DCMPG:
            case Constants.DCMPL:
            case Constants.DDIV:
            case Constants.DMUL:
            case Constants.DREM:
            case Constants.DSUB:
            case Constants.FADD:
            case Constants.FCMPG:
            case Constants.FCMPL:
            case Constants.FDIV:
            case Constants.FMUL:
            case Constants.FREM:
            case Constants.FSUB:
            case Constants.IADD:
            case Constants.IAND:
            case Constants.IDIV:
            case Constants.IMUL:
            case Constants.IOR:
            case Constants.IREM:
            case Constants.ISHL:
            case Constants.ISHR:
            case Constants.ISUB:
            case Constants.IUSHR:
            case Constants.IXOR:
            case Constants.LADD:
            case Constants.LAND:
            case Constants.LCMP:
            case Constants.LDIV:
            case Constants.LMUL:
            case Constants.LOR:
            case Constants.LREM:
            case Constants.LSHL:
            case Constants.LSHR:
            case Constants.LSUB:
            case Constants.LUSHR:
            case Constants.LXOR:
              break;

            case Constants.LOOKUPSWITCH:
            case Constants.TABLESWITCH:
              break;

            case Constants.ANEWARRAY:
            case Constants.NEWARRAY:
              {
                break;
              }

            case Constants.MULTIANEWARRAY:
              {
                break;
              }

              // push the value at an index in an array
            case Constants.AALOAD:
            case Constants.BALOAD:
            case Constants.CALOAD:
            case Constants.DALOAD:
            case Constants.FALOAD:
            case Constants.IALOAD:
            case Constants.LALOAD:
            case Constants.SALOAD:
              {
                break;
              }

              // Pop the top of stack into an array location
            case Constants.AASTORE:
            case Constants.BASTORE:
            case Constants.CASTORE:
            case Constants.DASTORE:
            case Constants.FASTORE:
            case Constants.IASTORE:
            case Constants.LASTORE:
            case Constants.SASTORE:
              break;

            case Constants.ARETURN:
            case Constants.DRETURN:
            case Constants.FRETURN:
            case Constants.IRETURN:
            case Constants.LRETURN:
            case Constants.RETURN:
              {
                break;
              }

              // subroutine calls.
            case Constants.INVOKESTATIC:
            case Constants.INVOKEVIRTUAL:
            case Constants.INVOKESPECIAL:
            case Constants.INVOKEINTERFACE:
              break;

              // Throws an exception.
            case Constants.ATHROW:
              break;

              // Opcodes that don't need any modifications.  Here for reference
            case Constants.ACONST_NULL:
            case Constants.ALOAD:
            case Constants.ALOAD_0:
            case Constants.ALOAD_1:
            case Constants.ALOAD_2:
            case Constants.ALOAD_3:
            case Constants.ASTORE:
            case Constants.ASTORE_0:
            case Constants.ASTORE_1:
            case Constants.ASTORE_2:
            case Constants.ASTORE_3:
            case Constants.CHECKCAST:
            case Constants.D2F: // double to float
            case Constants.D2I: // double to integer
            case Constants.D2L: // double to long
            case Constants.DNEG: // Negate double on top of stack
            case Constants.F2D: // float to double
            case Constants.F2I: // float to integer
            case Constants.F2L: // float to long
            case Constants.FNEG: // Negate float on top of stack
            case Constants.GOTO:
            case Constants.GOTO_W:
            case Constants.I2B: // integer to byte
            case Constants.I2C: // integer to char
            case Constants.I2D: // integer to double
            case Constants.I2F: // integer to float
            case Constants.I2L: // integer to long
            case Constants.I2S: // integer to short
            case Constants.IFNONNULL:
            case Constants.IFNULL:
            case Constants.IINC: // increment local variable by a constant
            case Constants.INEG: // negate integer on top of stack
            case Constants.JSR: // pushes return address on the stack,
            case Constants.JSR_W:
            case Constants.L2D: // long to double
            case Constants.L2F: // long to float
            case Constants.L2I: // long to int
            case Constants.LNEG: // negate long on top of stack
            case Constants.MONITORENTER:
            case Constants.MONITOREXIT:
            case Constants.NEW:
            case Constants.NOP:
            case Constants.RET: // this is the internal JSR return
              break;

              // Make sure we didn't miss anything
            default:
              throw new Error("instruction " + inst + " unsupported");
          }
        }
      }
    }
    return result;
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
   * .cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
   */
  public void parse(final ClassNameAndSuperclassInfo.Builder cBuilder)
      throws InvalidClassFileFormatException {

    cBuilder.setCodeBaseEntry(codeBaseEntry);

    final TreeSet<ClassDescriptor> calledClassSet = new TreeSet<ClassDescriptor>();

    classReader.accept(
        new ClassVisitor() {

          boolean isInnerClass = false;

          public void visit(
              int version,
              int access,
              String name,
              String signature,
              String superName,
              String[] interfaces) {
            ClassParserUsingASM.this.slashedClassName = name;
            cBuilder.setClassfileVersion(version >>> 16, version & 0xffff);
            cBuilder.setAccessFlags(access);
            cBuilder.setClassDescriptor(DescriptorFactory.createClassDescriptor(name));
            cBuilder.setInterfaceDescriptorList(
                DescriptorFactory.createClassDescriptor(interfaces));
            if (superName != null)
              cBuilder.setSuperclassDescriptor(DescriptorFactory.createClassDescriptor(superName));
            if (cBuilder instanceof ClassInfo.Builder) {
              ((ClassInfo.Builder) cBuilder).setSourceSignature(signature);
            }
          }

          public org.objectweb.asm.AnnotationVisitor visitAnnotation(
              String desc, boolean isVisible) {
            if (cBuilder instanceof ClassInfo.Builder) {
              AnnotationValue value = new AnnotationValue(desc);
              ((ClassInfo.Builder) cBuilder).addAnnotation(desc, value);
              return value.getAnnotationVisitor();
            }
            return null;
          }

          public void visitAttribute(Attribute arg0) {
            // TODO Auto-generated method stub

          }

          public void visitEnd() {
            // TODO Auto-generated method stub

          }

          public FieldVisitor visitField(
              int access, String name, String desc, String signature, Object value) {
            if (name.equals("this$0")) isInnerClass = true;

            if (desc == null) throw new NullPointerException("Description cannot be null");
            if (cBuilder instanceof ClassInfo.Builder) {
              final ClassInfo.Builder cBuilder2 = (ClassInfo.Builder) cBuilder;
              if ((access & Opcodes.ACC_VOLATILE) != 0 || desc.contains("util/concurrent"))
                cBuilder2.setUsesConcurrency();
              final FieldInfo.Builder fBuilder =
                  new FieldInfo.Builder(slashedClassName, name, desc, access);
              fBuilder.setSourceSignature(signature);
              return new AbstractFieldAnnotationVisitor() {

                public org.objectweb.asm.AnnotationVisitor visitAnnotation(
                    final String desc, boolean visible) {
                  AnnotationValue value = new AnnotationValue(desc);
                  fBuilder.addAnnotation(desc, value);
                  return value.getAnnotationVisitor();
                }

                public void visitEnd() {
                  cBuilder2.addFieldDescriptor(fBuilder.build());
                }
              };
            }
            return null;
          }

          public void visitInnerClass(String name, String outerName, String innerName, int access) {
            if (name.equals(slashedClassName) && outerName != null) {
              if (cBuilder instanceof ClassInfo.Builder) {
                ClassDescriptor outerClassDescriptor =
                    DescriptorFactory.createClassDescriptor(outerName);
                ((ClassInfo.Builder) cBuilder).setImmediateEnclosingClass(outerClassDescriptor);
                ((ClassInfo.Builder) cBuilder).setAccessFlags(access);
              }
            }
          }

          public MethodVisitor visitMethod(
              final int access,
              final String methodName,
              final String methodDesc,
              String signature,
              String[] exceptions) {
            if (cBuilder instanceof ClassInfo.Builder) {
              final MethodInfo.Builder mBuilder =
                  new MethodInfo.Builder(slashedClassName, methodName, methodDesc, access);
              mBuilder.setSourceSignature(signature);
              mBuilder.setThrownExceptions(exceptions);
              if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) mBuilder.setUsesConcurrency();

              return new AbstractMethodVisitor() {

                int variable;

                boolean sawReturn = (access & Opcodes.ACC_NATIVE) != 0;

                boolean sawNormalThrow = false;

                boolean sawUnsupportedThrow = false;

                boolean sawSystemExit = false;

                boolean sawBranch = false;

                boolean sawBackBranch = false;

                int methodCallCount = 0;

                boolean sawStubThrow = false;

                boolean justSawInitializationOfUnsupportedOperationException;

                boolean isBridge =
                    (access & Opcodes.ACC_SYNTHETIC) != 0 && (access & Opcodes.ACC_BRIDGE) != 0;

                String bridgedMethodSignature;

                State state = State.INITIAL;

                StubState stubState = StubState.INITIAL;

                boolean isAccessMethod = methodName.startsWith("access$");

                String accessOwner, accessName, accessDesc;

                boolean accessIsStatic;

                HashSet<Label> labelsSeen = new HashSet<Label>();

                @Override
                public void visitLdcInsn(Object cst) {
                  if (cst.equals("Stub!")) stubState = StubState.LOADED_STUB;
                  else stubState = StubState.INITIAL;
                }

                @Override
                public void visitInsn(int opcode) {
                  if (opcode == Opcodes.MONITORENTER) mBuilder.setUsesConcurrency();
                  if (RETURN_OPCODE_SET.get(opcode)) sawReturn = true;
                  else if (opcode == Opcodes.ATHROW) {
                    if (stubState == StubState.INITIALIZE_RUNTIME) {
                      sawStubThrow = true;
                    } else if (justSawInitializationOfUnsupportedOperationException)
                      sawUnsupportedThrow = true;
                    else sawNormalThrow = true;
                  }
                  resetState();
                }

                public void resetState() {
                  if (state != State.AFTER_METHOD_CALL) state = State.INITIAL;
                  stubState = StubState.INITIAL;
                }

                @Override
                public void visitSomeInsn() {
                  resetState();
                }

                @Override
                public void visitVarInsn(int opcode, int var) {
                  if (opcode == Opcodes.ALOAD && var == 0) state = State.THIS_LOADED;
                  else if (state == State.THIS_LOADED)
                    switch (opcode) {
                      case Opcodes.ALOAD:
                      case Opcodes.ILOAD:
                      case Opcodes.LLOAD:
                      case Opcodes.DLOAD:
                      case Opcodes.FLOAD:
                        state = State.VARIABLE_LOADED;
                        variable = var;
                    }
                  else visitSomeInsn();
                }

                public org.objectweb.asm.AnnotationVisitor visitAnnotation(
                    final String desc, boolean visible) {
                  AnnotationValue value = new AnnotationValue(desc);
                  mBuilder.addAnnotation(desc, value);
                  return value.getAnnotationVisitor();
                }

                @Override
                public void visitMethodInsn(int opcode, String owner, String name, String desc) {
                  methodCallCount++;
                  if (isAccessMethod && methodCallCount == 1) {
                    this.accessOwner = owner;
                    this.accessName = name;
                    this.accessDesc = desc;
                    this.accessIsStatic = opcode == Opcodes.INVOKESTATIC;
                  }
                  if (stubState == StubState.LOADED_STUB
                      && opcode == Opcodes.INVOKESPECIAL
                      && owner.equals("java/lang/RuntimeException")
                      && name.equals("<init>")) stubState = StubState.INITIALIZE_RUNTIME;
                  else stubState = StubState.INITIAL;
                  if (owner.startsWith("java/util/concurrent")) mBuilder.setUsesConcurrency();
                  if (opcode == Opcodes.INVOKEINTERFACE) return;

                  if (owner.charAt(0) == '[' && owner.charAt(owner.length() - 1) != ';') {
                    // primitive array
                    return;
                  }
                  if (opcode == Opcodes.INVOKESTATIC
                      && owner.equals("java/lang/System")
                      && name.equals("exit")
                      && !sawReturn) sawSystemExit = true;
                  justSawInitializationOfUnsupportedOperationException =
                      opcode == Opcodes.INVOKESPECIAL
                          && owner.equals("java/lang/UnsupportedOperationException")
                          && name.equals("<init>");

                  if (isBridge && bridgedMethodSignature == null)
                    switch (opcode) {
                      case Opcodes.INVOKEVIRTUAL:
                      case Opcodes.INVOKESPECIAL:
                      case Opcodes.INVOKESTATIC:
                      case Opcodes.INVOKEINTERFACE:
                        if (desc != null && name.equals(methodName)) bridgedMethodSignature = desc;
                    }

                  // System.out.println("Call from " +
                  // ClassParserUsingASM.this.slashedClassName +
                  // " to " + owner + " : " + desc);
                  if (desc == null || desc.indexOf('[') == -1 && desc.indexOf('L') == -1) return;
                  if (ClassParserUsingASM.this.slashedClassName.equals(owner)) return;
                  ClassDescriptor classDescriptor =
                      DescriptorFactory.instance().getClassDescriptor(owner);
                  calledClassSet.add(classDescriptor);
                  // System.out.println("Added call from " +
                  // ClassParserUsingASM.this.slashedClassName +
                  // " to " + owner);
                  state = State.AFTER_METHOD_CALL;
                }

                @Override
                public void visitJumpInsn(int opcode, Label label) {
                  sawBranch = true;
                  if (labelsSeen.contains(label)) sawBackBranch = true;
                  super.visitJumpInsn(opcode, label);
                }

                @Override
                public void visitLabel(Label label) {
                  labelsSeen.add(label);
                  super.visitLabel(label);
                }

                public void visitEnd() {
                  labelsSeen.clear();
                  if (isAccessMethod && methodCallCount == 1) {
                    mBuilder.setAccessMethodFor(
                        accessOwner, accessName, accessDesc, accessIsStatic);
                  }
                  if (sawBackBranch) mBuilder.setHasBackBranch();
                  boolean sawThrow = sawNormalThrow | sawUnsupportedThrow | sawStubThrow;
                  if (sawThrow && !sawReturn || sawSystemExit && !sawBranch) {

                    mBuilder.setIsUnconditionalThrower();
                    if (!sawReturn && !sawNormalThrow) {
                      if (sawUnsupportedThrow) mBuilder.setUnsupported();
                      if (sawStubThrow) {
                        mBuilder.addAccessFlags(Constants.ACC_SYNTHETIC);
                        mBuilder.setIsStub();
                      }
                    }
                    // else
                    // System.out.println(slashedClassName+"."+methodName+methodDesc
                    // + " is thrower");
                  }
                  mBuilder.setNumberMethodCalls(methodCallCount);
                  MethodInfo methodInfo = mBuilder.build();
                  Builder classBuilder = (ClassInfo.Builder) cBuilder;
                  if (isBridge
                      && bridgedMethodSignature != null
                      && !bridgedMethodSignature.equals(methodDesc))
                    classBuilder.addBridgeMethodDescriptor(methodInfo, bridgedMethodSignature);
                  else classBuilder.addMethodDescriptor(methodInfo);

                  if (methodInfo.usesConcurrency()) classBuilder.setUsesConcurrency();
                  if (methodInfo.isStub()) classBuilder.setHasStubs();
                }

                public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(
                    int parameter, String desc, boolean visible) {
                  AnnotationValue value = new AnnotationValue(desc);
                  mBuilder.addParameterAnnotation(parameter, desc, value);
                  return value.getAnnotationVisitor();
                }
              };
            }
            return null;
          }

          public void visitOuterClass(String owner, String name, String desc) {}

          public void visitSource(String arg0, String arg1) {
            if (cBuilder instanceof ClassInfo.Builder) {
              ((ClassInfo.Builder) cBuilder).setSource(arg0);
            }
          }
        },
        ClassReader.SKIP_FRAMES);
    HashSet<ClassDescriptor> referencedClassSet = new HashSet<ClassDescriptor>();

    // collect class references

    int constantPoolCount = classReader.readUnsignedShort(8);
    int offset = 10;
    char[] buf = new char[1024];
    // System.out.println("constant pool count: " + constantPoolCount);
    for (int count = 1; count < constantPoolCount; count++) {
      int tag = classReader.readByte(offset);

      int size;
      switch (tag) {
        case Constants.CONSTANT_Methodref:
        case Constants.CONSTANT_InterfaceMethodref:
        case Constants.CONSTANT_Fieldref:
        case Constants.CONSTANT_Integer:
        case Constants.CONSTANT_Float:
        case Constants.CONSTANT_NameAndType:
          size = 5;
          break;
        case Constants.CONSTANT_Long:
        case Constants.CONSTANT_Double:
          size = 9;
          count++;
          break;
        case Constants.CONSTANT_Utf8:
          size = 3 + classReader.readUnsignedShort(offset + 1);
          break;
        case Constants.CONSTANT_Class:
          @SlashedClassName String className = classReader.readUTF8(offset + 1, buf);
          if (className.indexOf('[') >= 0) {
            ClassParser.extractReferencedClassesFromSignature(referencedClassSet, className);
          } else if (ClassName.isValidClassName(className)) {
            ClassDescriptor classDescriptor =
                DescriptorFactory.instance().getClassDescriptor(className);
            referencedClassSet.add(classDescriptor);
          }
          size = 3;
          break;
          // case ClassWriter.CLASS:
          // case ClassWriter.STR:
        case Constants.CONSTANT_String:
          size = 3;
          break;
        default:
          throw new IllegalStateException(
              "Unexpected tag of "
                  + tag
                  + " at offset "
                  + offset
                  + " while parsing "
                  + slashedClassName
                  + " from "
                  + codeBaseEntry);
      }
      // System.out.println(count + "@" + offset + " : [" + tag
      // +"] size="+size);
      offset += size;
    }
    cBuilder.setCalledClassDescriptors(calledClassSet);
    cBuilder.setReferencedClassDescriptors(referencedClassSet);
  }