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