1 package org.ibex.classgen;
6 /** A class representing a method in a generated classfile
7 @see ClassFile#addMethod */
8 public class MethodGen extends Type.Class.Method.Body implements CGConst {
9 private final static boolean EMIT_NOPS = false;
11 private static final int NO_CODE = -1;
13 public final Type.Class.Method method;
14 private final int flags;
15 private final ClassFile.AttrGen attrs;
16 private final ClassFile.AttrGen codeAttrs;
17 private final Vector exnTable = new Vector();
18 private final Hashtable thrownExceptions = new Hashtable();
20 private int maxStack = 16;
21 private int maxLocals;
27 private ConstantPool.Ent[] cparg;
30 // Constructors //////////////////////////////////////////////////////////////////////////////
32 MethodGen(Type.Class.Method method, int flags) {
34 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
38 attrs = new ClassFile.AttrGen();
39 codeAttrs = new ClassFile.AttrGen();
41 if (((flags & INTERFACE) != 0) || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
43 maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
46 MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
47 this(in.readShort(), cp.getUtf8KeyByIndex(in.readShort()), c, in, cp); }
49 private MethodGen(short flags, String name, Type.Class c, DataInput in, ConstantPool cp) throws IOException {
50 this(flags, name, c.method(name+cp.getUtf8KeyByIndex(in.readShort())), c, in, cp); }
51 private MethodGen(short flags, String name, Type.Class.Method m,
52 Type.Class c, DataInput in, ConstantPool cp) throws IOException {
55 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
57 this.attrs = new ClassFile.AttrGen(in,cp);
59 if ((flags & (NATIVE|ABSTRACT))==0) {
60 byte[] codeAttr = (byte[]) attrs.get("Code");
61 if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
62 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
63 maxStack = ci.readUnsignedShort();
64 maxLocals = ci.readUnsignedShort();
65 int codeLen = ci.readInt();
66 int[] bytecodeMap = parseCode(ci,codeLen,cp);
67 int numExns = ci.readUnsignedShort();
69 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
70 codeAttrs = new ClassFile.AttrGen(ci,cp);
71 // FEATURE: Support these
72 // NOTE: Until we can support them properly we HAVE to delete them,
73 // they'll be incorrect after we rewrite the constant pool, etc
74 codeAttrs.remove("LineNumberTable");
75 codeAttrs.remove("LocalVariableTable");
78 codeAttrs = new ClassFile.AttrGen();
81 if (attrs.contains("Exceptions")) {
82 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
83 int exnCount = ei.readUnsignedShort();
84 while(exnCount-- > 0) {
85 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
86 thrownExceptions.put(t,t);
91 // Parsing //////////////////////////////////////////////////////////////////////////////
93 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
94 int[] map = new int[codeLen];
96 for(pc=0;pc<map.length;pc++) map[pc] = -1;
97 for(pc=0;pc<codeLen;) {
98 byte op = in.readByte();
99 int opdata = OP_DATA[op&0xff];
100 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
101 if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
102 int argLength = opdata & OP_ARG_LENGTH_MASK;
105 pc += 1 + (argLength == 7 ? 0 : argLength);
106 if (argLength == 0) { add(op); continue; }
110 arg = new Pair(in.readUnsignedByte(),in.readByte());
115 for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
116 int def = in.readInt() + mypc;
118 if (op == LOOKUPSWITCH) {
119 Switch.Lookup lsi = new Switch.Lookup(in.readInt());
121 for(int i=0;i<lsi.size();i++) {
122 lsi.setVal(i,in.readInt());
123 lsi.setTarget(i,in.readInt() + mypc);
128 int lo = in.readInt();
129 int hi = in.readInt();
131 Switch.Table tsi = new Switch.Table(lo,hi);
132 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
135 si.setDefaultTarget(def);
139 byte wideop = in.readByte();
141 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
142 : new Wide(wideop,in.readUnsignedShort());
143 pc += wideop == IINC ? 5 : 3;
147 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
149 case INVOKEINTERFACE: {
150 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
151 if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
152 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
153 if (in.readByte() == 0 || in.readByte() != 0)
154 throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
158 if ((opdata&OP_CPENT_FLAG)!=0) {
159 ConstantPool.Ent ent =
160 cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
162 Object key = ent.key();
168 case CONSTANT_INTEGER:
171 case CONSTANT_DOUBLE:
172 case CONSTANT_STRING:
176 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
177 Integer.toString(op&0xff,16));
184 if (tag != CONSTANT_FIELDREF)
185 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
186 Integer.toString(op&0xff,16));
191 if (tag != CONSTANT_METHODREF)
192 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
193 Integer.toString(op&0xff,16));
199 if (tag != CONSTANT_CLASS)
200 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
201 Integer.toString(op&0xff,16));
204 throw new Error("should never happen");
208 // treat everything else (including branches for now) as plain old ints
210 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
211 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
212 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
213 else throw new Error("should never happen");
214 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
222 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
223 for(int i=0;i<size();i++) {
228 Switch si = (Switch) arg[i];
230 int pos = map[si.getDefaultTarget()];
232 throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
233 si.setDefaultTarget(pos);
235 for(int j=0;j<si.size();j++) {
236 pos = map[si.getTarget(j)];
237 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
243 if (OP_BRANCH(op[i])) {
244 int pos = map[((Integer)arg[i]).intValue()];
245 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
254 // Exception Table //////////////////////////////////////////////////////////////////////////////
260 final Type.Class type; // null type means all exceptions (for finally)
262 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
263 int startPC = in.readUnsignedShort();
264 int endPC = in.readUnsignedShort();
265 int handlerPC = in.readUnsignedShort();
266 int index = in.readUnsignedShort();
267 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
268 int max = bytecodeMap.length;
269 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
270 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
271 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
272 this.start = bytecodeMap[startPC];
273 this.end = bytecodeMap[endPC];
274 this.handler = bytecodeMap[handlerPC];
276 ExnTableEnt(int start, int end, int handler, Type.Class type) {
279 this.handler = handler;
282 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
283 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
284 o.writeShort(pc[start]);
285 o.writeShort(end==pc.length ? endPC : pc[end]);
286 o.writeShort(pc[handler]);
287 o.writeShort(type == null ? 0 : cp.getIndex(type));
291 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
292 @param start The instruction to start at (inclusive)
293 @param end The instruction to end at (exclusive)
294 @param handler The instruction of the excepton handler
295 @param type The type of exception that is to be handled (MUST inherit from Throwable)
297 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
298 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
301 /** Adds a exception type that can be thrown from this method
302 NOTE: This isn't enforced by the JVM. This is for reference
303 only. A method can throw exceptions not declared to be thrown
304 @param type The type of exception that can be thrown
306 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
308 private final void grow() { if (size == capacity) grow(size+1); }
309 private final void grow(int newCap) {
310 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
311 if (newCap <= capacity) return;
312 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
314 byte[] op2 = new byte[newCap];
315 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
318 Object[] arg2 = new Object[newCap];
319 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
325 // Accessors //////////////////////////////////////////////////////////////////////////////
327 public int getFlags() { return flags; }
328 public Hashtable getThrownExceptions() { return thrownExceptions; }
330 /** Returns the size (in instructions) of this method
331 @return The size of the method (in instructions)
333 public final int size() { return size; }
335 // These two are optimized for speed, they don't call set() below
336 /** Add a bytecode (with no argument) to the method */
337 public final int add(byte op) {
339 if (s == capacity) grow();
345 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
346 public final void set(int pos, byte op) { this.op[pos] = op; }
348 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
349 @return The position of the new bytecode
351 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
353 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
354 @return The position of the new bytecode
357 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
359 /** Adds a bytecode with an integer argument. This is equivalent
360 * to add(op, new Integer(arg)), but optimized to prevent the
361 * allocation when possible
362 @return The position of the new bytecode
363 @see #add(byte, Object)
365 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
367 /** Gets the bytecode at position <i>pos</i>
368 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
370 public final byte get(int pos) { return op[pos]; }
372 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
373 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
374 interal form when they are added. The value returned from this method for these instruction can be reused, but there
375 is no way to retrieve the original object
376 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
378 public final Object getArg(int pos) { return arg[pos]; }
380 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
381 * equivalent to set(pos, op, new Integer(arg)), but optimized to
382 * prevent the allocation when possible.
383 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
384 @see #setArg(int, Object) */
385 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
387 /** Sets the argument for <i>pos</i> to <i>arg</i>.
388 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
389 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
391 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
392 This is equivalent to set(pos, op, arg?1:0)
393 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
395 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
397 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
398 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
399 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
400 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
402 public final void set(int pos, byte op, int n) {
407 case -1: op = ICONST_M1; break OUTER;
408 case 0: op = ICONST_0; break OUTER;
409 case 1: op = ICONST_1; break OUTER;
410 case 2: op = ICONST_2; break OUTER;
411 case 3: op = ICONST_3; break OUTER;
412 case 4: op = ICONST_4; break OUTER;
413 case 5: op = ICONST_5; break OUTER;
415 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
416 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
419 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
420 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
421 if (n >= maxLocals) maxLocals = n + 1;
422 if (n >= 0 && n <= 3) {
425 case ILOAD: base = ILOAD_0; break;
426 case ISTORE: base = ISTORE_0; break;
427 case LLOAD: base = LLOAD_0; break;
428 case LSTORE: base = LSTORE_0; break;
429 case FLOAD: base = FLOAD_0; break;
430 case FSTORE: base = FSTORE_0; break;
431 case DLOAD: base = DLOAD_0; break;
432 case DSTORE: base = DSTORE_0; break;
433 case ALOAD: base = ALOAD_0; break;
434 case ASTORE: base = ASTORE_0; break;
436 op = (byte)((base&0xff) + n);
449 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
450 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
452 public final void set(int pos, byte op, Object arg) {
454 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
455 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
456 // set(int, byte, int) always handles these ops itself
457 set(pos, op, ((Integer)arg).intValue());
460 // set(int, byte, int) always handles these opts itself
461 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
462 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
464 if (arg instanceof Long) {
465 long l = ((Long)arg).longValue();
466 if (l == 0L || l == 1L) {
467 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
468 this.arg[pos] = null;
472 } else if (arg instanceof Double) {
477 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
482 /** Sets the maximum number of locals in the function to
483 <i>maxLocals</i>. NOTE: This defaults to 0 and is
484 automatically increased as necessary when *LOAD/*STORE
485 bytecodes are added. You do not need to call this function in
487 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
489 /** Sets the maxinum size of th stack for this function to
490 * <i>maxStack</i>. This defaults to 16< */
491 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
494 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
496 public static abstract class Switch {
497 public final Object[] targets;
498 public Object defaultTarget;
500 Switch(int size) { targets = new Object[size]; }
501 public void setTarget(int pos, Object val) { targets[pos] = val; }
502 public void setTarget(int pos, int val) { targets[pos] = N(val); }
503 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
504 public void setDefaultTarget(Object o) { defaultTarget = o; }
505 public int size() { return targets.length; }
507 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
508 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
510 abstract int length();
512 public static class Table extends Switch {
515 public Table(int lo, int hi) {
520 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
521 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
523 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
526 public static class Lookup extends Switch {
527 public final int[] vals;
528 public Lookup(int size) {
530 this.vals = new int[size];
532 public final void setVal(int pos, int val) { vals[pos] = val; }
534 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
538 /** This class represents the arguments to byecodes that take two integer arguments. */
539 public static class Pair {
542 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
545 public static class MultiANewArray {
546 public Type.Class type;
548 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
551 public static class Wide {
552 public final byte op;
553 public final int varNum;
555 Wide(byte op, int varNum) { this(op, varNum, 0); }
556 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
560 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
562 private Object resolveTarget(Object arg) {
564 if (arg instanceof PhantomTarget) {
565 target = ((PhantomTarget)arg).getTarget();
566 if (target == -1) throw new IllegalStateException("unresolved phantom target");
569 target = ((Integer)arg).intValue();
571 if (target < 0 || target >= size)
572 throw new IllegalStateException("invalid target address " + target + "/" + size);
576 /** Computes the final bytecode for this method.
577 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
578 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
580 void finish(ConstantPool cp) {
581 cp.addUtf8(method.name);
582 cp.addUtf8(method.getDescriptor());
584 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
585 cp.add(e.nextElement());
587 if (size == NO_CODE) return;
588 for(int i=0;i<exnTable.size();i++)
589 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
591 // We'll set these correctly later
592 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
593 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
595 codeAttrs.finish(cp);
597 cparg = new ConstantPool.Ent[size];
599 for(int i=0, p=0;i<size;i++) {
615 cparg[i] = cp.add(arg[i]);
617 case INVOKEINTERFACE:
618 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
621 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
627 private void generateCode(ConstantPool cp) {
630 } catch(IOException e) {
631 throw new Error("should never happen");
635 private void _generateCode(ConstantPool cp) throws IOException {
636 ByteArrayOutputStream baos = new ByteArrayOutputStream();
637 DataOutput o = new DataOutputStream(baos);
639 int[] pc = new int[size];
643 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
644 for(i=0, p=0;i<size;i++) {
645 byte op = this.op[i];
646 int opdata = OP_DATA[op&0xff];
650 if ((opdata & OP_BRANCH_FLAG)!= 0) {
652 arg[i] = resolveTarget(arg[i]);
653 } catch(RuntimeException e) {
654 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
660 // Speical caculations
663 int arg = ((Integer)this.arg[i]).intValue();
664 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
673 Switch si = (Switch) arg[i];
674 Object[] targets = si.targets;
675 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
676 si.defaultTarget = resolveTarget(si.defaultTarget);
677 p += 1 + 3 + si.length(); // opcode itself, padding, data
678 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
679 int[] vals = ((Switch.Lookup)si).vals;
680 for(j=1;j<vals.length;j++)
681 if (vals[j] <= vals[j-1])
682 throw new IllegalStateException("out of order/duplicate lookupswitch values");
687 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
690 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
691 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
693 int arg = ((Integer)this.arg[i]).intValue();
696 this.arg[i] = new Wide(op, arg);
703 Pair pair = (Pair) this.arg[i];
704 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
706 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
713 j = cp.getIndex(cparg[i]);
715 this.op[i] = op = LDC_W;
722 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
726 // Pass2 - Widen instructions if they can possibly be too short
727 for(i=0;i<size;i++) {
731 int arg = ((Integer)this.arg[i]).intValue();
732 int diff = maxpc[arg] - maxpc[i];
733 if (diff < -32768 || diff > 32767)
734 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
740 // Pass3 - Calculate actual pc
741 for(i=0, p=0;i<size;i++) {
742 byte op = this.op[i];
750 Switch si = (Switch) arg[i];
751 p++; // opcode itself
752 p = (p + 3) & ~3; // padding
754 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
755 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
759 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
762 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
763 if (l == 7) throw new Error("shouldn't be here");
770 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
772 o.writeShort(maxStack);
773 o.writeShort(maxLocals);
774 o.writeInt(codeSize);
776 // Pass 4 - Actually write the bytecodes
777 for(i=0;i<size;i++) {
778 byte op = this.op[i];
779 int opdata = OP_DATA[op&0xff];
780 if (op == NOP && !EMIT_NOPS) continue;
782 int argLength = opdata & OP_ARG_LENGTH_MASK;
784 if (argLength == 0) continue; // skip if no args
787 Object arg = this.arg[i];
791 Pair pair = (Pair) arg;
792 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
793 o.writeByte(pair.i1);
794 o.writeByte(pair.i2);
799 Switch si = (Switch) arg;
801 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
802 o.writeInt(pc[si.getDefaultTarget()] - mypc);
803 if (op == LOOKUPSWITCH) {
804 int[] vals = ((Switch.Lookup)si).vals;
805 o.writeInt(si.size());
806 for(int j=0;j<si.size();j++) {
808 o.writeInt(pc[si.getTarget(j)] - mypc);
811 Switch.Table tsi = (Switch.Table) si;
814 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
819 Wide wide = (Wide) arg;
820 o.writeByte(wide.op);
821 o.writeShort(wide.varNum);
822 if (wide.op == IINC) o.writeShort(wide.n);
825 case MULTIANEWARRAY: {
826 o.writeShort(cp.getIndex(cparg[i]));
827 int v = ((MultiANewArray) arg).dims;
828 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
832 case INVOKEINTERFACE:
833 o.writeShort(cp.getIndex(cparg[i]));
834 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
838 if ((opdata & OP_BRANCH_FLAG) != 0) {
839 int v = pc[((Integer)arg).intValue()] - pc[i];
840 if (argLength == 2) {
841 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
843 } else if (argLength == 4) {
846 throw new Error("should never happen");
848 } else if ((opdata & OP_CPENT_FLAG) != 0) {
849 int v = cp.getIndex(cparg[i]);
850 if (argLength == 1) o.writeByte(v);
851 else if (argLength == 2) o.writeShort(v);
852 else throw new Error("should never happen");
853 } else if (argLength == 7) {
854 throw new Error("should never happen - variable length instruction not explicitly handled");
856 int iarg = ((Integer)arg).intValue();
857 if (argLength == 1) {
858 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
859 throw new ClassFile.Exn("overflow of s/u1 option");
861 } else if (argLength == 2) {
862 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
863 throw new ClassFile.Exn("overflow of s/u2 option");
866 throw new Error("should never happen");
873 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
875 o.writeShort(exnTable.size());
876 for(i=0;i<exnTable.size();i++)
877 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
879 codeAttrs.dump(o,cp);
882 byte[] codeAttribute = baos.toByteArray();
883 attrs.put("Code", codeAttribute);
886 void generateExceptions(ConstantPool cp) throws IOException {
887 if (thrownExceptions.size() > 0) {
888 ByteArrayOutputStream baos = new ByteArrayOutputStream();
889 DataOutputStream o = new DataOutputStream(baos);
890 o.writeShort(thrownExceptions.size());
891 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
892 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
894 attrs.put("Exceptions", baos.toByteArray());
898 void dump(DataOutput o, ConstantPool cp) throws IOException {
899 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
900 generateExceptions(cp);
903 o.writeShort(cp.getUtf8Index(method.name));
904 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
909 /** Class that represents a target that isn't currently know. The
910 target MUST be set with setTarget() before the classfile is
911 written. This class is more or less a mutable integer */
912 public static class PhantomTarget {
913 private int target = -1;
914 public void setTarget(int target) { this.target = target; }
915 public int getTarget() { return target; }
918 private static Integer N(int n) { return new Integer(n); }
919 private static Long N(long n) { return new Long(n); }
920 private static Float N(float f) { return new Float(f); }
921 private static Double N(double d) { return new Double(d); }
922 private static int max(int a, int b) { return a > b ? a : b; }
924 private static final int OP_BRANCH_FLAG = 1<<3;
925 private static final int OP_CPENT_FLAG = 1<<4;
926 private static final int OP_UNSIGNED_FLAG = 1<<5;
927 private static final int OP_VALID_FLAG = 1<<6;
928 private static final int OP_ARG_LENGTH_MASK = 7;
929 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
930 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
931 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
932 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
933 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
935 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
936 private static final byte[] OP_DATA = {
937 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
938 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
939 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
940 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
941 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
942 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
943 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
944 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
945 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
946 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
947 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
948 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
949 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
950 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
951 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
952 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
955 // Debugging //////////////////////////////////////////////////////////////////////////////
957 public void debugBodyToString(StringBuffer sb) {
958 // This is intentionally a local variable so it can be removed by gcclass
959 final String[] OP_NAMES = new String[]{
960 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
961 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
962 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
963 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
964 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
965 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
966 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
967 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
968 "faload", "daload", "aaload", "baload", "caload", "saload",
969 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
970 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
971 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
972 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
973 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
974 "bastore", "castore", "sastore", "pop", "pop2", "dup",
975 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
976 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
977 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
978 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
979 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
980 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
981 "iand", "land", "ior", "lor", "ixor", "lxor",
982 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
983 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
984 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
985 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
986 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
987 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
988 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
989 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
990 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
991 "", "new", "newarray", "anewarray", "arraylength", "athrow",
992 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
993 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
994 "", "", "", "", "", "",
995 "", "", "", "", "", "",
996 "", "", "", "", "", "",
997 "", "", "", "", "", "",
998 "", "", "", "", "", "",
999 "", "", "", "", "", "",
1000 "", "", "", "", "", "",
1001 "", "", "", "", "", "",
1004 for(int i=0;i<size();i++) {
1006 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1007 sb.append(i).append(": ");
1008 sb.append(OP_NAMES[op[i]&0xff]);
1010 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1011 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1012 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1013 else if (arg[i] != null) s = arg[i].toString();
1014 if (s != null) sb.append(" ").append(s);
1019 // Unused //////////////////////////////////////////////////////////////////////////////
1021 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
1022 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1023 public static byte negate(byte op) {
1025 case IFEQ: return IFNE;
1026 case IFNE: return IFEQ;
1027 case IFLT: return IFGE;
1028 case IFGE: return IFLT;
1029 case IFGT: return IFLE;
1030 case IFLE: return IFGT;
1031 case IF_ICMPEQ: return IF_ICMPNE;
1032 case IF_ICMPNE: return IF_ICMPEQ;
1033 case IF_ICMPLT: return IF_ICMPGE;
1034 case IF_ICMPGE: return IF_ICMPLT;
1035 case IF_ICMPGT: return IF_ICMPLE;
1036 case IF_ICMPLE: return IF_ICMPGT;
1037 case IF_ACMPEQ: return IF_ACMPNE;
1038 case IF_ACMPNE: return IF_ACMPEQ;
1041 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));