b82567e531eb1a7e61b3bf9f99529a900c8c35f5
[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                     break;
300                 }
301                 case LDC:
302                     j = ((CPGen.Ent)arg[i]).getIndex();
303                     if(j >= 256) this.op[i] = op = LDC_W;
304                     // fall through
305                 default:
306                     if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
307                     p += 1 + j;
308                     break;
309             }
310         }
311         
312         // Pass2 - Widen instructions if they can possibly be too short
313         for(i=0;i<size;i++) {
314             switch(op[i]) {
315                 case GOTO:
316                 case JSR: {
317                     int arg = ((Integer)this.arg[i]).intValue();
318                     int diff = maxpc[arg] - maxpc[i];
319                     if(diff < -32768 || diff > 32767)
320                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
321                     break;
322                 }
323             }
324         }
325         
326         // Pass3 - Calculate actual pc
327         for(i=0,p=0;i<size;i++) {
328             byte op = this.op[i];
329             pc[i] = p;
330             switch(op) {
331                 case NOP:
332                     if(EMIT_NOPS) p++;
333                     break;
334                 case TABLESWITCH:
335                 case LOOKUPSWITCH: {
336                     SI si = (SI) arg[i];
337                     p++; // opcpde itself
338                     p = (p + 3) & ~3; // padding
339                     p += 4; // default
340                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
341                     else p += 4 + si.size() * 4 * 2; // count, key,val * targets
342                     break;
343                 }
344                 default: {
345                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
346                     if(l == 7) throw new Error("shouldn't be here");
347                     p += 1 + l;                    
348                 }
349             }
350         }
351         
352         int codeSize = p;
353         
354         o.writeShort(maxStack);
355         o.writeShort(maxLocals);
356         o.writeInt(codeSize);
357         
358         // Pass 4 - Actually write the bytecodes
359         for(i=0;i<size;i++) {
360             byte op = this.op[i];
361             int opdata = OP_DATA[op&0xff];
362             if(op == NOP && !EMIT_NOPS) continue;
363             
364             o.writeByte(op&0xff);
365             int argLength = opdata & OP_ARG_LENGTH_MASK;
366             
367             if(argLength == 0) continue; // skip if no args
368             
369             // Write args
370             Object arg = this.arg[i];  
371             
372             switch(op) {
373                 case IINC: {
374                     Pair pair = (Pair) arg;
375                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
376                     o.writeByte(pair.i1);
377                     o.writeByte(pair.i2);
378                 }
379                 case TABLESWITCH:
380                 case LOOKUPSWITCH: {
381                     SI si = (SI) arg;
382                     int mypc = pc[i];
383                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
384                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
385                     if(op == LOOKUPSWITCH) {
386                         int[] vals = ((LSI)si).vals;
387                         o.writeInt(si.size());
388                         for(int j=0;j<si.size();j++) {
389                             o.writeInt(vals[j]);
390                             o.writeInt(pc[si.getTarget(j)] - mypc);
391                         }
392                     } else {
393                         TSI tsi = (TSI) si;
394                         o.writeInt(tsi.lo);
395                         o.writeInt(tsi.hi);
396                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
397                     }
398                     break;
399                 }
400                     
401                 default:
402                     if((opdata & OP_BRANCH_FLAG) != 0) {
403                         int v = pc[((Integer)arg).intValue()] - pc[i];
404                         if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
405                         o.writeShort(v);
406                     } else if((opdata & OP_CPENT_FLAG) != 0) {
407                         int v = ((CPGen.Ent)arg).getIndex();
408                         if(argLength == 1) o.writeByte(v);
409                         else if(argLength == 2) o.writeShort(v);
410                         else throw new Error("should never happen");
411                     } else if(argLength == -1) {
412                         throw new Error("should never happen - variable length instruction not explicitly handled");
413                     } else {
414                         int iarg  = ((Integer)arg).intValue();
415                         if(argLength == 1) {
416                             if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
417                             o.writeByte(iarg);
418                         } else if(argLength == 2) {
419                             if(iarg < -32767 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option"); 
420                             o.writeShort(iarg);
421                         } else {
422                             throw new Error("should never happen");
423                         }
424                     }
425                     break;
426             }
427         }
428
429         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
430         
431         o.writeShort(exnTable.size());
432         for(Enumeration e = exnTable.keys();e.hasMoreElements();)
433             ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
434         
435         o.writeShort(codeAttrs.size());
436         codeAttrs.dump(o);
437         
438         baos.close();
439         
440         byte[] codeAttribute = baos.toByteArray();
441         attrs.add("Code",codeAttribute);
442         
443         baos.reset();
444         o.writeShort(thrownExceptions.size());
445         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
446             o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
447         attrs.add("Exceptions",baos.toByteArray());
448         
449         size = -1;        
450     }
451         
452     public void dump(DataOutput o) throws IOException {
453         o.writeShort(flags);
454         o.writeShort(cp.getUtf8Index(name));
455         o.writeShort(cp.getUtf8Index(getDescriptor()));
456         o.writeShort(attrs.size());
457         attrs.dump(o);
458     }
459     
460     public static byte negate(byte op) {
461         switch(op) {
462             case IFEQ: return IFNE;
463             case IFNE: return IFEQ;
464             case IFLT: return IFGE;
465             case IFGE: return IFLT;
466             case IFGT: return IFLE;
467             case IFLE: return IFGT;
468             case IF_ICMPEQ: return IF_ICMPNE;
469             case IF_ICMPNE: return IF_ICMPEQ;
470             case IF_ICMPLT: return IF_ICMPGE;
471             case IF_ICMPGE: return IF_ICMPLT;
472             case IF_ICMPGT: return IF_ICMPLE;
473             case IF_ICMPLE: return IF_ICMPGT;
474             case IF_ACMPEQ: return IF_ACMPNE;
475             case IF_ACMPNE: return IF_ACMPEQ;
476             
477             default:
478                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
479         }
480     }
481     
482     public static class PhantomTarget {
483         private int target = -1;
484         public void setTarget(int target) { this.target = target; }
485         public int getTarget() { return target; }
486     }
487     
488     private static Integer N(int n) { return new Integer(n); }
489     private static Long N(long n) { return new Long(n); }
490     private static Float N(float f) { return new Float(f); }
491     private static Double N(double d) { return new Double(d); }
492     private static int max(int a, int b) { return a > b ? a : b; }
493     
494     private static final int OP_BRANCH_FLAG = 1<<3;
495     private static final int OP_CPENT_FLAG = 1<<4;
496     private static final int OP_VALID_FLAG = 1<<5;
497     private static final int OP_ARG_LENGTH_MASK = 7;
498     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
499     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
500     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
501     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
502     
503     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
504     private static final byte[] OP_DATA = {
505             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
506             0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
507             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
508             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
509             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
514             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
515             0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
516             0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
517             0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
518             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
519             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
520             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
521     };
522 }