formatting
[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 /** A class representing a method in a generated classfile
7     @see ClassFile#addMethod */
8 public class MethodGen implements CGConst {
9     private final static boolean EMIT_NOPS = false;
10     
11     private static final int NO_CODE = -1;
12
13     private final Type.Class.Method method;
14     private final int flags;
15     private final ClassFile.AttrGen attrs;
16     private final ClassFile.AttrGen codeAttrs;
17     private final Vector exnTable = new Vector();
18     private final Hashtable thrownExceptions = new Hashtable();
19     
20     private int maxStack = 16;
21     private int maxLocals;
22     
23     private int size;
24     private int capacity;
25     private byte[] op;
26     private Object[] arg;
27     private ConstantPool.Ent[] cparg;
28     
29     // Constructors //////////////////////////////////////////////////////////////////////////////
30
31     MethodGen(Type.Class.Method method, int flags) {
32         if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
33         this.method = method;
34         this.flags = flags;
35         
36         attrs = new ClassFile.AttrGen();
37         codeAttrs = new ClassFile.AttrGen();
38         
39         if (((flags & INTERFACE) != 0) || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
40         
41         maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
42     }
43
44     MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
45         this.flags = in.readShort();
46         if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
47         String name = cp.getUtf8KeyByIndex(in.readShort());
48         this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
49         this.attrs = new ClassFile.AttrGen(in,cp);
50         
51         if ((flags & (NATIVE|ABSTRACT))==0)  {
52             byte[] codeAttr = (byte[]) attrs.get("Code");
53             if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
54             DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
55             maxStack = ci.readUnsignedShort();
56             maxLocals = ci.readUnsignedShort();
57             int codeLen = ci.readInt();
58             int[] bytecodeMap = parseCode(ci,codeLen,cp);
59             int numExns = ci.readUnsignedShort();
60             while(numExns-- > 0)
61                 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
62             codeAttrs = new ClassFile.AttrGen(ci,cp);
63             // FEATURE: Support these
64             // NOTE: Until we can support them properly we HAVE to delete them,
65             //       they'll be incorrect after we rewrite the constant pool, etc
66             codeAttrs.remove("LineNumberTable");
67             codeAttrs.remove("LocalVariableTable");
68             
69         } else {
70             codeAttrs = new ClassFile.AttrGen();
71         }
72
73         if (attrs.contains("Exceptions")) {
74             DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
75             int exnCount = ei.readUnsignedShort();
76             while(exnCount-- > 0) {
77                 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
78                 thrownExceptions.put(t,t);
79             }
80         }
81     }
82
83     // Parsing //////////////////////////////////////////////////////////////////////////////
84         
85     final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
86         int[] map = new int[codeLen];
87         int pc;
88         for(pc=0;pc<map.length;pc++) map[pc] = -1;
89         for(pc=0;pc<codeLen;) {
90             byte op = in.readByte();
91             int opdata = OP_DATA[op&0xff];
92             //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
93             if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
94             int argLength = opdata & OP_ARG_LENGTH_MASK;
95             int mypc = pc;
96             map[mypc] = size();
97             pc += 1 + (argLength == 7 ? 0 : argLength);
98             if (argLength == 0)  { add(op); continue; }
99             Object arg;
100             switch(op) {
101                 case IINC:
102                     arg = new Pair(in.readUnsignedByte(),in.readByte());
103                     break;
104                 case TABLESWITCH:
105                 case LOOKUPSWITCH:
106                     Switch si;
107                     for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
108                     int def = in.readInt() + mypc;
109                     pc += 4;
110                     if (op == LOOKUPSWITCH) {
111                         Switch.Lookup lsi = new Switch.Lookup(in.readInt());
112                         pc += 4;
113                         for(int i=0;i<lsi.size();i++) {
114                             lsi.setVal(i,in.readInt());
115                             lsi.setTarget(i,in.readInt() + mypc);
116                             pc += 8;
117                         }
118                         si = lsi;
119                     } else {
120                         int lo = in.readInt();
121                         int hi = in.readInt();
122                         pc += 8;
123                         Switch.Table tsi = new Switch.Table(lo,hi);
124                         for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
125                         si = tsi;
126                     }
127                     si.setDefaultTarget(def);
128                     arg = si;
129                     break;
130                 case WIDE: {
131                     byte wideop = in.readByte();
132                     arg = wideop == IINC 
133                         ? new Wide(wideop,in.readUnsignedShort(),in.readShort()) 
134                         : new Wide(wideop,in.readUnsignedShort());
135                     pc += wideop == IINC ? 5 : 3;
136                     break;
137                 }
138                 case MULTIANEWARRAY:
139                     arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
140                     break;
141                 case INVOKEINTERFACE: {
142                     ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
143                     if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
144                     arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
145                     if (in.readByte() == 0 || in.readByte() != 0) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
146                     break;
147                 }
148                 default:
149                     if ((opdata&OP_CPENT_FLAG)!=0) {
150                         ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
151                         int tag = ent.tag;
152                         Object key = ent.key();
153                         switch(op) {
154                             case LDC:
155                             case LDC_W:
156                             case LDC2_W:
157                                 switch(tag) {
158                                     case CONSTANT_INTEGER:
159                                     case CONSTANT_FLOAT:
160                                     case CONSTANT_LONG:
161                                     case CONSTANT_DOUBLE:
162                                     case CONSTANT_STRING:
163                                     case CONSTANT_CLASS:
164                                         break;
165                                     default:
166                                         throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
167                                 }
168                                 break;
169                             case GETSTATIC:
170                             case PUTSTATIC:
171                             case GETFIELD:
172                             case PUTFIELD:
173                                 if (tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
174                                 break;
175                             case INVOKEVIRTUAL:
176                             case INVOKESPECIAL:
177                             case INVOKESTATIC:
178                                 if (tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
179                                 break;
180                             case NEW:
181                             case ANEWARRAY:
182                             case CHECKCAST:
183                             case INSTANCEOF:
184                                 if (tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
185                                 break;                        
186                             default:
187                                 throw new Error("should never happen");
188                         }
189                         arg = key;
190                     } else {
191                         // treat everything else (including branches for now) as plain old ints
192                         int n;
193                         boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
194                         if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
195                         else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
196                         else throw new Error("should never happen");
197                         if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
198                         arg = N(n);
199                     }
200                     break;
201             }
202             add(op,arg);
203         }
204         if (pc != codeLen)
205             throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
206         for(int i=0;i<size();i++) {
207             switch(op[i]) {
208                 case TABLESWITCH:
209                 case LOOKUPSWITCH:
210                 {
211                     Switch si = (Switch) arg[i];
212                     
213                     int pos = map[si.getDefaultTarget()];
214                     if (pos < 0)  throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
215                     si.setDefaultTarget(pos);
216                     
217                     for(int j=0;j<si.size();j++) {
218                         pos = map[si.getTarget(j)];
219                         if (pos < 0)  throw new ClassFile.ClassReadExn("target points to invalid bytecode");
220                         si.setTarget(j,pos);
221                     }
222                     break;
223                 }
224                 default:
225                     if (OP_BRANCH(op[i])) {
226                         int pos = map[((Integer)arg[i]).intValue()];
227                         if (pos < 0)  throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
228                         arg[i] = N(pos);
229                     }
230                     break;
231             }
232         }
233         return map;
234     }
235
236     // Exception Table //////////////////////////////////////////////////////////////////////////////
237    
238     class ExnTableEnt {
239         final int start;
240         final int end;
241         final int handler;
242         final Type.Class type; // null type means all exceptions (for finally)
243         
244         ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
245             int startPC = in.readUnsignedShort();
246             int endPC = in.readUnsignedShort();
247             int handlerPC = in.readUnsignedShort();
248             int index = in.readUnsignedShort();
249             this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
250             int max = bytecodeMap.length;
251             if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
252             if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
253             if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
254             this.start = bytecodeMap[startPC];
255             this.end = bytecodeMap[endPC];
256             this.handler = bytecodeMap[handlerPC];
257         }
258         ExnTableEnt(int start, int end, int handler, Type.Class type) {
259             this.start = start;
260             this.end = end;
261             this.handler = handler;
262             this.type = type;
263         }
264         void finish(ConstantPool cp) { if (type != null) cp.add(type); }
265         void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
266             o.writeShort(pc[start]);
267             o.writeShort(end==pc.length ? endPC : pc[end]);
268             o.writeShort(pc[handler]);
269             o.writeShort(type == null ? 0 : cp.getIndex(type));
270         }
271     }
272     
273     /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
274         @param start The instruction to start at (inclusive)
275         @param end The instruction to end at (exclusive)
276         @param handler The instruction of the excepton handler
277         @param type The type of exception that is to be handled (MUST inherit from Throwable)
278     */
279     public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
280         exnTable.addElement(new ExnTableEnt(start, end, handler, type));
281     }
282     
283     /** Adds a exception type that can be thrown from this method
284         NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown 
285         @param type The type of exception that can be thrown 
286     */
287     public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
288     
289     private final void grow() { if (size == capacity) grow(size+1); }
290     private final void grow(int newCap) {
291         if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
292         if (newCap <= capacity) return;
293         newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
294         
295         byte[] op2 = new byte[newCap];
296         if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
297         op = op2;
298         
299         Object[] arg2 = new Object[newCap];
300         if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
301         arg = arg2;
302         
303         capacity = newCap;
304     }
305
306     // Accessors //////////////////////////////////////////////////////////////////////////////
307     
308     /** Returns the size (in instructions) of this method 
309         @return The size of the method (in instructions)
310     */
311     public final int size() { return size; }
312     
313     // These two are optimized for speed, they don't call set() below
314     /** Add a bytecode (with no argument) to the method */
315     public final int add(byte op) {
316         int s = size;
317         if (s == capacity) grow();
318         this.op[s] = op;
319         size++;
320         return s;
321     }
322
323     /** Set the bytecode at position <i>pos</i> to <i>op</i> */
324     public final void set(int pos, byte op) { this.op[pos] = op; }
325         
326     /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method 
327         @return The position of the new bytecode
328         */
329     public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
330
331     /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
332         @return The position of the new bytecode
333         @see #add(byte, int)
334     */
335     public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
336
337     /** Adds a bytecode with an integer argument. This is equivalent
338      * to add(op, new Integer(arg)), but optimized to prevent the
339      * allocation when possible
340         @return The position of the new bytecode
341         @see #add(byte, Object)
342     */
343     public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
344     
345     /** Gets the bytecode at position <i>pos</i>
346         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
347     */
348     public final byte get(int pos) { return op[pos]; }
349     
350     /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
351         Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
352         interal form when they are added. The value returned from this method for these instruction can be reused, but there
353         is no way to retrieve the original object 
354         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
355     */    
356     public final Object getArg(int pos) { return arg[pos]; }
357     
358     /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
359      * equivalent to set(pos, op, new Integer(arg)), but optimized to
360      * prevent the allocation when possible.
361         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
362         @see #setArg(int, Object) */
363     public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
364
365     /** Sets the argument for <i>pos</i> to <i>arg</i>.
366         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
367     public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
368     
369     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly. 
370         This is equivalent to set(pos, op, arg?1:0) 
371         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
372     */
373     public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
374     
375     // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
376     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
377         This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
378         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
379     */
380     public final void set(int pos, byte op, int n) {
381         Object arg = null;
382         OUTER: switch(op) {
383             case LDC:
384                 switch(n) {
385                     case -1: op = ICONST_M1; break OUTER;
386                     case 0:  op = ICONST_0;  break OUTER;
387                     case 1:  op = ICONST_1;  break OUTER;
388                     case 2:  op = ICONST_2;  break OUTER; 
389                     case 3:  op = ICONST_3;  break OUTER;
390                     case 4:  op = ICONST_4;  break OUTER;
391                     case 5:  op = ICONST_5;  break OUTER;
392                 }
393                 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
394                 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
395                 else { arg = N(n); }
396                 break;
397             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
398             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
399                 if (n >= maxLocals) maxLocals = n + 1;
400                 if (n >= 0 && n <= 3) {
401                     byte base = 0;
402                     switch(op) {
403                         case ILOAD:  base = ILOAD_0;  break;
404                         case ISTORE: base = ISTORE_0; break;
405                         case LLOAD:  base = LLOAD_0;  break;
406                         case LSTORE: base = LSTORE_0; break; 
407                         case FLOAD:  base = FLOAD_0;  break;
408                         case FSTORE: base = FSTORE_0; break;
409                         case DLOAD:  base = DLOAD_0;  break;
410                         case DSTORE: base = DSTORE_0; break;
411                         case ALOAD:  base = ALOAD_0;  break;
412                         case ASTORE: base = ASTORE_0; break;
413                     }
414                     op = (byte)((base&0xff) + n);
415                 } else {
416                     arg = N(n);
417                 }
418                 break;
419             default:
420                 set(pos, op, N(n));
421                 return;
422         }            
423         this.op[pos] = op;
424         this.arg[pos] = arg;
425     }
426     
427     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
428         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
429         */
430     public final void set(int pos, byte op, Object arg) {
431         switch(op) {
432             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
433             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
434                 // set(int, byte, int) always handles these ops itself
435                 set(pos, op, ((Integer)arg).intValue());
436                 return;
437             case LDC:
438                 // set(int, byte, int) always handles these opts itself
439                 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
440                 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
441                 
442                 if (arg instanceof Long) {
443                     long l = ((Long)arg).longValue();
444                     if (l == 0L || l == 1L) {
445                         this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
446                         this.arg[pos] = null; 
447                         return;
448                     }
449                     op = LDC2_W;
450                 } else if (arg instanceof Double) {
451                     op = LDC2_W;
452                 }
453                 break;
454         }
455         if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
456         this.op[pos] = op;
457         this.arg[pos] = arg;
458     }
459     
460     /** Sets the maximum number of locals in the function to
461         <i>maxLocals</i>. NOTE: This defaults to 0 and is
462         automatically increased as necessary when *LOAD/*STORE
463         bytecodes are added. You do not need to call this function in
464         most cases */
465     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
466
467     /** Sets the maxinum size of th stack for this function to
468      * <i>maxStack</i>. This defaults to 16< */
469     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
470     
471
472     // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
473
474     public static abstract class Switch {
475         public final Object[] targets;
476         public Object defaultTarget;
477
478         Switch(int size) { targets = new Object[size]; }
479         public void setTarget(int pos, Object val) { targets[pos] = val; }
480         public void setTarget(int pos, int val) { targets[pos] = N(val); }
481         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
482         public void setDefaultTarget(Object o) { defaultTarget = o; }
483         public int size() { return targets.length; }
484         
485         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
486         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }   
487         
488         abstract int length();
489     
490         public static class Table extends Switch {
491             public final int lo;
492             public final int hi;
493             public Table(int lo, int hi) {
494                 super(hi-lo+1);
495                 this.lo = lo;
496                 this.hi = hi;
497             }
498             public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
499             public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
500             
501             int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
502         }
503     
504         public static class Lookup extends Switch {
505             public final int[] vals;
506             public Lookup(int size) {
507                 super(size);
508                 this.vals = new int[size];
509             }
510             public final void setVal(int pos, int val) { vals[pos] = val; }
511             
512             int length() { return 8 + targets.length * 8; } // key/val per target, default, count
513         }
514     }
515     
516     /** This class represents the arguments to byecodes that take two integer arguments. */
517     public static class Pair {
518         public int i1;
519         public int i2;
520         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
521     }
522     
523     public static class MultiANewArray {
524         public Type.Class type;
525         public int dims;
526         public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
527     }
528     
529     public static class Wide {
530         public final byte op;
531         public final int varNum;
532         public final int n;
533         Wide(byte op, int varNum) { this(op, varNum, 0); }
534         Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
535     }
536
537
538     // Emitting Bits //////////////////////////////////////////////////////////////////////////////
539    
540     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
541         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
542     public static byte negate(byte op) {
543         switch(op) {
544             case IFEQ: return IFNE;
545             case IFNE: return IFEQ;
546             case IFLT: return IFGE;
547             case IFGE: return IFLT;
548             case IFGT: return IFLE;
549             case IFLE: return IFGT;
550             case IF_ICMPEQ: return IF_ICMPNE;
551             case IF_ICMPNE: return IF_ICMPEQ;
552             case IF_ICMPLT: return IF_ICMPGE;
553             case IF_ICMPGE: return IF_ICMPLT;
554             case IF_ICMPGT: return IF_ICMPLE;
555             case IF_ICMPLE: return IF_ICMPGT;
556             case IF_ACMPEQ: return IF_ACMPNE;
557             case IF_ACMPNE: return IF_ACMPEQ;
558             
559             default:
560                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
561         }
562     }
563
564     private Object resolveTarget(Object arg) {
565         int target;
566         if (arg instanceof PhantomTarget) {
567             target = ((PhantomTarget)arg).getTarget();
568             if (target == -1) throw new IllegalStateException("unresolved phantom target");
569             arg = N(target);
570         } else {
571             target = ((Integer)arg).intValue();
572         }
573         if (target < 0 || target >= size)
574             throw new IllegalStateException("invalid target address " + target + "/" + size);
575         return arg;
576     }
577     
578     /** Computes the final bytecode for this method. 
579         @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
580         @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
581     */
582     void finish(ConstantPool cp) {
583         cp.addUtf8(method.name);
584         cp.addUtf8(method.getDescriptor());
585         
586         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
587             cp.add(e.nextElement());
588         
589         if (size == NO_CODE) return;
590         for(int i=0;i<exnTable.size();i++)
591             ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
592         
593         // We'll set these correctly later
594         if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
595         if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
596         attrs.finish(cp);
597         codeAttrs.finish(cp);
598         
599         cparg = new ConstantPool.Ent[size];
600         
601         for(int i=0, p=0;i<size;i++) {
602             switch(op[i]) {
603                 case LDC:
604                 case LDC_W:
605                 case LDC2_W:
606                 case GETSTATIC:
607                 case PUTSTATIC:
608                 case GETFIELD:
609                 case PUTFIELD:
610                 case INVOKEVIRTUAL:
611                 case INVOKESPECIAL:
612                 case INVOKESTATIC:
613                 case NEW:
614                 case ANEWARRAY:
615                 case CHECKCAST:
616                 case INSTANCEOF:
617                     cparg[i] = cp.add(arg[i]);
618                     break;
619                 case INVOKEINTERFACE:
620                     cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
621                     break;
622                 case MULTIANEWARRAY:
623                     cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
624                     break;
625             }
626         }
627     }
628
629     private void generateCode(ConstantPool cp) {
630         try {
631             _generateCode(cp);
632         } catch(IOException e) {
633             throw new Error("should never happen");
634         }
635     }
636     
637     private void _generateCode(ConstantPool cp) throws IOException {
638         ByteArrayOutputStream baos = new ByteArrayOutputStream();
639         DataOutput o = new DataOutputStream(baos);
640     
641         int[] pc = new int[size];
642         int[] maxpc = pc;
643         int p, i;
644         
645         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
646         for(i=0, p=0;i<size;i++) {
647             byte op = this.op[i];
648             int opdata = OP_DATA[op&0xff];
649             int j;
650             maxpc[i] = p;
651             
652             if ((opdata & OP_BRANCH_FLAG)!= 0) { 
653                 try { 
654                     arg[i] = resolveTarget(arg[i]);
655                 } catch(RuntimeException e) {
656                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
657                     throw e;
658                 }
659             }
660             
661             switch(op) {
662                 // Speical caculations
663                 case GOTO:
664                 case JSR: {
665                     int arg = ((Integer)this.arg[i]).intValue();
666                     if (arg < i && p - maxpc[arg] <= 32768) p += 3; 
667                     else p += 5;
668                     continue;
669                 }
670                 case NOP:
671                     if (EMIT_NOPS) p++;
672                     continue;
673                 case LOOKUPSWITCH:
674                 case TABLESWITCH: {
675                     Switch si = (Switch) arg[i];
676                     Object[] targets = si.targets;
677                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
678                     si.defaultTarget = resolveTarget(si.defaultTarget);
679                     p += 1 + 3 + si.length(); // opcode itself, padding, data
680                     if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
681                         int[] vals = ((Switch.Lookup)si).vals;
682                         for(j=1;j<vals.length;j++)
683                             if (vals[j] <= vals[j-1])
684                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
685                     }
686                     continue;
687                 }
688                 case WIDE:
689                     p += ((Wide)arg[i]).op == IINC ? 5 : 3;
690                     continue;
691                 // May need widening
692                 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
693                 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
694                 case RET: {
695                     int arg = ((Integer)this.arg[i]).intValue();
696                     if (arg > 255) {
697                         this.op[i] = WIDE;
698                         this.arg[i] = new Wide(op, arg);
699                         p += 4;
700                         continue;
701                     }
702                     break;
703                 }
704                 case IINC: {
705                     Pair pair = (Pair) this.arg[i];
706                     if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
707                         this.op[i] = WIDE;
708                         this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
709                         p += 6;
710                         continue;
711                     }
712                     break;
713                 }
714                 case LDC:
715                     j = cp.getIndex(cparg[i]);
716                     if (j >= 256) {
717                         this.op[i] = op = LDC_W;
718                         p += 3;
719                         continue;
720                     }
721                     break;
722                 default:
723             }
724             if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
725             p += 1 + j;
726         }
727         
728         // Pass2 - Widen instructions if they can possibly be too short
729         for(i=0;i<size;i++) {
730             switch(op[i]) {
731                 case GOTO:
732                 case JSR: {
733                     int arg = ((Integer)this.arg[i]).intValue();
734                     int diff = maxpc[arg] - maxpc[i];
735                     if (diff < -32768 || diff > 32767)
736                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
737                     break;
738                 }
739             }
740         }
741         
742         // Pass3 - Calculate actual pc
743         for(i=0, p=0;i<size;i++) {
744             byte op = this.op[i];
745             pc[i] = p;
746             switch(op) {
747                 case NOP:
748                     if (EMIT_NOPS) p++;
749                     break;
750                 case TABLESWITCH:
751                 case LOOKUPSWITCH: {
752                     Switch si = (Switch) arg[i];
753                     p++; // opcode itself
754                     p = (p + 3) & ~3; // padding
755                     p += 4; // default
756                     if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
757                     else p += 4 + si.size() * 4 * 2; // count, key, val * targets
758                     break;
759                 }
760                 case WIDE:
761                     p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
762                     break;                
763                 default: {
764                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
765                     if (l == 7) throw new Error("shouldn't be here");
766                     p += 1 + l;                    
767                 }
768             }
769         }
770         int codeSize = p;
771         
772         if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
773         
774         o.writeShort(maxStack);
775         o.writeShort(maxLocals);
776         o.writeInt(codeSize);
777         
778         // Pass 4 - Actually write the bytecodes
779         for(i=0;i<size;i++) {
780             byte op = this.op[i];
781             int opdata = OP_DATA[op&0xff];
782             if (op == NOP && !EMIT_NOPS) continue;
783             o.writeByte(op);
784             int argLength = opdata & OP_ARG_LENGTH_MASK;
785             
786             if (argLength == 0) continue; // skip if no args
787             
788             // Write args
789             Object arg = this.arg[i];  
790             
791             switch(op) {
792                 case IINC: {
793                     Pair pair = (Pair) arg;
794                     if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg"); 
795                     o.writeByte(pair.i1);
796                     o.writeByte(pair.i2);
797                     break;
798                 }
799                 case TABLESWITCH:
800                 case LOOKUPSWITCH: {
801                     Switch si = (Switch) arg;
802                     int mypc = pc[i];
803                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
804                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
805                     if (op == LOOKUPSWITCH) {
806                         int[] vals = ((Switch.Lookup)si).vals;
807                         o.writeInt(si.size());
808                         for(int j=0;j<si.size();j++) {
809                             o.writeInt(vals[j]);
810                             o.writeInt(pc[si.getTarget(j)] - mypc);
811                         }
812                     } else {
813                         Switch.Table tsi = (Switch.Table) si;
814                         o.writeInt(tsi.lo);
815                         o.writeInt(tsi.hi);
816                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
817                     }
818                     break;
819                 }
820                 case WIDE: {
821                     Wide wide = (Wide) arg;
822                     o.writeByte(wide.op);
823                     o.writeShort(wide.varNum);
824                     if (wide.op == IINC) o.writeShort(wide.n);
825                     break;
826                 }
827                 case MULTIANEWARRAY: {
828                     o.writeShort(cp.getIndex(cparg[i]));
829                     int v = ((MultiANewArray) arg).dims;
830                     if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
831                     o.writeByte(v);
832                     break;
833                 }
834                 case INVOKEINTERFACE:
835                     o.writeShort(cp.getIndex(cparg[i]));
836                     o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
837                     o.writeByte(0);
838                     break;
839                 default:
840                     if ((opdata & OP_BRANCH_FLAG) != 0) {
841                         int v = pc[((Integer)arg).intValue()] - pc[i];
842                         if (argLength == 2) {
843                             if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
844                             o.writeShort(v);
845                         } else if (argLength == 4) {
846                             o.writeInt(v);
847                         } else {
848                             throw new Error("should never happen");
849                         }
850                     } else if ((opdata & OP_CPENT_FLAG) != 0) {
851                         int v = cp.getIndex(cparg[i]);
852                         if (argLength == 1) o.writeByte(v);
853                         else if (argLength == 2) o.writeShort(v);
854                         else throw new Error("should never happen");
855                     } else if (argLength == 7) {
856                         throw new Error("should never happen - variable length instruction not explicitly handled");
857                     } else {
858                         int iarg  = ((Integer)arg).intValue();
859                         if (argLength == 1) {
860                             if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
861                                 throw new ClassFile.Exn("overflow of s/u1 option");
862                             o.writeByte(iarg);
863                         } else if (argLength == 2) {
864                             if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
865                                 throw new ClassFile.Exn("overflow of s/u2 option");
866                             o.writeShort(iarg);
867                         } else {
868                             throw new Error("should never happen");
869                         }
870                     }
871                     break;
872             }
873         }
874
875         //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
876         
877         o.writeShort(exnTable.size());
878         for(i=0;i<exnTable.size();i++)
879             ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
880         
881         codeAttrs.dump(o,cp);
882         baos.close();
883         
884         byte[] codeAttribute = baos.toByteArray();
885         attrs.put("Code", codeAttribute);        
886     }
887         
888     void generateExceptions(ConstantPool cp) throws IOException {
889         if (thrownExceptions.size() > 0) {
890             ByteArrayOutputStream baos = new ByteArrayOutputStream();
891             DataOutputStream o = new DataOutputStream(baos);
892             o.writeShort(thrownExceptions.size());
893             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
894                 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
895             baos.close();
896             attrs.put("Exceptions", baos.toByteArray());
897         }
898     }
899     
900     void dump(DataOutput o, ConstantPool cp) throws IOException {
901         if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
902         generateExceptions(cp);
903         
904         o.writeShort(flags);
905         o.writeShort(cp.getUtf8Index(method.name));
906         o.writeShort(cp.getUtf8Index(method.getDescriptor()));
907         attrs.dump(o,cp);
908     }
909     
910    
911     /** Class that represents a target that isn't currently know. The
912         target MUST be set with setTarget() before the classfile is
913         written.  This class is more or less a mutable integer */
914     public static class PhantomTarget {
915         private int target = -1;
916         public void setTarget(int target) { this.target = target; }
917         public int getTarget() { return target; }
918     }
919     
920     private static Integer N(int n) { return new Integer(n); }
921     private static Long N(long n) { return new Long(n); }
922     private static Float N(float f) { return new Float(f); }
923     private static Double N(double d) { return new Double(d); }
924     private static int max(int a, int b) { return a > b ? a : b; }
925     
926     private static final int OP_BRANCH_FLAG = 1<<3;
927     private static final int OP_CPENT_FLAG = 1<<4;
928     private static final int OP_UNSIGNED_FLAG = 1<<5;
929     private static final int OP_VALID_FLAG = 1<<6;
930     private static final int OP_ARG_LENGTH_MASK = 7;
931     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
932     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
933     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
934     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
935     private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
936     
937     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
938     private static final byte[] OP_DATA = {
939         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
940         0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
941         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
942         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 
943         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
944         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
945         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
946         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
947         0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
948         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 
949         0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40, 
950         0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40, 
951         0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
952         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
953         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
954         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
955     };
956
957     // Debugging //////////////////////////////////////////////////////////////////////////////
958
959     public void debugToString(StringBuffer sb, String constructorName) {
960         // This is intentionally a local variable so it can be removed by gcclass
961         final String[] OP_NAMES = new String[]{
962             "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2", 
963             "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0", 
964             "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", 
965             "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload", 
966             "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3", 
967             "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", 
968             "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", 
969             "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload", 
970             "faload", "daload", "aaload", "baload", "caload", "saload", 
971             "istore", "lstore", "fstore", "dstore", "astore", "istore_0", 
972             "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2", 
973             "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0", 
974             "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2", 
975             "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", 
976             "bastore", "castore", "sastore", "pop", "pop2", "dup", 
977             "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", 
978             "iadd", "ladd", "fadd", "dadd", "isub", "lsub", 
979             "fsub", "dsub", "imul", "lmul", "fmul", "dmul", 
980             "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", 
981             "frem", "drem", "ineg", "lneg", "fneg", "dneg", 
982             "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", 
983             "iand", "land", "ior", "lor", "ixor", "lxor", 
984             "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", 
985             "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", 
986             "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl", 
987             "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", 
988             "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", 
989             "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto", 
990             "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn", 
991             "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic", 
992             "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface", 
993             "", "new", "newarray", "anewarray", "arraylength", "athrow", 
994             "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray", 
995             "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "", 
996             "", "", "", "", "", "", 
997             "", "", "", "", "", "", 
998             "", "", "", "", "", "", 
999             "", "", "", "", "", "", 
1000             "", "", "", "", "", "", 
1001             "", "", "", "", "", "", 
1002             "", "", "", "", "", "", 
1003             "", "", "", "", "", "", 
1004             "", "", "", ""
1005         };
1006         
1007         sb.append("  ").append(ClassFile.flagsToString(flags,false));
1008         sb.append(method.debugToString());
1009         if (thrownExceptions.size() > 0) {
1010             sb.append("throws");
1011             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
1012                 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1013             sb.setLength(sb.length()-1);
1014             sb.append(" ");
1015         }
1016         if ((flags & (NATIVE|ABSTRACT))==0) {
1017             sb.append("{\n");
1018             for(int i=0;i<size();i++) {
1019                 sb.append("    ");
1020                 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1021                 sb.append(i).append(": ");
1022                 sb.append(OP_NAMES[op[i]&0xff]);
1023                 String s = null;
1024                 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1025                 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1026                 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1027                 else if (arg[i] != null) s = arg[i].toString();
1028                 if (s != null) sb.append(" ").append(s);
1029                 sb.append("\n");
1030             }
1031             sb.append("  }\n");
1032         } else {
1033             sb.append(";");
1034         }
1035     }
1036
1037 }