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