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