bugfixes
[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     /** Set the bytecode at position <i>pos</i> to <i>op</i> */
323     public final void set(int pos, byte op) { this.op[pos] = op; }
324         
325     /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method 
326         @return The position of the new bytecode
327         */
328     public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
329     /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
330         @return The position of the new bytecode
331         @see #add(byte, int)
332     */
333     public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
334     /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
335         @return The position of the new bytecode
336         @see #add(byte, Object)
337     */
338     public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
339     
340     /** Gets the bytecode at position <i>pos</i>
341         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
342     */
343     public final byte get(int pos) { return op[pos]; }
344     
345     /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
346         Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
347         interal form when they are added. The value returned from this method for these instruction can be reused, but there
348         is no way to retrieve the original object 
349         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
350     */    
351     public final Object getArg(int pos) { return arg[pos]; }
352     
353     /** Sets the argument for <i>pos</i> to <i>arg</i>. This is equivalent to set(pos, op, new Integer(arg)), but optimized to prevent the allocation when possible.
354         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
355         @see #setArg(int, Object) */
356     public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
357     /** Sets the argument for <i>pos</i> to <i>arg</i>.
358         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
359     */
360     public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
361     
362     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly. 
363         This is equivalent to set(pos, op, arg?1:0) 
364         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
365     */
366     public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
367     
368     // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
369     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
370         This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
371         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
372     */
373     public final void set(int pos, byte op, int n) {
374         Object arg = null;
375         OUTER: switch(op) {
376             case LDC:
377                 switch(n) {
378                     case -1: op = ICONST_M1; break OUTER;
379                     case 0:  op = ICONST_0;  break OUTER;
380                     case 1:  op = ICONST_1;  break OUTER;
381                     case 2:  op = ICONST_2;  break OUTER; 
382                     case 3:  op = ICONST_3;  break OUTER;
383                     case 4:  op = ICONST_4;  break OUTER;
384                     case 5:  op = ICONST_5;  break OUTER;
385                 }
386                 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
387                 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
388                 else { arg = N(n); }
389                 break;
390             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
391             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
392                 if (n >= maxLocals) maxLocals = n + 1;
393                 if (n >= 0 && n <= 3) {
394                     byte base = 0;
395                     switch(op) {
396                         case ILOAD:  base = ILOAD_0;  break;
397                         case ISTORE: base = ISTORE_0; break;
398                         case LLOAD:  base = LLOAD_0;  break;
399                         case LSTORE: base = LSTORE_0; break; 
400                         case FLOAD:  base = FLOAD_0;  break;
401                         case FSTORE: base = FSTORE_0; break;
402                         case DLOAD:  base = DLOAD_0;  break;
403                         case DSTORE: base = DSTORE_0; break;
404                         case ALOAD:  base = ALOAD_0;  break;
405                         case ASTORE: base = ASTORE_0; break;
406                     }
407                     op = (byte)((base&0xff) + n);
408                 } else {
409                     arg = N(n);
410                 }
411                 break;
412             default:
413                 set(pos, op, N(n));
414                 return;
415         }            
416         this.op[pos] = op;
417         this.arg[pos] = arg;
418     }
419     
420     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
421         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
422         */
423     public final void set(int pos, byte op, Object arg) {
424         switch(op) {
425             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
426             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
427                 // set(int, byte, int) always handles these ops itself
428                 set(pos, op, ((Integer)arg).intValue());
429                 return;
430             case LDC:
431                 // set(int, byte, int) always handles these opts itself
432                 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
433                 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
434                 
435                 if (arg instanceof Long) {
436                     long l = ((Long)arg).longValue();
437                     if (l == 0L || l == 1L) {
438                         this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
439                         this.arg[pos] = null; 
440                         return;
441                     }
442                     op = LDC2_W;
443                 } else if (arg instanceof Double) {
444                     op = LDC2_W;
445                 }
446                 break;
447         }
448         if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
449         this.op[pos] = op;
450         this.arg[pos] = arg;
451     }
452     
453     public static abstract class Switch {
454         public final Object[] targets;
455         public Object defaultTarget;
456
457         Switch(int size) { targets = new Object[size]; }
458         public void setTarget(int pos, Object val) { targets[pos] = val; }
459         public void setTarget(int pos, int val) { targets[pos] = N(val); }
460         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
461         public void setDefaultTarget(Object o) { defaultTarget = o; }
462         public int size() { return targets.length; }
463         
464         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
465         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }   
466         
467         abstract int length();
468     
469         public static class Table extends Switch {
470             public final int lo;
471             public final int hi;
472             public Table(int lo, int hi) {
473                 super(hi-lo+1);
474                 this.lo = lo;
475                 this.hi = hi;
476             }
477             public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
478             public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
479             
480             int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
481         }
482     
483         public static class Lookup extends Switch {
484             public final int[] vals;
485             public Lookup(int size) {
486                 super(size);
487                 this.vals = new int[size];
488             }
489             public final void setVal(int pos, int val) { vals[pos] = val; }
490             
491             int length() { return 8 + targets.length * 8; } // key/val per target, default, count
492         }
493     }
494     
495     /** This class represents the arguments to byecodes that take two integer arguments. */
496     public static class Pair {
497         public int i1;
498         public int i2;
499         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
500     }
501     
502     public static class MultiANewArray {
503         public Type.Class type;
504         public int dims;
505         public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
506     }
507     
508     public static class Wide {
509         public final byte op;
510         public final int varNum;
511         public final int n;
512         Wide(byte op, int varNum) { this(op, varNum, 0); }
513         Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
514     }
515         
516     /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
517         necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
518     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
519     /** Sets the maxinum size of th stack for this function  to <i>maxStack</i>. This defaults to 16< */
520     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
521     
522     /** Computes the final bytecode for this method. 
523         @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
524         @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
525     */
526     void finish(ConstantPool cp) {
527         cp.addUtf8(method.name);
528         cp.addUtf8(method.getDescriptor());
529         
530         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
531             cp.add(e.nextElement());
532         
533         if (size == NO_CODE) return;
534         for(int i=0;i<exnTable.size();i++)
535             ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
536         
537         // We'll set these correctly later
538         if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
539         if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
540         attrs.finish(cp);
541         codeAttrs.finish(cp);
542         
543         cparg = new ConstantPool.Ent[size];
544         
545         for(int i=0, p=0;i<size;i++) {
546             switch(op[i]) {
547                 case LDC:
548                 case LDC_W:
549                 case LDC2_W:
550                 case GETSTATIC:
551                 case PUTSTATIC:
552                 case GETFIELD:
553                 case PUTFIELD:
554                 case INVOKEVIRTUAL:
555                 case INVOKESPECIAL:
556                 case INVOKESTATIC:
557                 case NEW:
558                 case ANEWARRAY:
559                 case CHECKCAST:
560                 case INSTANCEOF:
561                     cparg[i] = cp.add(arg[i]);
562                     break;
563                 case INVOKEINTERFACE:
564                     cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
565                     break;
566                 case MULTIANEWARRAY:
567                     cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
568                     break;
569             }
570         }
571     }
572     
573     private Object resolveTarget(Object arg) {
574         int target;
575         if (arg instanceof PhantomTarget) {
576             target = ((PhantomTarget)arg).getTarget();
577             if (target == -1) throw new IllegalStateException("unresolved phantom target");
578             arg = N(target);
579         } else {
580             target = ((Integer)arg).intValue();
581         }
582         if (target < 0 || target >= size)
583             throw new IllegalStateException("invalid target address " + target + "/" + size);
584         return arg;
585     }
586     
587     private void generateCode(ConstantPool cp) {
588         try {
589             _generateCode(cp);
590         } catch(IOException e) {
591             throw new Error("should never happen");
592         }
593     }
594     
595     private void _generateCode(ConstantPool cp) throws IOException {
596         ByteArrayOutputStream baos = new ByteArrayOutputStream();
597         DataOutput o = new DataOutputStream(baos);
598     
599         int[] pc = new int[size];
600         int[] maxpc = pc;
601         int p, i;
602         
603         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
604         for(i=0, p=0;i<size;i++) {
605             byte op = this.op[i];
606             int opdata = OP_DATA[op&0xff];
607             int j;
608             maxpc[i] = p;
609             
610             if ((opdata & OP_BRANCH_FLAG)!= 0) { 
611                 try { 
612                     arg[i] = resolveTarget(arg[i]);
613                 } catch(RuntimeException e) {
614                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
615                     throw e;
616                 }
617             }
618             
619             switch(op) {
620                 // Speical caculations
621                 case GOTO:
622                 case JSR: {
623                     int arg = ((Integer)this.arg[i]).intValue();
624                     if (arg < i && p - maxpc[arg] <= 32768) p += 3; 
625                     else p += 5;
626                     continue;
627                 }
628                 case NOP:
629                     if (EMIT_NOPS) p++;
630                     continue;
631                 case LOOKUPSWITCH:
632                 case TABLESWITCH: {
633                     Switch si = (Switch) arg[i];
634                     Object[] targets = si.targets;
635                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
636                     si.defaultTarget = resolveTarget(si.defaultTarget);
637                     p += 1 + 3 + si.length(); // opcode itself, padding, data
638                     if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
639                         int[] vals = ((Switch.Lookup)si).vals;
640                         for(j=1;j<vals.length;j++)
641                             if (vals[j] <= vals[j-1])
642                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
643                     }
644                     continue;
645                 }
646                 case WIDE:
647                     p += ((Wide)arg[i]).op == IINC ? 5 : 3;
648                     continue;
649                 // May need widening
650                 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
651                 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
652                 case RET: {
653                     int arg = ((Integer)this.arg[i]).intValue();
654                     if (arg > 255) {
655                         this.op[i] = WIDE;
656                         this.arg[i] = new Wide(op, arg);
657                         p += 4;
658                         continue;
659                     }
660                     break;
661                 }
662                 case IINC: {
663                     Pair pair = (Pair) this.arg[i];
664                     if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
665                         this.op[i] = WIDE;
666                         this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
667                         p += 6;
668                         continue;
669                     }
670                     break;
671                 }
672                 case LDC:
673                     j = cp.getIndex(cparg[i]);
674                     if (j >= 256) {
675                         this.op[i] = op = LDC_W;
676                         p += 3;
677                         continue;
678                     }
679                     break;
680                 default:
681             }
682             if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
683             p += 1 + j;
684         }
685         
686         // Pass2 - Widen instructions if they can possibly be too short
687         for(i=0;i<size;i++) {
688             switch(op[i]) {
689                 case GOTO:
690                 case JSR: {
691                     int arg = ((Integer)this.arg[i]).intValue();
692                     int diff = maxpc[arg] - maxpc[i];
693                     if (diff < -32768 || diff > 32767)
694                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
695                     break;
696                 }
697             }
698         }
699         
700         // Pass3 - Calculate actual pc
701         for(i=0, p=0;i<size;i++) {
702             byte op = this.op[i];
703             pc[i] = p;
704             switch(op) {
705                 case NOP:
706                     if (EMIT_NOPS) p++;
707                     break;
708                 case TABLESWITCH:
709                 case LOOKUPSWITCH: {
710                     Switch si = (Switch) arg[i];
711                     p++; // opcode itself
712                     p = (p + 3) & ~3; // padding
713                     p += 4; // default
714                     if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
715                     else p += 4 + si.size() * 4 * 2; // count, key, val * targets
716                     break;
717                 }
718                 case WIDE:
719                     p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
720                     break;                
721                 default: {
722                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
723                     if (l == 7) throw new Error("shouldn't be here");
724                     p += 1 + l;                    
725                 }
726             }
727         }
728         int codeSize = p;
729         
730         if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
731         
732         o.writeShort(maxStack);
733         o.writeShort(maxLocals);
734         o.writeInt(codeSize);
735         
736         // Pass 4 - Actually write the bytecodes
737         for(i=0;i<size;i++) {
738             byte op = this.op[i];
739             int opdata = OP_DATA[op&0xff];
740             if (op == NOP && !EMIT_NOPS) continue;
741             o.writeByte(op);
742             int argLength = opdata & OP_ARG_LENGTH_MASK;
743             
744             if (argLength == 0) continue; // skip if no args
745             
746             // Write args
747             Object arg = this.arg[i];  
748             
749             switch(op) {
750                 case IINC: {
751                     Pair pair = (Pair) arg;
752                     if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg"); 
753                     o.writeByte(pair.i1);
754                     o.writeByte(pair.i2);
755                     break;
756                 }
757                 case TABLESWITCH:
758                 case LOOKUPSWITCH: {
759                     Switch si = (Switch) arg;
760                     int mypc = pc[i];
761                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
762                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
763                     if (op == LOOKUPSWITCH) {
764                         int[] vals = ((Switch.Lookup)si).vals;
765                         o.writeInt(si.size());
766                         for(int j=0;j<si.size();j++) {
767                             o.writeInt(vals[j]);
768                             o.writeInt(pc[si.getTarget(j)] - mypc);
769                         }
770                     } else {
771                         Switch.Table tsi = (Switch.Table) si;
772                         o.writeInt(tsi.lo);
773                         o.writeInt(tsi.hi);
774                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
775                     }
776                     break;
777                 }
778                 case WIDE: {
779                     Wide wide = (Wide) arg;
780                     o.writeByte(wide.op);
781                     o.writeShort(wide.varNum);
782                     if (wide.op == IINC) o.writeShort(wide.n);
783                     break;
784                 }
785                 case MULTIANEWARRAY: {
786                     o.writeShort(cp.getIndex(cparg[i]));
787                     int v = ((MultiANewArray) arg).dims;
788                     if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
789                     o.writeByte(v);
790                     break;
791                 }
792                 case INVOKEINTERFACE:
793                     o.writeShort(cp.getIndex(cparg[i]));
794                     o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
795                     o.writeByte(0);
796                     break;
797                 default:
798                     if ((opdata & OP_BRANCH_FLAG) != 0) {
799                         int v = pc[((Integer)arg).intValue()] - pc[i];
800                         if (argLength == 2) {
801                             if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
802                             o.writeShort(v);
803                         } else if (argLength == 4) {
804                             o.writeInt(v);
805                         } else {
806                             throw new Error("should never happen");
807                         }
808                     } else if ((opdata & OP_CPENT_FLAG) != 0) {
809                         int v = cp.getIndex(cparg[i]);
810                         if (argLength == 1) o.writeByte(v);
811                         else if (argLength == 2) o.writeShort(v);
812                         else throw new Error("should never happen");
813                     } else if (argLength == 7) {
814                         throw new Error("should never happen - variable length instruction not explicitly handled");
815                     } else {
816                         int iarg  = ((Integer)arg).intValue();
817                         if (argLength == 1) {
818                             if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128)) throw new ClassFile.Exn("overflow of s/u1 option");
819                             o.writeByte(iarg);
820                         } else if (argLength == 2) {
821                             if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768)) throw new ClassFile.Exn("overflow of s/u2 option");
822                             o.writeShort(iarg);
823                         } else {
824                             throw new Error("should never happen");
825                         }
826                     }
827                     break;
828             }
829         }
830
831         //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
832         
833         o.writeShort(exnTable.size());
834         for(i=0;i<exnTable.size();i++)
835             ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
836         
837         codeAttrs.dump(o,cp);
838         baos.close();
839         
840         byte[] codeAttribute = baos.toByteArray();
841         attrs.put("Code", codeAttribute);        
842     }
843         
844     void generateExceptions(ConstantPool cp) throws IOException {
845         if (thrownExceptions.size() > 0) {
846             ByteArrayOutputStream baos = new ByteArrayOutputStream();
847             DataOutputStream o = new DataOutputStream(baos);
848             o.writeShort(thrownExceptions.size());
849             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
850                 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
851             baos.close();
852             attrs.put("Exceptions", baos.toByteArray());
853         }
854     }
855     
856     void dump(DataOutput o, ConstantPool cp) throws IOException {
857         if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
858         generateExceptions(cp);
859         
860         o.writeShort(flags);
861         o.writeShort(cp.getUtf8Index(method.name));
862         o.writeShort(cp.getUtf8Index(method.getDescriptor()));
863         attrs.dump(o,cp);
864     }
865     
866     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
867         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
868     public static byte negate(byte op) {
869         switch(op) {
870             case IFEQ: return IFNE;
871             case IFNE: return IFEQ;
872             case IFLT: return IFGE;
873             case IFGE: return IFLT;
874             case IFGT: return IFLE;
875             case IFLE: return IFGT;
876             case IF_ICMPEQ: return IF_ICMPNE;
877             case IF_ICMPNE: return IF_ICMPEQ;
878             case IF_ICMPLT: return IF_ICMPGE;
879             case IF_ICMPGE: return IF_ICMPLT;
880             case IF_ICMPGT: return IF_ICMPLE;
881             case IF_ICMPLE: return IF_ICMPGT;
882             case IF_ACMPEQ: return IF_ACMPNE;
883             case IF_ACMPNE: return IF_ACMPEQ;
884             
885             default:
886                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
887         }
888     }
889     
890     /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written. 
891         This class is more or less a mutable integer */
892     public static class PhantomTarget {
893         private int target = -1;
894         public void setTarget(int target) { this.target = target; }
895         public int getTarget() { return target; }
896     }
897     
898     private static Integer N(int n) { return new Integer(n); }
899     private static Long N(long n) { return new Long(n); }
900     private static Float N(float f) { return new Float(f); }
901     private static Double N(double d) { return new Double(d); }
902     private static int max(int a, int b) { return a > b ? a : b; }
903     
904     private static final int OP_BRANCH_FLAG = 1<<3;
905     private static final int OP_CPENT_FLAG = 1<<4;
906     private static final int OP_UNSIGNED_FLAG = 1<<5;
907     private static final int OP_VALID_FLAG = 1<<6;
908     private static final int OP_ARG_LENGTH_MASK = 7;
909     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
910     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
911     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
912     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
913     private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
914     
915     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
916     private static final byte[] OP_DATA = {
917         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
918         0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
919         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
920         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 
921         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
922         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
923         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
924         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
925         0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 
926         0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 
927         0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40, 
928         0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40, 
929         0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
930         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
931         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
932         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
933     };
934
935     // Debugging //////////////////////////////////////////////////////////////////////////////
936
937     public void debugToString(StringBuffer sb, String constructorName) {
938         // This is intentionally a local variable so it can be removed by gcclass
939         final String[] OP_NAMES = new String[]{
940             "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2", 
941             "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0", 
942             "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", 
943             "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload", 
944             "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3", 
945             "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", 
946             "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", 
947             "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload", 
948             "faload", "daload", "aaload", "baload", "caload", "saload", 
949             "istore", "lstore", "fstore", "dstore", "astore", "istore_0", 
950             "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2", 
951             "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0", 
952             "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2", 
953             "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", 
954             "bastore", "castore", "sastore", "pop", "pop2", "dup", 
955             "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", 
956             "iadd", "ladd", "fadd", "dadd", "isub", "lsub", 
957             "fsub", "dsub", "imul", "lmul", "fmul", "dmul", 
958             "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", 
959             "frem", "drem", "ineg", "lneg", "fneg", "dneg", 
960             "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", 
961             "iand", "land", "ior", "lor", "ixor", "lxor", 
962             "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", 
963             "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", 
964             "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl", 
965             "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", 
966             "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", 
967             "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto", 
968             "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn", 
969             "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic", 
970             "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface", 
971             "", "new", "newarray", "anewarray", "arraylength", "athrow", 
972             "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray", 
973             "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "", 
974             "", "", "", "", "", "", 
975             "", "", "", "", "", "", 
976             "", "", "", "", "", "", 
977             "", "", "", "", "", "", 
978             "", "", "", "", "", "", 
979             "", "", "", "", "", "", 
980             "", "", "", "", "", "", 
981             "", "", "", "", "", "", 
982             "", "", "", ""
983         };
984         
985         sb.append("  ").append(ClassFile.flagsToString(flags,false));
986         sb.append(method.debugToString());
987         if (thrownExceptions.size() > 0) {
988             sb.append("throws");
989             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
990                 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
991             sb.setLength(sb.length()-1);
992             sb.append(" ");
993         }
994         if ((flags & (NATIVE|ABSTRACT))==0) {
995             sb.append("{\n");
996             for(int i=0;i<size();i++) {
997                 sb.append("    ");
998                 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
999                 sb.append(i).append(": ");
1000                 sb.append(OP_NAMES[op[i]&0xff]);
1001                 String s = null;
1002                 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1003                 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1004                 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1005                 else if (arg[i] != null) s = arg[i].toString();
1006                 if (s != null) sb.append(" ").append(s);
1007                 sb.append("\n");
1008             }
1009             sb.append("  }\n");
1010         } else {
1011             sb.append(";");
1012         }
1013     }
1014
1015 }