sort of working
authorbrian <brian@brianweb.net>
Tue, 25 May 2004 05:06:42 +0000 (05:06 +0000)
committerbrian <brian@brianweb.net>
Tue, 25 May 2004 05:06:42 +0000 (05:06 +0000)
darcs-hash:20040525050642-24bed-f2b55804b1d69c571869d7ca34ee3d474264a468.gz

Makefile [new file with mode: 0644]
src/org/ibex/classgen/AttrGen.java [new file with mode: 0644]
src/org/ibex/classgen/CGConst.java [new file with mode: 0644]
src/org/ibex/classgen/CPGen.java [new file with mode: 0644]
src/org/ibex/classgen/ClassGen.java [new file with mode: 0644]
src/org/ibex/classgen/FieldGen.java [new file with mode: 0644]
src/org/ibex/classgen/MethodGen.java [new file with mode: 0644]
src/org/ibex/classgen/Type.java [new file with mode: 0644]
src/org/ibex/classgen/util/Sort.java [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2c9e9b5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+JAVAC = javac
+sources = $(shell find src -name '*.java')
+classes = $(sources:src/%.java=build/%.class)
+jar_classes = $(classes)
+
+all: $(classes)
+
+$(classes): $(sources) 
+       @mkdir -p build
+       $(JAVAC) -d build $(sources)
+
+test: $(classes)
+       java -cp build org.ibex.classgen.ClassGen
+
+clean: 
+       rm -rf build/*
+
+sizecheck:
+       @for c in $(jar_classes); do \
+               for f in `echo $$c|sed 's,\.class$$,,;'`*.class; do gzip -c $$f; done | wc -c | tr -d '\n'; \
+               echo -e "\t`echo $$c | sed 's,build/org/ibex,,;s,\.class$$,,;s,/,.,g;'`"; \
+       done | sort -rn | awk '{ sum += $$1; print }  END { print sum,"Total"; }'
diff --git a/src/org/ibex/classgen/AttrGen.java b/src/org/ibex/classgen/AttrGen.java
new file mode 100644 (file)
index 0000000..81d94b3
--- /dev/null
@@ -0,0 +1,32 @@
+package org.ibex.classgen;
+
+import java.io.*;
+import java.util.*;
+
+public class AttrGen {
+    private final CPGen cp;
+    private final Hashtable ht = new Hashtable();
+    
+    public AttrGen(CPGen cp) {
+        this.cp = cp;
+    }
+    
+    public void add(String s, byte[] data) {
+        cp.addUtf8(s);
+        ht.put(s,data);
+    }
+    
+    public boolean contains(String s) { return ht.get(s) != null; }
+    
+    public int size() { return ht.size(); }
+    
+    public void dump(DataOutput o) throws IOException {
+        for(Enumeration e = ht.keys(); e.hasMoreElements();) {
+            String name = (String) e.nextElement();
+            byte[] val = (byte[]) ht.get(name);
+            o.writeShort(cp.getUtf8(name).index);
+            o.writeInt(val.length);
+            o.write(val);
+        }
+    }
+}
diff --git a/src/org/ibex/classgen/CGConst.java b/src/org/ibex/classgen/CGConst.java
new file mode 100644 (file)
index 0000000..0dde25c
--- /dev/null
@@ -0,0 +1,230 @@
+package org.ibex.classgen;
+
+public interface CGConst {
+    public static final int ACC_PUBLIC    = (byte) 0x0001;
+    public static final int ACC_FINAL     = (byte) 0x0010;
+    
+    // Class only
+    public static final int ACC_INTERFACE = (byte) 0x0200;
+    public static final int ACC_SUPER     = (byte) 0x0020;
+
+    // Field/Method only
+    public static final int ACC_PRIVATE   = (byte) 0x0002;
+    public static final int ACC_PROTECTED = (byte) 0x0004;
+    public static final int ACC_STATIC    = (byte) 0x0008;
+
+    // Class/Method only
+    public static final int ACC_ABSTRACT  = (byte) 0x0400;
+    
+    // Method Only
+    public static final int ACC_SYNCHRONIZED = (byte) 0x0020;
+    public static final int ACC_NATIVE       = (byte) 0x0100;
+    public static final int ACC_STRICT       = (byte) 0x0800;
+
+    // Field only
+    public static final int ACC_VOLATILE  = (byte) 0x0040;
+    public static final int ACC_TRANSIENT = (byte) 0x0080;
+    
+    // Instructions
+    public static final byte NOP = (byte) 0x0; 
+    public static final byte ACONST_NULL = (byte) 0x01;
+    public static final byte ICONST_M1 = (byte) 0x02;
+    public static final byte ICONST_0 = (byte) 0x03;
+    public static final byte ICONST_1 = (byte) 0x04;
+    public static final byte ICONST_2 = (byte) 0x05;
+    public static final byte ICONST_3 = (byte) 0x06;
+    public static final byte ICONST_4 = (byte) 0x07;
+    public static final byte ICONST_5 = (byte) 0x08;
+    public static final byte LCONST_0 = (byte) 0x09;
+    public static final byte LCONST_1 = (byte) 0x0A;
+    public static final byte FCONST_0 = (byte) 0x0B;
+    public static final byte FCONST_1 = (byte) 0x0C;
+    public static final byte FCONST_2 = (byte) 0x0D;
+    public static final byte DCONST_0 = (byte) 0x0E;
+    public static final byte DCONST_1 = (byte) 0x0F;
+    public static final byte BIPUSH = (byte) 0x10;
+    public static final byte SIPUSH = (byte) 0x11;
+    public static final byte LDC = (byte) 0x12;
+    public static final byte LDC_W = (byte) 0x13;
+    public static final byte LDC2_W = (byte) 0x14;
+    public static final byte ILOAD = (byte) 0x15;
+    public static final byte LLOAD = (byte) 0x16;
+    public static final byte FLOAD = (byte) 0x17;
+    public static final byte DLOAD = (byte) 0x18;
+    public static final byte ALOAD = (byte) 0x19;
+    public static final byte ILOAD_0 = (byte) 0x1A;
+    public static final byte ILOAD_1 = (byte) 0x1B;
+    public static final byte ILOAD_2 = (byte) 0x1C;
+    public static final byte ILOAD_3 = (byte) 0x1D;
+    public static final byte LLOAD_0 = (byte) 0x1E;
+    public static final byte LLOAD_1 = (byte) 0x1F;
+    public static final byte LLOAD_2 = (byte) 0x20;
+    public static final byte LLOAD_3 = (byte) 0x21;
+    public static final byte FLOAD_0 = (byte) 0x22;
+    public static final byte FLOAD_1 = (byte) 0x23;
+    public static final byte FLOAD_2 = (byte) 0x24;
+    public static final byte FLOAD_3 = (byte) 0x25;
+    public static final byte DLOAD_0 = (byte) 0x26;
+    public static final byte DLOAD_1 = (byte) 0x27;
+    public static final byte DLOAD_2 = (byte) 0x28;
+    public static final byte DLOAD_3 = (byte) 0x29;
+    public static final byte ALOAD_0 = (byte) 0x2A;
+    public static final byte ALOAD_1 = (byte) 0x2B;
+    public static final byte ALOAD_2 = (byte) 0x2C;
+    public static final byte ALOAD_3 = (byte) 0x2D;
+    public static final byte IALOAD = (byte) 0x2E;
+    public static final byte LALOAD = (byte) 0x2F;
+    public static final byte FALOAD = (byte) 0x30;
+    public static final byte DALOAD = (byte) 0x31;
+    public static final byte AALOAD = (byte) 0x32;
+    public static final byte BALOAD = (byte) 0x33;
+    public static final byte CALOAD = (byte) 0x34;
+    public static final byte SALOAD = (byte) 0x35;
+    public static final byte ISTORE = (byte) 0x36;
+    public static final byte LSTORE = (byte) 0x37;
+    public static final byte FSTORE = (byte) 0x38;
+    public static final byte DSTORE = (byte) 0x39;
+    public static final byte ASTORE = (byte) 0x3A;
+    public static final byte ISTORE_0 = (byte) 0x3B;
+    public static final byte ISTORE_1 = (byte) 0x3C;
+    public static final byte ISTORE_2 = (byte) 0x3D;
+    public static final byte ISTORE_3 = (byte) 0x3E;
+    public static final byte LSTORE_0 = (byte) 0x3F;
+    public static final byte LSTORE_1 = (byte) 0x40;
+    public static final byte LSTORE_2 = (byte) 0x41;
+    public static final byte LSTORE_3 = (byte) 0x42;
+    public static final byte FSTORE_0 = (byte) 0x43;
+    public static final byte FSTORE_1 = (byte) 0x44;
+    public static final byte FSTORE_2 = (byte) 0x45;
+    public static final byte FSTORE_3 = (byte) 0x46;
+    public static final byte DSTORE_0 = (byte) 0x47;
+    public static final byte DSTORE_1 = (byte) 0x48;
+    public static final byte DSTORE_2 = (byte) 0x49;
+    public static final byte DSTORE_3 = (byte) 0x4A;
+    public static final byte ASTORE_0 = (byte) 0x4B;
+    public static final byte ASTORE_1 = (byte) 0x4C;
+    public static final byte ASTORE_2 = (byte) 0x4D;
+    public static final byte ASTORE_3 = (byte) 0x4E;
+    public static final byte IASTORE = (byte) 0x4F;
+    public static final byte LASTORE = (byte) 0x50;
+    public static final byte FASTORE = (byte) 0x51;
+    public static final byte DASTORE = (byte) 0x52;
+    public static final byte AASTORE = (byte) 0x53;
+    public static final byte BASTORE = (byte) 0x54;
+    public static final byte CASTORE = (byte) 0x55;
+    public static final byte SASTORE = (byte) 0x56;
+    public static final byte POP = (byte) 0x57;
+    public static final byte POP2 = (byte) 0x58;
+    public static final byte DUP = (byte) 0x59;
+    public static final byte DUP_X1 = (byte) 0x5A;
+    public static final byte DUP_X2 = (byte) 0x5B;
+    public static final byte DUP2 = (byte) 0x5C;
+    public static final byte DUP2_X1 = (byte) 0x5D;
+    public static final byte DUP2_X2 = (byte) 0x5E;
+    public static final byte SWAP = (byte) 0x5F;
+    public static final byte IADD = (byte) 0x60;
+    public static final byte LADD = (byte) 0x61;
+    public static final byte FADD = (byte) 0x62;
+    public static final byte DADD = (byte) 0x63;
+    public static final byte ISUB = (byte) 0x64;
+    public static final byte LSUB = (byte) 0x65;
+    public static final byte FSUB = (byte) 0x66;
+    public static final byte DSUB = (byte) 0x67;
+    public static final byte IMUL = (byte) 0x68;
+    public static final byte LMUL = (byte) 0x69;
+    public static final byte FMUL = (byte) 0x6A;
+    public static final byte DMUL = (byte) 0x6B;
+    public static final byte IDIV = (byte) 0x6C;
+    public static final byte LDIV = (byte) 0x6D;
+    public static final byte FDIV = (byte) 0x6E;
+    public static final byte DDIV = (byte) 0x6F;
+    public static final byte IREM = (byte) 0x70;
+    public static final byte LREM = (byte) 0x71;
+    public static final byte FREM = (byte) 0x72;
+    public static final byte DREM = (byte) 0x73;
+    public static final byte INET = (byte) 0x74;
+    public static final byte LNEG = (byte) 0x75;
+    public static final byte FNEG = (byte) 0x76;
+    public static final byte DNEG = (byte) 0x77;
+    public static final byte ISHL = (byte) 0x78;
+    public static final byte LSHL = (byte) 0x79;
+    public static final byte ISHR = (byte) 0x7A;
+    public static final byte LSHR = (byte) 0x7B;
+    public static final byte IUSHR = (byte) 0x7C;
+    public static final byte LUSHR = (byte) 0x7D;
+    public static final byte IAND = (byte) 0x7E;
+    public static final byte LAND = (byte) 0x7F;
+    public static final byte IOR = (byte) 0x80;
+    public static final byte LOR = (byte) 0x81;
+    public static final byte IXOR = (byte) 0x82;
+    public static final byte LXOR = (byte) 0x83;
+    public static final byte IINC = (byte) 0x84;
+    public static final byte I2L = (byte) 0x85;
+    public static final byte I2F = (byte) 0x86;
+    public static final byte I2D = (byte) 0x87;
+    public static final byte L2I = (byte) 0x88;
+    public static final byte L2F = (byte) 0x89;
+    public static final byte L2D = (byte) 0x8A;
+    public static final byte F2I = (byte) 0x8B;
+    public static final byte F2L = (byte) 0x8C;
+    public static final byte F2D = (byte) 0x8D;
+    public static final byte D2I = (byte) 0x8E;
+    public static final byte D2L = (byte) 0x8F;
+    public static final byte D2F = (byte) 0x90;
+    public static final byte I2B = (byte) 0x91;
+    public static final byte I2C = (byte) 0x92;
+    public static final byte I2S = (byte) 0x93;
+    public static final byte LCMP = (byte) 0x94;
+    public static final byte FCMPL = (byte) 0x95;
+    public static final byte FCMPG = (byte) 0x96;
+    public static final byte DCMPL = (byte) 0x97;
+    public static final byte DCMPG = (byte) 0x98;
+    public static final byte IFEQ = (byte) 0x99;
+    public static final byte IFNE = (byte) 0x9A;
+    public static final byte IFLT = (byte) 0x9B;
+    public static final byte IFGE = (byte) 0x9C;
+    public static final byte IFGT = (byte) 0x9D;
+    public static final byte IFLE = (byte) 0x9E;
+    public static final byte IF_ICMPEQ = (byte) 0x9F;
+    public static final byte IF_ICMPNE = (byte) 0xA0;
+    public static final byte IF_ICMPLT = (byte) 0xA1;
+    public static final byte IF_ICMPGE = (byte) 0xA2;
+    public static final byte IF_ICMPGT = (byte) 0xA3;
+    public static final byte IF_ICMPLE = (byte) 0xA4;
+    public static final byte IF_ACMPEQ = (byte) 0xA5;
+    public static final byte IF_ACMPNE = (byte) 0xA6;
+    public static final byte GOTO = (byte) 0xA7;
+    public static final byte JSR = (byte) 0xA8;
+    public static final byte RET = (byte) 0xA9;
+    public static final byte TABLESWITCH = (byte) 0xAA;
+    public static final byte LOOKUPSWITCH = (byte) 0xAB;
+    public static final byte IRETURN = (byte) 0xAC;
+    public static final byte LRETURN = (byte) 0xAD;
+    public static final byte FRETURN = (byte) 0xAE;
+    public static final byte DRETURN = (byte) 0xAF;
+    public static final byte ARETURN = (byte) 0xB0;
+    public static final byte RETURN = (byte) 0xB1;
+    public static final byte GETSTATIC = (byte) 0xB2;
+    public static final byte PUTSTATIC = (byte) 0xB3;
+    public static final byte GETFIELD = (byte) 0xB4;
+    public static final byte PUTFIELD = (byte) 0xB5;
+    public static final byte INVOKEVIRTUAL = (byte) 0xB6;
+    public static final byte INVOKESPECIAL = (byte) 0xB7;
+    public static final byte INVOKESTATIC = (byte) 0xB8;
+    public static final byte INVOKEINTERFACE = (byte) 0xB9;
+    public static final byte NEW = (byte) 0xBB;
+    public static final byte NEWARRAY = (byte) 0xBC;
+    public static final byte ANEWARRAY = (byte) 0xBD;
+    public static final byte ARRAYLENGTH = (byte) 0xBE;
+    public static final byte ATHROW = (byte) 0xBF;
+    public static final byte CHECKCAST = (byte) 0xC0;
+    public static final byte INSTANCEOF = (byte) 0xC1;
+    public static final byte MONITORENTER = (byte) 0xC2;
+    public static final byte MONITOREXIT = (byte) 0xC3;
+    public static final byte WIDE = (byte) 0xC4;
+    public static final byte MULTIANEWARRAY = (byte) 0xC5;
+    public static final byte IFNULL = (byte) 0xC6;
+    public static final byte IFNONNULL = (byte) 0xC7;
+    public static final byte GOTO_W = (byte) 0xC8;
+    public static final byte JSR_W = (byte) 0xC9;
+}
diff --git a/src/org/ibex/classgen/CPGen.java b/src/org/ibex/classgen/CPGen.java
new file mode 100644 (file)
index 0000000..91b002e
--- /dev/null
@@ -0,0 +1,183 @@
+package org.ibex.classgen;
+
+import java.util.*;
+import java.io.*;
+
+import org.ibex.classgen.util.*;
+
+public class CPGen {
+    private Hashtable entries = new Hashtable();
+    private int nextIndex = 1; // 0 is reserved
+    private boolean sealed;
+    
+    CPGen() { }
+    
+    /*
+     * Entries 
+     */
+    abstract static class Ent implements Sort.Comparable {
+        int index;
+        public abstract int tag();
+        public void dump(DataOutput o) throws IOException { o.writeByte(tag()); }
+        public int compareTo(Object o) {
+            if(!(o instanceof Ent)) return 1;
+            int oi = ((Ent)o).index;
+            if(index < oi) return -1;
+            if(index > oi) return 1;
+            return 0;
+        }
+    }
+    
+    abstract static class OneU2Ent extends Ent      { int i;  public void dump(DataOutput o) throws IOException { super.dump(o); o.writeShort(i);  } }
+    abstract static class OneU4Ent extends Ent      { int i;  public void dump(DataOutput o) throws IOException { super.dump(o); o.writeInt(i);    } }
+    abstract static class TwoU2Ent extends OneU2Ent { int i2; public void dump(DataOutput o) throws IOException { super.dump(o); o.writeShort(i2); } }
+    abstract static class TwoU4Ent extends OneU4Ent { int i2; public void dump(DataOutput o) throws IOException { super.dump(o); o.writeInt(i2);   } }
+    
+    static class IntEnt         extends OneU4Ent { public int tag() { return 3;  } } // word1: bytes
+    static class FloatEnt       extends OneU4Ent { public int tag() { return 4;  } } // word1: bytes
+    static class LongEnt        extends TwoU4Ent { public int tag() { return 5;  } } // word1/2: bytes
+    static class DoubleEnt      extends TwoU4Ent { public int tag() { return 6;  } } // word1/2: bytes
+    static class ClassEnt       extends OneU2Ent { public int tag() { return 7;  } } // word1: name_index
+    static class StringEnt      extends OneU2Ent { public int tag() { return 8;  } } // word1: string_index
+    static class FieldRefEnt    extends TwoU2Ent { public int tag() { return 9;  } } // word1: class_index word2: name_and_type_index
+    static class MethodRefEnt   extends TwoU2Ent { public int tag() { return 10; } } // word1: class_index word2: name_and_type_index
+    static class IMethodRefEnt  extends TwoU2Ent { public int tag() { return 11; } } // word1: class_index word2: name_and_type_index
+    static class NameAndTypeEnt extends TwoU2Ent { public int tag() { return 12; } } // word1: name_index  word2: descriptor_index
+    
+    static class Utf8Ent extends Ent {
+        String s;
+        public int tag() { return 1; }
+        public void dump(DataOutput o) throws IOException {
+            super.dump(o);
+            o.writeUTF(s);
+        }
+        public String toString() { return "Utf8: " + s; }
+    }
+    
+    /*
+     * Cache Keys
+     */
+    static class Utf8Key {
+        String s;
+        public Utf8Key(String s) { this.s = s; }
+        public boolean equals(Object o) { return o instanceof Utf8Key && ((Utf8Key)o).s.equals(s); }
+        public int hashCode() { return ~s.hashCode(); }
+    }
+    
+    public static class NameAndType {
+        String name;
+        String type;
+        public NameAndType(String name, String type) { this.name = name; this.type = type; }
+        public boolean equals(Object o_) {
+            if(!(o_ instanceof NameAndType)) return false;
+            NameAndType o = (NameAndType) o_;
+            return o.name.equals(name) && o.type.equals(type);
+        }
+        public int hashCode() { return name.hashCode() ^ type.hashCode(); }
+    }
+    
+    static abstract class FieldMethodRef {
+        Type.Object klass;
+        NameAndType nameAndType;
+        public FieldMethodRef(Type.Object klass, NameAndType nameAndType) { this.klass = klass; this.nameAndType = nameAndType; }
+        public boolean equals(Object o_) {
+            if(!(o_ instanceof FieldMethodRef)) return false;
+            FieldMethodRef o = (FieldMethodRef) o_;
+            return o.klass.equals(klass) && o.nameAndType.equals(nameAndType);
+        }
+    }
+    
+    public static class FieldRef   extends FieldMethodRef { public FieldRef  (Type.Object c, NameAndType t) { super(c,t); } }
+    public static class MethodRef  extends FieldMethodRef { public MethodRef (Type.Object c, NameAndType t) { super(c,t); } }
+    public static class IMethodRef extends FieldMethodRef { public IMethodRef(Type.Object c, NameAndType t) { super(c,t); } }
+    
+    /*
+     * Methods
+     */
+    public void seal() { sealed = true; }
+    
+    public final Ent get(Object o) { return (Ent) entries.get(o); }
+    public final Ent getUtf8(String s) { return get(new Utf8Key(s)); }
+    
+    public final Ent addNameAndType(String name, String descriptor) { return add(new NameAndType(name,descriptor)); }
+    public final Ent addUtf8(String s) { return add(new Utf8Key(s)); }
+    
+    public final Ent add(Object o) {
+        if(sealed) throw new IllegalStateException("constant pool is sealed");
+            
+        Ent ent = get(o);
+        if(ent != null) return ent;
+        
+        if(nextIndex == 65536) throw new ClassGen.Exn("constant pool full");
+        
+        if(o instanceof Type.Object) {
+            ClassEnt ce = new ClassEnt();
+            ce.i = addUtf8(((Type.Object)o).internalForm()).index;
+            ent = ce;
+        } else if(o instanceof String) {
+            StringEnt se = new StringEnt();
+            se.i = addUtf8((String)o).index;
+            ent = se;
+        } else if(o instanceof Integer) {
+            IntEnt ie = new IntEnt();
+            ie.i = ((Integer)o).intValue();
+            ent = ie;
+        } else if(o instanceof Float) {
+            FloatEnt fe = new FloatEnt();
+            fe.i = Float.floatToIntBits(((Float)o).floatValue());
+            ent = fe;
+        } else if(o instanceof Long) {
+            LongEnt le = new LongEnt();
+            long l = ((Long)o).longValue();
+            le.i = (int)(l>>>32);
+            le.i2 = (int)l;
+            ent = le;
+        } else if(o instanceof Double) {
+            DoubleEnt de = new DoubleEnt();
+            long l = Double.doubleToLongBits(((Double)o).doubleValue());
+            de.i = (int)(l>>>32);
+            de.i2 = (int)l;
+            ent = de;
+        } else if(o instanceof Utf8Key) {
+            Utf8Ent ue = new Utf8Ent();
+            ue.s = ((Utf8Key)o).s;
+            ent = ue;
+        } else if(o instanceof NameAndType) {
+            NameAndTypeEnt ne = new NameAndTypeEnt();
+            NameAndType key = (NameAndType) o;
+            ne.i = addUtf8(key.name).index;
+            ne.i2 = addUtf8(key.type).index;
+            ent = ne;
+        } else if(o instanceof FieldMethodRef) {
+            FieldMethodRef key = (FieldMethodRef) o;
+            TwoU2Ent fme;
+            if(o instanceof MethodRef) fme = new MethodRefEnt();
+            else if(o instanceof IMethodRef) fme = new IMethodRefEnt();
+            else if(o instanceof FieldRef) fme = new FieldRefEnt();
+            else throw new Error("should never happen");
+            fme.i = add(key.klass).index;
+            fme.i2 = add(key.nameAndType).index;
+            ent = fme;
+        } else {
+            throw new IllegalArgumentException("Unknown type passed to add");
+        }
+        
+        ent.index = nextIndex++;
+        entries.put(o,ent);
+        return ent;
+    }
+    
+    public int size() { return nextIndex; }
+    
+    public void dump(DataOutput o) throws IOException {
+        Ent[] ents = new Ent[nextIndex-1];
+        int i=0;
+        Enumeration e = entries.keys();
+        while(e.hasMoreElements()) ents[i++] = (Ent) entries.get(e.nextElement());
+        Sort.sort(ents);
+        for(i=0;i<ents.length;i++) {
+            //System.err.println("" + (i+1) + ": " + ents[i]);
+            ents[i].dump(o);
+        }
+    }
+}
diff --git a/src/org/ibex/classgen/ClassGen.java b/src/org/ibex/classgen/ClassGen.java
new file mode 100644 (file)
index 0000000..cd12045
--- /dev/null
@@ -0,0 +1,105 @@
+package org.ibex.classgen;
+
+import java.util.*;
+import java.io.*;
+
+public class ClassGen implements CGConst {
+    private Type.Object thisType;
+    private Type.Object superType;
+    int flags;
+    
+    private Vector interfaces = new Vector();
+    private Vector fields = new Vector();
+    private Vector methods = new Vector();
+    
+    final CPGen cp;
+    private final AttrGen attributes;
+    
+    public ClassGen(String name, String superName, int flags) {
+        this(new Type.Object(name),new Type.Object(superName),flags);
+    }
+    
+    public ClassGen(Type.Object thisType,Type.Object superType, int flags) {
+        if((flags & ~(ACC_PUBLIC|ACC_FINAL|ACC_SUPER|ACC_INTERFACE|ACC_ABSTRACT)) != 0)
+            throw new IllegalArgumentException("invalid flags");
+        this.thisType = thisType;
+        this.superType = superType;
+        this.flags = flags;
+        
+        cp = new CPGen();
+        attributes = new AttrGen(cp);
+    }
+    
+    public final MethodGen addMethod(String name, Type ret, Type[] args, int flags) {
+        MethodGen mg = new MethodGen(this,name,ret,args,flags);
+        methods.addElement(mg);
+        return mg;
+    }
+    
+    public void dump(String s) throws IOException { dump(new File(s)); }
+    public void dump(File f) throws IOException {
+        if(f.isDirectory()) {
+            String[] a = thisType.components();
+            int i;
+            for(i=0;i<a.length-1;i++) {
+                f = new File(f,a[i]);
+                f.mkdir();
+            }
+            f = new File(f,a[i] + ".class");
+        }
+        dump(new FileOutputStream(f));
+    }
+    public void dump(OutputStream os) throws IOException {
+        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(os));
+        _dump(dos);
+        dos.flush();
+    }
+    
+    private void _dump(DataOutput o) throws IOException {
+        cp.add(thisType);
+        cp.add(superType);
+        for(int i=0;i<interfaces.size();i++) cp.add((Type.Object)interfaces.elementAt(i));
+        for(int i=0;i<methods.size();i++) ((MethodGen)methods.elementAt(i)).finish();
+        cp.seal();
+        
+        o.writeInt(0xcafebabe); // magic
+        o.writeShort(0); // minor_version
+        o.writeShort(46); // major_version
+        o.writeShort(cp.size()); // constant_pool_count
+        cp.dump(o); // constant_pool
+        o.writeShort(flags);
+        o.writeShort(cp.get(thisType).index); // this_class
+        o.writeShort(cp.get(superType).index); // super_class
+        o.writeShort(interfaces.size()); // interfaces_count
+        for(int i=0;i<interfaces.size();i++) o.writeShort(cp.get((Type.Object)interfaces.elementAt(i)).index); // interfaces
+        o.writeShort(fields.size()); // fields_count
+        for(int i=0;i<fields.size();i++) ((FieldGen)fields.elementAt(i)).dump(o); // fields
+        o.writeShort(methods.size()); // methods_count
+        for(int i=0;i<methods.size();i++) ((MethodGen)methods.elementAt(i)).dump(o); // methods
+        o.writeShort(attributes.size()); // attributes_count
+        attributes.dump(o); // attributes        
+    }
+    
+    // FEATURE: Make some of these checked exceptions?
+    public static class Exn extends RuntimeException {
+        public Exn(String s) { super(s); }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ClassGen cg = new ClassGen("Test","java.lang.Object",ACC_PUBLIC|ACC_SUPER|ACC_FINAL);
+        MethodGen mg = cg.addMethod("main",Type.VOID,new Type[]{Type.arrayType(Type.STRING)},ACC_STATIC|ACC_PUBLIC);
+        mg.setMaxLocals(1);
+        mg.addPushConst(0);
+        mg.add(ISTORE_0);
+        int top = mg.size();
+        mg.add(GETSTATIC,mg.fieldRef(new Type.Object("java.lang.System"),"out",new Type.Object("java.io.PrintStream")));
+        mg.add(ILOAD_0);
+        mg.add(INVOKEVIRTUAL,mg.methodRef(new Type.Object("java.io.PrintStream"),"println",Type.VOID, new Type[]{Type.INT}));
+        mg.add(IINC,new int[]{0,1});
+        mg.add(ILOAD_0);
+        mg.addPushConst(10);
+        mg.add(IF_ICMPLT,top);
+        mg.add(RETURN);
+        cg.dump("Test.class");
+    }
+}
diff --git a/src/org/ibex/classgen/FieldGen.java b/src/org/ibex/classgen/FieldGen.java
new file mode 100644 (file)
index 0000000..805a727
--- /dev/null
@@ -0,0 +1,9 @@
+package org.ibex.classgen;
+
+import java.io.*;
+
+public class FieldGen {
+    public void dump(DataOutput o) {
+        throw new Error("FIXME");
+    }
+}
diff --git a/src/org/ibex/classgen/MethodGen.java b/src/org/ibex/classgen/MethodGen.java
new file mode 100644 (file)
index 0000000..b7cf78a
--- /dev/null
@@ -0,0 +1,327 @@
+package org.ibex.classgen;
+
+import java.io.*;
+
+public class MethodGen implements CGConst {
+    private final static boolean EMIT_NOPS = true;
+    
+    private final CPGen cp;
+    private final String name;
+    private final Type ret;
+    private final Type[] args;
+    private final int flags;
+    private final AttrGen attrs;
+    private final AttrGen codeAttrs;
+    
+    private final int nameIndex;
+    private final int descriptorIndex;
+    
+    private int maxStack = 16;
+    private int maxLocals=1;
+    
+    private int size;
+    private int capacity;
+    private byte[] op;
+    private Object[] arg;
+    
+    MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
+        if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
+            throw new IllegalArgumentException("invalid flags");
+        this.cp = owner.cp;
+        this.name = name;
+        this.ret = ret;
+        this.args = args;
+        this.flags = flags;
+        
+        attrs = new AttrGen(cp);
+        codeAttrs = new AttrGen(cp);
+        
+        nameIndex = cp.addUtf8(name).index;
+        descriptorIndex = cp.addUtf8(descriptor()).index;
+        
+        if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
+    }
+    
+    public String descriptor() { return descriptor(ret,args); }
+    public static String descriptor(Type ret, Type[] args) {
+        StringBuffer sb = new StringBuffer(args.length*4);
+        sb.append("(");
+        for(int i=0;i<args.length;i++) sb.append(args[i].getDescriptor());
+        sb.append(")");
+        sb.append(ret.getDescriptor());
+        return sb.toString();
+    }
+        
+    private final void grow() { if(size == capacity) grow(size+1); }
+    private final void grow(int newCap) {
+        if(newCap <= capacity) return;
+        newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
+        
+        byte[] op2 = new byte[newCap];
+        if(capacity != 0) System.arraycopy(op,0,op2,0,size);
+        op = op2;
+        
+        Object[] arg2 = new Object[newCap];
+        if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
+        arg = arg2;
+        
+        capacity = newCap;
+    }
+    public final int size() { return size; }
+    
+    public final int addPushConst(int n) { grow(); setPushConst(size,n); return size++; }
+    public final int addPushConst(long n) { grow(); setPushConst(size,n); return size++; }
+    public final int addPushConst(float n) { grow(); setPushConst(size,n); return size++; }
+    public final int addPushConst(double n) { grow(); setPushConst(size,n); return size++; }
+    public final int addPushConst(Object o) { grow(); setPushConst(size,o); return size++; }
+    public final int add(byte op, int arg) { return add(op,N(arg)); }
+    public final int add(byte op, Object arg) { grow(); set(size,op,arg); return size++; }
+    public final int add(byte op) { return add(op,null); }
+        
+    public final void set(int pos, byte op) { set(pos,op,null); }
+    public final void set(int pos, byte op, int n) { set(pos,op,N(n)); }
+    public final void set(int pos, byte op, Object arg) {
+        if(capacity == -1) throw new IllegalStateException("method can't have code");
+        if(size == -1) throw new IllegalStateException("method is finalized");
+        int iarg = arg instanceof Integer ? ((Integer)arg).intValue() : -1;
+        
+        switch(op) {
+            case LDC: if(iarg >= 256) op = LDC_W; break;
+        }
+        this.op[pos] = op;
+        this.arg[pos] = arg;
+    }
+        
+    public final void setPushConst(int pos, int n) {
+        switch(n) {
+            case -1: set(pos,ICONST_M1); break;
+            case 0:  set(pos,ICONST_0);  break;
+            case 1:  set(pos,ICONST_1);  break;
+            case 2:  set(pos,ICONST_2);  break; 
+            case 3:  set(pos,ICONST_3);  break;
+            case 4:  set(pos,ICONST_4);  break;
+            case 5:  set(pos,ICONST_5);  break;
+            default:
+                if(n >= -128 && n <= 127) set(pos,BIPUSH,n);
+                if(n >= -32767 && n <= 32767) set(pos,SIPUSH,n);
+                setLDC(pos,N(n));
+                break;
+        }
+    }
+    public final void setPushConst(int pos, long l) {
+        if(l==0) set(pos,LCONST_0);
+        else if(l==1) set(pos,LCONST_1);
+        else setLDC(pos,N(l));
+    }
+    
+    public final void setPushConst(int pos, float f) {
+        if(f == 1.0f) set(pos,FCONST_0);
+        else if(f == 1.0f) set(pos,FCONST_1);
+        else if(f == 2.0f) set(pos,FCONST_2);
+        else setLDC(pos,N(f));
+    }
+    public final void setPushConst(int pos, double d) {
+        if(d == 1.0) set(pos,DCONST_0);
+        else if(d == 2.0) set(pos,DCONST_1);
+        else setLDC(pos,N(d));
+    }
+    public final void setPushConst(int pos, Object o) {
+        if(o instanceof Integer) setPushConst(pos,((Integer)o).intValue());
+        else if(o instanceof Long) setPushConst(pos,((Long)o).longValue());
+        else if(o instanceof Float) setPushConst(pos,((Float)o).floatValue());
+        else if(o instanceof Double) setPushConst(pos,((Double)o).doubleValue());
+        else setLDC(pos,o);
+    }
+        
+    private void setLDC(int pos, Object o) { set(pos,LDC,cp.add(o)); }
+
+    public final CPGen.Ent methodRef(Type.Object c, String name, Type ret, Type[] args) {        
+        return methodRef(c,name,MethodGen.descriptor(ret,args));
+    }
+    public final CPGen.Ent methodRef(Type.Object c, String name, String descriptor) {
+        return cp.add(new CPGen.MethodRef(c,new CPGen.NameAndType(name,descriptor)));
+    }
+    public final CPGen.Ent fieldRef(Type.Object c, String name, Type type) {
+        return fieldRef(c,name,type.getDescriptor());
+    }
+    public final CPGen.Ent fieldRef(Type.Object c, String name, String descriptor) {
+        return cp.add(new CPGen.FieldRef(c,new CPGen.NameAndType(name,descriptor)));
+    }    
+    
+    public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
+    public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
+    
+    public void finish() {
+        try {
+            _finish();
+        } catch(IOException e) {
+            throw new Error("should never happen");
+        }
+    }
+    
+    private void _finish() throws IOException {
+        if(size == -1) return;
+        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutput o = new DataOutputStream(baos);
+    
+        int[] pc = new int[size];
+        int[] maxpc = pc;
+        int p,i;
+        
+        // Pass1 - Calculate maximum pc of each bytecode
+        for(i=0,p=0;i<size;i++) {
+            byte op = this.op[i];
+            maxpc[i] = p;
+            switch(op) {
+                case GOTO: p += 3; break;
+                case JSR: p += 3; break;
+                case NOP: if(!EMIT_NOPS) continue; /* fall though */
+                default: p += 1 + opArgLength(op); break;
+            }
+        }
+        // Pass2 - Widen instructions if they can possibly be too short
+        for(i=0;i<size;i++) {
+            switch(op[i]) {
+                case GOTO:
+                case JSR: {
+                    int arg = ((Integer)this.arg[i]).intValue();
+                    int diff = maxpc[arg] - maxpc[i];
+                    if(diff < -32768 || diff > 32767)
+                        op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
+                }
+            }
+        }
+        // Pass3 - Calculate actual pc
+        for(i=0,p=0;i<size;i++) {
+            byte op = this.op[i];
+            pc[i] = p;
+            p += op != NOP || EMIT_NOPS ? 1 + opArgLength(op) : 0;
+        }
+        
+        o.writeShort(maxStack);
+        o.writeShort(maxLocals);
+        o.writeInt(p);
+        
+        // Pass 4 - Actually write the bytecodes
+        for(i=0;i<size;i++) {
+            byte op = this.op[i];
+            System.err.println("" + i + " Writing " + Integer.toHexString(op) + " at " + pc[i]);
+            if(op == NOP && !EMIT_NOPS) continue;
+            int argLength = opArgLength(op);
+            o.writeByte(op&0xff);
+            if(argLength == 0) continue;
+            Object arg = this.arg[i];            
+            int iarg = arg instanceof Integer ? ((Integer)arg).intValue() : -1;
+            int outArg = iarg;
+            
+            switch(op) {
+                case IINC: {
+                    int[] pair = (int[]) arg;
+                    if(pair[0] > 255 || pair[1] < -128 || pair[1] > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
+                    o.writeByte(pair[0]);
+                    o.writeByte(pair[1]);
+                    continue;
+                }
+                case IF_ICMPLT:
+                case GOTO:
+                case GOTO_W:
+                case JSR:
+                case JSR_W:
+                    outArg = pc[iarg] - pc[i];
+                    if(outArg < -32768 || outArg > 32767) throw new ClassGen.Exn("overflow of s2 offset");
+                    break;
+                case INVOKESTATIC:
+                case INVOKESPECIAL:
+                case INVOKEVIRTUAL:
+                case GETSTATIC:
+                case PUTSTATIC:
+                case GETFIELD:
+                case PUTFIELD:
+                case LDC_W:
+                case LDC:
+                    outArg = ((CPGen.Ent)arg).index;
+                    break;
+            }
+            
+            if(argLength == 1) o.writeByte(outArg);
+            else if(argLength == 2) o.writeShort(outArg);
+            else throw new Error("should never happen");
+        }
+
+        o.writeShort(0); // FIXME: Exception table
+        o.writeShort(codeAttrs.size());
+        codeAttrs.dump(o);
+        
+        baos.close();
+        
+        byte[] codeAttribute = baos.toByteArray();
+        attrs.add("Code",codeAttribute);
+        size = -1;        
+    }
+        
+    public void dump(DataOutput o) throws IOException {
+        o.writeShort(flags);
+        o.writeShort(nameIndex);
+        o.writeShort(descriptorIndex);
+        o.writeShort(attrs.size());
+        attrs.dump(o);
+    }
+    
+    private static int opArgLength(byte op) {
+        switch(op) {
+            case NOP:
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+            case LCONST_0:
+            case LCONST_1:
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+            case DCONST_0:
+            case DCONST_1:
+            case ILOAD_0:
+            case ILOAD_1:
+            case ILOAD_2:
+            case ILOAD_3:
+            case ISTORE_0:
+            case ISTORE_1:
+            case ISTORE_2:
+            case ISTORE_3:
+            case RETURN:
+                return 0;
+            case LDC:
+            case BIPUSH:
+            case ILOAD:
+            case ISTORE:
+                return 1;
+            case LDC_W:
+            case SIPUSH:
+            case GETSTATIC:
+            case PUTSTATIC:
+            case GETFIELD:
+            case PUTFIELD:
+            case INVOKESTATIC:
+            case INVOKEVIRTUAL:
+            case INVOKESPECIAL:
+            case IINC:
+            case GOTO:
+            case JSR:
+            case IF_ICMPLT:
+                return 2;
+            default:
+                throw new ClassGen.Exn("unknown bytecode " + Integer.toHexString(op&0xff));
+        }
+    }
+        
+    private static Integer N(int n) { return new Integer(n); }
+    private static Long N(long n) { return new Long(n); }
+    private static Float N(float f) { return new Float(f); }
+    private static Double N(double d) { return new Double(d); }
+    private static int max(int a, int b) { return a > b ? a : b; }
+}
diff --git a/src/org/ibex/classgen/Type.java b/src/org/ibex/classgen/Type.java
new file mode 100644 (file)
index 0000000..87705b4
--- /dev/null
@@ -0,0 +1,49 @@
+package org.ibex.classgen;
+
+import java.util.StringTokenizer;
+
+public class Type {
+    public static final Type VOID = new Type("V");
+    public static final Type INT = new Type("I");
+    public static final Type STRING = new Type.Object("java.lang.String");
+    public static final Type[] NO_ARGS = new Type[0];
+    
+    String descriptor;
+    
+    Type() { }
+    Type(String descriptor) { this.descriptor = descriptor; }
+    
+    public final String getDescriptor() { return descriptor; }
+    public int hashCode() { return descriptor.hashCode(); }
+    public boolean equals(Object o) { return o instanceof Type && ((Type)o).descriptor.equals(descriptor); }
+    
+    public static Type arrayType(Type base) { return arrayType(base,1); }
+    public static Type arrayType(Type base, int dim) {
+        StringBuffer sb = new StringBuffer(base.descriptor.length() + dim);
+        for(int i=0;i<dim;i++) sb.append("[");
+        sb.append(base.descriptor);
+        return new Type(sb.toString());
+    }
+    
+    public static class Object extends Type {
+        public Object(String s) {
+            if(!s.startsWith("L") || !s.endsWith(";")) s = "L" + s.replace('.','/') + ";";
+            if(!validDescriptorString(s)) throw new IllegalArgumentException("invalid descriptor string");
+            descriptor = s;
+        }
+        
+        public String[] components() {
+            StringTokenizer st = new StringTokenizer(descriptor.substring(1,descriptor.length()-1),"/");
+            String[] a = new String[st.countTokens()];
+            for(int i=0;st.hasMoreTokens();i++) a[i] = st.nextToken();
+            return a;
+        }
+        
+        public String internalForm() { return descriptor.substring(1,descriptor.length()-1); }
+        
+        // FEATURE: Do a proper check here (invalid chars, etc)
+        public boolean validDescriptorString(String s) {
+            return s.startsWith("L") && s.endsWith(";");
+        }
+    }    
+}
diff --git a/src/org/ibex/classgen/util/Sort.java b/src/org/ibex/classgen/util/Sort.java
new file mode 100644 (file)
index 0000000..7bc1b8f
--- /dev/null
@@ -0,0 +1,48 @@
+package org.ibex.classgen.util;
+
+// FIXME: Share this with nestedvm somehow
+public final class Sort {
+    private Sort() { }
+    
+    public interface Comparable { public int compareTo(Object o); }
+    public interface CompareFunc { public int compare(Object a, Object b); }
+    
+    private static final CompareFunc comparableCompareFunc = new CompareFunc() {
+        public int compare(Object a,Object b) { return ((Comparable)a).compareTo(b); }
+    };
+    
+    public static void sort(Comparable[] a) { sort(a,comparableCompareFunc); }
+    public static void sort(Object[] a, CompareFunc c) { sort(a,c,0,a.length-1); }
+    
+    private static void sort(Object[] a, CompareFunc c, int start, int end) {
+        Object tmp;
+        if(start >= end) return;
+        if(end-start <= 6) {
+            for(int i=start+1;i<=end;i++) {
+                tmp = a[i];
+                int j;
+                for(j=i-1;j>=start;j--) {
+                    if(c.compare(a[j],tmp) <= 0) break;
+                    a[j+1] = a[j];
+                }
+                a[j+1] = tmp;
+            }
+            return;
+        }
+        
+        Object pivot = a[end];
+        int lo = start - 1;
+        int hi = end;
+        
+        do {
+            while((lo < hi) && c.compare(a[++lo],pivot) < 0) { }
+            while((hi > lo) && c.compare(a[--hi],pivot) > 0) { }
+            tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp;
+        } while(lo < hi);
+        
+        tmp = a[lo]; a[lo] = a[end]; a[end] = tmp;
+        
+        sort(a, c, start, lo-1);
+        sort(a, c, lo+1, end);
+    }
+}