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