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