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