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