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