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