/**
   * Checks a given class
   *
   * @param cr a <code>ClassReader</code> that contains bytecode for the analysis.
   * @param loader a <code>ClassLoader</code> which will be used to load referenced classes. This is
   *     useful if you are verifiying multiple interdependent classes.
   * @param dump true if bytecode should be printed out not only when errors are found.
   * @param pw write where results going to be printed
   */
  public static void verify(
      final ClassReader cr, final ClassLoader loader, final boolean dump, final PrintWriter pw) {
    ClassNode cn = new ClassNode();
    cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);

    Type syperType = cn.superName == null ? null : Type.getObjectType(cn.superName);
    List methods = cn.methods;

    List interfaces = new ArrayList();
    for (Iterator i = cn.interfaces.iterator(); i.hasNext(); ) {
      interfaces.add(Type.getObjectType(i.next().toString()));
    }

    for (int i = 0; i < methods.size(); ++i) {
      MethodNode method = (MethodNode) methods.get(i);
      SimpleVerifier verifier =
          new SimpleVerifier(Type.getObjectType(cn.name), syperType, interfaces, false);
      Analyzer a = new Analyzer(verifier);
      if (loader != null) {
        verifier.setClassLoader(loader);
      }
      try {
        a.analyze(cn.name, method);
        if (!dump) {
          continue;
        }
      } catch (Exception e) {
        e.printStackTrace(pw);
      }
      printAnalyzerResult(method, a, pw);
    }
    pw.flush();
  }
  static void printAnalyzerResult(MethodNode method, Analyzer a, final PrintWriter pw) {
    Frame[] frames = a.getFrames();
    TraceMethodVisitor mv = new TraceMethodVisitor();

    pw.println(method.name + method.desc);
    for (int j = 0; j < method.instructions.size(); ++j) {
      method.instructions.get(j).accept(mv);

      StringBuffer s = new StringBuffer();
      Frame f = frames[j];
      if (f == null) {
        s.append('?');
      } else {
        for (int k = 0; k < f.getLocals(); ++k) {
          s.append(getShortName(f.getLocal(k).toString())).append(' ');
        }
        s.append(" : ");
        for (int k = 0; k < f.getStackSize(); ++k) {
          s.append(getShortName(f.getStack(k).toString())).append(' ');
        }
      }
      while (s.length() < method.maxStack + method.maxLocals + 1) {
        s.append(' ');
      }
      pw.print(Integer.toString(j + 100000).substring(1));
      pw.print(" " + s + " : " + mv.buf); // mv.text.get(j));
    }
    for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
      ((TryCatchBlockNode) method.tryCatchBlocks.get(j)).accept(mv);
      pw.print(" " + mv.buf);
    }
    pw.println();
  }