1 package org.ibex.classgen;
6 public class MethodGen implements CGConst {
7 private final static boolean EMIT_NOPS = true;
9 private final CPGen cp;
10 private final String name;
11 private final Type ret;
12 private final Type[] args;
13 private final int flags;
14 private final ClassGen.AttrGen attrs;
15 private final ClassGen.AttrGen codeAttrs;
16 private final Hashtable exnTable = new Hashtable();
17 private final Hashtable thrownExceptions = new Hashtable();
19 private int maxStack = 16;
20 private int maxLocals;
27 MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
28 if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
29 throw new IllegalArgumentException("invalid flags");
36 attrs = new ClassGen.AttrGen(cp);
37 codeAttrs = new ClassGen.AttrGen(cp);
40 cp.addUtf8(getDescriptor());
42 if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
44 maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4);
47 public String getDescriptor() { return MethodRef.getDescriptor(ret,args); }
49 private class ExnTableEnt {
53 public CPGen.Ent typeEnt;
54 public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
57 this.handler = handler;
58 this.typeEnt = typeEnt;
60 public void dump(DataOutput o, int[] pc, int endPC) throws IOException {
61 o.writeShort(pc[start]);
62 o.writeShort(end==pc.length ? endPC : pc[end]);
63 o.writeShort(pc[handler]);
64 o.writeShort(typeEnt.getIndex());
68 public final void addExceptionHandler(int startPC, int endPC, int handlerPC, Type.Object type) {
69 exnTable.put(type, new ExnTableEnt(startPC,endPC,handlerPC,cp.add(type)));
72 public final void addThrow(Type.Object type) {
73 thrownExceptions.put(type,cp.add(type));
76 private final void grow() { if(size == capacity) grow(size+1); }
77 private final void grow(int newCap) {
78 if(capacity == -1) throw new IllegalStateException("method can't have code");
79 if(newCap <= capacity) return;
80 newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
82 byte[] op2 = new byte[newCap];
83 if(capacity != 0) System.arraycopy(op,0,op2,0,size);
86 Object[] arg2 = new Object[newCap];
87 if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
92 public final int size() { return size; }
94 // These two are optimized for speed, they don't call set() below
95 public final int add(byte op) {
97 if(s == capacity) grow();
102 public final void set(int pos, byte op) { this.op[pos] = op; }
104 public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
105 public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
106 public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
108 public final byte get(int pos) { return op[pos]; }
109 public final Object getArg(int pos) { return arg[pos]; }
111 public final void setArg(int pos, int arg) { set(pos,op[pos],N(arg)); }
112 public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); }
115 public final void set(int pos, byte op, boolean b) { set(pos,op,b?1:0); }
116 public final void set(int pos, byte op, int n) {
119 case -1: set(pos,ICONST_M1); return;
120 case 0: set(pos,ICONST_0); return;
121 case 1: set(pos,ICONST_1); return;
122 case 2: set(pos,ICONST_2); return;
123 case 3: set(pos,ICONST_3); return;
124 case 4: set(pos,ICONST_4); return;
125 case 5: set(pos,ICONST_5); return;
128 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
129 else if(n >= -32767 && n <= 32767) { op = SIPUSH; arg = N(n); }
130 else { arg = cp.add(N(n)); }
138 public void set(int pos, byte op, Object arg) {
140 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
141 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
143 int iarg = ((Integer)arg).intValue();
144 if(iarg >= 0 && iarg <= 3) {
147 case ILOAD: base = ILOAD_0; break;
148 case ISTORE: base = ISTORE_0; break;
149 case LLOAD: base = LLOAD_0; break;
150 case LSTORE: base = LSTORE_0; break;
151 case FLOAD: base = FLOAD_0; break;
152 case FSTORE: base = FSTORE_0; break;
153 case DLOAD: base = DLOAD_0; break;
154 case DSTORE: base = DSTORE_0; break;
155 case ALOAD: base = ALOAD_0; break;
156 case ASTORE: base = ASTORE_0; break;
158 op = (byte)((base&0xff) + iarg);
160 if(iarg >= maxLocals) maxLocals = iarg + 1;
165 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
166 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
167 if(arg instanceof Long) {
168 long l = ((Long)arg).longValue();
169 if(l == 0L) { set(pos,LCONST_0); return; }
170 if(l == 1L) { set(pos,LCONST_1); return; }
173 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
176 int opdata = OP_DATA[op&0xff];
177 if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
179 else if((opdata&OP_VALID_FLAG) == 0)
180 throw new IllegalArgumentException("unknown bytecode");
188 public static class SI {
189 public final Object[] targets;
190 public Object defaultTarget;
192 SI(int size) { targets = new Object[size]; }
193 public void setTarget(int pos, Object val) { targets[pos] = val; }
194 public void setTarget(int pos, int val) { targets[pos] = N(val); }
195 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
196 public void setDefaultTarget(Object o) { defaultTarget = o; }
197 public int size() { return targets.length; }
199 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
200 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
203 public static class TSI extends SI {
206 public int defaultTarget = -1;
207 public TSI(int lo, int hi) {
212 public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
213 public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
216 public static class LSI extends SI {
217 public final int[] vals;
218 public LSI(int size) {
220 this.vals = new int[size];
222 public final void setVal(int pos, int val) { vals[pos] = val; }
225 public static class Pair {
228 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
231 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
232 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
234 public void finish() {
237 } catch(IOException e) {
238 throw new Error("should never happen");
242 private Object resolveTarget(Object arg) {
244 if(arg instanceof PhantomTarget) {
245 target = ((PhantomTarget)arg).getTarget();
246 if(target == -1) throw new IllegalStateException("unresolved phantom target");
249 target = ((Integer)arg).intValue();
251 if(target < 0 || target >= size)
252 throw new IllegalStateException("invalid target address");
256 private void _finish() throws IOException {
257 if(size == -1) return;
259 ByteArrayOutputStream baos = new ByteArrayOutputStream();
260 DataOutput o = new DataOutputStream(baos);
262 int[] pc = new int[size];
266 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
267 for(i=0,p=0;i<size;i++) {
268 byte op = this.op[i];
269 int opdata = OP_DATA[op&0xff];
273 if((opdata & OP_BRANCH_FLAG)!= 0) {
275 arg[i] = resolveTarget(arg[i]);
276 } catch(RuntimeException e) {
277 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
293 Object[] targets = si.targets;
294 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
295 si.defaultTarget = resolveTarget(si.defaultTarget);
296 p += 1 + 3 + 4; // opcode itself, padding, default
297 if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
298 else p += 4 + targets.length * 4 * 2; // count, key,val * targets
299 if(op == LOOKUPSWITCH) {
300 int[] vals = ((LSI)si).vals;
301 for(j=1;j<vals.length;j++)
302 if(vals[j] <= vals[j-1])
303 throw new IllegalStateException("out of order/duplicate lookupswitch values");
308 j = ((CPGen.Ent)arg[i]).getIndex();
309 if(j >= 256) this.op[i] = op = LDC_W;
312 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
318 // Pass2 - Widen instructions if they can possibly be too short
319 for(i=0;i<size;i++) {
323 int arg = ((Integer)this.arg[i]).intValue();
324 int diff = maxpc[arg] - maxpc[i];
325 if(diff < -32768 || diff > 32767)
326 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
332 // Pass3 - Calculate actual pc
333 for(i=0,p=0;i<size;i++) {
334 byte op = this.op[i];
343 p++; // opcpde itself
344 p = (p + 3) & ~3; // padding
346 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
347 else p += 4 + si.size() * 4 * 2; // count, key,val * targets
351 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
352 if(l == 7) throw new Error("shouldn't be here");
360 o.writeShort(maxStack);
361 o.writeShort(maxLocals);
362 o.writeInt(codeSize);
364 // Pass 4 - Actually write the bytecodes
365 for(i=0;i<size;i++) {
366 byte op = this.op[i];
367 int opdata = OP_DATA[op&0xff];
368 if(op == NOP && !EMIT_NOPS) continue;
370 o.writeByte(op&0xff);
371 int argLength = opdata & OP_ARG_LENGTH_MASK;
373 if(argLength == 0) continue; // skip if no args
376 Object arg = this.arg[i];
380 Pair pair = (Pair) arg;
381 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg");
382 o.writeByte(pair.i1);
383 o.writeByte(pair.i2);
389 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
390 o.writeInt(pc[si.getDefaultTarget()] - mypc);
391 if(op == LOOKUPSWITCH) {
392 int[] vals = ((LSI)si).vals;
393 o.writeInt(si.size());
394 for(int j=0;j<si.size();j++) {
396 o.writeInt(pc[si.getTarget(j)] - mypc);
402 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
408 if((opdata & OP_BRANCH_FLAG) != 0) {
409 int v = pc[((Integer)arg).intValue()] - pc[i];
410 if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
412 } else if((opdata & OP_CPENT_FLAG) != 0) {
413 int v = ((CPGen.Ent)arg).getIndex();
414 if(argLength == 1) o.writeByte(v);
415 else if(argLength == 2) o.writeShort(v);
416 else throw new Error("should never happen");
417 } else if(argLength == -1) {
418 throw new Error("should never happen - variable length instruction not explicitly handled");
420 int iarg = ((Integer)arg).intValue();
422 if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
424 } else if(argLength == 2) {
425 if(iarg < -32767 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option");
428 throw new Error("should never happen");
435 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
437 o.writeShort(exnTable.size());
438 for(Enumeration e = exnTable.keys();e.hasMoreElements();)
439 ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
441 o.writeShort(codeAttrs.size());
446 byte[] codeAttribute = baos.toByteArray();
447 attrs.add("Code",codeAttribute);
450 o.writeShort(thrownExceptions.size());
451 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
452 o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
453 attrs.add("Exceptions",baos.toByteArray());
458 public void dump(DataOutput o) throws IOException {
460 o.writeShort(cp.getUtf8Index(name));
461 o.writeShort(cp.getUtf8Index(getDescriptor()));
462 o.writeShort(attrs.size());
466 public static byte negate(byte op) {
468 case IFEQ: return IFNE;
469 case IFNE: return IFEQ;
470 case IFLT: return IFGE;
471 case IFGE: return IFLT;
472 case IFGT: return IFLE;
473 case IFLE: return IFGT;
474 case IF_ICMPEQ: return IF_ICMPNE;
475 case IF_ICMPNE: return IF_ICMPEQ;
476 case IF_ICMPLT: return IF_ICMPGE;
477 case IF_ICMPGE: return IF_ICMPLT;
478 case IF_ICMPGT: return IF_ICMPLE;
479 case IF_ICMPLE: return IF_ICMPGT;
480 case IF_ACMPEQ: return IF_ACMPNE;
481 case IF_ACMPNE: return IF_ACMPEQ;
484 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
488 public static class PhantomTarget {
489 private int target = -1;
490 public void setTarget(int target) { this.target = target; }
491 public int getTarget() { return target; }
494 private static Integer N(int n) { return new Integer(n); }
495 private static Long N(long n) { return new Long(n); }
496 private static Float N(float f) { return new Float(f); }
497 private static Double N(double d) { return new Double(d); }
498 private static int max(int a, int b) { return a > b ? a : b; }
500 private static final int OP_BRANCH_FLAG = 1<<3;
501 private static final int OP_CPENT_FLAG = 1<<4;
502 private static final int OP_VALID_FLAG = 1<<5;
503 private static final int OP_ARG_LENGTH_MASK = 7;
504 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
505 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
506 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
507 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
509 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
510 private static final byte[] OP_DATA = {
511 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
512 0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
513 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
514 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
515 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
516 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
517 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
518 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
519 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
520 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
521 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
522 0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
523 0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
524 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
525 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
526 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01