more sanity checking
[org.ibex.classgen.git] / src / org / ibex / classgen / MethodGen.java
1 package org.ibex.classgen;
2
3 import java.io.*;
4 import java.util.*;
5
6 public class MethodGen implements CGConst {
7     private final static boolean EMIT_NOPS = true;
8     
9     private final CPGen cp;
10     private final String name;
11     private final Type ret;
12     private final Type[] args;
13     private final int flags;
14     private final ClassGen.AttrGen attrs;
15     private final ClassGen.AttrGen codeAttrs;
16     private final Hashtable exnTable = new Hashtable();
17     private final Hashtable thrownExceptions = new Hashtable();
18     
19     private int maxStack = 16;
20     private int maxLocals;
21     
22     private int size;
23     private int capacity;
24     private byte[] op;
25     private Object[] arg;
26     
27     MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
28         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
29             throw new IllegalArgumentException("invalid flags");
30         this.cp = owner.cp;
31         this.name = name;
32         this.ret = ret;
33         this.args = args;
34         this.flags = flags;
35         
36         attrs = new ClassGen.AttrGen(cp);
37         codeAttrs = new ClassGen.AttrGen(cp);
38         
39         cp.addUtf8(name);
40         cp.addUtf8(getDescriptor());
41         
42         if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
43         
44         maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4);
45     }
46     
47     public String getDescriptor() { return MethodRef.getDescriptor(ret,args); }
48     
49     private class ExnTableEnt {
50         public int start;
51         public int end;
52         public int handler;
53         public CPGen.Ent typeEnt;
54         public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
55             this.start = start;
56             this.end = end;
57             this.handler = handler;
58             this.typeEnt = typeEnt;
59         }
60         public void dump(DataOutput o, int[] pc, int endPC) throws IOException {
61             o.writeShort(pc[start]);
62             o.writeShort(end==pc.length ? endPC : pc[end]);
63             o.writeShort(pc[handler]);
64             o.writeShort(typeEnt.getIndex());
65         }
66     }
67     
68     public final void addExceptionHandler(int startPC, int endPC, int handlerPC, Type.Object type) {
69         exnTable.put(type, new ExnTableEnt(startPC,endPC,handlerPC,cp.add(type)));
70     }
71     
72     public final void addThrow(Type.Object type) {
73         thrownExceptions.put(type,cp.add(type));
74     }
75     
76     private final void grow() { if(size == capacity) grow(size+1); }
77     private final void grow(int newCap) {
78         if(capacity == -1) throw new IllegalStateException("method can't have code");
79         if(newCap <= capacity) return;
80         newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
81         
82         byte[] op2 = new byte[newCap];
83         if(capacity != 0) System.arraycopy(op,0,op2,0,size);
84         op = op2;
85         
86         Object[] arg2 = new Object[newCap];
87         if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
88         arg = arg2;
89         
90         capacity = newCap;
91     }
92     public final int size() { return size; }
93     
94     // These two are optimized for speed, they don't call set() below
95     public final int add(byte op) {
96         int s = size;
97         if(s == capacity) grow();
98         this.op[s] = op;
99         size++;
100         return s;
101     }
102     public final void set(int pos, byte op) { this.op[pos] = op; }
103         
104     public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
105     public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
106     public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
107     
108     public final byte get(int pos) { return op[pos]; }
109     public final Object getArg(int pos) { return arg[pos]; }
110     
111     public final void setArg(int pos, int arg) { set(pos,op[pos],N(arg)); }
112     public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); }
113     
114     
115     public final void set(int pos, byte op, boolean b) { set(pos,op,b?1:0); }
116     public final void set(int pos, byte op, int n) {
117         if(op == LDC) {
118             switch(n) {
119                 case -1: set(pos,ICONST_M1); return;
120                 case 0:  set(pos,ICONST_0);  return;
121                 case 1:  set(pos,ICONST_1);  return;
122                 case 2:  set(pos,ICONST_2);  return; 
123                 case 3:  set(pos,ICONST_3);  return;
124                 case 4:  set(pos,ICONST_4);  return;
125                 case 5:  set(pos,ICONST_5);  return;
126             }
127             Object arg;
128             if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
129             else if(n >= -32767 && n <= 32767) { op = SIPUSH; arg = N(n); }
130             else { arg = cp.add(N(n)); }
131             this.op[pos] = op;
132             this.arg[pos] = arg;
133         } else {
134             set(pos,op,N(n));
135         }
136     }
137     
138     public void set(int pos, byte op, Object arg) {
139         switch(op) {
140             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
141             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
142             {
143                 int iarg = ((Integer)arg).intValue();
144                 if(iarg >= 0 && iarg <= 3) {
145                     byte base = 0;
146                     switch(op) {
147                         case ILOAD:  base = ILOAD_0; break;
148                         case ISTORE: base = ISTORE_0; break;
149                         case LLOAD:  base = LLOAD_0; break;
150                         case LSTORE: base = LSTORE_0; break; 
151                         case FLOAD:  base = FLOAD_0; break;
152                         case FSTORE: base = FSTORE_0; break;
153                         case DLOAD:  base = DLOAD_0; break;
154                         case DSTORE: base = DSTORE_0; break;
155                         case ALOAD:  base = ALOAD_0; break;
156                         case ASTORE: base = ASTORE_0; break;
157                     }
158                     op = (byte)((base&0xff) + iarg);
159                 } else {
160                     if(iarg >= maxLocals) maxLocals = iarg + 1;
161                 }
162                 break;
163             }
164             case LDC:
165                 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
166                 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
167                 if(arg instanceof Long) {
168                     long l = ((Long)arg).longValue();
169                     if(l == 0L) { set(pos,LCONST_0); return; }
170                     if(l == 1L) { set(pos,LCONST_1); return; }
171                 }
172                 
173                 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
174                 // fall through
175             default: {
176                 int opdata = OP_DATA[op&0xff];
177                 if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
178                     arg = cp.add(arg);
179                 else if((opdata&OP_VALID_FLAG) == 0)
180                     throw new IllegalArgumentException("unknown bytecode");
181                 break;
182             }
183         }
184         this.op[pos] = op;
185         this.arg[pos] = arg;
186     }
187     
188     public static class SI {
189         public final Object[] targets;
190         public Object defaultTarget;
191
192         SI(int size) { targets = new Object[size]; }
193         public void setTarget(int pos, Object val) { targets[pos] = val; }
194         public void setTarget(int pos, int val) { targets[pos] = N(val); }
195         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
196         public void setDefaultTarget(Object o) { defaultTarget = o; }
197         public int size() { return targets.length; }
198         
199         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
200         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
201     }
202     
203     public static class TSI extends SI {
204         public final int lo;
205         public final int hi;
206         public int defaultTarget = -1;
207         public TSI(int lo, int hi) {
208             super(hi-lo+1);
209             this.lo = lo;
210             this.hi = hi;
211         }
212         public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
213         public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
214     }
215     
216     public static class LSI extends SI {
217         public final int[] vals;
218         public LSI(int size) {
219            super(size);
220            this.vals = new int[size];
221         }
222         public final void setVal(int pos, int val) { vals[pos] = val; }
223     }
224     
225     public static class Pair {
226         public int i1;
227         public int i2;
228         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
229     }
230         
231     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
232     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
233     
234     public void finish() {
235         try {
236             _finish();
237         } catch(IOException e) {
238             throw new Error("should never happen");
239         }
240     }
241     
242     private Object resolveTarget(Object arg) {
243         int target;
244         if(arg instanceof PhantomTarget) {
245             target = ((PhantomTarget)arg).getTarget();
246             if(target == -1) throw new IllegalStateException("unresolved phantom target");
247             arg = N(target);
248         } else {
249             target = ((Integer)arg).intValue();
250         }
251         if(target < 0 || target >= size)
252             throw new IllegalStateException("invalid target address");
253         return arg;
254     }
255     
256     private void _finish() throws IOException {
257         if(size == -1) return;
258         
259         ByteArrayOutputStream baos = new ByteArrayOutputStream();
260         DataOutput o = new DataOutputStream(baos);
261     
262         int[] pc = new int[size];
263         int[] maxpc = pc;
264         int p,i;
265         
266         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
267         for(i=0,p=0;i<size;i++) {
268             byte op = this.op[i];
269             int opdata = OP_DATA[op&0xff];
270             int j;
271             maxpc[i] = p;
272             
273             if((opdata & OP_BRANCH_FLAG)!= 0) { 
274                 try { 
275                     arg[i] = resolveTarget(arg[i]);
276                 } catch(RuntimeException e) {
277                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
278                     throw e;
279                 }
280             }
281             
282             switch(op) {
283                 case GOTO:
284                 case JSR:
285                     p += 3;
286                     break;
287                 case NOP:
288                     if(EMIT_NOPS) p++;
289                     break;
290                 case LOOKUPSWITCH:
291                 case TABLESWITCH: {
292                     SI si = (SI) arg[i];
293                     Object[] targets = si.targets;
294                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
295                     si.defaultTarget = resolveTarget(si.defaultTarget);
296                     p += 1 + 3 + 4; // opcode itself, padding, default
297                     if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
298                     else p += 4 + targets.length * 4 * 2; // count, key,val * targets
299                     if(op == LOOKUPSWITCH) {
300                         int[] vals = ((LSI)si).vals;
301                         for(j=1;j<vals.length;j++)
302                             if(vals[j] <= vals[j-1])
303                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
304                     }
305                     break;
306                 }
307                 case LDC:
308                     j = ((CPGen.Ent)arg[i]).getIndex();
309                     if(j >= 256) this.op[i] = op = LDC_W;
310                     // fall through
311                 default:
312                     if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
313                     p += 1 + j;
314                     break;
315             }
316         }
317         
318         // Pass2 - Widen instructions if they can possibly be too short
319         for(i=0;i<size;i++) {
320             switch(op[i]) {
321                 case GOTO:
322                 case JSR: {
323                     int arg = ((Integer)this.arg[i]).intValue();
324                     int diff = maxpc[arg] - maxpc[i];
325                     if(diff < -32768 || diff > 32767)
326                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
327                     break;
328                 }
329             }
330         }
331         
332         // Pass3 - Calculate actual pc
333         for(i=0,p=0;i<size;i++) {
334             byte op = this.op[i];
335             pc[i] = p;
336             switch(op) {
337                 case NOP:
338                     if(EMIT_NOPS) p++;
339                     break;
340                 case TABLESWITCH:
341                 case LOOKUPSWITCH: {
342                     SI si = (SI) arg[i];
343                     p++; // opcpde itself
344                     p = (p + 3) & ~3; // padding
345                     p += 4; // default
346                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
347                     else p += 4 + si.size() * 4 * 2; // count, key,val * targets
348                     break;
349                 }
350                 default: {
351                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
352                     if(l == 7) throw new Error("shouldn't be here");
353                     p += 1 + l;                    
354                 }
355             }
356         }
357         
358         int codeSize = p;
359         
360         o.writeShort(maxStack);
361         o.writeShort(maxLocals);
362         o.writeInt(codeSize);
363         
364         // Pass 4 - Actually write the bytecodes
365         for(i=0;i<size;i++) {
366             byte op = this.op[i];
367             int opdata = OP_DATA[op&0xff];
368             if(op == NOP && !EMIT_NOPS) continue;
369             
370             o.writeByte(op&0xff);
371             int argLength = opdata & OP_ARG_LENGTH_MASK;
372             
373             if(argLength == 0) continue; // skip if no args
374             
375             // Write args
376             Object arg = this.arg[i];  
377             
378             switch(op) {
379                 case IINC: {
380                     Pair pair = (Pair) arg;
381                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
382                     o.writeByte(pair.i1);
383                     o.writeByte(pair.i2);
384                 }
385                 case TABLESWITCH:
386                 case LOOKUPSWITCH: {
387                     SI si = (SI) arg;
388                     int mypc = pc[i];
389                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
390                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
391                     if(op == LOOKUPSWITCH) {
392                         int[] vals = ((LSI)si).vals;
393                         o.writeInt(si.size());
394                         for(int j=0;j<si.size();j++) {
395                             o.writeInt(vals[j]);
396                             o.writeInt(pc[si.getTarget(j)] - mypc);
397                         }
398                     } else {
399                         TSI tsi = (TSI) si;
400                         o.writeInt(tsi.lo);
401                         o.writeInt(tsi.hi);
402                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
403                     }
404                     break;
405                 }
406                     
407                 default:
408                     if((opdata & OP_BRANCH_FLAG) != 0) {
409                         int v = pc[((Integer)arg).intValue()] - pc[i];
410                         if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
411                         o.writeShort(v);
412                     } else if((opdata & OP_CPENT_FLAG) != 0) {
413                         int v = ((CPGen.Ent)arg).getIndex();
414                         if(argLength == 1) o.writeByte(v);
415                         else if(argLength == 2) o.writeShort(v);
416                         else throw new Error("should never happen");
417                     } else if(argLength == -1) {
418                         throw new Error("should never happen - variable length instruction not explicitly handled");
419                     } else {
420                         int iarg  = ((Integer)arg).intValue();
421                         if(argLength == 1) {
422                             if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
423                             o.writeByte(iarg);
424                         } else if(argLength == 2) {
425                             if(iarg < -32767 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option"); 
426                             o.writeShort(iarg);
427                         } else {
428                             throw new Error("should never happen");
429                         }
430                     }
431                     break;
432             }
433         }
434
435         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
436         
437         o.writeShort(exnTable.size());
438         for(Enumeration e = exnTable.keys();e.hasMoreElements();)
439             ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
440         
441         o.writeShort(codeAttrs.size());
442         codeAttrs.dump(o);
443         
444         baos.close();
445         
446         byte[] codeAttribute = baos.toByteArray();
447         attrs.add("Code",codeAttribute);
448         
449         baos.reset();
450         o.writeShort(thrownExceptions.size());
451         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
452             o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
453         attrs.add("Exceptions",baos.toByteArray());
454         
455         size = -1;        
456     }
457         
458     public void dump(DataOutput o) throws IOException {
459         o.writeShort(flags);
460         o.writeShort(cp.getUtf8Index(name));
461         o.writeShort(cp.getUtf8Index(getDescriptor()));
462         o.writeShort(attrs.size());
463         attrs.dump(o);
464     }
465     
466     public static byte negate(byte op) {
467         switch(op) {
468             case IFEQ: return IFNE;
469             case IFNE: return IFEQ;
470             case IFLT: return IFGE;
471             case IFGE: return IFLT;
472             case IFGT: return IFLE;
473             case IFLE: return IFGT;
474             case IF_ICMPEQ: return IF_ICMPNE;
475             case IF_ICMPNE: return IF_ICMPEQ;
476             case IF_ICMPLT: return IF_ICMPGE;
477             case IF_ICMPGE: return IF_ICMPLT;
478             case IF_ICMPGT: return IF_ICMPLE;
479             case IF_ICMPLE: return IF_ICMPGT;
480             case IF_ACMPEQ: return IF_ACMPNE;
481             case IF_ACMPNE: return IF_ACMPEQ;
482             
483             default:
484                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
485         }
486     }
487     
488     public static class PhantomTarget {
489         private int target = -1;
490         public void setTarget(int target) { this.target = target; }
491         public int getTarget() { return target; }
492     }
493     
494     private static Integer N(int n) { return new Integer(n); }
495     private static Long N(long n) { return new Long(n); }
496     private static Float N(float f) { return new Float(f); }
497     private static Double N(double d) { return new Double(d); }
498     private static int max(int a, int b) { return a > b ? a : b; }
499     
500     private static final int OP_BRANCH_FLAG = 1<<3;
501     private static final int OP_CPENT_FLAG = 1<<4;
502     private static final int OP_VALID_FLAG = 1<<5;
503     private static final int OP_ARG_LENGTH_MASK = 7;
504     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
505     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
506     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
507     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
508     
509     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
510     private static final byte[] OP_DATA = {
511             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
512             0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
513             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
514             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
515             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
516             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
517             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
518             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
519             0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
520             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
521             0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
522             0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
523             0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
524             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
525             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
526             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
527     };
528 }