Package net.sourceforge.retroweaver

Source Code of net.sourceforge.retroweaver.RefVerifier

package net.sourceforge.retroweaver;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import net.sourceforge.retroweaver.event.VerifierListener;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;


/**
* Reads through a class file searching for references to classes, methods, or
* fields, which don't exist on the specified classpath. This is primarily
* useful when trying to target one JDK while using the compiler for another.
*/
public class RefVerifier extends ClassAdapter {

  private final int target;

  private String currentclassName;

  private final RetroWeaverClassLoader classLoader;

  private final List<String> classPathArray;

  private Set<String> failedClasses;

  private final VerifierListener listener;

  private int warningCount;

  private final List<String> classes;

  private final Map<String, SoftReference<ClassReader>> classReaderCache = new HashMap<String, SoftReference<ClassReader>>();

  private static final String nl = System.getProperty("line.separator");

  public RefVerifier(int target, ClassVisitor cv, List<String> classPathArray, VerifierListener listener) {
    super(cv);
    classLoader = new RetroWeaverClassLoader();
    this.classPathArray = classPathArray;

    this.listener = listener;
    this.target = target;

    classes = new LinkedList<String>();
  }

  public void addClass(String className) {
    classes.add(className);
  }

  public void verifyJarFile(String jarFileName) throws IOException {
    JarFile jarFile = new JarFile(jarFileName);

    int count = classes.size();
    if (count > 0) {
      listener.verifyPathStarted("Verifying " + count + (count == 1?" class":" classes"));
    }
    classLoader.setClassPath(classPathArray);

    for (String name : classes) {
      JarEntry entry = jarFile.getJarEntry(name);
      InputStream is = jarFile.getInputStream(entry);
      verifyClass(is);
    }
  }

  public void verifyFiles() throws IOException {
    int count = classes.size();
    if (count > 0) {
      listener.verifyPathStarted("Verifying " + count + (count == 1?" class":" classes"));
    }
    classLoader.setClassPath(classPathArray);

    for (String sourcePath : classes) {
      verifyClass(new FileInputStream(sourcePath));     
    }
  }

  private void verifySingleClass(String classFileName) throws IOException {
    classLoader.setClassPath(classPathArray);

    verifyClass(new FileInputStream(classFileName));
  }

  private void verifyClass(InputStream sourceStream)
      throws IOException {

    failedClasses = new HashSet<String>();

        ClassReader cr = new ClassReader(sourceStream);
        cr.accept(this, 0);
  }

  private void unknowClassWarning(String className, String msg) {
    StringBuffer report = new StringBuffer().append(currentclassName)
      .append(": unknown class ").append(className);

    if (msg != null) {
      report.append(": ").append(msg);
    }

    warning(report);
  }

  private void unknownFieldWarning(String name, String desc, String msg) {
    StringBuffer report = new StringBuffer().append(currentclassName)
      .append(": unknown field ").append(name).append('/').append(desc.replace('/', '.'));

    if (msg != null) {
      report.append(", ").append(msg);
    }

    warning(report);
  }

  private void unknownMethodWarning(String name, String desc, String msg) {
    StringBuffer report = new StringBuffer().append(currentclassName)
      .append(": unknown method ").append(name).append('/').append(desc.replace('/', '.'));

    if (msg != null) {
      report.append(", ").append(msg);
    }

    warning(report);
  }

  private void invalidClassVersion(String className, int target, int version) {
    StringBuffer report = new StringBuffer().append(className)
      .append(": invalid class version ").append(version).append(", target is ").append(target);

    warning(report);
  }

  private void warning(StringBuffer report) {
    warningCount++;
    listener.acceptWarning(report.toString());
  }

  public void displaySummary() {
    if (warningCount != 0) {
      listener.displaySummary(warningCount);
    }
  }

  private ClassReader getClassReader(String className) throws ClassNotFoundException {
    ClassReader reader = null;
    SoftReference<ClassReader> ref = classReaderCache.get(className);
    if (ref != null) {
      reader = ref.get();
    }

    if (reader == null) {
      byte b[] = classLoader.getClassData(className);

      reader = new ClassReader(b);

      classReaderCache.put(className, new SoftReference<ClassReader>(reader));

      // class file version should not be higher than target
      int version = reader.readShort(6); // get major number only
      if (version > target) {
        invalidClassVersion(className.replace('/', '.'), target, version);
      }
    }
    return reader;   
  }

  public static String getUsage() {
    return "Usage: RefVerifier <options>" + nl + " Options: " + nl
        + " -class <path to class to verify> (required) " + nl
        + " -cp <classpath containing valid classes> (required)";
  }

  public static void main(String[] args) throws IOException {

    List<String> classpath = new ArrayList<String>();
    String classfile = null;

    for (int i = 0; i < args.length; ++i) {
      String command = args[i];
      ++i;

      if ("-class".equals(command)) {
        classfile = args[i];
      } else if ("-cp".equals(command)) {
        String path = args[i];
        StringTokenizer st = new StringTokenizer(path,
            File.pathSeparator);
        while (st.hasMoreTokens()) {
          classpath.add(st.nextToken());
        }
      } else {
        System.out.println("I don't understand the command: " + command); // NOPMD by xlv
        System.out.println(); // NOPMD by xlv
        System.out.println(getUsage()); // NOPMD by xlv
        return;
      }
    }

    if (classfile == null) {
      System.out.println("Option \"-class\" is required."); // NOPMD by xlv
      System.out.println(); // NOPMD by xlv
      System.out.println(getUsage()); // NOPMD by xlv
      return;
    }

    RefVerifier vr = new RefVerifier(Weaver.VERSION_1_4, EMPTY_VISITOR, classpath,
        new DefaultListener(true));
    vr.verifySingleClass(classfile);
    vr.displaySummary();
  }
 
  private void checkClassName(String className) {
    Type t = Type.getType(className);
    String name;

    switch (t.getSort()) {
    case Type.ARRAY:
      t = t.getElementType();
      if (t.getSort() != Type.OBJECT) {
        return;
      }

      // fall through to object processing
    case Type.OBJECT:
      name = t.getClassName();
      break;
    default:
      return;
    }
   
    checkSimpleClassName(name);
  }

  private void checkClassNameInType(String className) {
    switch (className.charAt(0)) {
      case 'L':
      case '[':
        checkClassName(className);
        break;
      default:
        checkSimpleClassName(className);
    }
  }

  private void checkSimpleClassName(String className) {
    String name = className.replace('.', '/');
    try {
      getClassReader(name);
    } catch (ClassNotFoundException e) {
      failedClasses.add(name);
      unknowClassWarning(name.replace('/', '.'), null);
    }
  }

  // visitor methods

    public void visit(
        final int version,
        final int access,
        final String name,
        final String signature,
        final String superName,
        final String[] interfaces)
    {
    listener.verifyClassStarted("Verifying " + name);

    currentclassName = name.replace('/', '.');

    if (superName != null) {
      checkSimpleClassName(superName);
    }
    if (interfaces != null) {
      for (int i = 0; i < interfaces.length; ++i) {
        checkSimpleClassName(interfaces[i]);
      }
    }

    cv.visit(version, access, name, signature, superName, interfaces);
    }

    public void visitOuterClass(
        final String owner,
        final String name,
        final String desc)
    {
      checkSimpleClassName(owner);

      cv.visitOuterClass(owner, name, desc);
    }

    public void visitInnerClass(
        final String name,
        final String outerName,
        final String innerName,
        final int access)
    {
        if (name != null) {
          checkSimpleClassName(name);
        }
        if (outerName != null) {
          checkSimpleClassName(outerName);
        }
       
        cv.visitInnerClass(name, outerName, innerName, access);
    }

    public MethodVisitor visitMethod(
        final int access,
        final String name,
        final String desc,
        final String signature,
        final String[] exceptions)
    {
        if (exceptions != null) {
            for (String s: exceptions) {
              checkSimpleClassName(s);
            }
        }
   
        return new MethodVerifier(cv.visitMethod(access, name, desc, signature, exceptions));
    }

    private class MethodVerifier extends MethodAdapter {

      MethodVerifier(MethodVisitor mv) {
        super(mv);
      }
       
      public void visitTypeInsn(int opcode, String desc) {
        checkClassNameInType(desc);
       
        mv.visitTypeInsn(opcode, desc);
      }
      public void visitFieldInsn(int opcode, String owner, String name, String desc) {
      // Don't report a field error, about a class for which we've
      // already shown an error
      if (!failedClasses.contains(owner)) {
        try {
          if (!findField(owner, name, desc)) {
            unknownFieldWarning(name,desc, "Field not found in " + owner.replace('/', '.'));
          }
        } catch (ClassNotFoundException e) {
            unknownFieldWarning(name,desc, "The class, " + owner.replace('/', '.')
            + ", could not be located: " + e.getMessage());
        }
      }
      mv.visitFieldInsn(opcode, owner, name, desc);
      }
     
      public void visitMethodInsn(int opcode, String owner, String name, String desc) {
      if (!failedClasses.contains(owner) && owner.charAt(0) != '[') {
        // Don't report a method error, about a class for which we've
        // already shown an error.
        // We just ignore methods called on arrays, because we know
        // they must exist      

        try {
          if (!findMethod(owner, name, desc)) {
            unknownMethodWarning(name, desc, "Method not found in " + owner.replace('/', '.'));           
          }
        } catch (ClassNotFoundException e) {
          unknownMethodWarning(name, desc, "The class, " + owner.replace('/', '.')
              + ", could not be located: " + e.getMessage());
        }
      }

      mv.visitMethodInsn(opcode, owner, name, desc);
      }

      public void visitMultiANewArrayInsn(String desc, int dims) {
        checkClassName(desc);
       
        mv.visitMultiANewArrayInsn(desc, dims);
      }

      public void visitLocalVariable(
              String name,
              String desc,
              String signature,
              Label start,
              Label end,
              int index) {
        checkClassName(desc);
       
        mv.visitLocalVariable(name, desc, signature, start, end, index);
      }

    }

  private boolean findField(String owner, final String name, final String c) throws ClassNotFoundException {
    String javaClassName = owner;
    while (true) {
      ClassReader reader = getClassReader(javaClassName);
      FindFieldOrMethodClassVisitor visitor = new FindFieldOrMethodClassVisitor(false, name, c);
   
      try {
        reader.accept(visitor, 0);
      } catch (Success s) {
        return true;
      }
      String[] is = visitor.classInterfaces;
      for (String i : is) {
        if (findField(i, name, c)) {
          return true;
        }
      }

      if ("java/lang/Object".equals(javaClassName)) {
        return false;
      }
      javaClassName = visitor.superClassName;
    }
  }

  private boolean findMethod(final String owner, final String name, final String desc) throws ClassNotFoundException {
    String javaClassName = owner;
    while (true) {
      ClassReader reader = getClassReader(javaClassName);
      FindFieldOrMethodClassVisitor visitor = new FindFieldOrMethodClassVisitor(true, name, desc);
      try {
        reader.accept(visitor, 0);
      } catch (Success s) {
        return true;
      }

      if (visitor.isInterface || visitor.isAbstract) {
        String[] is = visitor.classInterfaces;
        for (String i : is) {
          if (findMethod(i, name, desc)) {
            return true;
          }
        }
        if (visitor.isInterface) {
          return false;
        }
      }

      if ("java/lang/Object".equals(javaClassName)) {
        return false;
      }
      javaClassName = visitor.superClassName;
    }
  }

  private static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();

  private static class Success extends RuntimeException {};

  // Visitor to search for fields or methods in supplier classes

  private static class FindFieldOrMethodClassVisitor implements ClassVisitor {
    FindFieldOrMethodClassVisitor(boolean methdodMatcher, final String name, final String desc) {
      this.searchedName = name;
      this.searchedDesc = desc;
      this.methdodMatcher = methdodMatcher;
    }
    private final boolean methdodMatcher;
    private final String searchedName;
    private final String searchedDesc;

    protected String classInterfaces[];
    protected String superClassName;
    protected boolean isInterface;
    protected boolean isAbstract;

      public void visit(
              final int version,
              final int access,
              final String name,
              final String signature,
              final String superName,
              final String[] interfaces)
          {
          classInterfaces = interfaces;
          superClassName = superName;
          isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
          isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
          }

      public void visitSource(final String source, final String debug) {
      }

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

      public AnnotationVisitor visitAnnotation(
          final String desc,
          final boolean visible)
      {
          return EMPTY_VISITOR;
      }

      public void visitAttribute(final Attribute attr) {
      }

      public void visitInnerClass(
          final String name,
          final String outerName,
          final String innerName,
          final int access)
      {
      }

      public FieldVisitor visitField(
              final int access,
              final String name,
              final String desc,
              final String signature,
              final Object value) {
        if (!methdodMatcher && name.equals(searchedName) && desc.equals(searchedDesc)) {
           throw new Success();
        }
          return null;
      }

    public MethodVisitor visitMethod(
              int access,
              String name,
              String desc,
              String signature,
              String[] exceptions) {
      if (methdodMatcher && name.equals(searchedName) && desc.equals(searchedDesc)) {
        throw new Success();
      }
          return null;
     }

      public void visitEnd() {
      }
  }

  public static class DefaultListener implements VerifierListener {
 
    private final boolean verbose;
 
    DefaultListener(boolean verbose) {
      this.verbose = verbose;
    }
 
    public void verifyPathStarted(String msg) {
      System.out.println("[RefVerifier] " + msg); // NOPMD by xlv
    }
 
    public void verifyClassStarted(String msg) {
      if (verbose) {
        System.out.println("[RefVerifier] " + msg); // NOPMD by xlv
      }
    }
 
    public void acceptWarning(String msg) {
      System.out.println("[RefVerifier] " + msg); // NOPMD by xlv
    }
 
    public void displaySummary(int warningCount) {
      System.out.println("[RefVerifier] Verification complete, " + warningCount + " warning(s)."); // NOPMD by xlv
    }

  }

}
TOP

Related Classes of net.sourceforge.retroweaver.RefVerifier

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.