more reformatting
[org.ibex.classgen.git] / src / org / ibex / classgen / MethodGen.java
1 package org.ibex.classgen;
2
3 import java.io.*;
4 import java.util.*;
5
6 /** A class representing a method in a generated classfile
7     @see ClassFile#addMethod */
8 public class MethodGen implements CGConst {
9     private final static boolean EMIT_NOPS = false;
10     
11     private static final int NO_CODE = -1;
12
13     private final Type.Class.Method method;
14     private final int flags;
15     private final ClassFile.AttrGen attrs;
16     private final ClassFile.AttrGen codeAttrs;
17     private final Vector exnTable = new Vector();
18     private final Hashtable thrownExceptions = new Hashtable();
19     
20     private int maxStack = 16;
21     private int maxLocals;
22     
23     private int size;
24     private int capacity;
25     private byte[] op;
26     private Object[] arg;
27     private ConstantPool.Ent[] cparg;
28     
29     // Constructors //////////////////////////////////////////////////////////////////////////////
30
31     MethodGen(Type.Class.Method method, int flags) {
32         if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
33         this.method = method;
34         this.flags = flags;
35         
36         attrs = new ClassFile.AttrGen();
37         codeAttrs = new ClassFile.AttrGen();
38         
39         if (((flags & INTERFACE) != 0) || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
40         
41         maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
42     }
43
44     MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
45         this.flags = in.readShort();
46         if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
47         String name = cp.getUtf8KeyByIndex(in.readShort());
48         this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
49         this.attrs = new ClassFile.AttrGen(in,cp);
50         
51         if ((flags & (NATIVE|ABSTRACT))==0)  {
52             byte[] codeAttr = (byte[]) attrs.get("Code");
53             if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
54             DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
55             maxStack = ci.readUnsignedShort();
56             maxLocals = ci.readUnsignedShort();
57             int codeLen = ci.readInt();
58             int[] bytecodeMap = parseCode(ci,codeLen,cp);
59             int numExns = ci.readUnsignedShort();
60             while(numExns-- > 0)
61                 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
62             codeAttrs = new ClassFile.AttrGen(ci,cp);
63             // FEATURE: Support these
64             // NOTE: Until we can support them properly we HAVE to delete them,
65             //       they'll be incorrect after we rewrite the constant pool, etc
66             codeAttrs.remove("LineNumberTable");
67             codeAttrs.remove("LocalVariableTable");
68             
69         } else {
70             codeAttrs = new ClassFile.AttrGen();
71         }
72
73         if (attrs.contains("Exceptions")) {
74             DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
75             int exnCount = ei.readUnsignedShort();
76             while(exnCount-- > 0) {
77                 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
78                 thrownExceptions.put(t,t);
79             }
80         }
81     }
82
83     // Parsing //////////////////////////////////////////////////////////////////////////////
84         
85     final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
86         int[] map = new int[codeLen];
87         int pc;
88         for(pc=0;pc<map.length;pc++) map[pc] = -1;
89         for(pc=0;pc<codeLen;) {
90             byte op = in.readByte();
91             int opdata = OP_DATA[op&0xff];
92             //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
93             if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
94             int argLength = opdata & OP_ARG_LENGTH_MASK;
95             int mypc = pc;
96             map[mypc] = size();
97             pc += 1 + (argLength == 7 ? 0 : argLength);
98             if (argLength == 0)  { add(op); continue; }
99             Object arg;
100             switch(op) {
101                 case IINC:
102                     arg = new Pair(in.readUnsignedByte(),in.readByte());
103                     break;
104                 case TABLESWITCH:
105                 case LOOKUPSWITCH:
106                     Switch si;
107                     for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
108                     int def = in.readInt() + mypc;
109                     pc += 4;
110                     if (op == LOOKUPSWITCH) {
111                         Switch.Lookup lsi = new Switch.Lookup(in.readInt());
112                         pc += 4;
113                         for(int i=0;i<lsi.size();i++) {
114                             lsi.setVal(i,in.readInt());
115                             lsi.setTarget(i,in.readInt() + mypc);
116                             pc += 8;
117                         }
118                         si = lsi;
119                     } else {
120                         int lo = in.readInt();
121                         int hi = in.readInt();
122                         pc += 8;
123                         Switch.Table tsi = new Switch.Table(lo,hi);
124                         for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
125                         si = tsi;
126                     }
127                     si.setDefaultTarget(def);
128                     arg = si;
129                     break;
130                 case WIDE: {
131                     byte wideop = in.readByte();
132                     arg = wideop == IINC 
133                         ? new Wide(wideop,in.readUnsignedShort(),in.readShort()) 
134                         : new Wide(wideop,in.readUnsignedShort());
135                     pc += wideop == IINC ? 5 : 3;
136                     break;
137                 }
138                 case MULTIANEWARRAY:
139                     arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
140                     break;
141                 case INVOKEINTERFACE: {
142                     ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
143                     if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
144                     arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
145                     if (in.readByte() == 0 || in.readByte() != 0)
146                         throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
147                     break;
148                 }
149                 default:
150                     if ((opdata&OP_CPENT_FLAG)!=0) {
151                         ConstantPool.Ent ent =
152                             cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
153                         int tag = ent.tag;
154                         Object key = ent.key();
155                         switch(op) {
156                             case LDC:
157                             case LDC_W:
158                             case LDC2_W:
159                                 switch(tag) {
160                                     case CONSTANT_INTEGER:
161                                     case CONSTANT_FLOAT:
162                                     case CONSTANT_LONG:
163                                     case CONSTANT_DOUBLE:
164                                     case CONSTANT_STRING:
165                                     case CONSTANT_CLASS:
166                                         break;
167                                     default:
168                                         throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
169                                                                          Integer.toString(op&0xff,16));
170                                 }
171                                 break;
172                             case GETSTATIC:
173                             case PUTSTATIC:
174                             case GETFIELD:
175                             case PUTFIELD:
176                                 if (tag != CONSTANT_FIELDREF)
177                                     throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
178                                                                      Integer.toString(op&0xff,16));
179                                 break;
180                             case INVOKEVIRTUAL:
181                             case INVOKESPECIAL:
182                             case INVOKESTATIC:
183                                 if (tag != CONSTANT_METHODREF)
184                                     throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
185                                                                      Integer.toString(op&0xff,16));
186                                 break;
187                             case NEW:
188                             case ANEWARRAY:
189                             case CHECKCAST:
190                             case INSTANCEOF:
191                                 if (tag != CONSTANT_CLASS)
192                                     throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
193                                                                      Integer.toString(op&0xff,16));
194                                 break;                        
195                             default:
196                                 throw new Error("should never happen");
197                         }
198                         arg = key;
199                     } else {
200                         // treat everything else (including branches for now) as plain old ints
201                         int n;
202                         boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
203                         if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
204                         else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
205                         else throw new Error("should never happen");
206                         if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
207                         arg = N(n);
208                     }
209                     break;
210             }
211             add(op,arg);
212         }
213         if (pc != codeLen)
214             throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
215         for(int i=0;i<size();i++) {
216             switch(op[i]) {
217                 case TABLESWITCH:
218                 case LOOKUPSWITCH:
219                 {
220                     Switch si = (Switch) arg[i];
221                     
222                     int pos = map[si.getDefaultTarget()];
223                     if (pos < 0)
224                         throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
225                     si.setDefaultTarget(pos);
226                     
227                     for(int j=0;j<si.size();j++) {
228                         pos = map[si.getTarget(j)];
229                         if (pos < 0)  throw new ClassFile.ClassReadExn("target points to invalid bytecode");
230                         si.setTarget(j,pos);
231                     }
232                     break;
233                 }
234                 default:
235                     if (OP_BRANCH(op[i])) {
236                         int pos = map[((Integer)arg[i]).intValue()];
237                         if (pos < 0)  throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
238                         arg[i] = N(pos);
239                     }
240                     break;
241             }
242         }
243         return map;
244     }
245
246     // Exception Table //////////////////////////////////////////////////////////////////////////////
247    
248     class ExnTableEnt {
249         final int start;
250         final int end;
251         final int handler;
252         final Type.Class type; // null type means all exceptions (for finally)
253         
254         ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
255             int startPC = in.readUnsignedShort();
256             int endPC = in.readUnsignedShort();
257             int handlerPC = in.readUnsignedShort();
258             int index = in.readUnsignedShort();
259             this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
260             int max = bytecodeMap.length;
261             if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
262             if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
263             if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
264             this.start = bytecodeMap[startPC];
265             this.end = bytecodeMap[endPC];
266             this.handler = bytecodeMap[handlerPC];
267         }
268         ExnTableEnt(int start, int end, int handler, Type.Class type) {
269             this.start = start;
270             this.end = end;
271             this.handler = handler;
272             this.type = type;
273         }
274         void finish(ConstantPool cp) { if (type != null) cp.add(type); }
275         void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
276             o.writeShort(pc[start]);
277             o.writeShort(end==pc.length ? endPC : pc[end]);
278             o.writeShort(pc[handler]);
279             o.writeShort(type == null ? 0 : cp.getIndex(type));
280         }
281     }
282     
283     /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
284         @param start The instruction to start at (inclusive)
285         @param end The instruction to end at (exclusive)
286         @param handler The instruction of the excepton handler
287         @param type The type of exception that is to be handled (MUST inherit from Throwable)
288     */
289     public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
290         exnTable.addElement(new ExnTableEnt(start, end, handler, type));
291     }
292     
293     /** Adds a exception type that can be thrown from this method
294         NOTE: This isn't enforced by the JVM. This is for reference
295         only. A method can throw exceptions not declared to be thrown
296         @param type The type of exception that can be thrown 
297     */
298     public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
299     
300     private final void grow() { if (size == capacity) grow(size+1); }
301     private final void grow(int newCap) {
302         if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
303         if (newCap <= capacity) return;
304         newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
305         
306         byte[] op2 = new byte[newCap];
307         if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
308         op = op2;
309         
310         Object[] arg2 = new Object[newCap];
311         if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
312         arg = arg2;
313         
314         capacity = newCap;
315     }
316
317     // Accessors //////////////////////////////////////////////////////////////////////////////
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.getDescriptor());
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.getDescriptor()));
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 debugToString(StringBuffer sb, String constructorName) {
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         
994         sb.append("  ").append(ClassFile.flagsToString(flags,false));
995         sb.append(method.debugToString());
996         if (thrownExceptions.size() > 0) {
997             sb.append("throws");
998             for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
999                 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1000             sb.setLength(sb.length()-1);
1001             sb.append(" ");
1002         }
1003         if ((flags & (NATIVE|ABSTRACT))==0) {
1004             sb.append("{\n");
1005             for(int i=0;i<size();i++) {
1006                 sb.append("    ");
1007                 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1008                 sb.append(i).append(": ");
1009                 sb.append(OP_NAMES[op[i]&0xff]);
1010                 String s = null;
1011                 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1012                 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1013                 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1014                 else if (arg[i] != null) s = arg[i].toString();
1015                 if (s != null) sb.append(" ").append(s);
1016                 sb.append("\n");
1017             }
1018             sb.append("  }\n");
1019         } else {
1020             sb.append(";");
1021         }
1022     }
1023
1024     // Unused //////////////////////////////////////////////////////////////////////////////
1025
1026     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
1027         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1028     public static byte negate(byte op) {
1029         switch(op) {
1030             case IFEQ: return IFNE;
1031             case IFNE: return IFEQ;
1032             case IFLT: return IFGE;
1033             case IFGE: return IFLT;
1034             case IFGT: return IFLE;
1035             case IFLE: return IFGT;
1036             case IF_ICMPEQ: return IF_ICMPNE;
1037             case IF_ICMPNE: return IF_ICMPEQ;
1038             case IF_ICMPLT: return IF_ICMPGE;
1039             case IF_ICMPGE: return IF_ICMPLT;
1040             case IF_ICMPGT: return IF_ICMPLE;
1041             case IF_ICMPLE: return IF_ICMPGT;
1042             case IF_ACMPEQ: return IF_ACMPNE;
1043             case IF_ACMPNE: return IF_ACMPEQ;
1044             
1045             default:
1046                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
1047         }
1048     }
1049
1050
1051 }