hints support
[org.ibex.gcclass.git] / src / com / brian_web / gcclass / GCClass.java
index 5b83970..86b4271 100644 (file)
@@ -19,8 +19,6 @@ import org.apache.bcel.util.*;
 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",
@@ -37,6 +35,10 @@ public class GCClass {
         "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" };
     
     
@@ -46,7 +48,12 @@ public class GCClass {
             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]));
     }
@@ -56,6 +63,7 @@ public class GCClass {
     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 {
         if(classpath.startsWith("="))
@@ -113,6 +121,28 @@ public class GCClass {
             }
         }
     }
+    
+    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) {
         if(completed.get(m) != null) return;
@@ -130,9 +160,15 @@ public class GCClass {
         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);
     }
     
@@ -164,15 +200,16 @@ public class GCClass {
             }
         }
     }
-    
+        
     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);
@@ -268,6 +305,33 @@ public class GCClass {
             else if(i instanceof InvokeInstruction) // INVOKESTATIC, INVOKEVIRTUAL, INVOKESPECIAL
                 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) {
@@ -280,6 +344,16 @@ public class GCClass {
         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(); ) {
@@ -297,30 +371,53 @@ public class GCClass {
     }
     
     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());
             }
         }
         
@@ -330,9 +427,31 @@ public class GCClass {
             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());
             }
         }