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