1 package org.ibex.classgen;
6 public class MethodGen implements CGConst {
7 private final static boolean EMIT_NOPS = false;
9 private static final int NO_CODE = -1;
10 private static final int FINISHED = -2;
12 private final CPGen cp;
13 private final String name;
14 private final Type ret;
15 private final Type[] args;
16 private final int flags;
17 private final ClassGen.AttrGen attrs;
18 private final ClassGen.AttrGen codeAttrs;
19 private final Hashtable exnTable = new Hashtable();
20 private final Hashtable thrownExceptions = new Hashtable();
22 private int maxStack = 16;
23 private int maxLocals;
30 MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
31 if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
32 throw new IllegalArgumentException("invalid flags");
39 attrs = new ClassGen.AttrGen(cp);
40 codeAttrs = new ClassGen.AttrGen(cp);
43 cp.addUtf8(getDescriptor());
45 if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
47 maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4);
50 public String getDescriptor() { return MethodRef.getDescriptor(ret,args); }
52 private class ExnTableEnt {
56 public CPGen.Ent typeEnt;
57 public ExnTableEnt(int start, int end, int handler, CPGen.Ent typeEnt) {
60 this.handler = handler;
61 this.typeEnt = typeEnt;
63 public void dump(DataOutput o, int[] pc, int endPC) throws IOException {
64 o.writeShort(pc[start]);
65 o.writeShort(end==pc.length ? endPC : pc[end]);
66 o.writeShort(pc[handler]);
67 o.writeShort(typeEnt.getIndex());
71 public final void addExceptionHandler(int startPC, int endPC, int handlerPC, Type.Object type) {
72 exnTable.put(type, new ExnTableEnt(startPC,endPC,handlerPC,cp.add(type)));
75 public final void addThrow(Type.Object type) {
76 thrownExceptions.put(type,cp.add(type));
79 private final void grow() { if(size == capacity) grow(size+1); }
80 private final void grow(int newCap) {
81 if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
82 if(capacity == FINISHED) throw new IllegalStateException("method has been finished");
83 if(newCap <= capacity) return;
84 newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
86 byte[] op2 = new byte[newCap];
87 if(capacity != 0) System.arraycopy(op,0,op2,0,size);
90 Object[] arg2 = new Object[newCap];
91 if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
96 public final int size() { return size; }
98 // These two are optimized for speed, they don't call set() below
99 public final int add(byte op) {
101 if(s == capacity) grow();
106 public final void set(int pos, byte op) { this.op[pos] = op; }
108 public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
109 public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
110 public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; }
112 public final byte get(int pos) { return op[pos]; }
113 public final Object getArg(int pos) { return arg[pos]; }
115 public final void setArg(int pos, int arg) { set(pos,op[pos],N(arg)); }
116 public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); }
119 public final void set(int pos, byte op, boolean b) { set(pos,op,b?1:0); }
121 // This MUST handle x{LOAD,STORE} and LDC with an int arg WITHOUT falling back to set(int,byte,Object)
122 public final void set(int pos, byte op, int n) {
127 case -1: op = ICONST_M1; break OUTER;
128 case 0: op = ICONST_0; break OUTER;
129 case 1: op = ICONST_1; break OUTER;
130 case 2: op = ICONST_2; break OUTER;
131 case 3: op = ICONST_3; break OUTER;
132 case 4: op = ICONST_4; break OUTER;
133 case 5: op = ICONST_5; break OUTER;
135 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
136 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
137 else { arg = cp.add(N(n)); }
139 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
140 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
141 if(n >= 0 && n <= 3) {
144 case ILOAD: base = ILOAD_0; break;
145 case ISTORE: base = ISTORE_0; break;
146 case LLOAD: base = LLOAD_0; break;
147 case LSTORE: base = LSTORE_0; break;
148 case FLOAD: base = FLOAD_0; break;
149 case FSTORE: base = FSTORE_0; break;
150 case DLOAD: base = DLOAD_0; break;
151 case DSTORE: base = DSTORE_0; break;
152 case ALOAD: base = ALOAD_0; break;
153 case ASTORE: base = ASTORE_0; break;
155 op = (byte)((base&0xff) + n);
157 if(n >= maxLocals) maxLocals = n + 1;
169 public final void set(int pos, byte op, Object arg) {
171 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
172 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
173 // set(int,byte,int) always handles these ops itself
174 set(pos,op,((Integer)arg).intValue());
177 // set(int,byte,int) always handles these opts itself
178 if(arg instanceof Integer) { set(pos,op,((Integer)arg).intValue()); return; }
179 if(arg instanceof Boolean) { set(pos,op,((Boolean)arg).booleanValue()); return; }
181 if(arg instanceof Long) {
182 long l = ((Long)arg).longValue();
183 if(l == 0L) { this.op[pos] = LCONST_0; return; }
184 if(l == 1L) { this.op[pos] = LCONST_1; return; }
187 if(arg instanceof Long || arg instanceof Double) op = LDC2_W;
189 case INVOKEINTERFACE:
190 if(arg instanceof MethodRef) arg = new MethodRef.I((MethodRef)arg);
193 int opdata = OP_DATA[op&0xff];
194 if((opdata&OP_CPENT_FLAG) != 0 && !(arg instanceof CPGen.Ent))
196 else if((opdata&OP_VALID_FLAG) == 0)
197 throw new IllegalArgumentException("unknown bytecode");
202 public static class SI {
203 public final Object[] targets;
204 public Object defaultTarget;
206 SI(int size) { targets = new Object[size]; }
207 public void setTarget(int pos, Object val) { targets[pos] = val; }
208 public void setTarget(int pos, int val) { targets[pos] = N(val); }
209 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
210 public void setDefaultTarget(Object o) { defaultTarget = o; }
211 public int size() { return targets.length; }
213 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
214 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
217 public static class TSI extends SI {
220 public int defaultTarget = -1;
221 public TSI(int lo, int hi) {
226 public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
227 public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
230 public static class LSI extends SI {
231 public final int[] vals;
232 public LSI(int size) {
234 this.vals = new int[size];
236 public final void setVal(int pos, int val) { vals[pos] = val; }
239 public static class Pair {
242 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
245 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
246 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
248 public void finish() {
251 } catch(IOException e) {
252 throw new Error("should never happen");
256 private Object resolveTarget(Object arg) {
258 if(arg instanceof PhantomTarget) {
259 target = ((PhantomTarget)arg).getTarget();
260 if(target == -1) throw new IllegalStateException("unresolved phantom target");
263 target = ((Integer)arg).intValue();
265 if(target < 0 || target >= size)
266 throw new IllegalStateException("invalid target address");
270 private void _finish() throws IOException {
271 if(size == FINISHED) return;
273 ByteArrayOutputStream baos = new ByteArrayOutputStream();
274 DataOutput o = new DataOutputStream(baos);
276 int[] pc = new int[size];
280 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
281 for(i=0,p=0;i<size;i++) {
282 byte op = this.op[i];
283 int opdata = OP_DATA[op&0xff];
287 if((opdata & OP_BRANCH_FLAG)!= 0) {
289 arg[i] = resolveTarget(arg[i]);
290 } catch(RuntimeException e) {
291 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
307 Object[] targets = si.targets;
308 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
309 si.defaultTarget = resolveTarget(si.defaultTarget);
310 p += 1 + 3 + 4; // opcode itself, padding, default
311 if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
312 else p += 4 + targets.length * 4 * 2; // count, key,val * targets
313 if(op == LOOKUPSWITCH) {
314 int[] vals = ((LSI)si).vals;
315 for(j=1;j<vals.length;j++)
316 if(vals[j] <= vals[j-1])
317 throw new IllegalStateException("out of order/duplicate lookupswitch values");
322 j = ((CPGen.Ent)arg[i]).getIndex();
323 if(j >= 256) this.op[i] = op = LDC_W;
326 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
332 // Pass2 - Widen instructions if they can possibly be too short
333 for(i=0;i<size;i++) {
337 int arg = ((Integer)this.arg[i]).intValue();
338 int diff = maxpc[arg] - maxpc[i];
339 if(diff < -32768 || diff > 32767)
340 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
346 // Pass3 - Calculate actual pc
347 for(i=0,p=0;i<size;i++) {
348 byte op = this.op[i];
357 p++; // opcode itself
358 p = (p + 3) & ~3; // padding
360 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
361 else p += 4 + si.size() * 4 * 2; // count, key,val * targets
365 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
366 if(l == 7) throw new Error("shouldn't be here");
374 if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
376 o.writeShort(maxStack);
377 o.writeShort(maxLocals);
378 o.writeInt(codeSize);
380 // Pass 4 - Actually write the bytecodes
381 for(i=0;i<size;i++) {
382 byte op = this.op[i];
383 int opdata = OP_DATA[op&0xff];
384 if(op == NOP && !EMIT_NOPS) continue;
386 o.writeByte(op&0xff);
387 int argLength = opdata & OP_ARG_LENGTH_MASK;
389 if(argLength == 0) continue; // skip if no args
392 Object arg = this.arg[i];
396 Pair pair = (Pair) arg;
397 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassGen.Exn("overflow of iinc arg");
398 o.writeByte(pair.i1);
399 o.writeByte(pair.i2);
405 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
406 o.writeInt(pc[si.getDefaultTarget()] - mypc);
407 if(op == LOOKUPSWITCH) {
408 int[] vals = ((LSI)si).vals;
409 o.writeInt(si.size());
410 for(int j=0;j<si.size();j++) {
412 o.writeInt(pc[si.getTarget(j)] - mypc);
418 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
423 throw new Error("WIDE instruction not yet supported");
426 if((opdata & OP_BRANCH_FLAG) != 0) {
427 int v = pc[((Integer)arg).intValue()] - pc[i];
429 if(v < -32768 || v > 32767) throw new ClassGen.Exn("overflow of s2 offset");
431 } else if(argLength == 4) {
434 throw new Error("should never happen");
436 } else if((opdata & OP_CPENT_FLAG) != 0) {
437 int v = ((CPGen.Ent)arg).getIndex();
438 if(argLength == 1) o.writeByte(v);
439 else if(argLength == 2) o.writeShort(v);
440 else throw new Error("should never happen");
441 } else if(argLength == 7) {
442 throw new Error("should never happen - variable length instruction not explicitly handled");
444 int iarg = ((Integer)arg).intValue();
446 if(iarg < -128 || iarg >= 256) throw new ClassGen.Exn("overflow of s/u1 option");
448 } else if(argLength == 2) {
449 if(iarg < -32768 || iarg >= 65536) throw new ClassGen.Exn("overflow of s/u2 option");
452 throw new Error("should never happen");
459 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
461 o.writeShort(exnTable.size());
462 for(Enumeration e = exnTable.keys();e.hasMoreElements();)
463 ((ExnTableEnt)exnTable.get(e.nextElement())).dump(o,pc,codeSize);
465 o.writeShort(codeAttrs.size());
470 byte[] codeAttribute = baos.toByteArray();
471 attrs.add("Code",codeAttribute);
474 o.writeShort(thrownExceptions.size());
475 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
476 o.writeShort(((CPGen.Ent)thrownExceptions.get(e.nextElement())).getIndex());
477 attrs.add("Exceptions",baos.toByteArray());
479 size = capacity = FINISHED;
482 public void dump(DataOutput o) throws IOException {
484 o.writeShort(cp.getUtf8Index(name));
485 o.writeShort(cp.getUtf8Index(getDescriptor()));
486 o.writeShort(attrs.size());
490 public static byte negate(byte op) {
492 case IFEQ: return IFNE;
493 case IFNE: return IFEQ;
494 case IFLT: return IFGE;
495 case IFGE: return IFLT;
496 case IFGT: return IFLE;
497 case IFLE: return IFGT;
498 case IF_ICMPEQ: return IF_ICMPNE;
499 case IF_ICMPNE: return IF_ICMPEQ;
500 case IF_ICMPLT: return IF_ICMPGE;
501 case IF_ICMPGE: return IF_ICMPLT;
502 case IF_ICMPGT: return IF_ICMPLE;
503 case IF_ICMPLE: return IF_ICMPGT;
504 case IF_ACMPEQ: return IF_ACMPNE;
505 case IF_ACMPNE: return IF_ACMPEQ;
508 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
512 public static class PhantomTarget {
513 private int target = -1;
514 public void setTarget(int target) { this.target = target; }
515 public int getTarget() { return target; }
518 private static Integer N(int n) { return new Integer(n); }
519 private static Long N(long n) { return new Long(n); }
520 private static Float N(float f) { return new Float(f); }
521 private static Double N(double d) { return new Double(d); }
522 private static int max(int a, int b) { return a > b ? a : b; }
524 private static final int OP_BRANCH_FLAG = 1<<3;
525 private static final int OP_CPENT_FLAG = 1<<4;
526 private static final int OP_VALID_FLAG = 1<<5;
527 private static final int OP_ARG_LENGTH_MASK = 7;
528 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
529 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
530 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
531 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
533 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
534 private static final byte[] OP_DATA = {
535 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
536 0x21, 0x22, 0x31, 0x32, 0x32, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
537 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
538 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20,
539 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
540 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
541 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
542 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
543 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
544 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
545 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x21, 0x27, 0x27, 0x20, 0x20, 0x20, 0x20,
546 0x20, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x01, 0x32, 0x21, 0x32, 0x20, 0x20,
547 0x32, 0x32, 0x20, 0x20, 0x27, 0x23, 0x2a, 0x2a, 0x2c, 0x2c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
548 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
549 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
550 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01