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