01ef8400f647446813047ca7dd9aefacd06c90c7
[org.ibex.classgen.git] / src / org / ibex / classgen / CPGen.java
1 package org.ibex.classgen;
2
3 import java.util.*;
4 import java.io.*;
5
6 import org.ibex.classgen.util.*;
7
8 class CPGen {
9     private final Hashtable entries = new Hashtable();
10     private Ent[] entriesByIndex; // only valid when stable
11     
12     private int usedSlots = 1; // 0 is reserved
13     private int state = OPEN;
14     private static final int OPEN = 0;
15     private static final int STABLE = 1; // existing entries won't change
16     private static final int SEALED = 2; // no new entries
17     
18     CPGen() { }
19     
20     /*
21      * Entries 
22      */
23     public abstract static class Ent {
24         CPGen owner; // don't need this yet, but we will to implement ref() and unref()
25         int n; // this is the refcount if state == OPEN, index if >= STABLE
26         int tag;
27         Ent e0;
28         
29         Ent(CPGen owner, int tag) { this.owner = owner; this.tag = tag; }
30         
31         void dump(DataOutput o) throws IOException { o.writeByte(tag); }
32         String debugToString() { return toString(); } // so we can remove this method when not debugging
33         abstract Object key() throws ClassGen.ClassReadExn; // be careful using this, it drags in a bunch of code
34     }
35     
36     // INVARIANTS: tag == 3 || tag == 4
37     static class IntEnt extends Ent {
38         int i;
39         IntEnt(CPGen owner, int tag) { super(owner,tag); }
40         void dump(DataOutput o) throws IOException { super.dump(o); o.writeInt(i);  }
41         Object key() {
42             switch(tag) {
43                 case 3: return new Integer(i);
44                 case 4: return new Float(Float.intBitsToFloat(i));
45                 default: throw new Error("should never happen");
46             }
47         }
48     }
49     
50     // INVARIANTS: tag == 5 || tag == 6
51     static class LongEnt extends Ent {
52         long l;
53         LongEnt(CPGen owner, int tag) { super(owner,tag); }
54         void dump(DataOutput o) throws IOException { super.dump(o); o.writeLong(l); }
55         Object key() {
56             switch(tag) {
57                 case 5: return new Long(l);
58                 case 6: return new Double(Double.longBitsToDouble(l));
59                 default: throw new Error("should never happen");
60             }
61         }
62     }
63     
64     /* INVARIANTS:
65         tag >= 7 && tag <= 12
66         if(tag == 7 || tag == 8) e0 instanceof Utf8Ent
67         if(tag == 9 || tag == 10 || tag == 11) {
68             e0 instanceof CPRefEnt && e0.tag == 7
69             e1 instanceof CPRefEnt && e0.tag == 12
70         }
71         if(tag == 12) {
72             e0 instanceof Utf8Ent
73         }
74     */
75     static class CPRefEnt extends Ent {
76         Ent e1;
77         Ent e2;
78         CPRefEnt(CPGen owner, int tag) { super(owner,tag); }
79         
80         String debugToString() { return "[" + e1.n + ":" + e1.debugToString() + (e2 == null ? "" : " + " + e2.n + ":" + e2.debugToString()) + "]"; }
81         
82         void dump(DataOutput o) throws IOException {
83             super.dump(o);
84             o.writeShort(e1.n);
85             if(e2 != null) o.writeShort(e2.n);
86         }
87         
88         private String fixme() { throw new Error("fixme"); }
89         Object key() throws ClassGen.ClassReadExn {
90             switch(tag) {
91                 case 7: return Type.fromDescriptor(((Utf8Ent)e0).s);
92                 case 8: return (((Utf8Ent)e1).s);
93                 case 9: {
94                     NameAndTypeKey nt = (NameAndTypeKey) e2.key();
95                     Type t = Type.fromDescriptor(nt.type);
96                     if(t == null) throw new ClassGen.ClassReadExn("invalid type descriptor");
97                     return new FieldRef((Type.Object)e1.key(), nt.name, t);
98                 }
99                 case 10: {
100                     NameAndTypeKey nt = (NameAndTypeKey) e2.key();
101                     return new MethodRef((Type.Object)e1.key(), fixme(), null, null);
102                 }
103             }
104             throw new Error("FIXME");
105         }
106     }
107         
108     static class Utf8Ent extends Ent {
109         String s;
110         Utf8Ent(CPGen owner) { super(owner,1); }
111         String debugToString() { return s; }
112         void dump(DataOutput o) throws IOException { super.dump(o); o.writeUTF(s); }
113         Object key() { throw new Error("Brian is lame"); }
114     }
115     
116     /*
117      * Cache Keys
118      */
119     public static class Utf8Key {
120         String s;
121         public Utf8Key(String s) { this.s = s; }
122         public boolean equals(Object o) { return o instanceof Utf8Key && ((Utf8Key)o).s.equals(s); }
123         public int hashCode() { return ~s.hashCode(); }
124     }
125         
126     public static class NameAndTypeKey {
127         String name;
128         String type;
129         NameAndTypeKey(String name, String type) { this.name = name; this.type = type; }
130         public boolean equals(Object o_) {
131             if(!(o_ instanceof NameAndTypeKey)) return false;
132             NameAndTypeKey o = (NameAndTypeKey) o_;
133             return o.name.equals(name) && o.type.equals(type);
134         }
135         public int hashCode() { return name.hashCode() ^ type.hashCode(); }
136     }
137     
138     /*
139      * Methods
140      */
141     
142     public final Ent get(Object o) { return (Ent) entries.get(o); }
143     public final Ent getUtf8(String s) { return get(new Utf8Key(s)); }
144     public final int getIndex(Object o) {
145         Ent e = get(o);
146         if(e == null) throw new IllegalStateException("entry not found");
147         return getIndex(e);
148     }
149     public final int getUtf8Index(String s) {
150         Ent e = getUtf8(s);
151         if(e == null) throw new IllegalStateException("entry not found");
152         return getIndex(e);
153     }
154     public final int getIndex(Ent ent) {
155         if(state < STABLE) throw new IllegalStateException("constant pool is not stable");
156         return ent.n;
157     }
158
159     public final Type.Object getType(int index) { return Type.fromDescriptor(((Utf8Ent)getByIndex(index)).s).asObject(); }
160
161     public final Ent getByIndex(int index) {
162         if(state < STABLE) throw new IllegalStateException("constant pool is not stable");
163         Ent e;
164         if(index >= 65536 || index >= entriesByIndex.length || (e = entriesByIndex[index]) == null) 
165             throw new IllegalStateException("invalid cp index");
166         return e;
167     }
168     
169     public final Ent addNameAndType(String name, String descriptor) { return add(new NameAndTypeKey(name,descriptor)); }
170     public final Ent addUtf8(String s) { return add(new Utf8Key(s)); }
171     
172     public final Ent add(Object o) {
173         if(state == SEALED) throw new IllegalStateException("constant pool is sealed");
174             
175         Ent ent = get(o);
176         if(ent != null) {
177             if(state == OPEN) ent.n++;
178             return ent;
179         }
180         
181         if(o instanceof Type.Object) {
182             CPRefEnt ce = new CPRefEnt(this,7);
183             ce.e1 = addUtf8(((Type.Object)o).internalForm());
184             ent = ce;
185         } else if(o instanceof String) {
186             CPRefEnt ce = new CPRefEnt(this,8);
187             ce.e1 = addUtf8((String)o);
188             ent = ce;
189         } else if(o instanceof Integer) {
190             IntEnt ue = new IntEnt(this,3);
191             ue.i = ((Integer)o).intValue();
192             ent = ue;
193         } else if(o instanceof Float) {
194             IntEnt ue = new IntEnt(this,4);
195             ue.i = Float.floatToIntBits(((Float)o).floatValue());
196             ent = ue;
197         } else if(o instanceof Long) {
198             LongEnt le = new LongEnt(this,5);
199             le.l = ((Long)o).longValue();
200             ent = le;
201         } else if(o instanceof Double) {
202             LongEnt le = new LongEnt(this,6);
203             le.l = Double.doubleToLongBits(((Double)o).doubleValue());
204             ent = le;
205         } else if(o instanceof Utf8Key) {
206             Utf8Ent ue = new Utf8Ent(this);
207             ue.s = ((Utf8Key)o).s;
208             ent = ue;
209         } else if(o instanceof NameAndTypeKey) {
210             CPRefEnt ce = new CPRefEnt(this,12);
211             NameAndTypeKey key = (NameAndTypeKey) o;
212             ce.e1 = addUtf8(key.name);
213             ce.e2 = addUtf8(key.type);
214             ent = ce;
215         } else if(o instanceof ClassGen.FieldOrMethodRef) {
216             ClassGen.FieldOrMethodRef key = (ClassGen.FieldOrMethodRef) o;
217             int tag = o instanceof FieldRef ? 9 : o instanceof MethodRef ? 10 : o instanceof MethodRef.I ? 11 : 0;
218             if(tag == 0) throw new Error("should never happen");
219             CPRefEnt ce = new CPRefEnt(this,tag);
220             ce.e1 = add(key.klass);
221             ce.e2 = addNameAndType(key.name,key.descriptor);
222             ent = ce;
223         } else {
224             throw new IllegalArgumentException("Unknown type passed to add");
225         }
226         
227         int spaces = ent instanceof LongEnt ? 2 : 1;        
228         if(usedSlots + spaces > 65536) throw new ClassGen.Exn("constant pool full");
229         
230         ent.n = state == OPEN ? 1 : usedSlots; // refcount or index
231
232         usedSlots += spaces;        
233
234         entries.put(o,ent);
235         return ent;
236     }
237     
238     public int slots() { return usedSlots; }
239
240     public void seal() { state = SEALED; }
241     
242     private Ent[] asArray() {
243         int count = entries.size();
244         Ent[] ents = new Ent[count];
245         int i=0;
246         Enumeration e = entries.keys();
247         while(e.hasMoreElements()) ents[i++] = (Ent) entries.get(e.nextElement());
248         if(i != count) throw new Error("should never happen");
249         return ents;
250     }
251     
252     private void assignIndex(Ent[] ents) {
253         int index = 1;
254         entriesByIndex = new Ent[ents.length*2];
255         for(int i=0;i<ents.length;i++) {
256             Ent ent = ents[i];
257             ent.n = index;
258             entriesByIndex[index] = ent;
259             index += ent instanceof LongEnt ? 2 : 1;
260         }
261     }
262         
263     public void stable() {
264         if(state != OPEN) return;
265         state = STABLE;
266         assignIndex(asArray());
267     } 
268
269     private static final Sort.CompareFunc compareFunc = new Sort.CompareFunc() {
270         public int compare(Object a_, Object b_) {
271             return ((Ent)a_).n - ((Ent)b_).n;
272         }
273     };
274     
275     private static final Sort.CompareFunc reverseCompareFunc = new Sort.CompareFunc() {
276         public int compare(Object a_, Object b_) {
277             return ((Ent)b_).n - ((Ent)a_).n;
278         }
279     };
280     
281     public void optimize() {
282         if(state != OPEN) throw new IllegalStateException("can't optimize a stable constant pool");
283         Ent[] ents = asArray();
284         Sort.sort(ents,reverseCompareFunc);
285         state = STABLE;
286         assignIndex(ents);
287     }
288     
289     public void unsafeReopen() {
290         if(state == OPEN) return;
291         for(int i=1;i<entriesByIndex.length;i++) {
292             Ent e = entriesByIndex[i];
293             if(e == null) continue;
294             e.n = 0;
295         }
296         entriesByIndex = null;
297         state = OPEN;
298     }
299     
300     public void dump(DataOutput o) throws IOException {
301         Ent[] ents = asArray();
302         Sort.sort(ents,compareFunc);
303         o.writeShort(usedSlots);
304         for(int i=0;i<ents.length;i++) {
305             //System.err.println("" + ents[i].n + ": " + ents[i].debugToString());
306             ents[i].dump(o);
307         }
308     }
309     
310     CPGen(DataInput in) throws ClassGen.ClassReadExn, IOException {
311         usedSlots = in.readUnsignedShort();
312         if(usedSlots==0) throw new ClassGen.ClassReadExn("invalid used slots");
313         
314         // these are to remember the CPRefEnt e0 and e1s we have to fix up
315         int[] e0s = new int[usedSlots];
316         int[] e1s = new int[usedSlots];
317         int[] e2s = new int[usedSlots];
318         
319         entriesByIndex = new Ent[usedSlots];
320         
321         for(int index=1;index<usedSlots;index++) {
322             byte tag = in.readByte();
323             Ent e;
324             switch(tag) {
325                 case 7: // Object Type
326                 case 8: // String
327                 case 9: // FieldRef
328                 case 10: // MethodRef
329                 case 11: // Instance Method Ref
330                 case 12: // NameAndType
331                 {
332                     e = new CPRefEnt(this,tag);
333                     e1s[index] = in.readUnsignedShort();;
334                     if(tag != 7 && tag != 8) e2s[index] = in.readUnsignedShort();
335                     break;
336                 }
337                 case 3: // Integer
338                 case 4: // Float
339                 {
340                     IntEnt ie;
341                     e = ie = new IntEnt(this,tag);
342                     ie.i = in.readInt();
343                     break;
344                 }
345                 case 5: // Long
346                 case 6: // Double
347                 {
348                     LongEnt le;
349                     e = le = new LongEnt(this,tag);
350                     le.l = in.readLong();
351                     index++;
352                     break;
353                 }
354                 case 1: // Utf8
355                 {
356                     Utf8Ent ue;
357                     e = ue = new Utf8Ent(this);
358                     ue.s = in.readUTF();
359                     break;
360                 }
361                 default:
362                     throw new ClassGen.ClassReadExn("invalid cp ent tag");
363             }
364             entriesByIndex[index] = e;
365         }
366         
367         for(int index=1;index<usedSlots;index++) {
368             int i = index;
369             Ent e = entriesByIndex[index];
370             if(e == null) throw new Error("should never happen");
371             if(e instanceof CPRefEnt) {
372                 CPRefEnt ce = (CPRefEnt) e;
373                 if(e0s[i] == 0 || e0s[i] >= usedSlots) throw new ClassGen.ClassReadExn("invalid cp index");
374                 ce.e0 = entriesByIndex[e0s[i]];
375                 if(ce.e0 == null)  throw new ClassGen.ClassReadExn("invalid cp index");
376                 if(ce.tag != 7 && ce.tag != 8) {
377                     if(e1s[i] == 0 || e1s[i] >= usedSlots) throw new ClassGen.ClassReadExn("invalid cp index");
378                     ce.e1 = entriesByIndex[e1s[i]];
379                     if(ce.e1 == null)  throw new ClassGen.ClassReadExn("invalid cp index");
380                 }
381                 switch(ce.tag) {
382                     case 7:
383                     case 8:
384                         if(!(ce.e0 instanceof Utf8Ent)) throw new ClassGen.ClassReadExn("expected a utf8 ent");
385                         break;
386                     case 9:
387                     case 10:
388                     case 11:
389                         if(!(ce.e1 instanceof CPRefEnt) || ((CPRefEnt)ce.e1).tag != 7)
390                             throw new ClassGen.ClassReadExn("expected a type ent");
391                         if(!(ce.e2 instanceof CPRefEnt) || ((CPRefEnt)ce.e2).tag != 12)
392                             throw new ClassGen.ClassReadExn("expected a name and type ent");
393                         break;
394                     case 12:
395                         if(!(ce.e1 instanceof Utf8Ent)) throw new ClassGen.ClassReadExn("expected a utf8 ent");
396                         if(!(ce.e2 instanceof Utf8Ent)) throw new ClassGen.ClassReadExn("expected a utf8 ent");
397                 }
398             } else if(e instanceof LongEnt) {
399                 index++;
400             }
401             entries.put(e.key(),e);
402         }
403         state = STABLE;
404     }
405 }