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 {
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 ClassFile.AttrGen codeAttrs;
15 private final Vector exnTable = new Vector();
16 private final Hashtable thrownExceptions = new Hashtable();
25 private ConstantPool.Ent[] cparg;
27 public void insertBlank(int idx) {
28 for(int i=0;i<size();i++) {
32 Switch si = (Switch) arg[i];
33 int pos = si.getDefaultTarget();
34 if (pos >= idx) si.setDefaultTarget(pos+1);
35 for(int j=0;j<si.size();j++) {
36 pos = si.getTarget(j);
37 if (pos >= idx) si.setTarget(j, pos+1);
42 if (OP_BRANCH(op[i])) {
43 int pos = ((Integer)arg[i]).intValue();
44 if (pos >= idx) arg[i] = N(pos+1);
50 for(int i=size; i>idx; i--) {
58 // Constructors //////////////////////////////////////////////////////////////////////////////
60 MethodGen(Type.Class.Method method, int flags) {
61 method.super(flags, new ClassFile.AttrGen());
63 codeAttrs = new ClassFile.AttrGen();
64 if (!isConcrete()) size = capacity = -1;
65 maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
68 MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
69 this(in.readShort(), cp.getUtf8KeyByIndex(in.readShort()), c, in, cp); }
71 private MethodGen(short flags, String name, Type.Class c, DataInput in, ConstantPool cp) throws IOException {
72 this(flags, name, c.method(name,cp.getUtf8KeyByIndex(in.readShort())), c, in, cp); }
73 private MethodGen(short flags, String name, Type.Class.Method m,
74 Type.Class c, DataInput in, ConstantPool cp) throws IOException {
75 m.super(flags, new ClassFile.AttrGen(in,cp));
79 byte[] codeAttr = (byte[]) attrs.get("Code");
80 if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
81 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
82 maxStack = ci.readUnsignedShort();
83 maxLocals = ci.readUnsignedShort();
84 int codeLen = ci.readInt();
85 int[] bytecodeMap = parseCode(ci,codeLen,cp);
86 int numExns = ci.readUnsignedShort();
88 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
89 codeAttrs = new ClassFile.AttrGen(ci,cp);
90 // FEATURE: Support these
91 // NOTE: Until we can support them properly we HAVE to delete them,
92 // they'll be incorrect after we rewrite the constant pool, etc
93 codeAttrs.remove("LineNumberTable");
94 codeAttrs.remove("LocalVariableTable");
97 codeAttrs = new ClassFile.AttrGen();
100 if (attrs.contains("Exceptions")) {
101 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
102 int exnCount = ei.readUnsignedShort();
103 while(exnCount-- > 0) {
104 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
105 thrownExceptions.put(t,t);
110 // Parsing //////////////////////////////////////////////////////////////////////////////
112 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
113 int[] map = new int[codeLen];
115 for(pc=0;pc<map.length;pc++) map[pc] = -1;
116 for(pc=0;pc<codeLen;) {
117 byte op = in.readByte();
118 int opdata = OP_DATA[op&0xff];
119 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
120 if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
121 int argLength = opdata & OP_ARG_LENGTH_MASK;
124 pc += 1 + (argLength == 7 ? 0 : argLength);
125 if (argLength == 0) { add(op); continue; }
129 arg = new Pair(in.readUnsignedByte(),in.readByte());
134 for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
135 int def = in.readInt() + mypc;
137 if (op == LOOKUPSWITCH) {
138 Switch.Lookup lsi = new Switch.Lookup(in.readInt());
140 for(int i=0;i<lsi.size();i++) {
141 lsi.setVal(i,in.readInt());
142 lsi.setTarget(i,in.readInt() + mypc);
147 int lo = in.readInt();
148 int hi = in.readInt();
150 Switch.Table tsi = new Switch.Table(lo,hi);
151 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
154 si.setDefaultTarget(def);
158 byte wideop = in.readByte();
160 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
161 : new Wide(wideop,in.readUnsignedShort());
162 pc += wideop == IINC ? 5 : 3;
166 arg = new MultiANewArray((Type.Array)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
168 case INVOKEINTERFACE: {
169 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
170 if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
171 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
172 if (in.readByte() == 0 || in.readByte() != 0)
173 throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
177 if ((opdata&OP_CPENT_FLAG)!=0) {
178 ConstantPool.Ent ent =
179 cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
181 Object key = ent.key();
187 case CONSTANT_INTEGER:
190 case CONSTANT_DOUBLE:
191 case CONSTANT_STRING:
195 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
196 Integer.toString(op&0xff,16));
203 if (tag != CONSTANT_FIELDREF)
204 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
205 Integer.toString(op&0xff,16));
210 if (tag != CONSTANT_METHODREF)
211 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
212 Integer.toString(op&0xff,16));
218 if (tag != CONSTANT_CLASS)
219 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
220 Integer.toString(op&0xff,16));
223 throw new Error("should never happen");
227 // treat everything else (including branches for now) as plain old ints
229 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
230 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
231 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
232 else throw new Error("should never happen");
233 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
241 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
242 for(int i=0;i<size();i++) {
247 Switch si = (Switch) arg[i];
249 int pos = map[si.getDefaultTarget()];
251 throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
252 si.setDefaultTarget(pos);
254 for(int j=0;j<si.size();j++) {
255 pos = map[si.getTarget(j)];
256 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
262 if (OP_BRANCH(op[i])) {
263 int pos = map[((Integer)arg[i]).intValue()];
264 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
273 // Exception Table //////////////////////////////////////////////////////////////////////////////
279 final Type.Class type; // null type means all exceptions (for finally)
281 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
282 int startPC = in.readUnsignedShort();
283 int endPC = in.readUnsignedShort();
284 int handlerPC = in.readUnsignedShort();
285 int index = in.readUnsignedShort();
286 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
287 int max = bytecodeMap.length;
288 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
289 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
290 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
291 this.start = bytecodeMap[startPC];
292 this.end = bytecodeMap[endPC];
293 this.handler = bytecodeMap[handlerPC];
295 ExnTableEnt(int start, int end, int handler, Type.Class type) {
298 this.handler = handler;
301 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
302 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
303 o.writeShort(pc[start]);
304 o.writeShort(end==pc.length ? endPC : pc[end]);
305 o.writeShort(pc[handler]);
306 o.writeShort(type == null ? 0 : cp.getIndex(type));
310 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
311 @param start The instruction to start at (inclusive)
312 @param end The instruction to end at (exclusive)
313 @param handler The instruction of the excepton handler
314 @param type The type of exception that is to be handled (MUST inherit from Throwable)
316 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
317 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
320 /** Adds a exception type that can be thrown from this method
321 NOTE: This isn't enforced by the JVM. This is for reference
322 only. A method can throw exceptions not declared to be thrown
323 @param type The type of exception that can be thrown
325 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
327 private final void grow() { if (size == capacity) grow(size+1); }
328 private final void grow(int newCap) {
329 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
330 if (newCap <= capacity) return;
331 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
333 byte[] op2 = new byte[newCap];
334 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
337 Object[] arg2 = new Object[newCap];
338 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
344 // Accessors //////////////////////////////////////////////////////////////////////////////
346 public int getFlags() { return flags; }
347 public Hashtable getThrownExceptions() { return thrownExceptions; }
349 /** Returns the size (in instructions) of this method
350 @return The size of the method (in instructions)
352 public final int size() { return size; }
354 // These two are optimized for speed, they don't call set() below
355 /** Add a bytecode (with no argument) to the method */
356 public final int add(byte op) {
358 if (s == capacity) grow();
364 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
365 public final void set(int pos, byte op) { this.op[pos] = op; }
367 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
368 @return The position of the new bytecode
370 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
372 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
373 @return The position of the new bytecode
376 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
378 /** Adds a bytecode with an integer argument. This is equivalent
379 * to add(op, new Integer(arg)), but optimized to prevent the
380 * allocation when possible
381 @return The position of the new bytecode
382 @see #add(byte, Object)
384 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
386 /** Gets the bytecode at position <i>pos</i>
387 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
389 public final byte get(int pos) { return op[pos]; }
391 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
392 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
393 interal form when they are added. The value returned from this method for these instruction can be reused, but there
394 is no way to retrieve the original object
395 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
397 public final Object getArg(int pos) { return arg[pos]; }
399 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
400 * equivalent to set(pos, op, new Integer(arg)), but optimized to
401 * prevent the allocation when possible.
402 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
403 @see #setArg(int, Object) */
404 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
406 /** Sets the argument for <i>pos</i> to <i>arg</i>.
407 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
408 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
410 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
411 This is equivalent to set(pos, op, arg?1:0)
412 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
414 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
416 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
417 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
418 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
419 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
421 public final void set(int pos, byte op, int n) {
426 case -1: op = ICONST_M1; break OUTER;
427 case 0: op = ICONST_0; break OUTER;
428 case 1: op = ICONST_1; break OUTER;
429 case 2: op = ICONST_2; break OUTER;
430 case 3: op = ICONST_3; break OUTER;
431 case 4: op = ICONST_4; break OUTER;
432 case 5: op = ICONST_5; break OUTER;
434 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
435 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
438 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
439 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
440 if (n >= maxLocals) maxLocals = n + 1;
441 if (n >= 0 && n <= 3) {
444 case ILOAD: base = ILOAD_0; break;
445 case ISTORE: base = ISTORE_0; break;
446 case LLOAD: base = LLOAD_0; break;
447 case LSTORE: base = LSTORE_0; break;
448 case FLOAD: base = FLOAD_0; break;
449 case FSTORE: base = FSTORE_0; break;
450 case DLOAD: base = DLOAD_0; break;
451 case DSTORE: base = DSTORE_0; break;
452 case ALOAD: base = ALOAD_0; break;
453 case ASTORE: base = ASTORE_0; break;
455 op = (byte)((base&0xff) + n);
468 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
469 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
471 public final void set(int pos, byte op, Object arg) {
473 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
474 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
475 // set(int, byte, int) always handles these ops itself
476 set(pos, op, ((Integer)arg).intValue());
479 // set(int, byte, int) always handles these opts itself
480 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
481 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
483 if (arg instanceof Long) {
484 long l = ((Long)arg).longValue();
485 if (l == 0L || l == 1L) {
486 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
487 this.arg[pos] = null;
491 } else if (arg instanceof Double) {
496 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
501 /** Sets the maximum number of locals in the function to
502 <i>maxLocals</i>. NOTE: This defaults to 0 and is
503 automatically increased as necessary when *LOAD/*STORE
504 bytecodes are added. You do not need to call this function in
506 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
508 /** Sets the maxinum size of th stack for this function to
509 * <i>maxStack</i>. This defaults to 16< */
510 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
513 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
515 public static abstract class Switch {
516 public final Object[] targets;
517 public Object defaultTarget;
519 Switch(int size) { targets = new Object[size]; }
520 public void setTarget(int pos, Object val) { targets[pos] = val; }
521 public void setTarget(int pos, int val) { targets[pos] = N(val); }
522 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
523 public void setDefaultTarget(Object o) { defaultTarget = o; }
524 public int size() { return targets.length; }
526 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
527 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
529 abstract int length();
531 public static class Table extends Switch {
534 public Table(int lo, int hi) {
539 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
540 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
542 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
545 public static class Lookup extends Switch {
546 public final int[] vals;
547 public Lookup(int size) {
549 this.vals = new int[size];
551 public final void setVal(int pos, int val) { vals[pos] = val; }
553 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
557 /** This class represents the arguments to byecodes that take two integer arguments. */
558 public static class Pair {
561 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
564 public static class MultiANewArray {
565 public Type.Array type;
567 public MultiANewArray(Type.Array type, int dims) { this.type = type; this.dims = dims; }
570 public static class Wide {
571 public final byte op;
572 public final int varNum;
574 Wide(byte op, int varNum) { this(op, varNum, 0); }
575 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
579 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
581 private Object resolveTarget(Object arg) {
583 if (arg instanceof PhantomTarget) {
584 target = ((PhantomTarget)arg).getTarget();
585 if (target == -1) throw new IllegalStateException("unresolved phantom target");
588 target = ((Integer)arg).intValue();
590 if (target < 0 || target >= size)
591 throw new IllegalStateException("invalid target address " + target + "/" + size);
595 /** Computes the final bytecode for this method.
596 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
597 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
599 void finish(ConstantPool cp) {
600 cp.addUtf8(method.name);
601 cp.addUtf8(method.getTypeDescriptor());
603 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
604 cp.add(e.nextElement());
606 if (size == NO_CODE) return;
607 for(int i=0;i<exnTable.size();i++)
608 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
610 // We'll set these correctly later
611 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
612 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
614 codeAttrs.finish(cp);
616 cparg = new ConstantPool.Ent[size];
618 for(int i=0, p=0;i<size;i++) {
634 cparg[i] = cp.add(arg[i]);
636 case INVOKEINTERFACE:
637 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
640 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
646 private void generateCode(ConstantPool cp) {
649 } catch(IOException e) {
650 throw new Error("should never happen");
654 private void _generateCode(ConstantPool cp) throws IOException {
655 ByteArrayOutputStream baos = new ByteArrayOutputStream();
656 DataOutput o = new DataOutputStream(baos);
658 int[] pc = new int[size];
662 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
663 for(i=0, p=0;i<size;i++) {
664 byte op = this.op[i];
665 int opdata = OP_DATA[op&0xff];
669 if ((opdata & OP_BRANCH_FLAG)!= 0) {
671 arg[i] = resolveTarget(arg[i]);
672 } catch(RuntimeException e) {
673 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
679 // Speical caculations
682 int arg = ((Integer)this.arg[i]).intValue();
683 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
692 Switch si = (Switch) arg[i];
693 Object[] targets = si.targets;
694 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
695 si.defaultTarget = resolveTarget(si.defaultTarget);
696 p += 1 + 3 + si.length(); // opcode itself, padding, data
697 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
698 int[] vals = ((Switch.Lookup)si).vals;
699 for(j=1;j<vals.length;j++)
700 if (vals[j] <= vals[j-1])
701 throw new IllegalStateException("out of order/duplicate lookupswitch values");
706 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
709 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
710 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
712 int arg = ((Integer)this.arg[i]).intValue();
715 this.arg[i] = new Wide(op, arg);
722 Pair pair = (Pair) this.arg[i];
723 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
725 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
732 j = cp.getIndex(cparg[i]);
734 this.op[i] = op = LDC_W;
741 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
745 // Pass2 - Widen instructions if they can possibly be too short
746 for(i=0;i<size;i++) {
750 int arg = ((Integer)this.arg[i]).intValue();
751 int diff = maxpc[arg] - maxpc[i];
752 if (diff < -32768 || diff > 32767)
753 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
759 // Pass3 - Calculate actual pc
760 for(i=0, p=0;i<size;i++) {
761 byte op = this.op[i];
769 Switch si = (Switch) arg[i];
770 p++; // opcode itself
771 p = (p + 3) & ~3; // padding
773 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
774 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
778 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
781 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
782 if (l == 7) throw new Error("shouldn't be here");
789 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
791 o.writeShort(maxStack);
792 o.writeShort(maxLocals);
793 o.writeInt(codeSize);
795 // Pass 4 - Actually write the bytecodes
796 for(i=0;i<size;i++) {
797 byte op = this.op[i];
798 int opdata = OP_DATA[op&0xff];
799 if (op == NOP && !EMIT_NOPS) continue;
801 int argLength = opdata & OP_ARG_LENGTH_MASK;
803 if (argLength == 0) continue; // skip if no args
806 Object arg = this.arg[i];
810 Pair pair = (Pair) arg;
811 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
812 o.writeByte(pair.i1);
813 o.writeByte(pair.i2);
818 Switch si = (Switch) arg;
820 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
821 o.writeInt(pc[si.getDefaultTarget()] - mypc);
822 if (op == LOOKUPSWITCH) {
823 int[] vals = ((Switch.Lookup)si).vals;
824 o.writeInt(si.size());
825 for(int j=0;j<si.size();j++) {
827 o.writeInt(pc[si.getTarget(j)] - mypc);
830 Switch.Table tsi = (Switch.Table) si;
833 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
838 Wide wide = (Wide) arg;
839 o.writeByte(wide.op);
840 o.writeShort(wide.varNum);
841 if (wide.op == IINC) o.writeShort(wide.n);
844 case MULTIANEWARRAY: {
845 o.writeShort(cp.getIndex(cparg[i]));
846 int v = ((MultiANewArray) arg).dims;
847 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
851 case INVOKEINTERFACE:
852 o.writeShort(cp.getIndex(cparg[i]));
853 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
857 if ((opdata & OP_BRANCH_FLAG) != 0) {
858 int v = pc[((Integer)arg).intValue()] - pc[i];
859 if (argLength == 2) {
860 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
862 } else if (argLength == 4) {
865 throw new Error("should never happen");
867 } else if ((opdata & OP_CPENT_FLAG) != 0) {
868 int v = cp.getIndex(cparg[i]);
869 if (argLength == 1) o.writeByte(v);
870 else if (argLength == 2) o.writeShort(v);
871 else throw new Error("should never happen");
872 } else if (argLength == 7) {
873 throw new Error("should never happen - variable length instruction not explicitly handled");
875 int iarg = (arg instanceof Type.Primitive)
876 ? ((Type.Primitive)arg).toArraySpec()
877 : ((Integer)arg).intValue();
878 if (argLength == 1) {
879 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
880 throw new ClassFile.Exn("overflow of s/u1 option");
882 } else if (argLength == 2) {
883 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
884 throw new ClassFile.Exn("overflow of s/u2 option");
887 throw new Error("should never happen");
894 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
896 o.writeShort(exnTable.size());
897 for(i=0;i<exnTable.size();i++)
898 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
900 codeAttrs.dump(o,cp);
903 byte[] codeAttribute = baos.toByteArray();
904 attrs.put("Code", codeAttribute);
907 void generateExceptions(ConstantPool cp) throws IOException {
908 if (thrownExceptions.size() > 0) {
909 ByteArrayOutputStream baos = new ByteArrayOutputStream();
910 DataOutputStream o = new DataOutputStream(baos);
911 o.writeShort(thrownExceptions.size());
912 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
913 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
915 attrs.put("Exceptions", baos.toByteArray());
919 void dump(DataOutput o, ConstantPool cp) throws IOException {
920 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
921 generateExceptions(cp);
924 o.writeShort(cp.getUtf8Index(method.name));
925 o.writeShort(cp.getUtf8Index(method.getTypeDescriptor()));
930 /** Class that represents a target that isn't currently know. The
931 target MUST be set with setTarget() before the classfile is
932 written. This class is more or less a mutable integer */
933 public static class PhantomTarget {
934 private int target = -1;
935 public void setTarget(int target) { this.target = target; }
936 public int getTarget() { return target; }
939 private static Integer N(int n) { return new Integer(n); }
940 private static Long N(long n) { return new Long(n); }
941 private static Float N(float f) { return new Float(f); }
942 private static Double N(double d) { return new Double(d); }
943 private static int max(int a, int b) { return a > b ? a : b; }
945 private static final int OP_BRANCH_FLAG = 1<<3;
946 private static final int OP_CPENT_FLAG = 1<<4;
947 private static final int OP_UNSIGNED_FLAG = 1<<5;
948 private static final int OP_VALID_FLAG = 1<<6;
949 private static final int OP_ARG_LENGTH_MASK = 7;
950 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
951 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
952 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
953 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
954 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
956 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
957 private static final byte[] OP_DATA = {
958 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
959 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
960 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
961 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
962 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
963 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
964 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
965 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
966 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
967 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
968 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
969 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
970 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
971 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
972 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
973 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
976 // Debugging //////////////////////////////////////////////////////////////////////////////
978 public void debugBodyToString(StringBuffer sb) {
979 // This is intentionally a local variable so it can be removed by gcclass
980 final String[] OP_NAMES = new String[]{
981 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
982 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
983 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
984 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
985 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
986 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
987 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
988 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
989 "faload", "daload", "aaload", "baload", "caload", "saload",
990 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
991 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
992 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
993 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
994 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
995 "bastore", "castore", "sastore", "pop", "pop2", "dup",
996 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
997 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
998 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
999 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
1000 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
1001 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
1002 "iand", "land", "ior", "lor", "ixor", "lxor",
1003 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
1004 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
1005 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
1006 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
1007 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
1008 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
1009 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
1010 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
1011 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
1012 "", "new", "newarray", "anewarray", "arraylength", "athrow",
1013 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
1014 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
1015 "", "", "", "", "", "",
1016 "", "", "", "", "", "",
1017 "", "", "", "", "", "",
1018 "", "", "", "", "", "",
1019 "", "", "", "", "", "",
1020 "", "", "", "", "", "",
1021 "", "", "", "", "", "",
1022 "", "", "", "", "", "",
1025 for(int i=0;i<size();i++) {
1027 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1028 sb.append(i).append(": ");
1029 sb.append(OP_NAMES[op[i]&0xff]);
1031 if (arg[i] instanceof Type) s = ((Type)arg[i]).toString();
1032 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1033 else if (arg[i] instanceof String) s = "\"" + arg[i] + "\"";
1034 else if (arg[i] != null) s = arg[i].toString();
1035 if (s != null) sb.append(" ").append(s);
1040 // Unused //////////////////////////////////////////////////////////////////////////////
1042 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
1043 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1044 public static byte negate(byte op) {
1046 case IFEQ: return IFNE;
1047 case IFNE: return IFEQ;
1048 case IFLT: return IFGE;
1049 case IFGE: return IFLT;
1050 case IFGT: return IFLE;
1051 case IFLE: return IFGT;
1052 case IF_ICMPEQ: return IF_ICMPNE;
1053 case IF_ICMPNE: return IF_ICMPEQ;
1054 case IF_ICMPLT: return IF_ICMPGE;
1055 case IF_ICMPGE: return IF_ICMPLT;
1056 case IF_ICMPGT: return IF_ICMPLE;
1057 case IF_ICMPLE: return IF_ICMPGT;
1058 case IF_ACMPEQ: return IF_ACMPNE;
1059 case IF_ACMPNE: return IF_ACMPEQ;
1062 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));