pulled {Method,Member,Field}Ref into Type.Class; made them inner classes; much cleaner
[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     private static final int FINISHED = -2;
13
14     private final ClassFile owner;
15     private final CPGen cp;
16     private final String name;
17     private final Type ret;
18     private final Type[] args;
19     private final int flags;
20     private final ClassFile.AttrGen attrs;
21     private final ClassFile.AttrGen codeAttrs;
22     private final Hashtable exnTable = new Hashtable();
23     private final Hashtable thrownExceptions = new Hashtable();
24     
25     private int maxStack = 16;
26     private int maxLocals;
27     
28     private int size;
29     private int capacity;
30     private byte[] op;
31     private Object[] arg;
32     
33     public String toString() { StringBuffer sb = new StringBuffer(); toString(sb, "<init>"); return sb.toString(); }
34     public void   toString(StringBuffer sb, String constructorName) {
35         sb.append(ClassFile.flagsToString(flags));
36         sb.append(ret);
37         sb.append(" ");
38
39         if (name.equals("<clinit>")) sb.append("static ");
40         else {
41             if (name.equals("<init>")) sb.append(constructorName);
42             else sb.append(name);
43             sb.append("(");
44             for(int i=0; i<args.length; i++)
45                 sb.append((i==0?"":", ")+args[i]);
46             sb.append(") ");
47         }
48         sb.append("{");
49         sb.append("}");
50         // FIXME: attrs, body
51     }
52
53     MethodGen(CPGen cp, DataInput in, ClassFile owner) throws IOException {
54         this.cp = cp;
55         this.owner = owner;
56         flags = in.readShort();
57         name = cp.getUtf8ByIndex(in.readShort());
58         String descriptor = cp.getUtf8ByIndex(in.readShort());
59         String ret = descriptor.substring(descriptor.indexOf(')')+1);
60         this.ret = Type.instance(ret);
61         //String args = descriptor.substring(1, descriptor.indexOf(')'));
62         args = new Type[0]; // FIXME
63         codeAttrs = null;
64         attrs = new ClassFile.AttrGen(cp, in);
65     }
66
67     MethodGen(ClassFile owner, String name, Type ret, Type[] args, int flags) {
68         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
69             throw new IllegalArgumentException("invalid flags");
70         this.cp = owner.cp;
71         this.name = name;
72         this.ret = ret;
73         this.args = args;
74         this.flags = flags;
75         this.owner = owner;
76         
77         attrs = new ClassFile.AttrGen(cp);
78         codeAttrs = new ClassFile.AttrGen(cp);
79         
80         cp.addUtf8(name);
81         cp.addUtf8(owner.getType().method(name, ret, args).getDescriptor());
82         
83         if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
84         
85         maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0, 4);
86     }
87     
88     private class ExnTableEnt {
89         public int start;
90         public int end;
91         public int handler;
92         public CPGen.Ent typeEnt;
93         public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
94             this.start = start;
95             this.end = end;
96             this.handler = handler;
97             this.typeEnt = typeEnt;
98         }
99         public void dump(DataOutput o, int[] pc, int endPC) throws IOException {
100             o.writeShort(pc[start]);
101             o.writeShort(end==pc.length ? endPC : pc[end]);
102             o.writeShort(pc[handler]);
103             o.writeShort(cp.getIndex(typeEnt));
104         }
105     }
106     
107     /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
108         @param start The instruction to start at (inclusive)
109         @param end The instruction to end at (exclusive)
110         @param handler The instruction of the excepton handler
111         @param type The type of exception that is to be handled (MUST inherit from Throwable)
112     */
113     public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
114         exnTable.put(type, new ExnTableEnt(start, end, handler, cp.add(type)));
115     }
116     
117     /** Adds a exception type that can be thrown from this method
118         NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown 
119         @param type The type of exception that can be thrown 
120     */
121     public final void addThrow(Type.Class type) {
122         thrownExceptions.put(type, cp.add(type));
123     }
124     
125     private final void grow() { if(size == capacity) grow(size+1); }
126     private final void grow(int newCap) {
127         if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
128         if(capacity == FINISHED) throw new IllegalStateException("method has been finished");
129         if(newCap <= capacity) return;
130         newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
131         
132         byte[] op2 = new byte[newCap];
133         if(capacity != 0) System.arraycopy(op, 0, op2, 0, size);
134         op = op2;
135         
136         Object[] arg2 = new Object[newCap];
137         if(capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
138         arg = arg2;
139         
140         capacity = newCap;
141     }
142     
143     /** Returns the size (in instructions) of this method 
144         @return The size of the method (in instructions)
145     */
146     public final int size() { return size; }
147     
148     // These two are optimized for speed, they don't call set() below
149     /** Add a bytecode (with no argument) to the method */
150     public final int add(byte op) {
151         int s = size;
152         if(s == capacity) grow();
153         this.op[s] = op;
154         size++;
155         return s;
156     }
157     /** Set the bytecode at position <i>pos</i> to <i>op</i> */
158     public final void set(int pos, byte op) { this.op[pos] = op; }
159         
160     /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method 
161         @return The position of the new bytecode
162         */
163     public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
164     /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
165         @return The position of the new bytecode
166         @see #add(byte, int)
167     */
168     public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
169     /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
170         @return The position of the new bytecode
171         @see #add(byte, Object)
172     */
173     public final int add(byte op, int arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
174     
175     /** Gets the bytecode at position <i>pos</i>
176         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
177     */
178     public final byte get(int pos) { return op[pos]; }
179     
180     /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
181         Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
182         interal form when they are added. The value returned from this method for these instruction can be reused, but there
183         is no way to retrieve the original object 
184         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
185     */    
186     public final Object getArg(int pos) { return arg[pos]; }
187     
188     /** 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.
189         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
190         @see #setArg(int, Object) */
191     public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
192     /** Sets the argument for <i>pos</i> to <i>arg</i>.
193         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
194     */
195     public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
196     
197     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly. 
198         This is equivalent to set(pos, op, arg?1:0) 
199         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
200     */
201     public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
202     
203     // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
204     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
205         This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
206         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
207     */
208     public final void set(int pos, byte op, int n) {
209         Object arg = null;
210         OUTER: switch(op) {
211             case LDC:
212                 switch(n) {
213                     case -1: op = ICONST_M1; break OUTER;
214                     case 0:  op = ICONST_0;  break OUTER;
215                     case 1:  op = ICONST_1;  break OUTER;
216                     case 2:  op = ICONST_2;  break OUTER; 
217                     case 3:  op = ICONST_3;  break OUTER;
218                     case 4:  op = ICONST_4;  break OUTER;
219                     case 5:  op = ICONST_5;  break OUTER;
220                 }
221                 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); } 
222                 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
223                 else { arg = cp.add(N(n)); }
224                 break;
225             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
226             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
227                 if(n >= maxLocals) maxLocals = n + 1;
228                 if(n >= 0 && n <= 3) {
229                     byte base = 0;
230                     switch(op) {
231                         case ILOAD:  base = ILOAD_0;  break;
232                         case ISTORE: base = ISTORE_0; break;
233                         case LLOAD:  base = LLOAD_0;  break;
234                         case LSTORE: base = LSTORE_0; break; 
235                         case FLOAD:  base = FLOAD_0;  break;
236                         case FSTORE: base = FSTORE_0; break;
237                         case DLOAD:  base = DLOAD_0;  break;
238                         case DSTORE: base = DSTORE_0; break;
239                         case ALOAD:  base = ALOAD_0;  break;
240                         case ASTORE: base = ASTORE_0; break;
241                     }
242                     op = (byte)((base&0xff) + n);
243                 } else {
244                     arg = N(n);
245                 }
246                 break;
247             default:
248                 set(pos, op, N(n));
249                 return;
250         }            
251         this.op[pos] = op;
252         this.arg[pos] = arg;
253     }
254     
255     /** Sets the bytecode and argument  at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
256         @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
257         */
258     public final void set(int pos, byte op, Object arg) {
259         switch(op) {
260             case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
261             case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
262                 // set(int, byte, int) always handles these ops itself
263                 set(pos, op, ((Integer)arg).intValue());
264                 return;
265             case LDC:
266                 // set(int, byte, int) always handles these opts itself
267                 if(arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
268                 if(arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
269                 
270                 if(arg instanceof Long) {
271                     long l = ((Long)arg).longValue();
272                     if(l == 0L) { this.op[pos] = LCONST_0; return; }
273                     if(l == 1L) { this.op[pos] = LCONST_1; return; }
274                 }
275                 
276                 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
277                 break;
278             case INVOKEINTERFACE: {
279                 break;
280             }
281         }
282         int opdata = OP_DATA[op&0xff];
283         if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent)) {
284             if (op==INVOKEINTERFACE) arg = cp.add(arg, true);
285             else arg = cp.add(arg);
286         }
287         else if((opdata&OP_VALID_FLAG) == 0)
288             throw new IllegalArgumentException("unknown bytecode");
289         this.op[pos] = op;
290         this.arg[pos] = arg;
291     }
292     
293     /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
294         @see MethodGen.TSI
295         @see MethodGen.LSI
296     */
297     public static abstract class SI {
298         public final Object[] targets;
299         public Object defaultTarget;
300
301         SI(int size) { targets = new Object[size]; }
302         public void setTarget(int pos, Object val) { targets[pos] = val; }
303         public void setTarget(int pos, int val) { targets[pos] = N(val); }
304         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
305         public void setDefaultTarget(Object o) { defaultTarget = o; }
306         public int size() { return targets.length; }
307         
308         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
309         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }   
310         
311         abstract int length();
312     }
313     
314     /** This class represents the arguments to the TABLESWITCH bytecode */
315     public static class TSI extends SI {
316         public final int lo;
317         public final int hi;
318         public TSI(int lo, int hi) {
319             super(hi-lo+1);
320             this.lo = lo;
321             this.hi = hi;
322         }
323         public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
324         public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
325         
326         int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
327     }
328     
329     /** This class represents the arguments to the LOOKUPSWITCH bytecode */
330     public static class LSI extends SI {
331         public final int[] vals;
332         public LSI(int size) {
333            super(size);
334            this.vals = new int[size];
335         }
336         public final void setVal(int pos, int val) { vals[pos] = val; }
337         
338         int length() { return 8 + targets.length * 8; } // key/val per target, default, count
339     }
340     
341     /** This class represents the arguments to byecodes that take two integer arguments. */
342     public static class Pair {
343         public int i1;
344         public int i2;
345         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
346     }
347     
348     public static class Wide {
349         public final byte op;
350         public final int varNum;
351         public final int n;
352         Wide(byte op, int varNum) { this(op, varNum, 0); }
353         Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
354     }
355         
356     /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
357         necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
358     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
359     /** Sets the maxinum size of th stack for this function  to <i>maxStack</i>. This defaults to 16< */
360     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
361     
362     /** Computes the final bytecode for this method. 
363         @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
364         @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
365     */
366     public void finish() {
367         try {
368             _finish();
369         } catch(IOException e) {
370             throw new Error("should never happen");
371         }
372     }
373     
374     private Object resolveTarget(Object arg) {
375         int target;
376         if(arg instanceof PhantomTarget) {
377             target = ((PhantomTarget)arg).getTarget();
378             if(target == -1) throw new IllegalStateException("unresolved phantom target");
379             arg = N(target);
380         } else {
381             target = ((Integer)arg).intValue();
382         }
383         if(target < 0 || target >= size)
384             throw new IllegalStateException("invalid target address");
385         return arg;
386     }
387     
388     private void _finish() throws IOException {
389         if(size == FINISHED) return;
390         
391         cp.stable();
392         
393         ByteArrayOutputStream baos = new ByteArrayOutputStream();
394         DataOutput o = new DataOutputStream(baos);
395     
396         int[] pc = new int[size];
397         int[] maxpc = pc;
398         int p, i;
399         
400         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
401         for(i=0, p=0;i<size;i++) {
402             byte op = this.op[i];
403             int opdata = OP_DATA[op&0xff];
404             int j;
405             maxpc[i] = p;
406             
407             if((opdata & OP_BRANCH_FLAG)!= 0) { 
408                 try { 
409                     arg[i] = resolveTarget(arg[i]);
410                 } catch(RuntimeException e) {
411                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
412                     throw e;
413                 }
414             }
415             
416             switch(op) {
417                 // Speical caculations
418                 case GOTO:
419                 case JSR: {
420                     int arg = ((Integer)this.arg[i]).intValue();
421                     if(arg < i && p - maxpc[arg] <= 32768) p += 3; 
422                     else p += 5;
423                     continue;
424                 }
425                 case NOP:
426                     if(EMIT_NOPS) p++;
427                     continue;
428                 case LOOKUPSWITCH:
429                 case TABLESWITCH: {
430                     SI si = (SI) arg[i];
431                     Object[] targets = si.targets;
432                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
433                     si.defaultTarget = resolveTarget(si.defaultTarget);
434                     p += 1 + 3 + si.length(); // opcode itself, padding, data
435                     if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
436                         int[] vals = ((LSI)si).vals;
437                         for(j=1;j<vals.length;j++)
438                             if(vals[j] <= vals[j-1])
439                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
440                     }
441                     continue;
442                 }
443                 // May need widening
444                 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
445                 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
446                 case RET: {
447                     int arg = ((Integer)this.arg[i]).intValue();
448                     if(arg > 255) {
449                         this.op[i] = WIDE;
450                         this.arg[i] = new Wide(op, arg);
451                     }
452                     break;
453                 }
454                 case IINC: {
455                     Pair pair = (Pair) this.arg[i];
456                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
457                         this.op[i] = WIDE;
458                         this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
459                     }
460                     break;
461                 }
462                 case LDC:
463                     j = cp.getIndex((CPGen.Ent)arg[i]);
464                     if(j >= 256) this.op[i] = op = LDC_W;
465                     break;
466                 default:
467             }
468             if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
469             p += 1 + j;
470         }
471         
472         // Pass2 - Widen instructions if they can possibly be too short
473         for(i=0;i<size;i++) {
474             switch(op[i]) {
475                 case GOTO:
476                 case JSR: {
477                     int arg = ((Integer)this.arg[i]).intValue();
478                     int diff = maxpc[arg] - maxpc[i];
479                     if(diff < -32768 || diff > 32767)
480                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
481                     break;
482                 }
483             }
484         }
485         
486         // Pass3 - Calculate actual pc
487         for(i=0, p=0;i<size;i++) {
488             byte op = this.op[i];
489             pc[i] = p;
490             switch(op) {
491                 case NOP:
492                     if(EMIT_NOPS) p++;
493                     break;
494                 case TABLESWITCH:
495                 case LOOKUPSWITCH: {
496                     SI si = (SI) arg[i];
497                     p++; // opcode itself
498                     p = (p + 3) & ~3; // padding
499                     p += 4; // default
500                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
501                     else p += 4 + si.size() * 4 * 2; // count, key, val * targets
502                     break;
503                 }
504                 case WIDE:
505                     p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
506                     break;                
507                 default: {
508                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
509                     if(l == 7) throw new Error("shouldn't be here");
510                     p += 1 + l;                    
511                 }
512             }
513         }
514         int codeSize = p;
515         
516         if(codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
517         
518         o.writeShort(maxStack);
519         o.writeShort(maxLocals);
520         o.writeInt(codeSize);
521         
522         // Pass 4 - Actually write the bytecodes
523         for(i=0;i<size;i++) {
524             byte op = this.op[i];
525             int opdata = OP_DATA[op&0xff];
526             if(op == NOP && !EMIT_NOPS) continue;
527             o.writeByte(op);
528             int argLength = opdata & OP_ARG_LENGTH_MASK;
529             
530             if(argLength == 0) continue; // skip if no args
531             
532             // Write args
533             Object arg = this.arg[i];  
534             
535             switch(op) {
536                 case IINC: {
537                     Pair pair = (Pair) arg;
538                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg"); 
539                     o.writeByte(pair.i1);
540                     o.writeByte(pair.i2);
541                     break;
542                 }
543                 case TABLESWITCH:
544                 case LOOKUPSWITCH: {
545                     SI si = (SI) arg;
546                     int mypc = pc[i];
547                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
548                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
549                     if(op == LOOKUPSWITCH) {
550                         int[] vals = ((LSI)si).vals;
551                         o.writeInt(si.size());
552                         for(int j=0;j<si.size();j++) {
553                             o.writeInt(vals[j]);
554                             o.writeInt(pc[si.getTarget(j)] - mypc);
555                         }
556                     } else {
557                         TSI tsi = (TSI) si;
558                         o.writeInt(tsi.lo);
559                         o.writeInt(tsi.hi);
560                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
561                     }
562                     break;
563                 }
564                 case WIDE: {
565                     Wide wide = (Wide) arg;
566                     o.writeByte(wide.op);
567                     o.writeShort(wide.varNum);
568                     if(wide.op == IINC) o.writeShort(wide.n);
569                     break;
570                 }
571                     
572                 default:
573                     if((opdata & OP_BRANCH_FLAG) != 0) {
574                         int v = pc[((Integer)arg).intValue()] - pc[i];
575                         if(argLength == 2) {
576                             if(v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
577                             o.writeShort(v);
578                         } else if(argLength == 4) {
579                             o.writeInt(v);
580                         } else {
581                             throw new Error("should never happen");
582                         }
583                     } else if((opdata & OP_CPENT_FLAG) != 0) {
584                         int v = cp.getIndex((CPGen.Ent)arg);
585                         if(argLength == 1) o.writeByte(v);
586                         else if(argLength == 2) o.writeShort(v);
587                         else throw new Error("should never happen");
588                     } else if(argLength == 7) {
589                         throw new Error("should never happen - variable length instruction not explicitly handled");
590                     } else {
591                         int iarg  = ((Integer)arg).intValue();
592                         if(argLength == 1) {
593                             if(iarg < -128 || iarg >= 256) throw new ClassFile.Exn("overflow of s/u1 option");
594                             o.writeByte(iarg);
595                         } else if(argLength == 2) {
596                             if(iarg < -32768 || iarg >= 65536) throw new ClassFile.Exn("overflow of s/u2 option"); 
597                             o.writeShort(iarg);
598                         } else {
599                             throw new Error("should never happen");
600                         }
601                     }
602                     break;
603             }
604         }
605
606         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
607         
608         o.writeShort(exnTable.size());
609         for(Enumeration e = exnTable.keys();e.hasMoreElements();)
610             ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o, pc, codeSize);
611         
612         o.writeShort(codeAttrs.size());
613         codeAttrs.dump(o);
614         
615         baos.close();
616         
617         byte[] codeAttribute = baos.toByteArray();
618         attrs.add("Code", codeAttribute);
619         
620         baos.reset();
621         o.writeShort(thrownExceptions.size());
622         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
623             o.writeShort(cp.getIndex((CPGen.Ent)thrownExceptions.get(e.nextElement())));
624         attrs.add("Exceptions", baos.toByteArray());
625         
626         size = capacity = FINISHED;        
627     }
628         
629     void dump(DataOutput o) throws IOException {
630         o.writeShort(flags);
631         o.writeShort(cp.getUtf8Index(name));
632         o.writeShort(cp.getUtf8Index(owner.getType().method(name, ret, args).getDescriptor()));
633         o.writeShort(attrs.size());
634         attrs.dump(o);
635     }
636     
637     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
638         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
639     public static byte negate(byte op) {
640         switch(op) {
641             case IFEQ: return IFNE;
642             case IFNE: return IFEQ;
643             case IFLT: return IFGE;
644             case IFGE: return IFLT;
645             case IFGT: return IFLE;
646             case IFLE: return IFGT;
647             case IF_ICMPEQ: return IF_ICMPNE;
648             case IF_ICMPNE: return IF_ICMPEQ;
649             case IF_ICMPLT: return IF_ICMPGE;
650             case IF_ICMPGE: return IF_ICMPLT;
651             case IF_ICMPGT: return IF_ICMPLE;
652             case IF_ICMPLE: return IF_ICMPGT;
653             case IF_ACMPEQ: return IF_ACMPNE;
654             case IF_ACMPNE: return IF_ACMPEQ;
655             
656             default:
657                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
658         }
659     }
660     
661     /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written. 
662         This class is more or less a mutable integer */
663     public static class PhantomTarget {
664         private int target = -1;
665         public void setTarget(int target) { this.target = target; }
666         public int getTarget() { return target; }
667     }
668     
669     private static Integer N(int n) { return new Integer(n); }
670     private static Long N(long n) { return new Long(n); }
671     private static Float N(float f) { return new Float(f); }
672     private static Double N(double d) { return new Double(d); }
673     private static int max(int a, int b) { return a > b ? a : b; }
674     
675     private static final int OP_BRANCH_FLAG = 1<<3;
676     private static final int OP_CPENT_FLAG = 1<<4;
677     private static final int OP_VALID_FLAG = 1<<5;
678     private static final int OP_ARG_LENGTH_MASK = 7;
679     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
680     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
681     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
682     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
683     
684     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
685     private static final byte[] OP_DATA = {
686             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
687             0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
688             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
689             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
690             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
691             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
692             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
693             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
694             0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
695             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
696             0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
697             0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
698             0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
699             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
700             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
701             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
702     };
703 }