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
302 j = ((CPGen.Ent)arg[i]).getIndex();
303 if(j >= 256) this.op[i] = op = LDC_W;
306 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
312 // Pass2 - Widen instructions if they can possibly be too short
313 for(i=0;i<size;i++) {
317 int arg = ((Integer)this.arg[i]).intValue();
318 int diff = maxpc[arg] - maxpc[i];
319 if(diff < -32768 || diff > 32767)
320 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
326 // Pass3 - Calculate actual pc
327 for(i=0,p=0;i<size;i++) {
328 byte op = this.op[i];
337 p++; // opcpde itself
338 p = (p + 3) & ~3; // padding
340 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
341 else p += 4 + si.size() * 4 * 2; // count, key,val * targets
345 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
346 if(l == 7) throw new Error("shouldn't be here");
354 o.writeShort(maxStack);
355 o.writeShort(maxLocals);
356 o.writeInt(codeSize);
358 // Pass 4 - Actually write the bytecodes
359 for(i=0;i<size;i++) {
360 byte op = this.op[i];
361 int opdata = OP_DATA[op&0xff];
362 if(op == NOP && !EMIT_NOPS) continue;
364 o.writeByte(op&0xff);
365 int argLength = opdata & OP_ARG_LENGTH_MASK;
367 if(argLength == 0) continue; // skip if no args
370 Object arg = this.arg[i];
374 Pair pair = (Pair) arg;
375 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg");
376 o.writeByte(pair.i1);
377 o.writeByte(pair.i2);
383 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
384 o.writeInt(pc[si.getDefaultTarget()] - mypc);
385 if(op == LOOKUPSWITCH) {
386 int[] vals = ((LSI)si).vals;
387 o.writeInt(si.size());
388 for(int j=0;j<si.size();j++) {
390 o.writeInt(pc[si.getTarget(j)] - mypc);
396 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
402 if((opdata & OP_BRANCH_FLAG) != 0) {
403 int v = pc[((Integer)arg).intValue()] - pc[i];
404 if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
406 } else if((opdata & OP_CPENT_FLAG) != 0) {
407 int v = ((CPGen.Ent)arg).getIndex();
408 if(argLength == 1) o.writeByte(v);
409 else if(argLength == 2) o.writeShort(v);
410 else throw new Error("should never happen");
411 } else if(argLength == -1) {
412 throw new Error("should never happen - variable length instruction not explicitly handled");
414 int iarg = ((Integer)arg).intValue();
416 if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
418 } else if(argLength == 2) {
419 if(iarg < -32767 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option");
422 throw new Error("should never happen");
429 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
431 o.writeShort(exnTable.size());
432 for(Enumeration e = exnTable.keys();e.hasMoreElements();)
433 ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
435 o.writeShort(codeAttrs.size());
440 byte[] codeAttribute = baos.toByteArray();
441 attrs.add("Code",codeAttribute);
444 o.writeShort(thrownExceptions.size());
445 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
446 o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
447 attrs.add("Exceptions",baos.toByteArray());
452 public void dump(DataOutput o) throws IOException {
454 o.writeShort(cp.getUtf8Index(name));
455 o.writeShort(cp.getUtf8Index(getDescriptor()));
456 o.writeShort(attrs.size());
460 public static byte negate(byte op) {
462 case IFEQ: return IFNE;
463 case IFNE: return IFEQ;
464 case IFLT: return IFGE;
465 case IFGE: return IFLT;
466 case IFGT: return IFLE;
467 case IFLE: return IFGT;
468 case IF_ICMPEQ: return IF_ICMPNE;
469 case IF_ICMPNE: return IF_ICMPEQ;
470 case IF_ICMPLT: return IF_ICMPGE;
471 case IF_ICMPGE: return IF_ICMPLT;
472 case IF_ICMPGT: return IF_ICMPLE;
473 case IF_ICMPLE: return IF_ICMPGT;
474 case IF_ACMPEQ: return IF_ACMPNE;
475 case IF_ACMPNE: return IF_ACMPEQ;
478 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
482 public static class PhantomTarget {
483 private int target = -1;
484 public void setTarget(int target) { this.target = target; }
485 public int getTarget() { return target; }
488 private static Integer N(int n) { return new Integer(n); }
489 private static Long N(long n) { return new Long(n); }
490 private static Float N(float f) { return new Float(f); }
491 private static Double N(double d) { return new Double(d); }
492 private static int max(int a, int b) { return a > b ? a : b; }
494 private static final int OP_BRANCH_FLAG = 1<<3;
495 private static final int OP_CPENT_FLAG = 1<<4;
496 private static final int OP_VALID_FLAG = 1<<5;
497 private static final int OP_ARG_LENGTH_MASK = 7;
498 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
499 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
500 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
501 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
503 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
504 private static final byte[] OP_DATA = {
505 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
506 0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
507 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
508 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
509 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
510 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
511 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
512 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
513 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
514 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
515 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
516 0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
517 0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
518 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
519 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
520 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01