
net.sourceforge.retroweaver.RetroWeaver Maven / Gradle / Ivy
Go to download
Retroweaver is a tool, which converts Java 5 (or 6) compliant
class files into Java 1.x compliant class files. The jar file
retroweaver.jar contains both the class processor (which may
be used at compile time) and the runtime classes. Additionally
there is the jar file retroweaver-rt.jar (which contains the
runtime classes only).
The newest version!
package net.sourceforge.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.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import net.sourceforge.retroweaver.event.WeaveListener;
import net.sourceforge.retroweaver.optimizer.ClassConstantsCollector;
import net.sourceforge.retroweaver.optimizer.Constant;
import net.sourceforge.retroweaver.optimizer.ConstantComparator;
import net.sourceforge.retroweaver.optimizer.ConstantPool;
import net.sourceforge.retroweaver.translator.NameSpace;
import net.sourceforge.retroweaver.translator.NameTranslator;
import net.sourceforge.retroweaver.translator.NameTranslatorClassVisitor;
import net.sourceforge.retroweaver.translator.TranslatorException;
import org.objectweb.asm.Attribute;
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;
/**
* 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 final int target;
private boolean lazy;
/**
* Indicates whether the generic signatures should be stripped. Default to false
.
*/
private boolean stripSignatures;
/**
* Indicates whether the custom retroweaver attributes should be stripped. Default to false
.
*/
private boolean stripAttributes;
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 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) {
if (n > 0) {
listener.weavingStarted("Processing " + n + (n == 1?" class":" classes"));
}
}
private void displayEndMessage() {
if (weavedClassCount > 0) {
listener.weavingCompleted(Integer.toString(weavedClassCount) + (weavedClassCount == 1?" class":" classes") + " weaved.");
}
}
public void weave(File path) throws IOException {
ArrayList fileSets = new ArrayList();
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 weaved = new HashSet();
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 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(new JarEntry(name));
// writing data
int len;
final byte[] buf = new byte[1024];
while ((len = dataStream.read(buf)) >= 0) {
out.write(buf, 0, len);
}
}
out.close();
jarFile.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++;
String path;
if (outputPath == null) {
path = sourcePath;
} else {
path = outputPath;
// create parent dir if necessary
File parentDir = new File(path).getParentFile();
if (parentDir != null) {
parentDir.mkdirs();
}
}
FileOutputStream fos = new FileOutputStream(path);
fos.write(bos.toByteArray());
fos.close();
recordFileForVerifier(path);
} 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) { // NOPMD by xlv
}
}
}
private void recordFileForVerifier(String fileName) {
if (verifier != null) {
verifier.addClass(fileName);
}
}
private static final boolean COMPACT_CONSTANTS = true;
protected static final Attribute[] CUSTOM_ATTRIBUTES = {
new RetroWeaverAttribute(Weaver.getBuildNumber(), Weaver.VERSION_1_5)
};
private boolean classpathChecked;
private boolean isRuntimeInClassPath() {
if (!classpathChecked) {
try {
Class.forName("net.sourceforge.retroweaver.runtime.java.lang.annotation.AIB");
classpathChecked = true;
} catch (ClassNotFoundException e) {
listener.weavingError("Error: the retroweaver runtime must be in the classpath");
return false;
}
}
return true;
}
protected boolean weave(InputStream sourceStream, String fileName, ByteArrayOutputStream bos)
throws IOException {
if (!isRuntimeInClassPath()) {
return false;
}
ClassReader cr = new ClassReader(sourceStream);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
try {
// chain class visitors
ClassVisitor classVisitor = cw;
ConstantPool cp;
if (COMPACT_CONSTANTS) {
cp = new ConstantPool();
classVisitor = new ClassConstantsCollector(classVisitor, cp);
}
classVisitor = new NameTranslatorClassVisitor(classVisitor, NameTranslator.getGeneralTranslator());
classVisitor = new ClassWeaver(classVisitor,
lazy, stripAttributes, target, listener);
classVisitor = new NameTranslatorClassVisitor(classVisitor, NameTranslator.getHarmonyTranslator());
// StringBuilder translation will be done before general weaving and
// mirror translation: trimToSize() calls will be processed correctly
// and no need to do translations in general weaving process
classVisitor = new NameTranslatorClassVisitor(classVisitor, NameTranslator.getStringBuilderTranslator());
if (stripSignatures) {
classVisitor = new SignatureStripper(classVisitor);
}
cr.accept(classVisitor, CUSTOM_ATTRIBUTES, ClassReader.EXPAND_FRAMES);
if (COMPACT_CONSTANTS) {
Set constants = new TreeSet(new ConstantComparator());
constants.addAll(cp.values());
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(0);
for(Constant c: constants) {
c.write(cw);
}
cr.accept(cw, 0);
}
bos.write(cw.toByteArray());
return true;
} catch (TranslatorException te) {
listener.weavingError(te.getMessage());
return false;
} catch (LazyException e) {
return false;
}
}
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 + " " + newLine
+ " [
© 2015 - 2025 Weber Informatics LLC | Privacy Policy