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