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