Package org.jboss.ant.tasks.retro

Source Code of org.jboss.ant.tasks.retro.Weaver

/*
* 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.retro;

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.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Inherited;
import java.net.MalformedURLException;
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.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javassist.ClassPool;
import javassist.CodeConverter;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ClassFileWriter;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.annotation.Annotation;
import javassist.expr.ExprEditor;

/**
* takes jar or class files and retro weaves the bytecode
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
* @version $Revision: 1.29 $
*/
public class Weaver
{

   /**
    * The paths in the classpath
    */
   private ArrayList<URL> paths = new ArrayList<URL>();

   /**
    * The list of class files to be translated.
    */
   private ArrayList<File> files = new ArrayList<File>();

   private String classPathParam = "";

   private static Logger log = Logger.getLogger(Weaver.class.getName());

   public boolean verbose = false;

   public boolean suppress = true;

   private String outputDir = null;

   /**
    * Parses command line arguments and calls setters.
    * @param args
    */
   public void parseArgs(String[] args)
   {
      if (args.length == 0 || args[0].equalsIgnoreCase("-h") || args[0].equalsIgnoreCase("-help"))
      {
         printUsageAndExit();
      }
      for (int i = 0; i < args.length; i++)
      {
         if (args[i].equals("-verbose"))
         {
            verbose = true;
         }
         else if (args[i].equals("-suppress"))
         {
            suppress = true;
         }
         else if (args[i].equals("-destdir"))
         {
            outputDir = args[i + 1];
            ++i;
         }
         else if (args[i].equals("-cp") || args[i].equals("-classpath"))
         {
            if (i + 1 > args.length - 1)
            {
               printUsageAndExit();
            }
            ++i;
            StringTokenizer tokenizer = new StringTokenizer(args[i], File.pathSeparator);
            classPathParam = args[i];
            while (tokenizer.hasMoreTokens())
            {
               String cpath = tokenizer.nextToken();
               File f = new File(cpath);
               try
               {
                  paths.add(f.toURL());
               }
               catch (MalformedURLException mue)
               {
                  throw new IllegalArgumentException("Malformed URL in classpath param: " + mue);
               }
            }
         }
         else if (args[i].equals("--SOURCEPATH"))
         {
            if (i + 1 > args.length - 1)
            {
               printUsageAndExit();
            }
            addFilesFromSourcePathFile(files, args[++i]);
         }
         else
         {
            try
            {
               File f = new File(args[i]).getCanonicalFile();
               files.add(f);
            }
            catch (IOException ioe)
            {
               throw new IllegalArgumentException("Problem accessing source path: " + ioe);
            }
         }
      }
   }

   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();
      }
   };

   /**
    * Main method can be called from the command line.
    * Usage: Weaver [-cp <classpath>] [-classpath <classpath>] [-verbose] [--SOURCEPATH <source-path-file>] <dir>+
    * @param args command line arguments
    * @throws Exception if anything goes wrong
    */
   public static void main(String[] args) throws Exception
   {
      long start = System.currentTimeMillis();
      Weaver weaver = new Weaver();
      try
      {
         weaver.weave(args);
      }
      catch (Exception e)
      {
         if (weaver.verbose)
            throw e;
         throw e;
      }
      System.out.println("Build Successful: " + (System.currentTimeMillis() - start) + " ms");
   }

   private void printUsageAndExit()
   {
      usage();
      System.exit(1);
   }

   public void usage()
   {
      System.err
            .println("Usage: Weaver [-cp <classpath>] [-classpath <classpath>] [-verbose] [-destdir <output-dir>] [-suppress] [--SOURCEPATH <source-path-file>] <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 weave(String[] args) throws Exception
   {
      parseArgs(args);
      weave();
   }

   public void weave() throws Exception
   {
      // FIXME class rename configurable
      Map<String, String> classRenames = new HashMap<String, String>();
      classRenames.put("java/lang/annotation/Annotation", "org/jboss/lang/Annotation");
      classRenames.put("java/lang/annotation/Inherited", "org/jboss/lang/annotation/Inherited");
      classRenames.put("java/lang/Enum", "org/jboss/lang/EnumImpl");
      classRenames.put("java/lang/Iterable", "org/jboss/lang/Iterable");
      classRenames.put("java/lang/StringBuilder", "org/jboss/lang/JBossStringBuilder");
      classRenames.put("java/lang/reflect/GenericDeclaration", "org/jboss/lang/reflect/GenericDeclaration");
      classRenames.put("java/lang/reflect/MalformedParameterizedTypeException", "org/jboss/lang/reflect/MalformedParameterizedTypeException");
      classRenames.put("java/lang/reflect/Type", "org/jboss/lang/reflect/Type");
      classRenames.put("java/lang/reflect/TypeVariable", "org/jboss/lang/reflect/TypeVariable");
      classRenames.put("java/lang/reflect/ParameterizedType", "org/jboss/lang/reflect/ParameterizedType");
      classRenames.put("java/util/AbstractCollection", "edu/emory/mathcs/backport/java/util/AbstractCollection");
      classRenames.put("java/util/AbstractMap", "edu/emory/mathcs/backport/java/util/AbstractMap");
      classRenames.put("java/util/AbstractList", "edu/emory/mathcs/backport/java/util/AbstractList");
      classRenames.put("java/util/AbstractQueue", "edu/emory/mathcs/backport/java/util/AbstractQueue");
      classRenames.put("java/util/AbstractSequentialList", "edu/emory/mathcs/backport/java/util/AbstractSequentialList");
      classRenames.put("java/util/AbstractSet", "edu/emory/mathcs/backport/java/util/AbstractSet");
      classRenames.put("java/util/ArrayDeque", "edu/emory/mathcs/backport/java/util/ArrayDeque");
      classRenames.put("java/util/Arrays", "edu/emory/mathcs/backport/java/util/Arrays");
      classRenames.put("java/util/Collections", "edu/emory/mathcs/backport/java/util/Collections");
      classRenames.put("java/util/Deque", "edu/emory/mathcs/backport/java/util/Deque");
      classRenames.put("java/util/EnumSet", "org/jboss/util/EnumSet");
      classRenames.put("java/util/EnumMap", "org/jboss/util/EnumMap");
      classRenames.put("java/util/LinkedList", "edu/emory/mathcs/backport/java/util/LinkedList");
      classRenames.put("java/util/NavigableMap", "edu/emory/mathcs/backport/java/util/NavigableMap");
      classRenames.put("java/util/NavigableSet", "edu/emory/mathcs/backport/java/util/NavigableSet");
      classRenames.put("java/util/PriorityQueue", "edu/emory/mathcs/backport/java/util/PriorityQueue");
      classRenames.put("java/util/Queue", "edu/emory/mathcs/backport/java/util/Queue");
      classRenames.put("java/util/TreeMap", "edu/emory/mathcs/backport/java/util/TreeMap");
      classRenames.put("java/util/TreeSet", "edu/emory/mathcs/backport/java/util/TreeSet");
      classRenames.put("java/util/concurrent/AbstractExecutorService", "edu/emory/mathcs/backport/java/util/concurrent/AbstractExecutorService");
      classRenames.put("java/util/concurrent/ArrayBlockingQueue", "edu/emory/mathcs/backport/java/util/concurrent/ArrayBlockingQueue");
      classRenames.put("java/util/concurrent/BlockingDeque", "edu/emory/mathcs/backport/java/util/concurrent/BlockingDeque");
      classRenames.put("java/util/concurrent/BlockingQueue", "edu/emory/mathcs/backport/java/util/concurrent/BlockingQueue");
      classRenames.put("java/util/concurrent/BrokenBarrierException", "edu/emory/mathcs/backport/java/util/concurrent/BrokenBarrierException");
      classRenames.put("java/util/concurrent/Callable", "edu/emory/mathcs/backport/java/util/concurrent/Callable");
      classRenames.put("java/util/concurrent/CancellationException", "edu/emory/mathcs/backport/java/util/concurrent/CancellationException");
      classRenames.put("java/util/concurrent/CompletionService", "edu/emory/mathcs/backport/java/util/concurrent/CompletionService");
      classRenames.put("java/util/concurrent/ConcurrentHashMap", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentHashMap");
      classRenames.put("java/util/concurrent/ConcurrentLinkedQueue", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentLinkedQueue");
      classRenames.put("java/util/concurrent/ConcurrentMap", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentMap");
      classRenames.put("java/util/concurrent/ConcurrentNavigableMap", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentNavigableMap");
      classRenames.put("java/util/concurrent/ConcurrentSkipListMap", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentSkipListMap");
      classRenames.put("java/util/concurrent/ConcurrentSkipListet", "edu/emory/mathcs/backport/java/util/concurrent/ConcurrentSkipListSet");
      classRenames.put("java/util/concurrent/CopyOnWriteArrayList", "edu/emory/mathcs/backport/java/util/concurrent/CopyOnWriteArrayList");
      classRenames.put("java/util/concurrent/CopyOnWriteArraySet", "edu/emory/mathcs/backport/java/util/concurrent/CopyOnWriteArraySet");
      classRenames.put("java/util/concurrent/CountDownLatch", "edu/emory/mathcs/backport/java/util/concurrent/CountDownLatch");
      classRenames.put("java/util/concurrent/CyclicBarrier", "edu/emory/mathcs/backport/java/util/concurrent/CyclicBarrier");
      classRenames.put("java/util/concurrent/Delayed", "edu/emory/mathcs/backport/java/util/concurrent/Delayed");
      classRenames.put("java/util/concurrent/DelayQueue", "org/jboss/util/concurrent/DelayQueue");
      classRenames.put("java/util/concurrent/Exchanger", "edu/emory/mathcs/backport/java/util/concurrent/Exchanger");
      classRenames.put("java/util/concurrent/ExecutionException", "edu/emory/mathcs/backport/java/util/concurrent/ExecutionException");
      classRenames.put("java/util/concurrent/Executor", "edu/emory/mathcs/backport/java/util/concurrent/Executor");
      classRenames.put("java/util/concurrent/ExecutorCompletionService", "edu/emory/mathcs/backport/java/util/concurrent/ExecutorCompletionService");
      classRenames.put("java/util/concurrent/Executors", "edu/emory/mathcs/backport/java/util/concurrent/Executors");
      classRenames.put("java/util/concurrent/ExecutorService", "edu/emory/mathcs/backport/java/util/concurrent/ExecutorService");
      classRenames.put("java/util/concurrent/Future", "edu/emory/mathcs/backport/java/util/concurrent/Future");
      classRenames.put("java/util/concurrent/FutureTask", "edu/emory/mathcs/backport/java/util/concurrent/FutureTask");
      classRenames.put("java/util/concurrent/LinkedBlockingDeque", "edu/emory/mathcs/backport/java/util/concurrent/LinkedBlockingDeque");
      classRenames.put("java/util/concurrent/LinkedBlockingQueue", "edu/emory/mathcs/backport/java/util/concurrent/LinkedBlockingQueue");
      classRenames.put("java/util/concurrent/PriorityBlockingQueue", "edu/emory/mathcs/backport/java/util/concurrent/PriorityBlockingQueue");
      classRenames.put("java/util/concurrent/RejectedExecutionException", "edu/emory/mathcs/backport/java/util/concurrent/RejectedExecutionException");
      classRenames.put("java/util/concurrent/RejectedExecutionHandler", "edu/emory/mathcs/backport/java/util/concurrent/RejectedExecutionHandler");
      classRenames.put("java/util/concurrent/RunnableFuture", "edu/emory/mathcs/backport/java/util/concurrent/RunnableFuture");
      classRenames.put("java/util/concurrent/RunnableScheduledFuture", "edu/emory/mathcs/backport/java/util/concurrent/RunnableScheduledFuture");
      classRenames.put("java/util/concurrent/ScheduledExecutorService", "edu/emory/mathcs/backport/java/util/concurrent/ScheduledExecutorService");
      classRenames.put("java/util/concurrent/ScheduledFuture", "edu/emory/mathcs/backport/java/util/concurrent/ScheduledFuture");
      classRenames.put("java/util/concurrent/ScheduledThreadPoolExecutor", "edu/emory/mathcs/backport/java/util/concurrent/ScheduledThreadPoolExecutor");
      classRenames.put("java/util/concurrent/Semaphore", "edu/emory/mathcs/backport/java/util/concurrent/Semaphore");
      classRenames.put("java/util/concurrent/SynchronousQueue", "edu/emory/mathcs/backport/java/util/concurrent/SynchronousQueue");
      classRenames.put("java/util/concurrent/ThreadFactory", "edu/emory/mathcs/backport/java/util/concurrent/ThreadFactory");
      classRenames.put("java/util/concurrent/ThreadPoolExecutor", "edu/emory/mathcs/backport/java/util/concurrent/ThreadPoolExecutor");
      classRenames.put("java/util/concurrent/ThreadPoolExecutor$AbortPolicy", "edu/emory/mathcs/backport/java/util/concurrent/ThreadPoolExecutor$AbortPolicy");
      classRenames.put("java/util/concurrent/ThreadPoolExecutor$CallerRunsPolicy", "edu/emory/mathcs/backport/java/util/concurrent/ThreadPoolExecutor$CallerRunsPolicy");
      classRenames.put("java/util/concurrent/ThreadPoolExecutor$DiscardOldestPolicy", "edu/emory/mathcs/backport/java/util/concurrent/ThreadPoolExecutor$DiscardOldestPolicy");
      classRenames.put("java/util/concurrent/ThreadPoolExecutor$DiscardPolicy", "edu/emory/mathcs/backport/java/util/concurrent/ThreadPoolExecutor$DiscardPolicy");
      classRenames.put("java/util/concurrent/TimeoutException", "edu/emory/mathcs/backport/java/util/concurrent/TimeoutException");
      classRenames.put("java/util/concurrent/TimeUnit", "edu/emory/mathcs/backport/java/util/concurrent/TimeUnit");
      classRenames.put("java/util/concurrent/atomic/AtomicBoolean", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicBoolean");
      classRenames.put("java/util/concurrent/atomic/AtomicIntegerArray", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicIntegerArray");
      classRenames.put("java/util/concurrent/atomic/AtomicInteger", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicInteger");
      classRenames.put("java/util/concurrent/atomic/AtomicIntegerFieldUpdater", "org/jboss/util/concurrent/atomic/AtomicIntegerFieldUpdater");
      classRenames.put("java/util/concurrent/atomic/AtomicLongArray", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicLongArray");
      classRenames.put("java/util/concurrent/atomic/AtomicLong", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicLong");
      classRenames.put("java/util/concurrent/atomic/AtomicLongFieldUpdater", "org/jboss/util/concurrent/atomic/AtomicLongFieldUpdater");
      classRenames.put("java/util/concurrent/atomic/AtomicMarkableReference", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicMarkableReference");
      classRenames.put("java/util/concurrent/atomic/AtomicReferenceArray", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicReferenceArray");
      classRenames.put("java/util/concurrent/atomic/AtomicReference", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicReference");
      classRenames.put("java/util/concurrent/atomic/AtomicReferenceFieldUpdater", "org/jboss/util/concurrent/atomic/AtomicReferenceFieldUpdater");
      classRenames.put("java/util/concurrent/atomic/AtomicStampedReference", "edu/emory/mathcs/backport/java/util/concurrent/atomic/AtomicStampedReference");
      classRenames.put("java/util/concurrent/locks/Condition", "edu/emory/mathcs/backport/java/util/concurrent/locks/Condition");
      classRenames.put("java/util/concurrent/locks/CondVar", "edu/emory/mathcs/backport/java/util/concurrent/locks/CondVar");
      classRenames.put("java/util/concurrent/locks/FIFOCondVar", "edu/emory/mathcs/backport/java/util/concurrent/locks/FIFOCondVar");
      classRenames.put("java/util/concurrent/locks/Lock", "edu/emory/mathcs/backport/java/util/concurrent/locks/Lock");
      classRenames.put("java/util/concurrent/locks/ReadWriteLock", "edu/emory/mathcs/backport/java/util/concurrent/locks/ReadWriteLock");
      classRenames.put("java/util/concurrent/locks/ReentrantLock", "edu/emory/mathcs/backport/java/util/concurrent/locks/ReentrantLock");
      classRenames.put("java/util/concurrent/locks/ReentrantReadWriteLock", "org/jboss/util/concurrent/locks/ReentrantReadWriteLock");
      classRenames.put("java/util/concurrent/locks/ReentrantReadWriteLock$ReadLock", "org/jboss/util/concurrent/locks/ReentrantReadWriteLock$ReadLock");
      classRenames.put("java/util/concurrent/locks/ReentrantReadWriteLock$WriteLock", "org/jboss/util/concurrent/locks/ReentrantReadWriteLock$WriteLock");

      // FIXME ExprEditor configurable
      ExprEditor[] editors =
      {
        new ClassRedirectEditor()
      };

      ClassPool pool = ClassPool.getDefault();
      pool.appendPathList(classPathParam);

      // FIXME CodeConverter configurable
      CodeConverter[] converters =
      {
        new AutoboxCodeConverter()
      };

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

      Thread.currentThread().setContextClassLoader(loader);

      //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("[retro] " + f + " is neither a java class or a directory");
            }
         }
      }

      //Compile each class
      for (String className : classesToCompile.keySet())
      {
         CompilerClassInfo info = classesToCompile.get(className);
         compileFile(info, classRenames, converters, editors);
      }
   }

   private HashMap<String, CompilerClassInfo> classesToCompile = new HashMap<String, CompilerClassInfo>();

   private void addDirectory(File dir, ClassPool pool) throws IOException, NotFoundException
   {
      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 IOException, NotFoundException
   {
      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);
      CompilerClassInfo info = new CompilerClassInfo(file, srcRoot, clazz);
      classesToCompile.put(className, info);
   }

   private ClassFile createClassFile(final File file) throws IOException
   {
      return createClassFile(new FileInputStream(file));
   }

   private ClassFile createClassFile(InputStream is) throws IOException
   {
      DataInputStream dis = new DataInputStream(is);
      ClassFile cf = new ClassFile(dis);
      dis.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 (IOException ioe)
      {
         log.warning("Problem reading sourcepath file: " + ioe);
         try
         {
            reader.close();
         }
         catch (IOException ioe1)
         {
            throw new RuntimeException(ioe);
         }
      }
   }

   public void compileFile(CompilerClassInfo info, Map<String, String> classRenames, CodeConverter[] converters,
         ExprEditor[] editors) throws Exception
   {
      if (info.isCompiled())
         return;

      if (verbose)
         System.out.println("[compileFile] " + 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;
      }

      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() + "; proceeding. ");
      }
      boolean weaved = doWeave(loader, info, classRenames, converters, editors);
      if (weaved == false)
      {
         if (verbose)
            System.out.println("[no comp needed] " + info.getFile());
         return;
      }
      info.setCompiled(true);
      if (verbose)
         System.out.println("[compiled] " + info.getFile());
   }

   public boolean doWeave(ClassLoader cl, CompilerClassInfo info, Map classRenames, CodeConverter[] converters,
         ExprEditor[] editors) throws Exception
   {
      CtClass clazz = info.getClazz();
      ClassFile file = clazz.getClassFile();

      // Already done
      if (file.getMajorVersion() <= 48)
         return false;

      // Set the major version
      file.setMajorVersion(48);

      // Rename known classes
      file.renameClass(classRenames);

      ConstPool constPool = file.getConstPool();

      // Rename classes with + to have $
      HashMap<String, String> mapClasses = new HashMap<String, String>();
      for (String name : (Set<String>) constPool.getClassNames())
      {
         if (name.indexOf('+') != -1)
            mapClasses.put(name, name.replace('+', '$'));
      }
      if (mapClasses.size() != 0)
         constPool.renameClass(mapClasses);

      // Replace LDC/LDC_W
      for (MethodInfo method : (List<MethodInfo>) file.getMethods())
         rewriteLDC(constPool, method);

      // Rewrite any enum classes
      if (info.isEnum())
      {
         clazz = rewriteEnum(cl, info);
         info.setClazz(clazz);
      }

      if (clazz.isAnnotation())
      {
         rewriteSystemAnnotations(file);
      }

      // Run the converters
      for (CodeConverter converter : converters)
         clazz.instrument(converter);

      // Run the editors
      for (ExprEditor editor : editors)
         clazz.instrument(editor);

      if (verbose)
      {
         PrintWriter out = new PrintWriter(System.out, true);
         out.println("*** constant pool ***");
         file.getConstPool().print(out);
         out.println();
         out.println("*** members ***");
         ClassFileWriter.print(file, out);
      }

      // Write out the changes
      if (outputDir != null)
         clazz.writeFile(outputDir);
      else
         clazz.writeFile(info.getSrcRoot());

      return true;
   }

   /**
    * This rewrites the load constant ClassInfo
    * to be Class.forName()
    *
    * @param constPool the constant pool
    * @param method the method
    * @throws Exception for any error
    */
   public static void rewriteLDC(ConstPool constPool, MethodInfo method) throws BadBytecode
   {
      CodeAttribute code = method.getCodeAttribute();
      if (code == null)
         return;
      CodeIterator iterator = code.iterator();
      while (iterator.hasNext())
      {
         int index = iterator.next();
         int op = iterator.byteAt(index);

         if (op == Opcode.LDC || op == Opcode.LDC_W)
         {
            int index0 = iterator.byteAt(index + 1);
            int constIndex = index0;
            if (op == Opcode.LDC_W)
            {
               int index1 = iterator.byteAt(index + 2);
               constIndex = (index0 << 8) + index1;
            }
            if (7 == constPool.getTag(constIndex))
            {
               String className = constPool.getClassInfo(constIndex);
               int theClassName = constPool.addStringInfo(className.replace('/', '.'));
               if (op == Opcode.LDC_W)
               {
                  int b0 = theClassName >>> 8;
                  int b1 = theClassName & 0x0FF;
                  iterator.writeByte(b0, index + 1);
                  iterator.writeByte(b1, index + 2);
               }
               else
               {
                  iterator.writeByte(theClassName, index + 1);
               }
               int classClass = constPool.addClassInfo("java/lang/Class");
               int descriptor = constPool.addMethodrefInfo(classClass, "forName",
                     "(Ljava/lang/String;)Ljava/lang/Class;");
               iterator.insert(new byte[]
               {(byte) Opcode.INVOKESTATIC, (byte) (descriptor >>> 8), (byte) descriptor});
            }
         }
      }
   }

   /**
    * Transform a jdk5 java.lang.Enum subtype into a jdk14 compatible org.jboss.lang.Enum
    * subtype. This requires jdk5 APIs that cannot be weaved.
    *
    * @param cl the classloader
    * @param info the compiler info
    * @throws Exception for any error
    */
   public static CtClass rewriteEnum(ClassLoader cl, CompilerClassInfo info) throws Exception
   {
      // Get the jdk5 class enum values
      String classname = info.getClassName();
      Class<Enum> c = (Class<Enum>) cl.loadClass(classname);
      Enum[] enums = c.getEnumConstants();

      // Rewrite the enum class
      CtClass oldEnum = info.getClazz();
      ClassPool pool = oldEnum.getClassPool();
      oldEnum.detach();
      CtClass baseEnum = pool.get("org.jboss.lang.Enum");
      CtClass newEnum = pool.makeClass(classname, baseEnum);

      // Create the enum ctor
      String ctorSrc = "protected " + newEnum.getSimpleName() + "(String name, int ord){super(name, ord);}";
      CtConstructor ctor = CtNewConstructor.make(ctorSrc, newEnum);
      newEnum.addConstructor(ctor);

      // Create the enum constants by iterating over the jdk5 class values
      for (Enum e : enums)
      {
         String fieldName = e.name();
         String fieldSrc = "public static final " + classname + " " + fieldName + " = new " + classname + "(\""
               + fieldName + "\", " + e.ordinal() + ");";
         CtField f2 = CtField.make(fieldSrc, newEnum);
         f2.setName(fieldName);
         newEnum.addField(f2);
      }

      // Add a T valueOf(String) method
      String valueOfSrc = "public " + newEnum.getName() + " valueOf(String name) {"
            + "return org.jboss.lang.Enum.valueOf(getClass(), name);" + "}";
      CtMethod valueOf = CtNewMethod.make(valueOfSrc, newEnum);
      newEnum.addMethod(valueOf);

      // Add a T[] values() method
      String valuesSrc = "public static final " + newEnum.getName() + "[] values() {"
            + "return org.jboss.lang.Enum.values($class);" + "}";
      CtMethod values = CtNewMethod.make(valuesSrc, newEnum);
      newEnum.addMethod(values);
      return newEnum;
   }

   private void rewriteSystemAnnotations(ClassFile file)
   {
      AnnotationsAttribute visible = (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.visibleTag);
      if (visible != null)
      {
         //Only bother with the @Inherited annotation for now, as this is the main thing affecting the container tests
         Annotation[] annotations = visible.getAnnotations();
         boolean changed = false;
         for (int i = 0; i < annotations.length; i++)
         {
            if (annotations[i].getTypeName().equals(Inherited.class.getName()))
            {
               annotations[i] = new Annotation("org/jboss/lang/annotation/Inherited", file.getConstPool());
               changed = true;
            }
         }
         if (changed)
         {
            visible.setAnnotations(annotations);
         }
      }
   }

   private class CompilerClassInfo
   {
      File file;

      String srcRoot;

      String className;

      CtClass clazz;

      boolean compiled;

      CompilerClassInfo(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 boolean isEnum() throws NotFoundException
      {
         ClassPool pool = clazz.getClassPool();
         CtClass enumClass = pool.get("java.lang.Enum");
         return clazz.subtypeOf(enumClass);
      }

      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.retro.Weaver

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.