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