1 package org.ibex.classgen;
6 // FEATURE: Support WIDE bytecodes
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;
13 private static final int NO_CODE = -1;
14 private static final int FINISHED = -2;
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();
26 private int maxStack = 16;
27 private int maxLocals;
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");
43 attrs = new ClassGen.AttrGen(cp);
44 codeAttrs = new ClassGen.AttrGen(cp);
47 cp.addUtf8(getDescriptor());
49 if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
51 maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4);
54 /** Returns the descriptor string for this method */
55 public String getDescriptor() { return MethodRef.getDescriptor(ret,args); }
57 private class ExnTableEnt {
61 public CPGen.Ent typeEnt;
62 public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
65 this.handler = handler;
66 this.typeEnt = typeEnt;
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());
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)
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)));
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
90 public final void addThrow(Type.Object type) {
91 thrownExceptions.put(type,cp.add(type));
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);
101 byte[] op2 = new byte[newCap];
102 if(capacity != 0) System.arraycopy(op,0,op2,0,size);
105 Object[] arg2 = new Object[newCap];
106 if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
112 /** Returns the size (in instructions) of this method
113 @return The size of the method (in instructions)
115 public final int size() { return size; }
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) {
121 if(s == capacity) grow();
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; }
129 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
130 @return The position of the new bytecode
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
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)
142 public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
144 /** Gets the bytecode at position <i>pos</i>
145 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
147 public final byte get(int pos) { return op[pos]; }
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()
155 public final Object getArg(int pos) { return arg[pos]; }
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()
164 public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); }
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()
170 public final void set(int pos, byte op, boolean arg) { set(pos,op,arg?1:0); }
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()
177 public final void set(int pos, byte op, int 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;
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)); }
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 >= 0 && n <= 3) {
199 case ILOAD: base = ILOAD_0; break;
200 case ISTORE: base = ISTORE_0; break;
201 case LLOAD: base = LLOAD_0; break;
202 case LSTORE: base = LSTORE_0; break;
203 case FLOAD: base = FLOAD_0; break;
204 case FSTORE: base = FSTORE_0; break;
205 case DLOAD: base = DLOAD_0; break;
206 case DSTORE: base = DSTORE_0; break;
207 case ALOAD: base = ALOAD_0; break;
208 case ASTORE: base = ASTORE_0; break;
210 op = (byte)((base&0xff) + n);
212 if(n >= maxLocals) maxLocals = n + 1;
224 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
225 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
227 public final void set(int pos, byte op, Object arg) {
229 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
230 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
231 // set(int,byte,int) always handles these ops itself
232 set(pos,op,((Integer)arg).intValue());
235 // set(int,byte,int) always handles these opts itself
236 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
237 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
239 if(arg instanceof Long) {
240 long l = ((Long)arg).longValue();
241 if(l == 0L) { this.op[pos] = LCONST_0; return; }
242 if(l == 1L) { this.op[pos] = LCONST_1; return; }
245 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
247 case INVOKEINTERFACE:
248 if(arg instanceof MethodRef) arg = new MethodRef.I((MethodRef)arg);
251 int opdata = OP_DATA[op&0xff];
252 if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
254 else if((opdata&OP_VALID_FLAG) == 0)
255 throw new IllegalArgumentException("unknown bytecode");
260 /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
264 public static class SI {
265 public final Object[] targets;
266 public Object defaultTarget;
268 SI(int size) { targets = new Object[size]; }
269 public void setTarget(int pos, Object val) { targets[pos] = val; }
270 public void setTarget(int pos, int val) { targets[pos] = N(val); }
271 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
272 public void setDefaultTarget(Object o) { defaultTarget = o; }
273 public int size() { return targets.length; }
275 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
276 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
279 /** This class represents the arguments to the TABLESWITCH bytecode */
280 public static class TSI extends SI {
283 public TSI(int lo, int hi) {
288 public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
289 public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
292 /** This class represents the arguments to the LOOKUPSWITCH bytecode */
293 public static class LSI extends SI {
294 public final int[] vals;
295 public LSI(int size) {
297 this.vals = new int[size];
299 public final void setVal(int pos, int val) { vals[pos] = val; }
302 /** This class represents the arguments to byecodes that take two integer arguments. */
303 public static class Pair {
306 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
309 /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
310 necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
311 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
312 /** Sets the maxinum size of th stack for this function to <i>maxStack</i>. This defaults to 16< */
313 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
315 /** Computes the final bytecode for this method.
316 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
317 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
319 public void finish() {
322 } catch(IOException e) {
323 throw new Error("should never happen");
327 private Object resolveTarget(Object arg) {
329 if(arg instanceof PhantomTarget) {
330 target = ((PhantomTarget)arg).getTarget();
331 if(target == -1) throw new IllegalStateException("unresolved phantom target");
334 target = ((Integer)arg).intValue();
336 if(target < 0 || target >= size)
337 throw new IllegalStateException("invalid target address");
341 private void _finish() throws IOException {
342 if(size == FINISHED) return;
344 ByteArrayOutputStream baos = new ByteArrayOutputStream();
345 DataOutput o = new DataOutputStream(baos);
347 int[] pc = new int[size];
351 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
352 for(i=0,p=0;i<size;i++) {
353 byte op = this.op[i];
354 int opdata = OP_DATA[op&0xff];
358 if((opdata & OP_BRANCH_FLAG)!= 0) {
360 arg[i] = resolveTarget(arg[i]);
361 } catch(RuntimeException e) {
362 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
378 Object[] targets = si.targets;
379 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
380 si.defaultTarget = resolveTarget(si.defaultTarget);
381 p += 1 + 3 + 4; // opcode itself, padding, default
382 if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
383 else p += 4 + targets.length * 4 * 2; // count, key,val * targets
384 if(op == LOOKUPSWITCH) {
385 int[] vals = ((LSI)si).vals;
386 for(j=1;j<vals.length;j++)
387 if(vals[j] <= vals[j-1])
388 throw new IllegalStateException("out of order/duplicate lookupswitch values");
393 j = ((CPGen.Ent)arg[i]).getIndex();
394 if(j >= 256) this.op[i] = op = LDC_W;
397 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
403 // Pass2 - Widen instructions if they can possibly be too short
404 for(i=0;i<size;i++) {
408 int arg = ((Integer)this.arg[i]).intValue();
409 int diff = maxpc[arg] - maxpc[i];
410 if(diff < -32768 || diff > 32767)
411 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
417 // Pass3 - Calculate actual pc
418 for(i=0,p=0;i<size;i++) {
419 byte op = this.op[i];
428 p++; // opcode itself
429 p = (p + 3) & ~3; // padding
431 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
432 else p += 4 + si.size() * 4 * 2; // count, key,val * targets
436 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
437 if(l == 7) throw new Error("shouldn't be here");
445 if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
447 o.writeShort(maxStack);
448 o.writeShort(maxLocals);
449 o.writeInt(codeSize);
451 // Pass 4 - Actually write the bytecodes
452 for(i=0;i<size;i++) {
453 byte op = this.op[i];
454 int opdata = OP_DATA[op&0xff];
455 if(op == NOP && !EMIT_NOPS) continue;
457 o.writeByte(op&0xff);
458 int argLength = opdata & OP_ARG_LENGTH_MASK;
460 if(argLength == 0) continue; // skip if no args
463 Object arg = this.arg[i];
467 Pair pair = (Pair) arg;
468 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg");
469 o.writeByte(pair.i1);
470 o.writeByte(pair.i2);
477 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
478 o.writeInt(pc[si.getDefaultTarget()] - mypc);
479 if(op == LOOKUPSWITCH) {
480 int[] vals = ((LSI)si).vals;
481 o.writeInt(si.size());
482 for(int j=0;j<si.size();j++) {
484 o.writeInt(pc[si.getTarget(j)] - mypc);
490 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
495 throw new Error("WIDE instruction not yet supported");
498 if((opdata & OP_BRANCH_FLAG) != 0) {
499 int v = pc[((Integer)arg).intValue()] - pc[i];
501 if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
503 } else if(argLength == 4) {
506 throw new Error("should never happen");
508 } else if((opdata & OP_CPENT_FLAG) != 0) {
509 int v = ((CPGen.Ent)arg).getIndex();
510 if(argLength == 1) o.writeByte(v);
511 else if(argLength == 2) o.writeShort(v);
512 else throw new Error("should never happen");
513 } else if(argLength == 7) {
514 throw new Error("should never happen - variable length instruction not explicitly handled");
516 int iarg = ((Integer)arg).intValue();
518 if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
520 } else if(argLength == 2) {
521 if(iarg < -32768 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option");
524 throw new Error("should never happen");
531 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
533 o.writeShort(exnTable.size());
534 for(Enumeration e = exnTable.keys();e.hasMoreElements();)
535 ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
537 o.writeShort(codeAttrs.size());
542 byte[] codeAttribute = baos.toByteArray();
543 attrs.add("Code",codeAttribute);
546 o.writeShort(thrownExceptions.size());
547 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
548 o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
549 attrs.add("Exceptions",baos.toByteArray());
551 size = capacity = FINISHED;
554 void dump(DataOutput o) throws IOException {
556 o.writeShort(cp.getUtf8Index(name));
557 o.writeShort(cp.getUtf8Index(getDescriptor()));
558 o.writeShort(attrs.size());
562 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
563 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
564 public static byte negate(byte op) {
566 case IFEQ: return IFNE;
567 case IFNE: return IFEQ;
568 case IFLT: return IFGE;
569 case IFGE: return IFLT;
570 case IFGT: return IFLE;
571 case IFLE: return IFGT;
572 case IF_ICMPEQ: return IF_ICMPNE;
573 case IF_ICMPNE: return IF_ICMPEQ;
574 case IF_ICMPLT: return IF_ICMPGE;
575 case IF_ICMPGE: return IF_ICMPLT;
576 case IF_ICMPGT: return IF_ICMPLE;
577 case IF_ICMPLE: return IF_ICMPGT;
578 case IF_ACMPEQ: return IF_ACMPNE;
579 case IF_ACMPNE: return IF_ACMPEQ;
582 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
586 /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written.
587 This class is more or less a mutable integer */
588 public static class PhantomTarget {
589 private int target = -1;
590 public void setTarget(int target) { this.target = target; }
591 public int getTarget() { return target; }
594 private static Integer N(int n) { return new Integer(n); }
595 private static Long N(long n) { return new Long(n); }
596 private static Float N(float f) { return new Float(f); }
597 private static Double N(double d) { return new Double(d); }
598 private static int max(int a, int b) { return a > b ? a : b; }
600 private static final int OP_BRANCH_FLAG = 1<<3;
601 private static final int OP_CPENT_FLAG = 1<<4;
602 private static final int OP_VALID_FLAG = 1<<5;
603 private static final int OP_ARG_LENGTH_MASK = 7;
604 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
605 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
606 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
607 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
609 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
610 private static final byte[] OP_DATA = {
611 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
612 0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
613 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
614 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
615 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
616 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
617 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
618 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
619 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
620 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
621 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
622 0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
623 0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
624 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
625 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
626 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01