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