/*
* 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;
}
}
}