Package org.jboss.ant.tasks.retrocheck

Source Code of org.jboss.ant.tasks.retrocheck.Checker

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ant.tasks.retrocheck;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;

/**
* takes jar or class files and checks the weave the bytecode
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
* @version $Revision: 58 $
*/
public class Checker
{
   private static final String INIT = "<init>";
   private static final String CLINIT = "<clinit>";
  
   private FileFilter classFileFilter = new FileFilter()
   {
      public boolean accept(File pathname)
      {
         return pathname.getName().endsWith(".class");
      }
   };

   private FileFilter directoryFilter = new FileFilter()
   {
      public boolean accept(File pathname)
      {
         return pathname.isDirectory();
      }
   };

   public boolean verbose = false;

   public boolean suppress = true;

   public boolean isJarFile(File src)
   {
      boolean isJarFile = false;
      if( src.isFile() )
      {
         String name = src.getName().toLowerCase();
         isJarFile = name.endsWith(".jar") || name.endsWith(".zip");
      }
      return isJarFile;
   }

   public static void main(String[] args) throws Exception
   {
      long start = System.currentTimeMillis();
      Checker checker = new Checker();
      try
      {
         checker.check(args);
      }
      catch (Exception e)
      {
         throw e;
      }
      System.out.println("Check Successful: " + (System.currentTimeMillis() - start) + " ms");
   }

   public void usage()
   {
      System.err.println("Usage: RetroCheck [-cp <classpath>] [-classpath <classpath>] [-verbose] <dir>+");
   }

   // Make public and static so that transformers can locate it to do work
   // transformers may generate class files and they need to determine
   // file locations and such.  This will also be used as a flag to tell
   // transformers whether they are in compile or load-time mode.
   public static URLClassLoader loader;

   public void check(String[] args) throws Exception
   {
      if (args.length == 0)
      {
         usage();
         System.exit(1);
         return;
      }
      ArrayList<URL> paths = new ArrayList<URL>();
      ArrayList<File> files = new ArrayList<File>();
      for (int i = 0; i < args.length; i++)
      {
         if (args[i].equals("-verbose"))
         {
            verbose = true;
            continue;
         }
         else if (args[i].equals("-suppress"))
         {
            suppress = true;
            continue;
         }
         else if (args[i].equals("-cp") || args[i].equals("-classpath"))
         {
            if (i + 1 > args.length - 1)
            {
               usage();
               System.exit(1);
               return;
            }
            ++i;
            StringTokenizer tokenizer = new StringTokenizer(args[i], File.pathSeparator);
            while (tokenizer.hasMoreTokens())
            {
               String cpath = tokenizer.nextToken();
               File f = new File(cpath);
               paths.add(f.toURL());
            }
            continue;
         }
         else if (args[i].equals("--SOURCEPATH"))
         {
            addFilesFromSourcePathFile(files, args[++i]);
            continue;
         }
         File f = new File(args[i]).getCanonicalFile();
         files.add(f);
      }

      URL[] urls = paths.toArray(new URL[paths.size()]);
      loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());

      Thread.currentThread().setContextClassLoader(loader);

      String classpath = System.getProperty("java.class.path", ".");
      String pathSeparator = System.getProperty("path.separator");
      StringTokenizer st = new StringTokenizer(classpath, pathSeparator);
      paths.clear();
      while (st.hasMoreTokens())
      {
         String cpath = st.nextToken();
         File f = new File(cpath);
         paths.add(f.toURL());
      }
      urls = paths.toArray(new URL[paths.size()]);
      ClassLoader poolLoader = new URLClassLoader(urls, EmptyClassLoader.EMPTY);
     
      ClassPool pool = new ClassPool(false);
      pool.appendClassPath(new CheckerClassPath(poolLoader));
      pool.appendClassPath(new ClassClassPath(Object.class));
     
      //Add all the classes to compile
      for (File f : files)
      {
         if (f.isDirectory())
         {
            addDirectory(f, pool);
         }
         else if (classFileFilter.accept(f))
         {
            addFile(f, pool);
         }
         else
         {
            if (verbose)
               System.out.println("[retrocheck] " + f + " is neither a java class or a directory");
         }
      }

      int failed = 0;
     
      //Check each class
      for (CheckClassInfo info : classesToCheck.values())
         failed += checkFile(info);
     
      if (failed > 0)
      {
         System.out.println("Total errors/warnings: " + failed);
         System.exit(1);
      }
   }

   private HashMap<String, CheckClassInfo> classesToCheck = new HashMap<String, CheckClassInfo>();

   private void addDirectory(File dir, ClassPool pool) throws Exception
   {
      File[] classFiles = dir.listFiles(classFileFilter);
      for (File classFile : classFiles)
         addFile(classFile, pool);
      File[] directories = dir.listFiles(directoryFilter);
      for (File directory : directories)
         addDirectory(directory, pool);
   }

   private void addFile(File file, ClassPool pool) throws Exception
   {
      ClassFile cf = createClassFile(file);
      CtClass clazz = pool.get(cf.getName());
      String className = cf.getName();
      String srcRoot = file.getCanonicalPath();
      srcRoot = srcRoot.substring(0, srcRoot.length() - className.length() - 6);
      CheckClassInfo info = new CheckClassInfo(file, srcRoot, clazz);
      classesToCheck.put(className, info);
   }

   private ClassFile createClassFile(final File file) throws Exception
   {
      DataInputStream is = new DataInputStream(new FileInputStream(file));
      ClassFile cf = new ClassFile(is);
      is.close();
      return cf;
   }

   private void addFilesFromSourcePathFile(ArrayList<File> files, String sourcePathFile)
   {
      BufferedReader reader = null;

      try
      {
         reader = new BufferedReader(new FileReader(new File(sourcePathFile).getCanonicalFile()));

         String fileName = reader.readLine();
         while (fileName != null)
         {
            files.add(new File(fileName).getCanonicalFile());
            fileName = reader.readLine();
         }
      }
      catch (Exception e)
      {
         try
         {
            reader.close();
         }
         catch (IOException e1)
         {
         }
         throw new RuntimeException(e);
      }
   }

   public int checkFile(CheckClassInfo info) throws Exception
   {
      if (info.isCompiled())
         return 0;

      if( verbose )
        System.out.println("[checkFile] " + info.getClassName());
      URL classUrl = loader.getResource(info.getClassName().replace('.', '/') + ".class");
      if (classUrl == null)
      {
         System.out.println("[warning] Unable to find " + info.getFile()
               + " within classpath.  Make sure all transforming classes are within classpath.");
         return 1;
      }

      File classUrlFile = new File(URLDecoder.decode(classUrl.getFile(), "UTF-8"));
      File infoFile = new File(URLDecoder.decode(info.getFile().toString(), "UTF-8"));

      if (classUrlFile.equals(infoFile) == false)
      {
         System.out.println("[warning] Trying to compile " + info.getFile() + " and found it also within "
               + classUrl.getFile() + " will not proceed. ");
         return 1;
      }
      int failed = doCheck(loader, info);
      info.setCompiled(true);
      if (verbose)
      {
         if (failed == 0)
            System.out.println("[ok] " + info.getClassName());
      }
      return failed;
   }

   public int doCheck(ClassLoader cl, CheckClassInfo info)
   {
      CtClass clazz = info.getClazz();
      ClassFile file = clazz.getClassFile();
      ClassPool pool = clazz.getClassPool();
     
      int failed = 0;
     
      // Incorrect major version
      int major = file.getMajorVersion();
      if (major > 48)
      {
         if (failed == 0)
            System.out.println("==== " + info.getFile());
         System.out.println("Wrong major version " + major);
         ++failed;
      }

      // Check the super class exists
      String superClassName = file.getSuperclass();
      try
      {
         pool.get(superClassName);
      }
      catch (NotFoundException e)
      {
         if (failed == 0)
            System.out.println("==== " + info.getFile());
         System.out.println("SuperClass not found " + superClassName);
         ++failed;
      }
     
      // Check the interfaces exist
      String[] intfs = file.getInterfaces();
      for (String intf : intfs)
      {
         try
         {
            pool.get(intf);
         }
         catch (NotFoundException e)
         {
            if (failed == 0)
               System.out.println("==== " + info.getFile());
            System.out.println("Interface not found " + intf);
            ++failed;
         }
      }

      // Check the field types
      List<FieldInfo> fields = (List<FieldInfo>) file.getFields();
      for (FieldInfo field : fields)
      {
         String name = field.getName();
         String typeName = Descriptor.toJavaName(field.getDescriptor());
         try
         {
            CtField ctField = clazz.getField(name);
            ctField.getType();
         }
         catch (NotFoundException e)
         {
            if (failed == 0)
               System.out.println("==== " + info.getFile());
            System.out.println("Class not found " + typeName + " for field " + name);
            ++failed;
         }
      }

      // Check the method types
      List<MethodInfo> methods = (List<MethodInfo>) file.getMethods();
      for (MethodInfo method : methods)
      {
         String name = method.getName();
         String descriptor = method.getDescriptor();
        
         if (CLINIT.equals(name))
            continue;
        
         try
         {
            if (INIT.equals(name) == false)
            {
               CtMethod ctMethod = clazz.getMethod(name, descriptor);
               ctMethod.getReturnType();
            }
         }
         catch (NotFoundException e)
         {
            if (failed == 0)
               System.out.println("==== " + info.getFile());
            System.out.println("Return type not found for method " + name + "." + descriptor);
            ++failed;
         }
        
         if (INIT.equals(name))
         {
            try
            {
               CtConstructor ctConstructor = clazz.getConstructor(descriptor);
               ctConstructor.getParameterTypes();
            }
            catch (NotFoundException e)
            {
               if (failed == 0)
                  System.out.println("==== " + info.getFile());
               System.out.println("Cannot find constructor parameter types " + name + "." + descriptor);
               ++failed;
            }
            try
            {
               CtConstructor ctConstructor = clazz.getConstructor(descriptor);
               ctConstructor.getExceptionTypes();
            }
            catch (NotFoundException e)
            {
               if (failed == 0)
                  System.out.println("==== " + info.getFile());
               System.out.println("Cannot find constructor exception types " + name + "." + descriptor);
               ++failed;
            }
         }
         else
         {
            try
            {
               CtMethod ctMethod = clazz.getMethod(name, descriptor);
               ctMethod.getParameterTypes();
            }
            catch (NotFoundException e)
            {
               if (failed == 0)
                  System.out.println("==== " + info.getFile());
               System.out.println("Cannot find method parameter types " + name + "." + descriptor);
               ++failed;
            }
            try
            {
               CtMethod ctMethod = clazz.getMethod(name, descriptor);
               ctMethod.getExceptionTypes();
            }
            catch (NotFoundException e)
            {
               if (failed == 0)
                  System.out.println("==== " + info.getFile());
               System.out.println("Cannot find method exception types " + name + "." + descriptor);
               ++failed;
            }
         }
      }

      // Now we've checked the signatures of the class, let's look at the references
      file.compact();
      ConstPool consts = file.getConstPool();
      for (int i = 1; i < consts.getSize(); ++i) // Yes start at 1
      {
         switch (consts.getTag(i))
         {
            case ConstPool.CONST_Fieldref:
            {
               String type = consts.getFieldrefClassName(i);
               String name = consts.getFieldrefName(i);
               try
               {
                  CtClass ctClazz = pool.get(type);
                  ctClazz.getField(name);
               }
               catch (NotFoundException e)
               {
                  if (failed == 0)
                     System.out.println("==== " + info.getFile());
                  System.out.println("Cannot find field " + type + "." + name);
                  ++failed;
               }
               break;
            }
            case ConstPool.CONST_InterfaceMethodref:
            {
               String type = consts.getInterfaceMethodrefClassName(i);
               String name = consts.getInterfaceMethodrefName(i);
               String descriptor = consts.getInterfaceMethodrefType(i);
               try
               {
                  CtClass ctClazz = pool.get(type);
                  ctClazz.getMethod(name, descriptor);
               }
               catch (NotFoundException e)
               {
                  if (failed == 0)
                     System.out.println("==== " + info.getFile());
                  System.out.println("Cannot find interface method " + type + "." + name + descriptor);
                  ++failed;
               }
               break;
            }
            case ConstPool.CONST_Methodref:
            {
               String type = consts.getMethodrefClassName(i);
               String name = consts.getMethodrefName(i);
               String descriptor = consts.getMethodrefType(i);
               if (INIT.equals(name))
               {
                  try
                  {
                     CtClass ctClazz = pool.get(type);
                     ctClazz.getConstructor(descriptor);
                  }
                  catch (NotFoundException e)
                  {
                     if (failed == 0)
                        System.out.println("==== " + info.getFile());
                     System.out.println("Cannot find constructor " + type + descriptor);
                     ++failed;
                  }
               }
               else
               {
                  try
                  {
                     CtClass ctClazz = pool.get(type);
                     ctClazz.getMethod(name, descriptor);
                  }
                  catch (NotFoundException e)
                  {
                     if (failed == 0)
                        System.out.println("==== " + info.getFile());
                     System.out.println("Cannot find method " + type + "." + name + descriptor);
                     ++failed;
                  }
               }
               break;
            }
         }
      }

      if (failed > 0)
      {
         System.out.println(failed + " errors for " + info.getFile());
         System.out.println();
      }
     
      // Return the result
      return failed;
   }

   private class CheckClassInfo
   {
      File file;
      String srcRoot;
      String className;

      CtClass clazz;
     
      boolean compiled;

      CheckClassInfo(File file, String srcRoot, CtClass clazz)
      {
         this.file = file;
         this.srcRoot = srcRoot;
         this.className = clazz.getName();
         this.clazz = clazz;
      }

      public File getFile()
      {
         return file;
      }

      public String getSrcRoot()
      {
         return srcRoot;
      }

      public boolean isCompiled()
      {
         return compiled;
      }

      public void setCompiled(boolean compiled)
      {
         this.compiled = compiled;
      }

      public String getClassName()
      {
         return className;
      }

      public CtClass getClazz()
      {
         return clazz;
      }
      public void setClazz(CtClass clazz)
      {
         this.clazz = clazz;
      }
   }
}
TOP

Related Classes of org.jboss.ant.tasks.retrocheck.Checker

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.