introduced Type.Ref as common superclass of Type.Class and Type.Array
[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 ClassGen#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 CPGen cp;
15     private final String name;
16     private final Type ret;
17     private final Type[] args;
18     private final int flags;
19     private final ClassGen.AttrGen attrs;
20     private final ClassGen.AttrGen codeAttrs;
21     private final Hashtable exnTable = new Hashtable();
22     private final Hashtable thrownExceptions = new Hashtable();
23     
24     private int maxStack = 16;
25     private int maxLocals;
26     
27     private int size;
28     private int capacity;
29     private byte[] op;
30     private Object[] arg;
31     
32     public String toString() { StringBuffer sb = new StringBuffer(); toString(sb, "<init>"); return sb.toString(); }
33     public void   toString(StringBuffer sb, String constructorName) {
34         sb.append(ClassGen.flagsToString(flags));
35         sb.append(ret);
36         sb.append(" ");
37
38         if (name.equals("<clinit>")) sb.append("static ");
39         else {
40             if (name.equals("<init>")) sb.append(constructorName);
41             else sb.append(name);
42             sb.append("(");
43             for(int i=0; i<args.length; i++)
44                 sb.append((i==0?"":", ")+args[i]);
45             sb.append(") ");
46         }
47         sb.append("{");
48         sb.append("}");
49         // FIXME: attrs, body
50     }
51
52     MethodGen(CPGen cp, DataInput in) throws IOException {
53         this.cp = cp;
54         flags = in.readShort();
55         name = cp.getUtf8ByIndex(in.readShort());
56         String descriptor = cp.getUtf8ByIndex(in.readShort());
57         String ret = descriptor.substring(descriptor.indexOf(')')+1);
58         this.ret = Type.fromDescriptor(ret);
59         //String args = descriptor.substring(1, descriptor.indexOf(')'));
60         args = new Type[0]; // FIXME
61         codeAttrs = null;
62         attrs = new ClassGen.AttrGen(cp, in);
63     }
64
65     MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
66         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
67             throw new IllegalArgumentException("invalid flags");
68         this.cp = owner.cp;
69         this.name = name;
70         this.ret = ret;
71         this.args = args;
72         this.flags = flags;
73         
74         attrs = new ClassGen.AttrGen(cp);
75         codeAttrs = new ClassGen.AttrGen(cp);
76         
77         cp.addUtf8(name);
78         cp.addUtf8(getDescriptor());
79         
80         if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
81         
82         maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0, 4);
83     }
84     
85     /** Returns the descriptor string for this method */
86     public String getDescriptor() { return MethodRef.getDescriptor(ret, args); }
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                 if(arg instanceof MethodRef) arg = new MethodRef.I((MethodRef)arg);
280                 break;
281         }
282         int opdata = OP_DATA[op&0xff];
283         if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
284             arg = cp.add(arg);
285         else if((opdata&OP_VALID_FLAG) == 0)
286             throw new IllegalArgumentException("unknown bytecode");
287         this.op[pos] = op;
288         this.arg[pos] = arg;
289     }
290     
291     /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
292         @see MethodGen.TSI
293         @see MethodGen.LSI
294     */
295     public static abstract class SI {
296         public final Object[] targets;
297         public Object defaultTarget;
298
299         SI(int size) { targets = new Object[size]; }
300         public void setTarget(int pos, Object val) { targets[pos] = val; }
301         public void setTarget(int pos, int val) { targets[pos] = N(val); }
302         public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
303         public void setDefaultTarget(Object o) { defaultTarget = o; }
304         public int size() { return targets.length; }
305         
306         public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
307         public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }   
308         
309         abstract int length();
310     }
311     
312     /** This class represents the arguments to the TABLESWITCH bytecode */
313     public static class TSI extends SI {
314         public final int lo;
315         public final int hi;
316         public TSI(int lo, int hi) {
317             super(hi-lo+1);
318             this.lo = lo;
319             this.hi = hi;
320         }
321         public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
322         public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
323         
324         int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
325     }
326     
327     /** This class represents the arguments to the LOOKUPSWITCH bytecode */
328     public static class LSI extends SI {
329         public final int[] vals;
330         public LSI(int size) {
331            super(size);
332            this.vals = new int[size];
333         }
334         public final void setVal(int pos, int val) { vals[pos] = val; }
335         
336         int length() { return 8 + targets.length * 8; } // key/val per target, default, count
337     }
338     
339     /** This class represents the arguments to byecodes that take two integer arguments. */
340     public static class Pair {
341         public int i1;
342         public int i2;
343         public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
344     }
345     
346     public static class Wide {
347         public final byte op;
348         public final int varNum;
349         public final int n;
350         Wide(byte op, int varNum) { this(op, varNum, 0); }
351         Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
352     }
353         
354     /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
355         necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
356     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
357     /** Sets the maxinum size of th stack for this function  to <i>maxStack</i>. This defaults to 16< */
358     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
359     
360     /** Computes the final bytecode for this method. 
361         @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
362         @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
363     */
364     public void finish() {
365         try {
366             _finish();
367         } catch(IOException e) {
368             throw new Error("should never happen");
369         }
370     }
371     
372     private Object resolveTarget(Object arg) {
373         int target;
374         if(arg instanceof PhantomTarget) {
375             target = ((PhantomTarget)arg).getTarget();
376             if(target == -1) throw new IllegalStateException("unresolved phantom target");
377             arg = N(target);
378         } else {
379             target = ((Integer)arg).intValue();
380         }
381         if(target < 0 || target >= size)
382             throw new IllegalStateException("invalid target address");
383         return arg;
384     }
385     
386     private void _finish() throws IOException {
387         if(size == FINISHED) return;
388         
389         cp.stable();
390         
391         ByteArrayOutputStream baos = new ByteArrayOutputStream();
392         DataOutput o = new DataOutputStream(baos);
393     
394         int[] pc = new int[size];
395         int[] maxpc = pc;
396         int p, i;
397         
398         // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
399         for(i=0, p=0;i<size;i++) {
400             byte op = this.op[i];
401             int opdata = OP_DATA[op&0xff];
402             int j;
403             maxpc[i] = p;
404             
405             if((opdata & OP_BRANCH_FLAG)!= 0) { 
406                 try { 
407                     arg[i] = resolveTarget(arg[i]);
408                 } catch(RuntimeException e) {
409                     System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
410                     throw e;
411                 }
412             }
413             
414             switch(op) {
415                 // Speical caculations
416                 case GOTO:
417                 case JSR: {
418                     int arg = ((Integer)this.arg[i]).intValue();
419                     if(arg < i && p - maxpc[arg] <= 32768) p += 3; 
420                     else p += 5;
421                     continue;
422                 }
423                 case NOP:
424                     if(EMIT_NOPS) p++;
425                     continue;
426                 case LOOKUPSWITCH:
427                 case TABLESWITCH: {
428                     SI si = (SI) arg[i];
429                     Object[] targets = si.targets;
430                     for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
431                     si.defaultTarget = resolveTarget(si.defaultTarget);
432                     p += 1 + 3 + si.length(); // opcode itself, padding, data
433                     if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
434                         int[] vals = ((LSI)si).vals;
435                         for(j=1;j<vals.length;j++)
436                             if(vals[j] <= vals[j-1])
437                                 throw new IllegalStateException("out of order/duplicate lookupswitch values");
438                     }
439                     continue;
440                 }
441                 // May need widening
442                 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
443                 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
444                 case RET: {
445                     int arg = ((Integer)this.arg[i]).intValue();
446                     if(arg > 255) {
447                         this.op[i] = WIDE;
448                         this.arg[i] = new Wide(op, arg);
449                     }
450                     break;
451                 }
452                 case IINC: {
453                     Pair pair = (Pair) this.arg[i];
454                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
455                         this.op[i] = WIDE;
456                         this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
457                     }
458                     break;
459                 }
460                 case LDC:
461                     j = cp.getIndex((CPGen.Ent)arg[i]);
462                     if(j >= 256) this.op[i] = op = LDC_W;
463                     break;
464                 default:
465             }
466             if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
467             p += 1 + j;
468         }
469         
470         // Pass2 - Widen instructions if they can possibly be too short
471         for(i=0;i<size;i++) {
472             switch(op[i]) {
473                 case GOTO:
474                 case JSR: {
475                     int arg = ((Integer)this.arg[i]).intValue();
476                     int diff = maxpc[arg] - maxpc[i];
477                     if(diff < -32768 || diff > 32767)
478                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
479                     break;
480                 }
481             }
482         }
483         
484         // Pass3 - Calculate actual pc
485         for(i=0, p=0;i<size;i++) {
486             byte op = this.op[i];
487             pc[i] = p;
488             switch(op) {
489                 case NOP:
490                     if(EMIT_NOPS) p++;
491                     break;
492                 case TABLESWITCH:
493                 case LOOKUPSWITCH: {
494                     SI si = (SI) arg[i];
495                     p++; // opcode itself
496                     p = (p + 3) & ~3; // padding
497                     p += 4; // default
498                     if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
499                     else p += 4 + si.size() * 4 * 2; // count, key, val * targets
500                     break;
501                 }
502                 case WIDE:
503                     p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
504                     break;                
505                 default: {
506                     int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
507                     if(l == 7) throw new Error("shouldn't be here");
508                     p += 1 + l;                    
509                 }
510             }
511         }
512         int codeSize = p;
513         
514         if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
515         
516         o.writeShort(maxStack);
517         o.writeShort(maxLocals);
518         o.writeInt(codeSize);
519         
520         // Pass 4 - Actually write the bytecodes
521         for(i=0;i<size;i++) {
522             byte op = this.op[i];
523             int opdata = OP_DATA[op&0xff];
524             if(op == NOP && !EMIT_NOPS) continue;
525             o.writeByte(op);
526             int argLength = opdata & OP_ARG_LENGTH_MASK;
527             
528             if(argLength == 0) continue; // skip if no args
529             
530             // Write args
531             Object arg = this.arg[i];  
532             
533             switch(op) {
534                 case IINC: {
535                     Pair pair = (Pair) arg;
536                     if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
537                     o.writeByte(pair.i1);
538                     o.writeByte(pair.i2);
539                     break;
540                 }
541                 case TABLESWITCH:
542                 case LOOKUPSWITCH: {
543                     SI si = (SI) arg;
544                     int mypc = pc[i];
545                     for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
546                     o.writeInt(pc[si.getDefaultTarget()] - mypc);
547                     if(op == LOOKUPSWITCH) {
548                         int[] vals = ((LSI)si).vals;
549                         o.writeInt(si.size());
550                         for(int j=0;j<si.size();j++) {
551                             o.writeInt(vals[j]);
552                             o.writeInt(pc[si.getTarget(j)] - mypc);
553                         }
554                     } else {
555                         TSI tsi = (TSI) si;
556                         o.writeInt(tsi.lo);
557                         o.writeInt(tsi.hi);
558                         for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
559                     }
560                     break;
561                 }
562                 case WIDE: {
563                     Wide wide = (Wide) arg;
564                     o.writeByte(wide.op);
565                     o.writeShort(wide.varNum);
566                     if(wide.op == IINC) o.writeShort(wide.n);
567                     break;
568                 }
569                     
570                 default:
571                     if((opdata & OP_BRANCH_FLAG) != 0) {
572                         int v = pc[((Integer)arg).intValue()] - pc[i];
573                         if(argLength == 2) {
574                             if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
575                             o.writeShort(v);
576                         } else if(argLength == 4) {
577                             o.writeInt(v);
578                         } else {
579                             throw new Error("should never happen");
580                         }
581                     } else if((opdata & OP_CPENT_FLAG) != 0) {
582                         int v = cp.getIndex((CPGen.Ent)arg);
583                         if(argLength == 1) o.writeByte(v);
584                         else if(argLength == 2) o.writeShort(v);
585                         else throw new Error("should never happen");
586                     } else if(argLength == 7) {
587                         throw new Error("should never happen - variable length instruction not explicitly handled");
588                     } else {
589                         int iarg  = ((Integer)arg).intValue();
590                         if(argLength == 1) {
591                             if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
592                             o.writeByte(iarg);
593                         } else if(argLength == 2) {
594                             if(iarg < -32768 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option"); 
595                             o.writeShort(iarg);
596                         } else {
597                             throw new Error("should never happen");
598                         }
599                     }
600                     break;
601             }
602         }
603
604         //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
605         
606         o.writeShort(exnTable.size());
607         for(Enumeration e = exnTable.keys();e.hasMoreElements();)
608             ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o, pc, codeSize);
609         
610         o.writeShort(codeAttrs.size());
611         codeAttrs.dump(o);
612         
613         baos.close();
614         
615         byte[] codeAttribute = baos.toByteArray();
616         attrs.add("Code", codeAttribute);
617         
618         baos.reset();
619         o.writeShort(thrownExceptions.size());
620         for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
621             o.writeShort(cp.getIndex((CPGen.Ent)thrownExceptions.get(e.nextElement())));
622         attrs.add("Exceptions", baos.toByteArray());
623         
624         size = capacity = FINISHED;        
625     }
626         
627     void dump(DataOutput o) throws IOException {
628         o.writeShort(flags);
629         o.writeShort(cp.getUtf8Index(name));
630         o.writeShort(cp.getUtf8Index(getDescriptor()));
631         o.writeShort(attrs.size());
632         attrs.dump(o);
633     }
634     
635     /** Negates the IF* instruction, <i>op</i>  (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ,  etc)
636         @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
637     public static byte negate(byte op) {
638         switch(op) {
639             case IFEQ: return IFNE;
640             case IFNE: return IFEQ;
641             case IFLT: return IFGE;
642             case IFGE: return IFLT;
643             case IFGT: return IFLE;
644             case IFLE: return IFGT;
645             case IF_ICMPEQ: return IF_ICMPNE;
646             case IF_ICMPNE: return IF_ICMPEQ;
647             case IF_ICMPLT: return IF_ICMPGE;
648             case IF_ICMPGE: return IF_ICMPLT;
649             case IF_ICMPGT: return IF_ICMPLE;
650             case IF_ICMPLE: return IF_ICMPGT;
651             case IF_ACMPEQ: return IF_ACMPNE;
652             case IF_ACMPNE: return IF_ACMPEQ;
653             
654             default:
655                 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
656         }
657     }
658     
659     /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written. 
660         This class is more or less a mutable integer */
661     public static class PhantomTarget {
662         private int target = -1;
663         public void setTarget(int target) { this.target = target; }
664         public int getTarget() { return target; }
665     }
666     
667     private static Integer N(int n) { return new Integer(n); }
668     private static Long N(long n) { return new Long(n); }
669     private static Float N(float f) { return new Float(f); }
670     private static Double N(double d) { return new Double(d); }
671     private static int max(int a, int b) { return a > b ? a : b; }
672     
673     private static final int OP_BRANCH_FLAG = 1<<3;
674     private static final int OP_CPENT_FLAG = 1<<4;
675     private static final int OP_VALID_FLAG = 1<<5;
676     private static final int OP_ARG_LENGTH_MASK = 7;
677     private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
678     private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
679     private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
680     private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
681     
682     // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
683     private static final byte[] OP_DATA = {
684             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
685             0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
686             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
687             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 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, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
693             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
694             0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
695             0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
696             0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
697             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
698             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
699             0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
700     };
701 }