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); }
117 // This MUST handle x{LOAD,STORE} and LDC with an int arg WITHOUT falling back to set(int,byte,Object)
118 public final void set(int pos, byte op, int n) {
123 case -1: op = ICONST_M1; break OUTER;
124 case 0: op = ICONST_0; break OUTER;
125 case 1: op = ICONST_1; break OUTER;
126 case 2: op = ICONST_2; break OUTER;
127 case 3: op = ICONST_3; break OUTER;
128 case 4: op = ICONST_4; break OUTER;
129 case 5: op = ICONST_5; break OUTER;
131 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
132 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
133 else { arg = cp.add(N(n)); }
135 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
136 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
137 if(n >= 0 && n <= 3) {
140 case ILOAD: base = ILOAD_0; break;
141 case ISTORE: base = ISTORE_0; break;
142 case LLOAD: base = LLOAD_0; break;
143 case LSTORE: base = LSTORE_0; break;
144 case FLOAD: base = FLOAD_0; break;
145 case FSTORE: base = FSTORE_0; break;
146 case DLOAD: base = DLOAD_0; break;
147 case DSTORE: base = DSTORE_0; break;
148 case ALOAD: base = ALOAD_0; break;
149 case ASTORE: base = ASTORE_0; break;
151 op = (byte)((base&0xff) + n);
153 if(n >= maxLocals) maxLocals = n + 1;
165 public final void set(int pos, byte op, Object arg) {
167 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
168 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
169 // set(int,byte,int) always handles these ops itself
170 set(pos,op,((Integer)arg).intValue());
173 // set(int,byte,int) always handles these opts itself
174 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
175 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
177 if(arg instanceof Long) {
178 long l = ((Long)arg).longValue();
179 if(l == 0L) { this.op[pos] = LCONST_0; return; }
180 if(l == 1L) { this.op[pos] = LCONST_1; return; }
183 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
185 case INVOKEINTERFACE:
186 if(arg instanceof MethodRef) arg = new MethodRef.I((MethodRef)arg);
189 int opdata = OP_DATA[op&0xff];
190 if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
192 else if((opdata&OP_VALID_FLAG) == 0)
193 throw new IllegalArgumentException("unknown bytecode");
198 public static class SI {
199 public final Object[] targets;
200 public Object defaultTarget;
202 SI(int size) { targets = new Object[size]; }
203 public void setTarget(int pos, Object val) { targets[pos] = val; }
204 public void setTarget(int pos, int val) { targets[pos] = N(val); }
205 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
206 public void setDefaultTarget(Object o) { defaultTarget = o; }
207 public int size() { return targets.length; }
209 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
210 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
213 public static class TSI extends SI {
216 public int defaultTarget = -1;
217 public TSI(int lo, int hi) {
222 public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
223 public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
226 public static class LSI extends SI {
227 public final int[] vals;
228 public LSI(int size) {
230 this.vals = new int[size];
232 public final void setVal(int pos, int val) { vals[pos] = val; }
235 public static class Pair {
238 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
241 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
242 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
244 public void finish() {
247 } catch(IOException e) {
248 throw new Error("should never happen");
252 private Object resolveTarget(Object arg) {
254 if(arg instanceof PhantomTarget) {
255 target = ((PhantomTarget)arg).getTarget();
256 if(target == -1) throw new IllegalStateException("unresolved phantom target");
259 target = ((Integer)arg).intValue();
261 if(target < 0 || target >= size)
262 throw new IllegalStateException("invalid target address");
266 private void _finish() throws IOException {
267 if(size == -1) return;
269 ByteArrayOutputStream baos = new ByteArrayOutputStream();
270 DataOutput o = new DataOutputStream(baos);
272 int[] pc = new int[size];
276 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
277 for(i=0,p=0;i<size;i++) {
278 byte op = this.op[i];
279 int opdata = OP_DATA[op&0xff];
283 if((opdata & OP_BRANCH_FLAG)!= 0) {
285 arg[i] = resolveTarget(arg[i]);
286 } catch(RuntimeException e) {
287 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
303 Object[] targets = si.targets;
304 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
305 si.defaultTarget = resolveTarget(si.defaultTarget);
306 p += 1 + 3 + 4; // opcode itself, padding, default
307 if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
308 else p += 4 + targets.length * 4 * 2; // count, key,val * targets
309 if(op == LOOKUPSWITCH) {
310 int[] vals = ((LSI)si).vals;
311 for(j=1;j<vals.length;j++)
312 if(vals[j] <= vals[j-1])
313 throw new IllegalStateException("out of order/duplicate lookupswitch values");
318 j = ((CPGen.Ent)arg[i]).getIndex();
319 if(j >= 256) this.op[i] = op = LDC_W;
322 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
328 // Pass2 - Widen instructions if they can possibly be too short
329 for(i=0;i<size;i++) {
333 int arg = ((Integer)this.arg[i]).intValue();
334 int diff = maxpc[arg] - maxpc[i];
335 if(diff < -32768 || diff > 32767)
336 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
342 // Pass3 - Calculate actual pc
343 for(i=0,p=0;i<size;i++) {
344 byte op = this.op[i];
353 p++; // opcode itself
354 p = (p + 3) & ~3; // padding
356 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
357 else p += 4 + si.size() * 4 * 2; // count, key,val * targets
361 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
362 if(l == 7) throw new Error("shouldn't be here");
370 o.writeShort(maxStack);
371 o.writeShort(maxLocals);
372 o.writeInt(codeSize);
374 // Pass 4 - Actually write the bytecodes
375 for(i=0;i<size;i++) {
376 byte op = this.op[i];
377 int opdata = OP_DATA[op&0xff];
378 if(op == NOP && !EMIT_NOPS) continue;
380 o.writeByte(op&0xff);
381 int argLength = opdata & OP_ARG_LENGTH_MASK;
383 if(argLength == 0) continue; // skip if no args
386 Object arg = this.arg[i];
390 Pair pair = (Pair) arg;
391 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg");
392 o.writeByte(pair.i1);
393 o.writeByte(pair.i2);
399 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
400 o.writeInt(pc[si.getDefaultTarget()] - mypc);
401 if(op == LOOKUPSWITCH) {
402 int[] vals = ((LSI)si).vals;
403 o.writeInt(si.size());
404 for(int j=0;j<si.size();j++) {
406 o.writeInt(pc[si.getTarget(j)] - mypc);
412 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
417 throw new Error("WIDE instruction not yet supported");
420 if((opdata & OP_BRANCH_FLAG) != 0) {
421 int v = pc[((Integer)arg).intValue()] - pc[i];
422 if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
424 } else if((opdata & OP_CPENT_FLAG) != 0) {
425 int v = ((CPGen.Ent)arg).getIndex();
426 if(argLength == 1) o.writeByte(v);
427 else if(argLength == 2) o.writeShort(v);
428 else throw new Error("should never happen");
429 } else if(argLength == 7) {
430 throw new Error("should never happen - variable length instruction not explicitly handled");
432 int iarg = ((Integer)arg).intValue();
434 if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
436 } else if(argLength == 2) {
437 if(iarg < -32768 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option");
440 throw new Error("should never happen");
447 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
449 o.writeShort(exnTable.size());
450 for(Enumeration e = exnTable.keys();e.hasMoreElements();)
451 ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
453 o.writeShort(codeAttrs.size());
458 byte[] codeAttribute = baos.toByteArray();
459 attrs.add("Code",codeAttribute);
462 o.writeShort(thrownExceptions.size());
463 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
464 o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
465 attrs.add("Exceptions",baos.toByteArray());
470 public void dump(DataOutput o) throws IOException {
472 o.writeShort(cp.getUtf8Index(name));
473 o.writeShort(cp.getUtf8Index(getDescriptor()));
474 o.writeShort(attrs.size());
478 public static byte negate(byte op) {
480 case IFEQ: return IFNE;
481 case IFNE: return IFEQ;
482 case IFLT: return IFGE;
483 case IFGE: return IFLT;
484 case IFGT: return IFLE;
485 case IFLE: return IFGT;
486 case IF_ICMPEQ: return IF_ICMPNE;
487 case IF_ICMPNE: return IF_ICMPEQ;
488 case IF_ICMPLT: return IF_ICMPGE;
489 case IF_ICMPGE: return IF_ICMPLT;
490 case IF_ICMPGT: return IF_ICMPLE;
491 case IF_ICMPLE: return IF_ICMPGT;
492 case IF_ACMPEQ: return IF_ACMPNE;
493 case IF_ACMPNE: return IF_ACMPEQ;
496 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
500 public static class PhantomTarget {
501 private int target = -1;
502 public void setTarget(int target) { this.target = target; }
503 public int getTarget() { return target; }
506 private static Integer N(int n) { return new Integer(n); }
507 private static Long N(long n) { return new Long(n); }
508 private static Float N(float f) { return new Float(f); }
509 private static Double N(double d) { return new Double(d); }
510 private static int max(int a, int b) { return a > b ? a : b; }
512 private static final int OP_BRANCH_FLAG = 1<<3;
513 private static final int OP_CPENT_FLAG = 1<<4;
514 private static final int OP_VALID_FLAG = 1<<5;
515 private static final int OP_ARG_LENGTH_MASK = 7;
516 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
517 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
518 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
519 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
521 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
522 private static final byte[] OP_DATA = {
523 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
524 0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
525 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
526 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
527 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
528 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
529 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
530 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
531 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
532 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
533 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
534 0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
535 0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
536 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
537 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
538 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01