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