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