import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.*;
-// FEATURE: Rebuild each method with a new constant pool to eliminate extra constant pool entries
-
public class GCClass {
private static final String[] PRE_REF = {
"java.lang.Thread.run",
"java.security.*"
};
+ private static final String[] IGNORED_FIELDS = {
+ "java.io.ObjectInputStream.SUBCLASS_IMPLEMENTATION_PERMISSION"
+ };
+
private static final String[] NO_OUTPUT = { "java", "javax", "sun", "com.sun", "apple", "com.apple" };
public static void main(String[] args) throws Exception {
if(args.length < 3) {
- System.err.println("Usage GCClass classpath outdir entrypoint1 ... [ entrypoint n]");
+ System.err.println("Usage GCClass classpath outdir entrypoint1 ... [ entrypoint n]");
System.exit(1);
}
GCClass gc = new GCClass(args[0]);
- for(int i=2;i<args.length;i++) gc.referenceMethod(args[i]);
+ for(int i=2;i<args.length;i++) {
+ if(args[i].startsWith("hint:"))
+ gc.parseHint(args[i].substring(5));
+ else
+ gc.referenceMethod(args[i]);
+ }
gc.go();
gc.dump(new File(args[1]));
}
private final Hashtable completed = new Hashtable();
private final Hashtable references = new Hashtable();
private final Hashtable instansiated = new Hashtable();
+ private final Hashtable hints = new Hashtable();
public GCClass(String classpath) throws ClassNotFoundException {
- repo = SyntheticRepository.getInstance(new ClassPath(ClassPath.SYSTEM_CLASS_PATH + File.pathSeparator + classpath));
- for(int i=0;i<PRE_REF.length;i++) referenceMethod(PRE_REF[i]);
+ if(classpath.startsWith("="))
+ classpath = classpath.substring(1);
+ else
+ classpath = ClassPath.SYSTEM_CLASS_PATH + File.pathSeparator + classpath;
+ repo = SyntheticRepository.getInstance(new ClassPath(classpath));
+ for(int i=0;i<PRE_REF.length;i++) {
+ try {
+ referenceMethod(PRE_REF[i]);
+ } catch(ClassNotFoundException e) {
+ System.err.println("WARNING: Couldn't preref: " + PRE_REF[i]);
+ }
+ }
}
private Hashtable classRefHash(JavaClass c) { return classRefHash(new ObjectType(c.getClassName())); }
if(p == -1) throw new IllegalArgumentException("invalid class/method string");
String cs = s.substring(0,p);
String ms = s.substring(p+1);
+ boolean skip = false;
+
+ if(cs.startsWith("-")) { cs = cs.substring(1); skip = true; }
- if(ms.equals("<init>")) instansiated.put(new ObjectType(cs),Boolean.TRUE);
+ if(!skip && (ms.equals("*") || ms.equals("<init>")))
+ instansiated.put(new ObjectType(cs),Boolean.TRUE);
JavaClass c = repoGet(cs);
Method[] methods = c.getMethods();
- for(int i=0;i<methods.length;i++)
- if(methods[i].getName().equals(ms))
- referenceMethod(new MethodRef(c,methods[i]));
+ for(int i=0;i<methods.length;i++) {
+ if(ms.equals("*") || methods[i].getName().equals(ms)) {
+ MethodRef mr = new MethodRef(c,methods[i]);
+ if(skip) completed.put(mr,Boolean.TRUE);
+ else referenceMethod(mr);
+ }
+ }
+ }
+
+ public void parseHint(String s) throws ClassNotFoundException {
+ int p = s.indexOf(':');
+ if(p == -1) throw new IllegalArgumentException("invalid hint");
+ String cms = s.substring(0,p);
+ String hint = s.substring(p+1);
+ p = cms.lastIndexOf('.');
+ if(p == -1) throw new IllegalArgumentException("invalid hint");
+ String cs = cms.substring(0,p);
+ String ms = cms.substring(p+1);
+
+ JavaClass c = repoGet(cs);
+ Method[] methods = c.getMethods();
+ for(int i=0;i<methods.length;i++) {
+ if(ms.equals("*") || methods[i].getName().equals(ms)) {
+ MethodRef mr = new MethodRef(c,methods[i]);
+ Vector v = (Vector) hints.get(mr);
+ if(v == null) hints.put(mr,v=new Vector());
+ v.add(hint);
+ }
+ }
}
private final void referenceMethod(MethodRef m) {
for(int i=0;i<m.args.length;i++) referenceClass(m.args[i]);
}
- private final void referenceField(FieldRef f) {
+ private final void referenceField(FieldRef f) throws ClassNotFoundException {
+ if(completed.get(f) != null) return;
+
Hashtable h = classRefHash(f.c);
h.put(f,Boolean.TRUE);
+
+ // process(FieldRef) doesn't create much work so we don't bother queuing it
+ process(f);
+
referenceClass(f.ftype);
}
return (JavaClass) o;
}
- public void go() throws Exn, ClassNotFoundException {
+ public void go() throws Exn {
while(work.size() != 0) {
- while(work.size() != 0) process((MethodRef) work.remove(work.size()-1));
- fixup();
+ while(work.size() != 0) {
+ MethodRef mr = (MethodRef) work.remove(work.size()-1);
+ try {
+ process(mr);
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ String refBy = mr.refBy == null ? "unknown" : mr.refBy.toString();
+ throw new Exn("ERROR: " + refBy + " references " + mr + " which cannot be found");
+ }
+ }
+ try {
+ fixup();
+ } catch(ClassNotFoundException e) {
+ e.printStackTrace();
+ throw new Exn("ClassNotFoundException in fixup");
+ }
}
}
-
+
private void fixup() throws ClassNotFoundException {
for(Enumeration e = references.keys(); e.hasMoreElements(); ) {
ObjectType t = (ObjectType) e.nextElement();
JavaClass c = repoGet(t);
- if(c == null) continue;
-
Hashtable refs = (Hashtable) references.get(t);
- // add a ref to clinit is any fields/methods are referenced
+
+ if(c == null) continue;
+
+ // add a ref to clinit if any fields/methods are referenced
if(refs.size() != 0) {
MethodRef clinit = new MethodRef(t,"<clinit>",Type.VOID,Type.NO_ARGS);
if(findMethod(c,clinit) != null) referenceMethod(clinit);
String pat = IGNORED_METHODS[i];
if(pat.endsWith("*") ? sig.startsWith(pat.substring(0,pat.length()-1)) : sig.equals(pat)) return;
}
- throw new Exn("Couldn't find " + sig);
+ throw new ClassNotFoundException("" + mr + " not found (but the class was)");
}
Code code = m.getCode();
else if(i instanceof FieldInstruction) // GETFIED, GETSTATIC, PUTFIELD, PUTSTATIC
referenceField(new FieldRef((FieldInstruction)i,cpg));
else if(i instanceof InvokeInstruction) // INVOKESTATIC, INVOKEVIRTUAL, INVOKESPECIAL
- referenceMethod(new MethodRef((InvokeInstruction)i,cpg));
+ referenceMethod(new MethodRef((InvokeInstruction)i,cpg,mr));
+ }
+
+ if(hints.get(mr) != null) {
+ Vector v = (Vector) hints.get(mr);
+ for(int i=0;i<v.size();i++) referenceMethod((String) v.elementAt(i));
}
}
+ private void process(FieldRef fr) throws ClassNotFoundException {
+ if(completed.get(fr) != null) return;
+ completed.put(fr,Boolean.TRUE);
+
+ JavaClass c = repoGet(fr.c.toString());
+ Field f = findField(c,fr);
+ if(f == null) {
+ JavaClass supers[] = c.getSuperClasses();
+ for(int i=0;i<supers.length;i++) {
+ f = findField(supers[i],fr);
+ if(f != null) { referenceField(new FieldRef(supers[i],f)); return; }
+ }
+ String sig = fr.toString();
+ for(int i=0;i<IGNORED_FIELDS.length;i++) {
+ String pat = IGNORED_FIELDS[i];
+ if(pat.endsWith("*") ? sig.startsWith(pat.substring(0,pat.length()-1)) : sig.equals(pat)) return;
+ }
+ throw new ClassNotFoundException("" + fr + " not found (but the class was)");
+ }
+ /* nothing to do */
+ }
+
private static Method findMethod(JavaClass c, MethodRef mr) {
Method[] ms = c.getMethods();
for(int i=0;i<ms.length;i++) {
return null;
}
+ private static Field findField(JavaClass c, FieldRef fr) {
+ Field[] fs = c.getFields();
+ for(int i=0;i<fs.length;i++) {
+ Field f = fs[i];
+ if(f.getName().equals(fr.name) && f.getType().equals(fr.ftype))
+ return f;
+ }
+ return null;
+ }
+
public void dump(File outdir) throws IOException, ClassNotFoundException {
if(!outdir.isDirectory()) throw new IOException("" + outdir + " is not a directory");
OUTER: for(Enumeration e = references.keys(); e.hasMoreElements(); ) {
}
private void dumpClass(JavaClass c, Hashtable refs, boolean staticOnly, File file) throws IOException {
- ClassGen cg = new ClassGen(c);
- Method[] methods= c.getMethods();
+ ClassGen oldCG = new ClassGen(c);
+ ConstantPoolGen oldCP = oldCG.getConstantPool();
+
+ ConstantPoolGen cp = new ConstantPoolGen();
+ ClassGen cg = new ClassGen(c.getClassName(),c.getSuperclassName(),c.getSourceFileName(),c.getAccessFlags(),c.getInterfaceNames(),cp);
+
+ Method[] methods= oldCG.getMethods();
for(int i=0;i<methods.length;i++) {
Method m = methods[i];
MethodRef mr = new MethodRef(c,m);
if((staticOnly && !m.isStatic()) || refs.get(mr) == null) {
System.err.println("Removing method " + mr);
- if(false) {
- cg.removeMethod(m);
- } else {
+ if(true) {
InstructionFactory fac = new InstructionFactory(cg,cg.getConstantPool());
InstructionList il = new InstructionList();
- MethodGen mg = new MethodGen(m.getAccessFlags(),m.getReturnType(),m.getArgumentTypes(),null,m.getName(),c.getClassName(),il,cg.getConstantPool());
+ MethodGen mg = new MethodGen(m.getAccessFlags(),m.getReturnType(),m.getArgumentTypes(),null,m.getName(),c.getClassName(),il,cp);
il.append(fac.createNew("java.lang.UnsatisfiedLinkError"));
il.append(InstructionConstants.DUP);
- il.append(new PUSH(cg.getConstantPool(),"" + mr + " has been pruned"));
- il.append(fac.createInvoke("java.lang.UnsatisfiedLinkError","<init>",Type.VOID, new Type[]{Type.STRING},Constants.INVOKESPECIAL));
+ if(false) {
+ il.append(new PUSH(cg.getConstantPool(),"" + mr + " has been pruned"));
+ il.append(fac.createInvoke("java.lang.UnsatisfiedLinkError","<init>",Type.VOID, new Type[]{Type.STRING},Constants.INVOKESPECIAL));
+ } else {
+ il.append(fac.createInvoke("java.lang.UnsatisfiedLinkError","<init>",Type.VOID,Type.NO_ARGS,Constants.INVOKESPECIAL));
+ }
il.append(InstructionConstants.ATHROW);
mg.setMaxStack();
mg.setMaxLocals();
- cg.replaceMethod(m,mg.getMethod());
+ cg.addMethod(mg.getMethod());
}
- } else {
- //System.err.println("Keeping method " + mr);
+ } else {
+ MethodGen mg = new MethodGen(m,cg.getClassName(),oldCP);
+ mg.setConstantPool(cp);
+ if(mg.getInstructionList() != null) mg.getInstructionList().replaceConstantPool(oldCP, cp);
+
+ Attribute[] attrs = m.getAttributes();
+ for(int j=0;j<attrs.length;j++) {
+ Attribute a = attrs[j];
+ if(a instanceof Code || a instanceof ExceptionTable) continue;
+ mg.removeAttribute(a);
+ Constant con = oldCP.getConstant(a.getNameIndex());
+ a.setNameIndex(cp.addConstant(con,oldCP));
+ mg.addAttribute(a);
+ }
+
+ mg.removeLineNumbers();
+ mg.removeLocalVariables();
+ cg.addMethod(mg.getMethod());
}
}
FieldRef fr = new FieldRef(c,f);
if(refs.get(fr) == null) {
System.err.println("Removing field " + fr);
- cg.removeField(f);
} else {
//System.err.println("Keeping field " + fr);
+ FieldGen fg = new FieldGen(f.getAccessFlags(),f.getType(),f.getName(),cp);
+ Attribute[] attrs = f.getAttributes();
+ for(int j=0;j<attrs.length;j++) {
+ if(attrs[j] instanceof ConstantValue) {
+ ConstantObject co = (ConstantObject) oldCP.getConstant(((ConstantValue)attrs[i]).getConstantValueIndex());
+ Object o = co.getConstantValue(oldCP.getConstantPool());
+ if(co instanceof ConstantLong) fg.setInitValue(((Number)o).longValue());
+ else if(co instanceof ConstantInteger) fg.setInitValue(((Number)o).intValue());
+ else if(co instanceof ConstantFloat) fg.setInitValue(((Number)o).floatValue());
+ else if(co instanceof ConstantDouble) fg.setInitValue(((Number)o).floatValue());
+ else if(co instanceof ConstantString) fg.setInitValue((String)o);
+ else throw new Error("should never happen");
+ } else {
+ Attribute a = attrs[j];
+ Constant con = oldCP.getConstant(a.getNameIndex());
+ a.setNameIndex(cp.addConstant(con,oldCP));
+ //System.err.println("Adding attribute: " + attrs[j]);
+ fg.addAttribute(a);
+ }
+ }
+ /*if(f.getConstantValue() != null) throw new Error("this might be broken");
+ FieldGen fg = new FieldGen(f.getAccessFlags(),f.getType(),f.getName(),cp);*/
+ cg.addField(fg.getField());
}
}
String name;
Type ret;
Type[] args;
-
+ MethodRef refBy;
+
public MethodRef(JavaClass c, Method m) {
this(new ObjectType(c.getClassName()),m.getName(),m.getReturnType(),m.getArgumentTypes());
}
- public MethodRef(InvokeInstruction i, ConstantPoolGen cp) {
+ public MethodRef(InvokeInstruction i, ConstantPoolGen cp, MethodRef refBy) {
this(i.getClassType(cp),i.getMethodName(cp),i.getReturnType(cp),i.getArgumentTypes(cp));
+ this.refBy = refBy;
}
public MethodRef(ObjectType c, String name, Type ret, Type[] args) { this.c = c; this.name = name; this.ret = ret; this.args = args; }
ObjectType c;
String name;
Type ftype;
+ MethodRef refBy;
public FieldRef(JavaClass c, Field f) {
this(new ObjectType(c.getClassName()),f.getName(),f.getType());