You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@abdera.apache.org by jm...@apache.org on 2006/07/07 00:08:33 UTC
svn commit: r419720 [3/5] - in /incubator/abdera/java/trunk/build: ./ tools/
tools/retroweaver/ tools/retroweaver/docs/ tools/retroweaver/docs/guide/
tools/retroweaver/docs/images/ tools/retroweaver/lib/
tools/retroweaver/release/ tools/retroweaver/src...
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,575 @@
+package com.rc.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 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;
+
+import com.rc.retroweaver.event.VerifierListener;
+
+
+/**
+ * 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 int target;
+
+ private String currentclassName;
+
+ private RetroWeaverClassLoader classLoader;
+
+ private List<String> classPathArray;
+
+ private Set<String> failedClasses;
+
+ private VerifierListener listener;
+
+ private int warningCount;
+
+ private List<String> classes;
+
+ private 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);
+
+ listener.verifyPathStarted("Verifying " + classes.size() + " classe(s)");
+ classLoader.setClassPath(classPathArray);
+
+ for (String name : classes) {
+ JarEntry entry = jarFile.getJarEntry(name);
+ InputStream is = jarFile.getInputStream(entry);
+ verifyClass(is, name);
+ }
+ }
+
+ public void verifyFiles() throws IOException {
+ listener.verifyPathStarted("Verifying " + classes.size() + " classe(s)");
+ classLoader.setClassPath(classPathArray);
+
+ for (String sourcePath : classes) {
+ verifyClass(new FileInputStream(sourcePath), sourcePath);
+ }
+ }
+
+ private void verifySingleClass(String classFileName) throws IOException {
+ classLoader.setClassPath(classPathArray);
+
+ verifyClass(new FileInputStream(classFileName), classFileName);
+ }
+
+ private void verifyClass(InputStream sourceStream, String classFileName)
+ throws IOException {
+
+ failedClasses = new HashSet<String>();
+
+ ClassReader cr = new ClassReader(sourceStream);
+ cr.accept(this, false);
+ }
+
+ private void unknowClassWarning(String className, String msg) {
+ String report = currentclassName + ": unknown class "
+ + className;
+
+ if (msg != null) {
+ report += ": " + msg;
+ }
+
+ warning(report);
+ }
+
+ private void unknownFieldWarning(String owner, String name, String desc, String msg) {
+ String report = currentclassName + ": unknown field " + name + '/' + desc.replace('/', '.');
+
+ if (msg != null) {
+ report += ", " + msg;
+ }
+
+ warning(report);
+ }
+
+ private void unknownMethodWarning(String owner, String name, String desc, String msg) {
+ String report = currentclassName + ": unknown method " + name + '/' + desc.replace('/', '.');
+
+ if (msg != null) {
+ report += ", " + msg;
+ }
+
+ warning(report);
+ }
+
+ private void invalidClassVersion(String className, int target, int version) {
+ String report = className + ": invalid class version " + version + ", target is " + target;
+
+ warning(report);
+ }
+
+ private void warning(String report) {
+ warningCount++;
+ listener.acceptWarning(report);
+ }
+
+ 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 (command.equals("-class")) {
+ classfile = args[i];
+ } else if (command.equals("-cp")) {
+ 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);
+ System.out.println();
+ System.out.println(getUsage());
+ return;
+ }
+ }
+
+ if (classfile == null) {
+ System.out.println("Option \"-class\" is required.");
+ System.out.println();
+ System.out.println(getUsage());
+ return;
+ }
+
+ RefVerifier vr = new RefVerifier(Weaver.VERSION_1_4, EMPTY_VISITOR, classpath,
+ new DefaultRefVerifierListener(true));
+ vr.verifySingleClass(classfile);
+ vr.displaySummary();
+ }
+
+ private void checkClassName(String className) {
+ Type t = Type.getType(className);
+
+ switch (t.getSort()) {
+ case Type.ARRAY:
+ t = t.getElementType();
+ if (t.getSort() != Type.OBJECT)
+ return;
+
+ // fall through to object processing
+ case Type.OBJECT:
+ className = t.getClassName();
+ break;
+ default:
+ return;
+ }
+
+ checkSimpleClassName(className);
+ }
+
+ private void checkClassNameInType(String className) {
+ switch (className.charAt(0)) {
+ case 'L':
+ case '[':
+ checkClassName(className);
+ break;
+ default:
+ checkSimpleClassName(className);
+ }
+ }
+
+ private void checkSimpleClassName(String className) {
+ try {
+ className = className.replace('.', '/');
+ getClassReader(className);
+ } catch (ClassNotFoundException e) {
+ failedClasses.add(className);
+ unknowClassWarning(className.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(owner, name,desc, "Field not found in " + owner.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ unknownFieldWarning(owner, 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(owner, name, desc, "Method not found in " + owner.replace('/', '.'));
+ }
+ } catch (ClassNotFoundException e) {
+ unknownMethodWarning(owner, 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, false);
+ } catch (Success s) {
+ return true;
+ }
+ String[] is = visitor.classInterfaces;
+ for (String i : is)
+ if (findField(i, name, c))
+ return true;
+
+ if (javaClassName.equals("java/lang/Object"))
+ 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, false);
+ } 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 (javaClassName.equals("java/lang/Object"))
+ 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;
+ }
+ boolean methdodMatcher;
+ final String searchedName;
+ final String searchedDesc;
+
+ String classInterfaces[];
+ String superClassName;
+ boolean isInterface;
+ 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)
+ if (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)
+ if (name.equals(searchedName) && desc.equals(searchedDesc)) {
+ throw new Success();
+ }
+ return null;
+ }
+
+ public void visitEnd() {
+ }
+ }
+
+}
+
+class DefaultRefVerifierListener implements VerifierListener {
+
+ private boolean verbose;
+
+ DefaultRefVerifierListener(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void verifyPathStarted(String msg) {
+ System.out.println("[RefVerifier] " + msg);
+ }
+
+ public void verifyClassStarted(String msg) {
+ if (verbose)
+ System.out.println("[RefVerifier] " + msg);
+ }
+
+ public void acceptWarning(String msg) {
+ System.out.println("[RefVerifier] " + msg);
+ }
+
+ public void displaySummary(int warningCount) {
+ System.out.println("[RefVerifier] Verification complete, "
+ + warningCount + " warning(s).");
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,1013 @@
+package com.rc.retroweaver;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+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 com.rc.retroweaver.event.WeaveListener;
+import com.rc.retroweaver.optimizer.ClassConstantsCollector;
+import com.rc.retroweaver.optimizer.Constant;
+import com.rc.retroweaver.optimizer.ConstantComparator;
+import com.rc.retroweaver.optimizer.ConstantPool;
+
+/**
+ * A bytecode enhancer that translates Java 1.5 class files into Java 1.4 class
+ * files. The enhancer performs primarily two tasks: 1) Reverses changes made to
+ * the class file format in 1.5 to the former 1.4 format. 2) Replaces compiler
+ * generated calls into the new 1.5 runtime with calls into RetroWeaver's
+ * replacement runtime.
+ */
+public class RetroWeaver {
+
+ private int target;
+
+ private boolean lazy;
+
+ /**
+ * Indicates whether the generic signatures should be stripped. Default to <code>false</code>.
+ */
+ private boolean stripSignatures;
+
+ private int weavedClassCount;
+
+ private WeaveListener listener;
+
+ private RefVerifier verifier;
+
+ private static final String newLine = System.getProperty("line.separator");
+
+ public RetroWeaver(int target) {
+ this.target = target;
+ }
+
+ protected static final FileFilter classFilter = new FileFilter() {
+ public boolean accept(File f) {
+ return f.getName().endsWith(".class");
+ }
+ };
+
+ protected static final FileFilter subdirFilter = new FileFilter() {
+ public boolean accept(File f) {
+ return f.isDirectory();
+ }
+ };
+
+ protected static void buildFileSets(ArrayList<File[]> fileSets, File path) {
+ File[] files = path.listFiles(classFilter);
+ if (files != null) {
+ fileSets.add(files);
+ }
+
+ File[] subdirs = path.listFiles(subdirFilter);
+ if (subdirs != null) {
+ for (File subdir : subdirs)
+ buildFileSets(fileSets, subdir);
+ }
+ }
+
+ private void displayStartMessage(int n) {
+ listener.weavingStarted("Processing " + n + " classe(s)");
+ }
+
+ private void displayEndMessage() {
+ listener.weavingCompleted(Integer.toString(weavedClassCount) + " classe(s) weaved.");
+ }
+
+ public void weave(File path) throws IOException {
+ ArrayList<File[]> fileSets = new ArrayList<File[]>();
+
+ buildFileSets(fileSets, path);
+
+ int n = 0;
+ for (File[] set : fileSets)
+ n += set.length;
+ displayStartMessage(n);
+
+ for (int i = 0; i < fileSets.size(); i++) {
+ for (File file : fileSets.get(i)) {
+ String sourcePath = file.getCanonicalPath();
+ weave(sourcePath, null);
+ }
+ }
+ displayEndMessage();
+
+ if (verifier != null) {
+ verifier.verifyFiles();
+ verifier.displaySummary();
+ }
+ }
+
+ public void weave(File[] baseDirs, String[][] fileSets, File outputDir)
+ throws IOException {
+ int n = 0;
+ for (String[] set : fileSets)
+ n += set.length;
+ displayStartMessage(n);
+
+ Set<String> weaved = new HashSet<String>();
+ for (int i = 0; i < fileSets.length; i++) {
+ for (String fileName : fileSets[i]) {
+ File file = new File(baseDirs[i], fileName);
+ String sourcePath = file.getCanonicalPath();
+ String outputPath = null;
+ if (outputDir != null)
+ outputPath = new File(outputDir, fileName)
+ .getCanonicalPath();
+
+ // Weave it unless already weaved.
+ if (!weaved.contains(sourcePath)) {
+ weave(sourcePath, outputPath);
+ weaved.add(sourcePath);
+ }
+ }
+ }
+ displayEndMessage();
+
+ if (verifier != null) {
+ verifier.verifyFiles();
+ verifier.displaySummary();
+ }
+ }
+
+ public void weaveJarFile(String sourceJarFileName, String destJarFileName)
+ throws IOException {
+ JarFile jarFile = new JarFile(sourceJarFileName);
+ ArrayList<JarEntry> entries = Collections.list(jarFile.entries());
+
+ OutputStream os = new FileOutputStream(destJarFileName);
+ JarOutputStream out = new JarOutputStream(os);
+
+ int n = 0;
+ for (JarEntry entry : entries) {
+ if (entry.getName().endsWith(".class"))
+ n++;
+ }
+ displayStartMessage(n);
+
+ for (JarEntry entry : entries) {
+ String name = entry.getName();
+ InputStream dataStream = null;
+ if (name.endsWith(".class")) {
+ // weave class
+ InputStream is = jarFile.getInputStream(entry);
+ ByteArrayOutputStream classStream = new ByteArrayOutputStream();
+ if (weave(is, name, classStream)) {
+ // class file was modified
+ weavedClassCount++;
+
+ dataStream = new ByteArrayInputStream(classStream
+ .toByteArray());
+
+ // create new entry
+ entry = new JarEntry(name);
+ recordFileForVerifier(name);
+ }
+ }
+
+ if (dataStream == null) {
+ // not a class file or class wasn't no
+ dataStream = jarFile.getInputStream(entry);
+ }
+ // writing entry
+ out.putNextEntry(entry);
+
+ // writing data
+ int len;
+ final byte[] buf = new byte[1024];
+ while ((len = dataStream.read(buf)) >= 0) {
+ out.write(buf, 0, len);
+ }
+ }
+ out.close();
+
+ displayEndMessage();
+
+ if (verifier != null) {
+ verifier.verifyJarFile(destJarFileName);
+ verifier.displaySummary();
+ }
+ }
+
+ public void weave(String sourcePath, String outputPath) throws IOException {
+ InputStream is = new FileInputStream(sourcePath);
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ if (weave(is, sourcePath, bos)) {
+ // new class was generated
+ weavedClassCount++;
+
+ if (outputPath == null) {
+ outputPath = sourcePath;
+ } else {
+ // create parent dir if necessary
+ File parentDir = new File(outputPath).getParentFile();
+ if (parentDir != null) {
+ parentDir.mkdirs();
+ }
+ }
+ FileOutputStream fos = new FileOutputStream(outputPath);
+ fos.write(bos.toByteArray());
+ fos.close();
+
+ recordFileForVerifier(outputPath);
+ } else {
+ // We're lazy and the class already has the target version.
+
+ if (outputPath == null) {
+ // weaving in place
+ return;
+ }
+
+ File dir = new File(outputPath).getParentFile();
+ if (dir != null)
+ dir.mkdirs();
+
+ File sf = new File(sourcePath);
+ File of = new File(outputPath);
+
+ if (!of.isFile()
+ || !of.getCanonicalPath().equals(sf.getCanonicalPath())) {
+ // Target doesn't exist or is different from source so copy
+ // the file and transfer utime.
+ FileInputStream fis = new FileInputStream(sf);
+ byte[] bytes = new byte[(int) sf.length()];
+ fis.read(bytes);
+ fis.close();
+ FileOutputStream fos = new FileOutputStream(of);
+ fos.write(bytes);
+ fos.close();
+ of.setLastModified(sf.lastModified());
+ }
+ }
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ private void recordFileForVerifier(String fileName) {
+ if (verifier != null) {
+ verifier.addClass(fileName);
+ }
+ }
+
+ private static final boolean COMPACT_CONSTANTS = true;
+
+ protected boolean weave(InputStream sourceStream, String fileName, ByteArrayOutputStream bos)
+ throws IOException {
+
+ ClassReader cr = new ClassReader(sourceStream);
+ ClassWriter cw = new ClassWriter(true);
+
+ try {
+ NameTranslatorClassVisitor translator;
+ ConstantPool cp;
+ if (COMPACT_CONSTANTS) {
+ cp = new ConstantPool();
+ ClassConstantsCollector ccc = new ClassConstantsCollector(cw, cp);
+ translator = new NameTranslatorClassVisitor(ccc);
+ } else {
+ translator = new NameTranslatorClassVisitor(cw);
+ }
+ ClassWeaver classWeaver = new ClassWeaver(translator, lazy, target, listener);
+ ClassVisitor v;
+ if (stripSignatures) {
+ v = new SignatureStripper(classWeaver);
+ } else {
+ v = classWeaver;
+ }
+ cr.accept(v, false);
+
+ if (COMPACT_CONSTANTS) {
+ Set<Constant> constants = new TreeSet<Constant>(new ConstantComparator());
+ constants.addAll(cp.values());
+
+ cr = new ClassReader(cw.toByteArray());
+ cw = new ClassWriter(false);
+ for(Constant c: constants)
+ c.write(cw);
+ cr.accept(cw, false);
+ }
+
+ bos.write(cw.toByteArray());
+ return true;
+ } catch (LazyException e) {
+ return false;
+ }
+ }
+
+ static void optimize(final InputStream is, final ByteArrayOutputStream os)
+ throws IOException
+ {
+ }
+
+ public void setListener(WeaveListener listener) {
+ this.listener = listener;
+ }
+
+ public void setLazy(boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ public void setVerifier(RefVerifier verifier) {
+ this.verifier = verifier;
+ }
+
+ public static String getUsage() {
+ return "Usage: RetroWeaver " + newLine + " <source path>" + newLine
+ + " [<output path>]";
+ }
+
+ public static void main(String[] args) {
+
+ if (args.length < 1) {
+ System.out.println(getUsage());
+ return;
+ }
+
+ String sourcePath = args[0];
+ String outputPath = null;
+
+ if (args.length > 1) {
+ outputPath = args[1];
+ }
+
+ try {
+ RetroWeaver weaver = new RetroWeaver(Weaver.VERSION_1_4);
+ weaver.weave(sourcePath, outputPath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param stripSignatures The stripSignatures to set.
+ */
+ public void setStripSignatures(boolean stripSignatures) {
+ this.stripSignatures = stripSignatures;
+ }
+}
+
+class LazyException extends RuntimeException {
+}
+
+class ClassWeaver extends ClassAdapter implements Opcodes {
+
+ private boolean lazy;
+ private int target;
+ private WeaveListener listener;
+
+ private String className;
+
+ private boolean isEnum;
+ private boolean isInterface;
+
+ private Set<String> classLiteralCalls = new HashSet<String>();
+
+ public ClassWeaver(final ClassVisitor cv, boolean lazy, int target, WeaveListener listener) {
+ super(cv);
+ this.lazy = lazy;
+ this.target = target;
+ this.listener = listener;
+ }
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ if (lazy && (version <= target)) {
+ // abort all visitors
+ throw new LazyException();
+ }
+
+ if (listener != null) {
+ listener.weavingPath(name);
+ }
+
+ className = name;
+ isEnum = superName != null && superName.equals("java/lang/Enum");
+ isInterface = (access & ACC_INTERFACE) == ACC_INTERFACE;
+
+ cv.visit(target, // Changes the format of the class file from 1.5 to the target value.
+ access,
+ name,
+ signature,
+ superName,
+ interfaces);
+ }
+
+ public void visitInnerClass(
+ final String name,
+ final String outerName,
+ final String innerName,
+ final int access)
+ {
+ cv.visitInnerClass(name, outerName, innerName, access);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ return cv.visitField(access, name, desc, signature, value);
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ int newAccess;
+ if ((access&ACC_SYNTHETIC&ACC_BRIDGE) == (ACC_SYNTHETIC&ACC_BRIDGE)) {
+ /*
+ bridge methods for generic create problems with RMIC code in 1.4.
+ It's a known bug with 1.4, see SUN's bug database at:
+ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4811083
+ http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5035300
+
+ Problem found when implementing Comparable<E>, with bridge method
+ compareTo(Ljava/lang/Object;)I;
+ */
+ newAccess = access & ~ACC_SYNTHETIC & ~ACC_BRIDGE;
+ } else
+ newAccess = access;
+
+ MethodVisitor mv = new MethodWeaver(super.visitMethod(newAccess,
+ name,
+ desc,
+ signature,
+ exceptions));
+
+ if (!isEnum || !name.equals("<clinit>"))
+ return mv;
+
+ return new EnumMethodWeaver(mv);
+ }
+
+ public void visitEnd() {
+ if (isEnum) {
+ cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL + ACC_SYNTHETIC,
+ SERIAL_ID_FIELD,
+ SERIAL_ID_SIGNATURE,
+ null, new Long(0L));
+ }
+ if (classLiteralCalls.size() != 0) {
+ // generate synthetic fields and class$ method
+ for(String fieldName: classLiteralCalls) {
+ FieldVisitor fv = visitField(ACC_STATIC + ACC_SYNTHETIC + ACC_FINAL
+ + (isInterface?ACC_PUBLIC:ACC_PRIVATE),
+ fieldName,
+ CLASS_FIELD_DESC,
+ null, null);
+ fv.visitEnd();
+ }
+
+ if (!isInterface) {
+ // "class$" method
+ String exceptionTable[] = { JAVA_LANG_NOCLASSDEFFOUNDERROR };
+ MethodVisitor mv = cv.visitMethod(ACC_STATIC+ACC_SYNTHETIC,
+ CLASS_METHOD,
+ CLASS_SIGNATURE,
+ null, exceptionTable);
+
+ mv.visitCode();
+
+ mv.visitVarInsn(ALOAD, 0);
+ generateClassCall(mv);
+ mv.visitInsn(ARETURN);
+
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ }
+ cv.visitEnd();
+ }
+
+ /**
+ * Generate the byte code equivalent to ".class"
+ *
+ * Note: assumes the class name is already on the stack
+ *
+ * @param mv method visitor to use
+ */
+ private void generateClassCall(MethodVisitor mv) {
+ /*
+ * generate the code equivalent to ".class"
+ *
+
+ try {
+ c = Class.forName(name);
+ } catch (ClassNotFoundException e) {
+ NoClassDefFoundError t = new NoClassDefFoundError(e.getMessage());
+ try {
+ t.initCause(e); // only works with 1.4+
+ } catch (NoSuchMethodError nsm) {
+ }
+ throw t;
+ }
+ */
+ Label start1 = new Label();
+ Label end1 = new Label();
+ Label handler1 = new Label();
+ mv.visitTryCatchBlock(start1, end1, handler1, JAVA_LANG_CLASSNOTFOUNDEXCEPTION);
+ Label start2 = new Label();
+ Label end2 = new Label();
+ Label handler2 = new Label();
+ mv.visitTryCatchBlock(start2, end2, handler2, JAVA_LANG_NOSUCHMETHODERROR);
+
+ mv.visitLabel(start1);
+ mv.visitMethodInsn(INVOKESTATIC, JAVA_LANG_CLASS, FOR_NAME_METHOD, FOR_NAME_SIGNATURE);
+ mv.visitVarInsn(ASTORE, 1);
+ mv.visitLabel(end1);
+ Label gotoLabel1 = new Label();
+ mv.visitJumpInsn(GOTO, gotoLabel1);
+
+ mv.visitLabel(handler1);
+ mv.visitVarInsn(ASTORE, 2);
+ mv.visitTypeInsn(NEW, JAVA_LANG_NOCLASSDEFFOUNDERROR);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_CLASSNOTFOUNDEXCEPTION, GET_MESSAGE_METHOD, GET_MESSAGE_SIGNATURE);
+ mv.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_NOCLASSDEFFOUNDERROR, INIT_METHOD, JAVA_LANG_NOCLASSDEFFOUNDERROR_INIT_SIGNATURE);
+ mv.visitVarInsn(ASTORE, 3);
+
+ mv.visitLabel(start2);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_NOCLASSDEFFOUNDERROR, INIT_CAUSE_METHOD, INIT_CAUSE_SIGNATURE);
+ mv.visitInsn(POP);
+ mv.visitLabel(end2);
+ Label gotoLabel2 = new Label();
+ mv.visitJumpInsn(GOTO, gotoLabel2);
+
+ mv.visitLabel(handler2);
+ mv.visitVarInsn(ASTORE, 4);
+
+ mv.visitLabel(gotoLabel2);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ATHROW);
+ mv.visitLabel(gotoLabel1);
+ mv.visitVarInsn(ALOAD, 1);
+ }
+
+ private class EnumMethodWeaver extends MethodAdapter implements Opcodes {
+ public EnumMethodWeaver(final MethodVisitor mv) {
+ super(mv);
+ }
+
+ public void visitInsn(final int opcode) {
+ if (opcode == RETURN) {
+ // add call to setEnumValues(Object[] values, Class c)
+
+ String owner = className.replace('.', '/');
+ String fullName = 'L' + owner + ';';
+ Type t = Type.getType(fullName);
+
+ mv.visitMethodInsn(INVOKESTATIC, owner, "values", "()[" + fullName);
+ mv.visitLdcInsn(t);
+ mv.visitMethodInsn(INVOKESTATIC, "com/rc/retroweaver/runtime/Enum_",
+ "setEnumValues", "([Ljava/lang/Object;Ljava/lang/Class;)V");
+ }
+ mv.visitInsn(opcode);
+ }
+
+ }
+
+ private static final String JAVA_LANG_CLASS = "java/lang/Class";
+ private static final String FOR_NAME_METHOD = "forName";
+ private static final String FOR_NAME_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/Class;";
+
+ private static final String SERIAL_ID_FIELD = "serialVersionUID";
+ private static final String SERIAL_ID_SIGNATURE = "J";
+
+ private static final String JAVA_LANG_NOCLASSDEFFOUNDERROR = "java/lang/NoClassDefFoundError";
+ private static final String INIT_METHOD = "<init>";
+ private static final String JAVA_LANG_NOCLASSDEFFOUNDERROR_INIT_SIGNATURE = "(Ljava/lang/String;)V";
+ private static final String INIT_CAUSE_METHOD = "initCause";
+ private static final String INIT_CAUSE_SIGNATURE = "(Ljava/lang/Throwable;)Ljava/lang/Throwable;";
+
+ private static final String JAVA_LANG_CLASSNOTFOUNDEXCEPTION = "java/lang/ClassNotFoundException";
+ private static final String GET_MESSAGE_METHOD = "getMessage";
+ private static final String GET_MESSAGE_SIGNATURE = "()Ljava/lang/String;";
+
+ private static final String JAVA_LANG_NOSUCHMETHODERROR = "java/lang/NoSuchMethodError";
+
+ private static final String CLASS_FIELD_DESC = "Ljava/lang/Class;";
+ private static final String CLASS_METHOD = "class$";
+ private static final String CLASS_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/Class;";
+
+ private static final String ITERABLE_CLASS = "java/lang/Iterable";
+ private static final String ITERATOR_METHOD = "iterator";
+ private static final String ITERATOR_SIGNATURE = "()Ljava/util/Iterator;";
+ private static final String ITERABLE_METHODS_CLASS = "com/rc/retroweaver/runtime/IterableMethods";
+ private static final String ITERABLE_METHODS_ITERATOR_SIGNATURE = "(Ljava/lang/Object;)Ljava/util/Iterator;";
+
+ private static final String JAVA_LANG_SYSTEM = "java/lang/System";
+ private static final String NANO_TIME_METHOD = "nanoTime";
+ private static final String NANO_TIME_SIGNATURE = "()J";
+ private static final String CURRENT_TIME_MILLIS_METHOD = "currentTimeMillis";
+ private static final String CURRENT_TIME_MILLIS_TIME_SIGNATURE = "()J";
+
+ private static final String CLASS_METHODS_CLASS = "com/rc/retroweaver/runtime/ClassMethods";
+ private static final String CAST_METHOD = "cast";
+ private static final String CAST_SIGNATURE = "(Ljava/lang/Object;)Ljava/lang/Object;";
+ private static final String CAST_NEW_SIGNATURE = "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;";
+ private static final String AS_SUB_CLASS_METHOD = "asSubclass";
+ private static final String AS_SUB_CLASS_SIGNATURE = "(Ljava/lang/Class;)Ljava/lang/Class;";
+ private static final String AS_SUB_CLASS_NEW_SIGNATURE = "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Class;";
+ private static final String IS_ENUM_METHOD = "isEnum";
+ private static final String IS_ENUM_SIGNATURE = "()Z";
+ private static final String IS_ENUM_NEW_SIGNATURE = "(Ljava/lang/Class;)Z";
+ private static final String GET_ENUM_CONSTANTS_METHOD = "getEnumConstants";
+ private static final String GET_ENUM_CONSTANTS_SIGNATURE = "()[Ljava/lang/Object;";
+ private static final String GET_ENUM_CONSTANTS_NEW_SIGNATURE = "(Ljava/lang/Class;)[Ljava/lang/Object;";
+
+ private static final String JAVA_LANG_STRINGBUFFER = "java/lang/StringBuffer";
+ private static final String JAVA_LANG_STRINGBUILDER = "java/lang/StringBuilder";
+ private static final String JAVA_LANG_STRINGBUFFER_INIT_SIGNATURE = "(Ljava/lang/CharSequence;)V";
+ private static final String JAVA_LANG_STRINGBUFFER_INIT_STRING_SIGNATURE = "(Ljava/lang/String;)V";
+ private static final String APPEND_METHOD = "append";
+ private static final String APPEND_SIGNATURE1 = "(Ljava/lang/CharSequence;)Ljava/lang/StringBuffer;";
+ private static final String APPEND_SIGNATURE2 = "(Ljava/lang/CharSequence;II)Ljava/lang/StringBuffer;";
+ private static final String APPEND_SIGNATURE3 = "(Ljava/lang/String;)Ljava/lang/StringBuffer;";
+ private static final String INSERT_METHOD = "insert";
+ private static final String INSERT_SIGNATURE1 = "(ILjava/lang/CharSequence;)Ljava/lang/StringBuffer;";
+ private static final String INSERT_SIGNATURE2 = "(ILjava/lang/CharSequence;II)Ljava/lang/StringBuffer;";
+ private static final String INSERT_SIGNATURE3 = "(ILjava/lang/String;)Ljava/lang/StringBuffer;";
+ private static final String TRIM_TO_SIZE = "trimToSize";
+ private static final String TRIM_TO_SIZE_SIGNATURE = "()V";
+
+ private static final String CHAR_SEQUENCE_CLASS = "java/lang/CharSequence";
+ private static final String TO_STRING_METHOD = "toString";
+ private static final String TO_STRING_SIGNATURE = "()Ljava/lang/String;";
+
+ private static final String SUB_SEQUENCE_METHOD = "subSequence";
+ private static final String SUB_SEQUENCE_SIGNATURE = "(II)Ljava/lang/CharSequence;";
+
+ private static final String AUTOBOX_CLASS = "com/rc/retroweaver/runtime/Autobox";
+ private static final String VALUE_OF_METHOD = "valueOf";
+
+ private static final String JAVA_MATH_BIGDECIMAL_CLASS = "java/math/BigDecimal";
+ private static final String INT_CONSTRUCTOR_SIGNATURE = "(I)V";
+ private static final String LONG_CONSTRUCTOR_SIGNATURE = "(J)V";
+ private static final String DOUBLE_CONSTRUCTOR_SIGNATURE = "(D)V";
+ private static final String STRING_CONSTRUCTOR_SIGNATURE = "(Ljava/lang/String;)V";
+ private static final String TO_STRING_LONG_SIGNATURE = "(J)Ljava/lang/String;";
+ private static final String JAVA_LANG_LONG = "java/lang/Long";
+
+ private static final String JAVA_UTIL_COLLECTIONS = "java/util/Collections";
+ private static final String EMPTY_LIST_METHOD = "emptyList";
+ private static final String EMPTY_LIST_SIGNATURE = "()Ljava/util/List;";
+ private static final String EMPTY_LIST_FIELD = "EMPTY_LIST";
+ private static final String EMPTY_LIST_FIELD_DESC = "Ljava/util/List;";
+ private static final String EMPTY_MAP_METHOD = "emptyMap";
+ private static final String EMPTY_MAP_SIGNATURE = "()Ljava/util/Map;";
+ private static final String EMPTY_MAP_FIELD = "EMPTY_MAP";
+ private static final String EMPTY_MAP_FIELD_DESC = "Ljava/util/Map;";
+ private static final String EMPTY_SET_METHOD = "emptySet";
+ private static final String EMPTY_SET_SIGNATURE = "()Ljava/util/Set;";
+ private static final String EMPTY_SET_FIELD = "EMPTY_SET";
+ private static final String EMPTY_SET_FIELD_DESC = "Ljava/util/Set;";
+
+ private static Map<String, String> boxSignatures = new HashMap<String, String>();
+ static {
+ boxSignatures.put("java/lang/Boolean", "(Z)Ljava/lang/Boolean;");
+ boxSignatures.put("java/lang/Byte", "(B)Ljava/lang/Byte;");
+ boxSignatures.put("java/lang/Character", "(C)Ljava/lang/Character;");
+ boxSignatures.put("java/lang/Short", "(S)Ljava/lang/Short;");
+ boxSignatures.put("java/lang/Integer", "(I)Ljava/lang/Integer;");
+ boxSignatures.put("java/lang/Long", "(J)Ljava/lang/Long;");
+ boxSignatures.put("java/lang/Float", "(F)Ljava/lang/Float;");
+ boxSignatures.put("java/lang/Double", "(D)Ljava/lang/Double;");
+ }
+
+
+ class MethodWeaver extends MethodAdapter implements Opcodes {
+
+ public MethodWeaver(final MethodVisitor mv) {
+ super(mv);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ mv.visitMaxs(maxStack, maxLocals);
+ }
+
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc)
+ {
+ // ClassMethods substitutions
+ if ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && owner.equals(JAVA_LANG_CLASS)) {
+ // Change calls to "cast" or "asSubclass" to an INVOKESTATIC
+ // targeting one of our runtime methods
+ if (name.equals(CAST_METHOD) && desc.equals(CAST_SIGNATURE)) {
+ super.visitMethodInsn(INVOKESTATIC,
+ CLASS_METHODS_CLASS,
+ name,
+ CAST_NEW_SIGNATURE);
+ return;
+ } else if (name.equals(AS_SUB_CLASS_METHOD) && desc.equals(AS_SUB_CLASS_SIGNATURE)) {
+ super.visitMethodInsn(INVOKESTATIC,
+ CLASS_METHODS_CLASS,
+ name,
+ AS_SUB_CLASS_NEW_SIGNATURE);
+ return;
+ } else if (name.equals(IS_ENUM_METHOD) && desc.equals(IS_ENUM_SIGNATURE)) {
+ super.visitMethodInsn(INVOKESTATIC,
+ CLASS_METHODS_CLASS,
+ name,
+ IS_ENUM_NEW_SIGNATURE);
+ return;
+ } else if (name.equals(GET_ENUM_CONSTANTS_METHOD) && desc.equals(GET_ENUM_CONSTANTS_SIGNATURE)) {
+ super.visitMethodInsn(INVOKESTATIC,
+ CLASS_METHODS_CLASS,
+ name,
+ GET_ENUM_CONSTANTS_NEW_SIGNATURE);
+ return;
+ }
+ } else if (owner.equals(JAVA_MATH_BIGDECIMAL_CLASS)&&name.equals(INIT_METHOD)) {
+ if (desc.equals(INT_CONSTRUCTOR_SIGNATURE)) {
+ // replace int constructor with double one after convertion i2d, no loss of precision
+ super.visitInsn(I2D);
+ super.visitMethodInsn(INVOKESPECIAL,
+ JAVA_MATH_BIGDECIMAL_CLASS,
+ INIT_METHOD,
+ DOUBLE_CONSTRUCTOR_SIGNATURE);
+ return;
+ } else if (desc.equals(LONG_CONSTRUCTOR_SIGNATURE)) {
+ // longs cannot be converted to double, use toString() instead
+ super.visitMethodInsn(INVOKESTATIC,
+ JAVA_LANG_LONG,
+ TO_STRING_METHOD,
+ TO_STRING_LONG_SIGNATURE);
+ super.visitMethodInsn(INVOKESPECIAL,
+ JAVA_MATH_BIGDECIMAL_CLASS,
+ INIT_METHOD,
+ STRING_CONSTRUCTOR_SIGNATURE);
+ return;
+ }
+ } else if (opcode == INVOKEINTERFACE &&
+ owner.equals(ITERABLE_CLASS) &&
+ name.equals(ITERATOR_METHOD) &&
+ desc.equals(ITERATOR_SIGNATURE)) {
+ super.visitMethodInsn(INVOKESTATIC,
+ ITERABLE_METHODS_CLASS,
+ ITERATOR_METHOD,
+ ITERABLE_METHODS_ITERATOR_SIGNATURE);
+ return;
+ } else if (opcode == INVOKESTATIC &&
+ owner.equals(JAVA_LANG_SYSTEM)&&
+ name.equals(NANO_TIME_METHOD)&&
+ desc.equals(NANO_TIME_SIGNATURE)) {
+ // call to nanoTime() is replaced by currentTimeMillis()*1000000L
+ super.visitMethodInsn(INVOKESTATIC,
+ JAVA_LANG_SYSTEM,
+ CURRENT_TIME_MILLIS_METHOD,
+ CURRENT_TIME_MILLIS_TIME_SIGNATURE);
+ super.visitLdcInsn(new Long(1000000L));
+ super.visitInsn(LMUL);
+ return;
+ } else if (owner.equals(JAVA_LANG_STRINGBUFFER)||owner.equals(JAVA_LANG_STRINGBUILDER)) {
+ if ((opcode == INVOKESPECIAL) && name.equals(INIT_METHOD) && desc.equals(JAVA_LANG_STRINGBUFFER_INIT_SIGNATURE)) {
+ // StringBuffer(CharSequence)
+ // add toString() before arg and call StringBuffer(String)
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ TO_STRING_METHOD,
+ TO_STRING_SIGNATURE);
+ super.visitMethodInsn(INVOKESPECIAL,
+ JAVA_LANG_STRINGBUFFER,
+ INIT_METHOD,
+ JAVA_LANG_STRINGBUFFER_INIT_STRING_SIGNATURE);
+ return;
+ } else if (opcode == INVOKEVIRTUAL) {
+ if (name.equals(TRIM_TO_SIZE) && desc.equals(TRIM_TO_SIZE_SIGNATURE)) {
+ // do nothing: call is removed as from the 1.5 javadoc,
+ // there is no garantee the buffer capacity will be reduced to
+ // fit the actual size
+ super.visitInsn(POP);
+ return;
+ } else if (name.equals(APPEND_METHOD)) {
+ String d = desc.replaceAll("StringBuilder", "StringBuffer");
+ if (d.equals(APPEND_SIGNATURE1)) {
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ TO_STRING_METHOD,
+ TO_STRING_SIGNATURE);
+ super.visitMethodInsn(INVOKEVIRTUAL,
+ JAVA_LANG_STRINGBUFFER,
+ APPEND_METHOD,
+ APPEND_SIGNATURE3);
+ return;
+ } else if (d.equals(APPEND_SIGNATURE2)) {
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ SUB_SEQUENCE_METHOD,
+ SUB_SEQUENCE_SIGNATURE);
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ TO_STRING_METHOD,
+ TO_STRING_SIGNATURE);
+ super.visitMethodInsn(INVOKEVIRTUAL,
+ JAVA_LANG_STRINGBUFFER,
+ APPEND_METHOD,
+ APPEND_SIGNATURE3);
+ return;
+ }
+ } else if (name.equals(INSERT_METHOD)) {
+ String d = desc.replaceAll("StringBuilder", "StringBuffer");
+ if (d.equals(INSERT_SIGNATURE1)) {
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ TO_STRING_METHOD,
+ TO_STRING_SIGNATURE);
+ super.visitMethodInsn(INVOKEVIRTUAL,
+ JAVA_LANG_STRINGBUFFER,
+ INSERT_METHOD,
+ INSERT_SIGNATURE3);
+ return;
+ } else if (d.equals(INSERT_SIGNATURE2)) {
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ SUB_SEQUENCE_METHOD,
+ SUB_SEQUENCE_SIGNATURE);
+ super.visitMethodInsn(INVOKEINTERFACE,
+ CHAR_SEQUENCE_CLASS,
+ TO_STRING_METHOD,
+ TO_STRING_SIGNATURE);
+ super.visitMethodInsn(INVOKEVIRTUAL,
+ JAVA_LANG_STRINGBUFFER,
+ INSERT_METHOD,
+ INSERT_SIGNATURE3);
+ return;
+
+ }
+ }
+ }
+ } else if (opcode == INVOKESTATIC && owner.equals(JAVA_UTIL_COLLECTIONS)) {
+ if (name.equals(EMPTY_LIST_METHOD) && desc.equals(EMPTY_LIST_SIGNATURE)) {
+ super.visitFieldInsn(GETSTATIC, owner, EMPTY_LIST_FIELD, EMPTY_LIST_FIELD_DESC);
+ return;
+ } else if (name.equals(EMPTY_MAP_METHOD) && desc.equals(EMPTY_MAP_SIGNATURE)) {
+ super.visitFieldInsn(GETSTATIC, owner, EMPTY_MAP_FIELD, EMPTY_MAP_FIELD_DESC);
+ return;
+ } else if (name.equals(EMPTY_SET_METHOD) && desc.equals(EMPTY_SET_SIGNATURE)) {
+ super.visitFieldInsn(GETSTATIC, owner, EMPTY_SET_FIELD, EMPTY_SET_FIELD_DESC);
+ return;
+ }
+ }
+
+ // Autoboxing
+ else if (opcode == INVOKESTATIC && name.equals(VALUE_OF_METHOD)) {
+ String sig = boxSignatures.get(owner);
+ if (sig != null && sig.equals(desc)) {
+ // owner and signature match
+ mv.visitMethodInsn(INVOKESTATIC,
+ AUTOBOX_CLASS,
+ VALUE_OF_METHOD,
+ boxSignatures.get(owner));
+
+ return;
+ }
+ }
+
+ // not a special case, use default implementation
+ super.visitMethodInsn(opcode, owner, name, desc);
+ }
+
+
+ public void visitLdcInsn(final Object cst) {
+ if (cst instanceof Type) {
+ /**
+ * Fix class literals. The 1.5 VM has had its ldc* instructions updated so
+ * that it knows how to deal with CONSTANT_Class in addition to the other
+ * types. So, we have to search for uses of ldc* that point to a
+ * CONSTANT_Class and replace them with synthetic field access the way
+ * it was generated in 1.4.
+ */
+
+ // LDC or LDC_W with a class as argument
+
+ Type t = (Type) cst;
+ String fieldName = getClassLiteralFieldName(t);
+
+ classLiteralCalls.add(fieldName);
+
+ mv.visitFieldInsn(GETSTATIC, className, fieldName, CLASS_FIELD_DESC);
+ Label nonNullLabel = new Label();
+ mv.visitJumpInsn(IFNONNULL, nonNullLabel);
+ String s;
+ if (t.getSort() == Type.OBJECT)
+ s = t.getInternalName();
+ else
+ s = t.getDescriptor();
+
+ /* convert retroweaver runtime classes:
+ * Enum into com.rc.retroweaver.runtime.Enum_
+ * concurrent classes into their backport equivalent
+ * ...
+ */
+ s = NameTranslator.translate(s);
+
+ mv.visitLdcInsn(s.replace('/', '.'));
+ if (isInterface) {
+ /* synthethic methods cannot be generated in interfaces so the byte
+ * code has to be inlined for each call to ".class"
+ */
+ generateClassCall(mv);
+ } else {
+ mv.visitMethodInsn(INVOKESTATIC, className, CLASS_METHOD, CLASS_SIGNATURE);
+ }
+ mv.visitInsn(DUP);
+ mv.visitFieldInsn(PUTSTATIC, className, fieldName, CLASS_FIELD_DESC);
+ Label endLabel = new Label();
+ mv.visitJumpInsn(GOTO, endLabel);
+ mv.visitLabel(nonNullLabel);
+ mv.visitFieldInsn(GETSTATIC, className, fieldName, CLASS_FIELD_DESC);
+ mv.visitLabel(endLabel);
+
+ } else {
+ super.visitLdcInsn(cst);
+ }
+ }
+
+ private String getClassLiteralFieldName(Type type) {
+ String fieldName;
+ if (type.getSort() == Type.ARRAY) {
+ fieldName = "array" + type.getDescriptor().replace('[', '$');
+ if (fieldName.charAt(fieldName.length()-1) == ';')
+ fieldName = fieldName.substring(0, fieldName.length()-1);
+ } else {
+ fieldName = "class$" + type.getInternalName();
+ }
+ fieldName = fieldName.replace('/', '$');
+
+ return fieldName;
+ }
+
+}
+
+}
+
+class DefaultWeaveListener implements WeaveListener {
+
+ private boolean verbose;
+
+ DefaultWeaveListener(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void weavingStarted(String msg) {
+ System.out.println("[RetroWeaver] " + msg);
+ }
+
+ public void weavingCompleted(String msg) {
+ System.out.println("[RetroWeaver] " + msg);
+ }
+
+ public void weavingPath(String sourcePath) {
+ if (verbose)
+ System.out.println("[RetroWeaver] Weaving " + sourcePath);
+ }
+};
+
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,245 @@
+package com.rc.retroweaver;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class RetroWeaverClassLoader extends ClassLoader {
+
+ private RetroWeaver retroWeaver;
+
+ private List<ClassPathElement> classPathElements;
+
+ protected void setWeaver(RetroWeaver retroWeaver) {
+ this.retroWeaver = retroWeaver;
+ }
+
+ protected void setClassPath(List<String> classPath) {
+ classPathElements = new LinkedList<ClassPathElement>();
+
+ for(String pathEntry: classPath) {
+ File f = new File(pathEntry);
+ if (f.exists()) {
+ if (f.isDirectory())
+ addDirectoryClassPathElement(pathEntry);
+ else
+ addJarClassPathElement(pathEntry);
+ }
+ }
+ }
+
+ protected void setClassPath(String classPath) {
+ List<String> l = new LinkedList<String>();
+
+ if (classPath != null) {
+ StringTokenizer t = new StringTokenizer(classPath, File.pathSeparator);
+ while (t.hasMoreTokens()) {
+ l.add(t.nextToken());
+ }
+ }
+
+ setClassPath(l);
+ }
+
+ protected void addDirectoryClassPathElement(String dirName) {
+ DirectoryElement e = new DirectoryElement(dirName);
+ classPathElements.add(e);
+ }
+
+ protected void addJarClassPathElement(String jarName) {
+ try {
+ JarElement e = new JarElement(jarName);
+ classPathElements.add(e);
+ } catch (IOException ioe) {
+ }
+ }
+
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ String resourceName = name.replace('.', '/') + ".class";
+ for (ClassPathElement e : classPathElements) {
+ if (e.hasResource(resourceName)) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ InputStream is = e.getResourceStream(resourceName);
+
+ byte b[];
+ boolean weaved;
+ try {
+ weaved = retroWeaver.weave(is, name, bos);
+ } catch (IOException ioe) {
+ throw new RetroWeaverException("Problem weaving class " + name
+ + ": " + ioe.getMessage());
+ }
+ if (weaved)
+ b = bos.toByteArray();
+ else
+ b = e.getResourceData(resourceName);
+
+ Class clazz = defineClass(name.replace('/', '.'), b, 0, b.length);
+
+ return clazz;
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ protected byte[] getClassData(String name) throws ClassNotFoundException {
+ String resourceName = name.replace('.', '/') + ".class";
+ for (ClassPathElement e : classPathElements) {
+ if (e.hasResource(resourceName)) {
+ byte b[] = e.getResourceData(resourceName);
+
+ return b;
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ protected URL findResource(String name) {
+ for (ClassPathElement e : classPathElements) {
+ if (e.hasResource(name))
+ return e.getResourceURL(name);
+ }
+ return null;
+ }
+
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ ArrayList<URL> l = new ArrayList<URL>();
+ for (ClassPathElement e : classPathElements) {
+ if (e.hasResource(name))
+ l.add(e.getResourceURL(name));
+ }
+ return Collections.enumeration(l);
+ }
+
+}
+
+abstract class ClassPathElement {
+
+ protected abstract boolean hasResource(String name);
+
+ protected abstract URL getResourceURL(String name);
+
+ protected abstract InputStream getResourceStream(String name);
+
+ protected byte[] getResourceData(String name) {
+ assert (hasResource(name));
+
+ InputStream is = getResourceStream(name);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ DataInputStream ds = new DataInputStream(is);
+
+ byte b[] = new byte[2048];
+ int i;
+ try {
+ while((i = ds.read(b)) != -1) {
+ bos.write(b, 0, i);
+ }
+ return bos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ } finally {
+ try {
+ ds.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+}
+
+class DirectoryElement extends ClassPathElement {
+
+ private String dirName;
+
+ DirectoryElement(String dirName) {
+ this.dirName = dirName;
+ }
+
+ protected boolean hasResource(String name) {
+ String fullPath = dirName + File.separatorChar + name;
+
+ File f = new File(fullPath);
+
+ return f.exists() && f.isFile();
+ }
+
+ protected URL getResourceURL(String name) {
+ assert (hasResource(name));
+
+ String fullPath = dirName + File.separatorChar + name;
+
+ try {
+ return new URL("file:" + fullPath);
+ } catch (MalformedURLException e) {
+ }
+ return null;
+ }
+
+ protected InputStream getResourceStream(String name) {
+ assert (hasResource(name));
+
+ try {
+ File f = new File(dirName + File.separatorChar + name);
+ return new FileInputStream(f);
+ } catch (IOException ioe) {
+ }
+ return null;
+ }
+
+}
+
+class JarElement extends ClassPathElement {
+
+ private String jarName;
+
+ private ZipFile jarFile;
+
+ JarElement(String jarName) throws IOException {
+ this.jarName = jarName;
+ jarFile = new ZipFile(jarName);
+ }
+
+ protected boolean hasResource(String name) {
+ ZipEntry entry = jarFile.getEntry(name);
+
+ return entry != null;
+ }
+
+ protected URL getResourceURL(String name) {
+ assert (hasResource(name));
+
+ try {
+ return new URL("jar:file:" + jarName + "!/" + name);
+ } catch (MalformedURLException e) {
+ }
+ return null;
+ }
+
+ protected InputStream getResourceStream(String name) {
+ assert (hasResource(name));
+
+ try {
+ ZipEntry entry = jarFile.getEntry(name);
+ return jarFile.getInputStream(entry);
+ } catch (IOException ioe) {
+ }
+ return null;
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,17 @@
+package com.rc.retroweaver;
+
+public class RetroWeaverException extends RuntimeException {
+
+ public RetroWeaverException(String message) {
+ super(message);
+ }
+
+ public RetroWeaverException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RetroWeaverException(Throwable cause) {
+ super(cause);
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,64 @@
+package com.rc.retroweaver;
+
+import org.objectweb.asm.ClassAdapter;
+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;
+
+public class SignatureStripper extends ClassAdapter {
+
+ public SignatureStripper(ClassVisitor cv) {
+ super(cv);
+ }
+
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces)
+ {
+ super.visit(version, access, name, null, superName, interfaces);
+ }
+
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final Object value)
+ {
+ return super.visitField(access, name, desc, null, value);
+ }
+
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions)
+ {
+ return new MethodSignatureStripper(super.visitMethod(access, name, desc, null, exceptions));
+ }
+
+ static class MethodSignatureStripper extends MethodAdapter {
+ MethodSignatureStripper(MethodVisitor mv) {
+ super(mv);
+ }
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ super.visitLocalVariable(name, desc, null, start, end, index);
+ }
+
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,160 @@
+package com.rc.retroweaver;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.jar.Attributes;
+
+public class WeaveRunner {
+
+ private RetroWeaver retroWeaver;
+
+ private RetroWeaverClassLoader classLoader;
+
+ public WeaveRunner(String classPath) {
+ // use the current JVM version as the target
+ String version = System.getProperty("java.version");
+ int target;
+ switch (version.charAt(2)) {
+ case '2':
+ target = Weaver.VERSION_1_2;
+ break;
+ case '3':
+ target = Weaver.VERSION_1_3;
+ break;
+ case '4':
+ target = Weaver.VERSION_1_4;
+ break;
+ case '5':
+ target = Weaver.VERSION_1_5;
+ break;
+ default:
+ throw new RetroWeaverException("Unsupported JVM version: " + version);
+ }
+ retroWeaver = new RetroWeaver(target);
+ retroWeaver.setLazy(true);
+
+ classLoader = new RetroWeaverClassLoader();
+ classLoader.setClassPath(classPath);
+ classLoader.setWeaver(retroWeaver);
+ }
+
+ public void run(String className, String[] args)
+ throws ClassNotFoundException, NoSuchMethodException {
+ Class clazz = classLoader.loadClass(className);
+
+ Method m = clazz.getMethod("main", new Class[] { args.getClass() });
+ m.setAccessible(true);
+ int mods = m.getModifiers();
+ if (m.getReturnType() != void.class || !Modifier.isStatic(mods)
+ || !Modifier.isPublic(mods)) {
+ throw new NoSuchMethodException("main");
+ }
+ try {
+ m.invoke(null, new Object[] { args });
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException ite) {
+ throw new RetroWeaverException(ite);
+ }
+ }
+
+ public void executeJar(String jarFileName, String[] args)
+ throws ClassNotFoundException, NoSuchMethodException {
+ // add jar to class path
+ classLoader.addJarClassPathElement(jarFileName);
+
+ // get class name from MANIFEST
+ String className = null;
+ try {
+ URL u = new URL("jar:file:" + jarFileName + "!/");
+ JarURLConnection uc = (JarURLConnection) u.openConnection();
+ Attributes attr = uc.getMainAttributes();
+
+ if (attr != null)
+ className = attr.getValue(Attributes.Name.MAIN_CLASS);
+ } catch (IOException ioe) {
+ }
+
+ if (className == null) {
+ System.err.println("No " + Attributes.Name.MAIN_CLASS
+ + " specified in jar file " + jarFileName);
+ } else {
+ run(className, args);
+ }
+ }
+
+ public static void main(String[] args) throws ClassNotFoundException,
+ NoSuchMethodException {
+ String classPath = null;
+ String mainClass = null;
+ String jarFileName = null;
+
+ int argIndex = 0;
+ while (argIndex < args.length) {
+ String command = args[argIndex++];
+
+ if (command.equals("-cp") || command.equals("-classpath")) {
+ classPath = args[argIndex++];
+ } else if (command.equals("-jar")) {
+ jarFileName = args[argIndex++];
+ break;
+ } else {
+ mainClass = command;
+ break;
+ }
+ }
+ if (jarFileName == null) {
+ String errorMsg = null;
+
+ if (classPath == null)
+ errorMsg = "Missing class path";
+ if (mainClass == null)
+ errorMsg = "Missing main class or jar option";
+
+ if (errorMsg != null) {
+ System.out.println(errorMsg);
+ System.out.println();
+ usage();
+ return;
+ }
+ }
+
+ String[] realArgs = new String[args.length - argIndex];
+ System.arraycopy(args, argIndex, realArgs, 0, args.length - argIndex);
+
+ WeaveRunner runner = new WeaveRunner(classPath);
+
+ if (jarFileName != null)
+ runner.executeJar(jarFileName, realArgs);
+ else
+ runner.run(mainClass, realArgs);
+ }
+
+ private static final String nl = System.getProperty("line.separator");
+
+ private static void usage() {
+ String msg = "Usage: WeaveRunner [-options] class [args...]"
+ + nl
+ + "\t\t(to execute a class)"
+ + nl
+ + "\tor WeaveRunner [-options] -jar jarfile [args...]"
+ + nl
+ + "\t\t(to execute a jar file)"
+ + nl
+ + nl
+ + "where options include:"
+ + nl
+ + "\t-cp <class search path of directories and zip/jar files>"
+ + nl
+ + "\t-classpath <class search path of directories and zip/jar files>"
+ + nl + "\t\tA " + File.pathSeparatorChar
+ + " separated list of directories, JAR archives," + nl
+ + "\t\tand ZIP archives to search for class files." + nl;
+ System.out.println(msg);
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,168 @@
+package com.rc.retroweaver;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Applies the RetroWeaver against a set of classes.
+ *
+ */
+public class Weaver {
+
+ private static final String VERSION;
+
+ private static final String BUILD_NUMBER;
+
+ public static final String getVersion() {
+ return VERSION + " (build " + BUILD_NUMBER + ')';
+ }
+
+ static {
+ ResourceBundle bundle = ResourceBundle.getBundle("retroweaver");
+ VERSION = bundle.getString("retroweaver.version");
+ BUILD_NUMBER = bundle.getString("retroweaver.buildNumber");
+ }
+
+ // Read the new class file format spec for how the version is computed.
+ public static final int VERSION_1_5 = 49;
+
+ public static final int VERSION_1_4 = 48;
+
+ public static final int VERSION_1_3 = 47;
+
+ public static final int VERSION_1_2 = 46;
+
+ private static final String nl = System.getProperty("line.separator");
+
+ public static void main(String[] args) {
+
+ String source = null;
+ String sourceJar = null;
+ String destJar = null;
+ int target = VERSION_1_4;
+ int currentArg = 0;
+ boolean lazy = false;
+ boolean stripSignatures = false;
+ boolean verbose = false;
+ String verifyPath = null;
+
+ while (currentArg < args.length) {
+ String command = args[currentArg];
+ ++currentArg;
+
+ if (command.equals("-source")) {
+ source = args[currentArg++];
+ } else if (command.equals("-jar")) {
+ sourceJar = args[currentArg++];
+ destJar = args[currentArg++];
+
+ if (sourceJar.equals(destJar)) {
+ System.out
+ .println("source and destination jar files can not be identical");
+ System.out.println();
+ System.exit(1);
+ }
+ } else if (command.equals("-version")) {
+ System.out.println("Retroweaver version " + getVersion());
+ System.exit(0);
+ } else if (command.equals("-target")) {
+ String verStr = args[currentArg++];
+ if (verStr.equals("1.4")) {
+ target = VERSION_1_4;
+ } else if (verStr.equals("1.3")) {
+ target = VERSION_1_3;
+ } else if (verStr.equals("1.2")) {
+ target = VERSION_1_2;
+ } else {
+ System.out.println("Invalid target version: " + verStr);
+ System.out.println();
+ System.out.println(getUsage());
+ System.exit(1);
+ }
+ } else if (command.equals("-lazy")) {
+ lazy = true;
+ } else if (command.equals("-stripSignatures")) {
+ stripSignatures = true;
+ } else if (command.equals("-verbose")) {
+ verbose = true;
+ } else if (command.equals("-verifyrefs")) {
+ verifyPath = args[currentArg++];
+ } else {
+ System.out
+ .println("I don't understand the command: " + command);
+ System.out.println();
+ System.out.println(getUsage());
+ System.exit(1);
+ }
+ }
+
+ if (source == null && sourceJar == null) {
+ System.out.println("Option \"-source\" or \"-jar\" is required.");
+ System.out.println();
+ System.out.println(getUsage());
+ System.exit(1);
+ }
+
+ if (source != null && sourceJar != null) {
+ System.out
+ .println("Only one of \"-source\" or \"-jar\" can be specified.");
+ System.out.println();
+ System.out.println(getUsage());
+ System.exit(1);
+ }
+
+ File sourcePath = null;
+
+ RetroWeaver weaver = new RetroWeaver(target);
+ weaver.setListener(new DefaultWeaveListener(verbose));
+ weaver.setLazy(lazy);
+ weaver.setStripSignatures(stripSignatures);
+
+ if (verifyPath != null) {
+ List<String> paths = new ArrayList<String>();
+ StringTokenizer st = new StringTokenizer(verifyPath,
+ File.pathSeparator);
+ while (st.hasMoreTokens()) {
+ paths.add(st.nextToken());
+ }
+ RefVerifier rv = new RefVerifier(target, new EmptyVisitor(), paths,
+ new DefaultRefVerifierListener(verbose));
+ weaver.setVerifier(rv);
+ }
+
+ try {
+ if (source != null) {
+ sourcePath = new File(source);
+
+ weaver.weave(sourcePath);
+ } else {
+ weaver.weaveJarFile(sourceJar, destJar);
+ }
+ } catch (Exception e) {
+ throw new RetroWeaverException("Weaving failed", e);
+ }
+ }
+
+ private static String getUsage() {
+ return "Usage: Weaver <options>"
+ + nl
+ + " Options: "
+ + nl
+ + " -source <source dir>"
+ + nl
+ + " -jar <source jar> <target jar>"
+ + nl
+ + " -target <target VM version> (one of {1.4, 1.3, 1.2}, default is 1.4)"
+ + nl + " -verifyrefs <classpath>" + nl
+ + " -stripSignatures (strip generic signatures, off by default)" + nl
+ + " -verbose (message for each processed class)" + nl
+ + " -version (display version number and exit)" + nl + nl
+ + "One of \"-source\" or \"-jar\" is required.";
+ }
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,402 @@
+package com.rc.retroweaver.ant;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import com.rc.retroweaver.RefVerifier;
+import com.rc.retroweaver.RetroWeaver;
+import com.rc.retroweaver.event.VerifierListener;
+import com.rc.retroweaver.event.WeaveListener;
+
+/**
+ * An Ant task for running RetroWeaver on a set of class files.
+ */
+public class RetroWeaverTask extends Task {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Constants and variables.
+
+ /**
+ * The destination directory for processd classes, or <code>null</code> for in place
+ * processing.
+ */
+ private File itsDestDir;
+
+ /**
+ * Indicates if an error should cause the script to fail. Default to <code>true</code>.
+ */
+ private boolean itsFailOnError = true;
+
+ /**
+ * The set of files to be weaved.
+ */
+ private List<FileSet> itsFileSets = new ArrayList<FileSet>();
+
+ private List<DirSet> itsDirSets = new ArrayList<DirSet>();
+
+ private String inputJar;
+
+ private String outputJar;
+
+ /**
+ * Indicates if classes should only be processed if their current version differ from the target version. Initially <code>true</code>.
+ */
+ private boolean itsLazy = true;
+
+ /**
+ * Indicates whether the generic signatures should be stripped. Default to <code>false</code>.
+ */
+ private boolean stripSignatures;
+
+ /**
+ * Indicates if each processed class should be logged. Initially set to <code>false</code>.
+ */
+ private boolean itsVerbose = false;
+
+ /**
+ * The classpath to use to verify the weaved result
+ */
+ private Path verifyClasspath;
+
+ private boolean verify = true;
+
+ /**
+ * The class file version number.
+ */
+ private int itsVersion = 48;
+
+ /**
+ * The class file version number.
+ */
+ private static final Map<String, Integer> itsVersionMap = new HashMap<String, Integer>();
+
+ /**
+ * Initialize the version map.
+ */
+ static {
+ itsVersionMap.put("1.2", 46);
+ itsVersionMap.put("1.3", 47);
+ itsVersionMap.put("1.4", 48);
+ itsVersionMap.put("1.5", 49);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Property accessors and mutators.
+
+ /**
+ * Set the destination directory for processed classes. Unless specified the classes
+ * are processed in place.
+ * @param pDir The destination directory.
+ */
+ public void setDestDir(File pDir) {
+ if (!pDir.isDirectory())
+ throw new BuildException(
+ "The destination directory doesn't exist: " + pDir,
+ getLocation());
+
+ itsDestDir = pDir;
+ }
+
+ /**
+ * Specify if an error should cause the script to fail. Default to <code>true</code>.
+ *
+ * @param pFailOnError <code>true</code> to fail, <code>false</code> to keep going.
+ */
+ public void setFailOnError(boolean pFailOnError) {
+ itsFailOnError = pFailOnError;
+ }
+
+ /**
+ * Add a set of files to be weaved.
+ * @param pSet The fileset.
+ */
+ public void addFileSet(FileSet pFileSet) {
+ itsFileSets.add(pFileSet);
+ }
+
+ public void addDirSet(DirSet pFileSet) {
+ itsDirSets.add(pFileSet);
+ }
+
+ /**
+ * Specify if classes should only be processed if their current version differ from the target version. Initially <code>true</code>.
+ * @param pLazy <code>true</code> for lazy processing.
+ */
+ public void setLazy(boolean pLazy) {
+ itsLazy = pLazy;
+ }
+
+ /**
+ * Set the source directory containing classes to process. This is a shortcut to
+ * using an embedded fileset with the specified base directory and which includes
+ * all class files.
+ * @param pDir The directory.
+ */
+ public void setSrcDir(File pDir) {
+ FileSet fileSet = new FileSet();
+ fileSet.setDir(pDir);
+ fileSet.setIncludes("**/*.class");
+
+ addFileSet(fileSet);
+ }
+
+ /**
+ * Specify if each processed class should be logged. Initially set to <code>false</code>.
+ * @param pVerbose <code>true</code> for verbose processing.
+ */
+ public void setVerbose(boolean pVerbose) {
+ itsVerbose = pVerbose;
+ }
+
+ /**
+ * Set the target class file version. Initially set to "1.4".
+ * @param target The JDK target version, e g "1.3".
+ */
+ public void setTarget(String target) {
+ Integer v = itsVersionMap.get(target);
+ if (v == null)
+ throw new BuildException("Unknown target: " + target, getLocation());
+ itsVersion = v;
+ }
+
+ /**
+ * Set the classpath to be used for verification.
+ * Retroweaver will report any references to fields/methods/classes which don't appear
+ * on refClassPath.
+ * @param classpath an Ant Path object containing the compilation classpath.
+ */
+ public void setClasspath(Path classpath) {
+ if (verifyClasspath == null) {
+ verifyClasspath = classpath;
+ } else {
+ verifyClasspath.append(classpath);
+ }
+ }
+
+ /**
+ * Gets the classpath to be used for verification.
+ * @return the class path
+ public Path getClasspath() {
+ return verifyClasspath;
+ }
+
+ /**
+ * Adds a path to the classpath.
+ * @return a class path to be configured
+ */
+ public Path createClasspath() {
+ if (verifyClasspath == null) {
+ verifyClasspath = new Path(getProject());
+ }
+ return verifyClasspath.createPath();
+ }
+
+ /**
+ * Adds a reference to a classpath defined elsewhere.
+ * @param r a reference to a classpath
+ */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Turn off verification if desired
+ * @return is verification enabled?
+ */
+ public void setVerify(boolean newVerify) {
+ verify = newVerify;
+ }
+
+ /**
+ * Turn off verification if desired
+ * @return is verification enabled?
+ */
+ public boolean doVerify() {
+ return verify;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Operations.
+
+ /**
+ * Run the RetroWeaver task.
+ * @throws BuildException If a build exception occurs.
+ */
+ public void execute() throws BuildException {
+
+ for (DirSet set : itsDirSets) {
+ File baseDir = set.getDir(getProject());
+ DirectoryScanner scanner = set.getDirectoryScanner(getProject());
+
+ // create a non recursive file set for each included directory
+ for (String fileName : scanner.getIncludedDirectories()) {
+ FileSet fileSet = new FileSet();
+ fileSet.setDir(new File(baseDir, fileName));
+ fileSet.setIncludes("*.class");
+ addFileSet(fileSet);
+ }
+ }
+
+ // Check arguments.
+
+ boolean hasFileSet = itsFileSets.size() != 0 || itsDirSets.size() != 0;
+
+ if (inputJar != null) {
+ if (outputJar == null)
+ throw new BuildException("'outputjar' must be set.");
+
+ if (hasFileSet)
+ throw new BuildException(
+ "'inputjar' is incompatible with filesets and dirsets");
+ } else if (!hasFileSet)
+ throw new BuildException(
+ "Either attribute 'srcdir' or 'inputjar' must be used or atleast one fileset or dirset must be embedded.",
+ getLocation());
+
+ // Create and configure the weaver.
+
+ RetroWeaver weaver = new RetroWeaver(itsVersion);
+ weaver.setLazy(itsLazy);
+ weaver.setStripSignatures(stripSignatures);
+
+ // Set up a listener.
+ weaver.setListener(new WeaveListener() {
+ public void weavingStarted(String msg) {
+ getProject().log(RetroWeaverTask.this, msg, Project.MSG_INFO);
+ }
+
+ public void weavingCompleted(String msg) {
+ getProject().log(RetroWeaverTask.this, msg, Project.MSG_INFO);
+ }
+
+ public void weavingPath(String pPath) {
+ if (itsVerbose)
+ getProject().log(RetroWeaverTask.this, "Weaving " + pPath,
+ Project.MSG_INFO);
+ }
+ });
+
+ if (verifyClasspath != null && doVerify()) {
+
+ List<String> refPath = new ArrayList<String>();
+
+ for (String pathItem : verifyClasspath.list()) {
+ refPath.add(pathItem);
+ }
+ if (itsDestDir != null) {
+ refPath.add(itsDestDir.getPath());
+ }
+
+ RefVerifier rv = new RefVerifier(itsVersion, new EmptyVisitor(), refPath, new VerifierListener() {
+ public void verifyPathStarted(String msg) {
+ getProject().log(RetroWeaverTask.this, msg,
+ Project.MSG_INFO);
+ }
+
+ public void verifyClassStarted(String msg) {
+ if (itsVerbose)
+ getProject().log(RetroWeaverTask.this, msg,
+ Project.MSG_INFO);
+ }
+
+ public void acceptWarning(String msg) {
+ getProject().log(RetroWeaverTask.this, msg,
+ Project.MSG_WARN);
+ }
+
+ public void displaySummary(int warningCount) {
+ String msg = "Verification complete, " + warningCount
+ + " warning(s).";
+ getProject().log(RetroWeaverTask.this, msg,
+ Project.MSG_WARN);
+
+ if (itsFailOnError)
+ throw new ExitStatusException(Integer
+ .toString(warningCount)
+ + " warning(s)", 1);
+ }
+ });
+ weaver.setVerifier(rv);
+ }
+
+ try {
+ if (inputJar != null) {
+ weaver.weaveJarFile(inputJar, outputJar);
+ } else {
+ // Weave the files in the filesets.
+
+ // Process each fileset.
+ String[][] fileSets = new String[itsFileSets.size()][];
+ File[] baseDirs = new File[itsFileSets.size()];
+ int i = 0;
+ for (FileSet fileSet : itsFileSets) {
+ // Create a directory scanner for the fileset.
+ File baseDir = fileSet.getDir(getProject());
+ DirectoryScanner scanner = fileSet
+ .getDirectoryScanner(getProject());
+ fileSets[i] = scanner.getIncludedFiles();
+ baseDirs[i++] = baseDir;
+ }
+
+ weaver.weave(baseDirs, fileSets, itsDestDir);
+ }
+ }
+ catch (BuildException ex) {
+ throw ex;
+ }
+ catch (Exception ex) {
+ // unexpected exception
+ ex.printStackTrace();
+ throw new BuildException(ex, getLocation());
+ }
+ }
+
+ /**
+ * @return Returns the inputJar.
+ */
+ public String getInputJar() {
+ return inputJar;
+ }
+
+ /**
+ * @param inputJar The inputJar to set.
+ */
+ public void setInputJar(String inputJar) {
+ this.inputJar = inputJar;
+ }
+
+ /**
+ * @return Returns the outputJar.
+ */
+ public String getOutputJar() {
+ return outputJar;
+ }
+
+ /**
+ * @param outputJar The outputJar to set.
+ */
+ public void setOutputJar(String outputJar) {
+ this.outputJar = outputJar;
+ }
+
+ /**
+ * @param stripSignatures The stripSignatures to set.
+ */
+ public void setStripSignatures(boolean stripSignatures) {
+ this.stripSignatures = stripSignatures;
+ }
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,13 @@
+package com.rc.retroweaver.event;
+
+public interface VerifierListener {
+
+ void verifyPathStarted(String msg);
+
+ void verifyClassStarted(String msg);
+
+ void acceptWarning(String msg);
+
+ void displaySummary(int warningCount);
+
+}
Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java Thu Jul 6 15:08:30 2006
@@ -0,0 +1,13 @@
+package com.rc.retroweaver.event;
+
+/**
+ * A callback interface to indicate weaving status.
+ */
+public interface WeaveListener {
+ void weavingStarted(String msg);
+
+ void weavingPath(String sourcePath);
+
+ void weavingCompleted(String msg);
+
+}