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