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