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