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();
18 private int maxStack = 16;
19 private int maxLocals;
25 private ConstantPool.Ent[] cparg;
28 // Constructors //////////////////////////////////////////////////////////////////////////////
30 MethodGen(Type.Class.Method method, int flags) {
31 method.super(flags, new ClassFile.AttrGen());
33 codeAttrs = new ClassFile.AttrGen();
34 if (!isConcrete()) size = capacity = -1;
35 maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
38 MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
39 this(in.readShort(), cp.getUtf8KeyByIndex(in.readShort()), c, in, cp); }
41 private MethodGen(short flags, String name, Type.Class c, DataInput in, ConstantPool cp) throws IOException {
42 this(flags, name, c.method(name,cp.getUtf8KeyByIndex(in.readShort())), c, in, cp); }
43 private MethodGen(short flags, String name, Type.Class.Method m,
44 Type.Class c, DataInput in, ConstantPool cp) throws IOException {
45 m.super(flags, new ClassFile.AttrGen(in,cp));
49 byte[] codeAttr = (byte[]) attrs.get("Code");
50 if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
51 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
52 maxStack = ci.readUnsignedShort();
53 maxLocals = ci.readUnsignedShort();
54 int codeLen = ci.readInt();
55 int[] bytecodeMap = parseCode(ci,codeLen,cp);
56 int numExns = ci.readUnsignedShort();
58 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
59 codeAttrs = new ClassFile.AttrGen(ci,cp);
60 // FEATURE: Support these
61 // NOTE: Until we can support them properly we HAVE to delete them,
62 // they'll be incorrect after we rewrite the constant pool, etc
63 codeAttrs.remove("LineNumberTable");
64 codeAttrs.remove("LocalVariableTable");
67 codeAttrs = new ClassFile.AttrGen();
70 if (attrs.contains("Exceptions")) {
71 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
72 int exnCount = ei.readUnsignedShort();
73 while(exnCount-- > 0) {
74 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
75 thrownExceptions.put(t,t);
80 // Parsing //////////////////////////////////////////////////////////////////////////////
82 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
83 int[] map = new int[codeLen];
85 for(pc=0;pc<map.length;pc++) map[pc] = -1;
86 for(pc=0;pc<codeLen;) {
87 byte op = in.readByte();
88 int opdata = OP_DATA[op&0xff];
89 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
90 if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
91 int argLength = opdata & OP_ARG_LENGTH_MASK;
94 pc += 1 + (argLength == 7 ? 0 : argLength);
95 if (argLength == 0) { add(op); continue; }
99 arg = new Pair(in.readUnsignedByte(),in.readByte());
104 for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
105 int def = in.readInt() + mypc;
107 if (op == LOOKUPSWITCH) {
108 Switch.Lookup lsi = new Switch.Lookup(in.readInt());
110 for(int i=0;i<lsi.size();i++) {
111 lsi.setVal(i,in.readInt());
112 lsi.setTarget(i,in.readInt() + mypc);
117 int lo = in.readInt();
118 int hi = in.readInt();
120 Switch.Table tsi = new Switch.Table(lo,hi);
121 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
124 si.setDefaultTarget(def);
128 byte wideop = in.readByte();
130 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
131 : new Wide(wideop,in.readUnsignedShort());
132 pc += wideop == IINC ? 5 : 3;
136 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
138 case INVOKEINTERFACE: {
139 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
140 if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
141 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
142 if (in.readByte() == 0 || in.readByte() != 0)
143 throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
147 if ((opdata&OP_CPENT_FLAG)!=0) {
148 ConstantPool.Ent ent =
149 cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
151 Object key = ent.key();
157 case CONSTANT_INTEGER:
160 case CONSTANT_DOUBLE:
161 case CONSTANT_STRING:
165 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
166 Integer.toString(op&0xff,16));
173 if (tag != CONSTANT_FIELDREF)
174 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
175 Integer.toString(op&0xff,16));
180 if (tag != CONSTANT_METHODREF)
181 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
182 Integer.toString(op&0xff,16));
188 if (tag != CONSTANT_CLASS)
189 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
190 Integer.toString(op&0xff,16));
193 throw new Error("should never happen");
197 // treat everything else (including branches for now) as plain old ints
199 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
200 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
201 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
202 else throw new Error("should never happen");
203 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
211 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
212 for(int i=0;i<size();i++) {
217 Switch si = (Switch) arg[i];
219 int pos = map[si.getDefaultTarget()];
221 throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
222 si.setDefaultTarget(pos);
224 for(int j=0;j<si.size();j++) {
225 pos = map[si.getTarget(j)];
226 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
232 if (OP_BRANCH(op[i])) {
233 int pos = map[((Integer)arg[i]).intValue()];
234 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
243 // Exception Table //////////////////////////////////////////////////////////////////////////////
249 final Type.Class type; // null type means all exceptions (for finally)
251 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
252 int startPC = in.readUnsignedShort();
253 int endPC = in.readUnsignedShort();
254 int handlerPC = in.readUnsignedShort();
255 int index = in.readUnsignedShort();
256 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
257 int max = bytecodeMap.length;
258 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
259 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
260 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
261 this.start = bytecodeMap[startPC];
262 this.end = bytecodeMap[endPC];
263 this.handler = bytecodeMap[handlerPC];
265 ExnTableEnt(int start, int end, int handler, Type.Class type) {
268 this.handler = handler;
271 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
272 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
273 o.writeShort(pc[start]);
274 o.writeShort(end==pc.length ? endPC : pc[end]);
275 o.writeShort(pc[handler]);
276 o.writeShort(type == null ? 0 : cp.getIndex(type));
280 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
281 @param start The instruction to start at (inclusive)
282 @param end The instruction to end at (exclusive)
283 @param handler The instruction of the excepton handler
284 @param type The type of exception that is to be handled (MUST inherit from Throwable)
286 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
287 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
290 /** Adds a exception type that can be thrown from this method
291 NOTE: This isn't enforced by the JVM. This is for reference
292 only. A method can throw exceptions not declared to be thrown
293 @param type The type of exception that can be thrown
295 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
297 private final void grow() { if (size == capacity) grow(size+1); }
298 private final void grow(int newCap) {
299 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
300 if (newCap <= capacity) return;
301 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
303 byte[] op2 = new byte[newCap];
304 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
307 Object[] arg2 = new Object[newCap];
308 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
314 // Accessors //////////////////////////////////////////////////////////////////////////////
316 public int getFlags() { return flags; }
317 public Hashtable getThrownExceptions() { return thrownExceptions; }
319 /** Returns the size (in instructions) of this method
320 @return The size of the method (in instructions)
322 public final int size() { return size; }
324 // These two are optimized for speed, they don't call set() below
325 /** Add a bytecode (with no argument) to the method */
326 public final int add(byte op) {
328 if (s == capacity) grow();
334 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
335 public final void set(int pos, byte op) { this.op[pos] = op; }
337 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
338 @return The position of the new bytecode
340 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
342 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
343 @return The position of the new bytecode
346 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
348 /** Adds a bytecode with an integer argument. This is equivalent
349 * to add(op, new Integer(arg)), but optimized to prevent the
350 * allocation when possible
351 @return The position of the new bytecode
352 @see #add(byte, Object)
354 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
356 /** Gets the bytecode at position <i>pos</i>
357 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
359 public final byte get(int pos) { return op[pos]; }
361 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
362 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
363 interal form when they are added. The value returned from this method for these instruction can be reused, but there
364 is no way to retrieve the original object
365 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
367 public final Object getArg(int pos) { return arg[pos]; }
369 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
370 * equivalent to set(pos, op, new Integer(arg)), but optimized to
371 * prevent the allocation when possible.
372 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
373 @see #setArg(int, Object) */
374 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
376 /** Sets the argument for <i>pos</i> to <i>arg</i>.
377 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
378 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
380 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
381 This is equivalent to set(pos, op, arg?1:0)
382 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
384 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
386 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
387 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
388 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
389 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
391 public final void set(int pos, byte op, int n) {
396 case -1: op = ICONST_M1; break OUTER;
397 case 0: op = ICONST_0; break OUTER;
398 case 1: op = ICONST_1; break OUTER;
399 case 2: op = ICONST_2; break OUTER;
400 case 3: op = ICONST_3; break OUTER;
401 case 4: op = ICONST_4; break OUTER;
402 case 5: op = ICONST_5; break OUTER;
404 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
405 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
408 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
409 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
410 if (n >= maxLocals) maxLocals = n + 1;
411 if (n >= 0 && n <= 3) {
414 case ILOAD: base = ILOAD_0; break;
415 case ISTORE: base = ISTORE_0; break;
416 case LLOAD: base = LLOAD_0; break;
417 case LSTORE: base = LSTORE_0; break;
418 case FLOAD: base = FLOAD_0; break;
419 case FSTORE: base = FSTORE_0; break;
420 case DLOAD: base = DLOAD_0; break;
421 case DSTORE: base = DSTORE_0; break;
422 case ALOAD: base = ALOAD_0; break;
423 case ASTORE: base = ASTORE_0; break;
425 op = (byte)((base&0xff) + n);
438 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
439 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
441 public final void set(int pos, byte op, Object arg) {
443 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
444 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
445 // set(int, byte, int) always handles these ops itself
446 set(pos, op, ((Integer)arg).intValue());
449 // set(int, byte, int) always handles these opts itself
450 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
451 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
453 if (arg instanceof Long) {
454 long l = ((Long)arg).longValue();
455 if (l == 0L || l == 1L) {
456 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
457 this.arg[pos] = null;
461 } else if (arg instanceof Double) {
466 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
471 /** Sets the maximum number of locals in the function to
472 <i>maxLocals</i>. NOTE: This defaults to 0 and is
473 automatically increased as necessary when *LOAD/*STORE
474 bytecodes are added. You do not need to call this function in
476 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
478 /** Sets the maxinum size of th stack for this function to
479 * <i>maxStack</i>. This defaults to 16< */
480 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
483 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
485 public static abstract class Switch {
486 public final Object[] targets;
487 public Object defaultTarget;
489 Switch(int size) { targets = new Object[size]; }
490 public void setTarget(int pos, Object val) { targets[pos] = val; }
491 public void setTarget(int pos, int val) { targets[pos] = N(val); }
492 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
493 public void setDefaultTarget(Object o) { defaultTarget = o; }
494 public int size() { return targets.length; }
496 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
497 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
499 abstract int length();
501 public static class Table extends Switch {
504 public Table(int lo, int hi) {
509 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
510 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
512 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
515 public static class Lookup extends Switch {
516 public final int[] vals;
517 public Lookup(int size) {
519 this.vals = new int[size];
521 public final void setVal(int pos, int val) { vals[pos] = val; }
523 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
527 /** This class represents the arguments to byecodes that take two integer arguments. */
528 public static class Pair {
531 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
534 public static class MultiANewArray {
535 public Type.Class type;
537 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
540 public static class Wide {
541 public final byte op;
542 public final int varNum;
544 Wide(byte op, int varNum) { this(op, varNum, 0); }
545 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
549 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
551 private Object resolveTarget(Object arg) {
553 if (arg instanceof PhantomTarget) {
554 target = ((PhantomTarget)arg).getTarget();
555 if (target == -1) throw new IllegalStateException("unresolved phantom target");
558 target = ((Integer)arg).intValue();
560 if (target < 0 || target >= size)
561 throw new IllegalStateException("invalid target address " + target + "/" + size);
565 /** Computes the final bytecode for this method.
566 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
567 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
569 void finish(ConstantPool cp) {
570 cp.addUtf8(method.name);
571 cp.addUtf8(method.getTypeDescriptor());
573 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
574 cp.add(e.nextElement());
576 if (size == NO_CODE) return;
577 for(int i=0;i<exnTable.size();i++)
578 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
580 // We'll set these correctly later
581 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
582 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
584 codeAttrs.finish(cp);
586 cparg = new ConstantPool.Ent[size];
588 for(int i=0, p=0;i<size;i++) {
604 cparg[i] = cp.add(arg[i]);
606 case INVOKEINTERFACE:
607 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
610 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
616 private void generateCode(ConstantPool cp) {
619 } catch(IOException e) {
620 throw new Error("should never happen");
624 private void _generateCode(ConstantPool cp) throws IOException {
625 ByteArrayOutputStream baos = new ByteArrayOutputStream();
626 DataOutput o = new DataOutputStream(baos);
628 int[] pc = new int[size];
632 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
633 for(i=0, p=0;i<size;i++) {
634 byte op = this.op[i];
635 int opdata = OP_DATA[op&0xff];
639 if ((opdata & OP_BRANCH_FLAG)!= 0) {
641 arg[i] = resolveTarget(arg[i]);
642 } catch(RuntimeException e) {
643 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
649 // Speical caculations
652 int arg = ((Integer)this.arg[i]).intValue();
653 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
662 Switch si = (Switch) arg[i];
663 Object[] targets = si.targets;
664 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
665 si.defaultTarget = resolveTarget(si.defaultTarget);
666 p += 1 + 3 + si.length(); // opcode itself, padding, data
667 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
668 int[] vals = ((Switch.Lookup)si).vals;
669 for(j=1;j<vals.length;j++)
670 if (vals[j] <= vals[j-1])
671 throw new IllegalStateException("out of order/duplicate lookupswitch values");
676 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
679 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
680 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
682 int arg = ((Integer)this.arg[i]).intValue();
685 this.arg[i] = new Wide(op, arg);
692 Pair pair = (Pair) this.arg[i];
693 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
695 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
702 j = cp.getIndex(cparg[i]);
704 this.op[i] = op = LDC_W;
711 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
715 // Pass2 - Widen instructions if they can possibly be too short
716 for(i=0;i<size;i++) {
720 int arg = ((Integer)this.arg[i]).intValue();
721 int diff = maxpc[arg] - maxpc[i];
722 if (diff < -32768 || diff > 32767)
723 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
729 // Pass3 - Calculate actual pc
730 for(i=0, p=0;i<size;i++) {
731 byte op = this.op[i];
739 Switch si = (Switch) arg[i];
740 p++; // opcode itself
741 p = (p + 3) & ~3; // padding
743 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
744 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
748 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
751 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
752 if (l == 7) throw new Error("shouldn't be here");
759 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
761 o.writeShort(maxStack);
762 o.writeShort(maxLocals);
763 o.writeInt(codeSize);
765 // Pass 4 - Actually write the bytecodes
766 for(i=0;i<size;i++) {
767 byte op = this.op[i];
768 int opdata = OP_DATA[op&0xff];
769 if (op == NOP && !EMIT_NOPS) continue;
771 int argLength = opdata & OP_ARG_LENGTH_MASK;
773 if (argLength == 0) continue; // skip if no args
776 Object arg = this.arg[i];
780 Pair pair = (Pair) arg;
781 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
782 o.writeByte(pair.i1);
783 o.writeByte(pair.i2);
788 Switch si = (Switch) arg;
790 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
791 o.writeInt(pc[si.getDefaultTarget()] - mypc);
792 if (op == LOOKUPSWITCH) {
793 int[] vals = ((Switch.Lookup)si).vals;
794 o.writeInt(si.size());
795 for(int j=0;j<si.size();j++) {
797 o.writeInt(pc[si.getTarget(j)] - mypc);
800 Switch.Table tsi = (Switch.Table) si;
803 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
808 Wide wide = (Wide) arg;
809 o.writeByte(wide.op);
810 o.writeShort(wide.varNum);
811 if (wide.op == IINC) o.writeShort(wide.n);
814 case MULTIANEWARRAY: {
815 o.writeShort(cp.getIndex(cparg[i]));
816 int v = ((MultiANewArray) arg).dims;
817 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
821 case INVOKEINTERFACE:
822 o.writeShort(cp.getIndex(cparg[i]));
823 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
827 if ((opdata & OP_BRANCH_FLAG) != 0) {
828 int v = pc[((Integer)arg).intValue()] - pc[i];
829 if (argLength == 2) {
830 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
832 } else if (argLength == 4) {
835 throw new Error("should never happen");
837 } else if ((opdata & OP_CPENT_FLAG) != 0) {
838 int v = cp.getIndex(cparg[i]);
839 if (argLength == 1) o.writeByte(v);
840 else if (argLength == 2) o.writeShort(v);
841 else throw new Error("should never happen");
842 } else if (argLength == 7) {
843 throw new Error("should never happen - variable length instruction not explicitly handled");
845 int iarg = ((Integer)arg).intValue();
846 if (argLength == 1) {
847 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
848 throw new ClassFile.Exn("overflow of s/u1 option");
850 } else if (argLength == 2) {
851 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
852 throw new ClassFile.Exn("overflow of s/u2 option");
855 throw new Error("should never happen");
862 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
864 o.writeShort(exnTable.size());
865 for(i=0;i<exnTable.size();i++)
866 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
868 codeAttrs.dump(o,cp);
871 byte[] codeAttribute = baos.toByteArray();
872 attrs.put("Code", codeAttribute);
875 void generateExceptions(ConstantPool cp) throws IOException {
876 if (thrownExceptions.size() > 0) {
877 ByteArrayOutputStream baos = new ByteArrayOutputStream();
878 DataOutputStream o = new DataOutputStream(baos);
879 o.writeShort(thrownExceptions.size());
880 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
881 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
883 attrs.put("Exceptions", baos.toByteArray());
887 void dump(DataOutput o, ConstantPool cp) throws IOException {
888 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
889 generateExceptions(cp);
892 o.writeShort(cp.getUtf8Index(method.name));
893 o.writeShort(cp.getUtf8Index(method.getTypeDescriptor()));
898 /** Class that represents a target that isn't currently know. The
899 target MUST be set with setTarget() before the classfile is
900 written. This class is more or less a mutable integer */
901 public static class PhantomTarget {
902 private int target = -1;
903 public void setTarget(int target) { this.target = target; }
904 public int getTarget() { return target; }
907 private static Integer N(int n) { return new Integer(n); }
908 private static Long N(long n) { return new Long(n); }
909 private static Float N(float f) { return new Float(f); }
910 private static Double N(double d) { return new Double(d); }
911 private static int max(int a, int b) { return a > b ? a : b; }
913 private static final int OP_BRANCH_FLAG = 1<<3;
914 private static final int OP_CPENT_FLAG = 1<<4;
915 private static final int OP_UNSIGNED_FLAG = 1<<5;
916 private static final int OP_VALID_FLAG = 1<<6;
917 private static final int OP_ARG_LENGTH_MASK = 7;
918 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
919 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
920 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
921 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
922 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
924 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
925 private static final byte[] OP_DATA = {
926 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
927 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
928 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
929 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
930 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
931 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
932 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
933 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
934 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
935 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
936 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
937 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
938 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
939 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
940 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
941 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
944 // Debugging //////////////////////////////////////////////////////////////////////////////
946 public void debugBodyToString(StringBuffer sb) {
947 // This is intentionally a local variable so it can be removed by gcclass
948 final String[] OP_NAMES = new String[]{
949 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
950 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
951 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
952 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
953 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
954 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
955 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
956 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
957 "faload", "daload", "aaload", "baload", "caload", "saload",
958 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
959 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
960 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
961 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
962 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
963 "bastore", "castore", "sastore", "pop", "pop2", "dup",
964 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
965 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
966 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
967 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
968 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
969 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
970 "iand", "land", "ior", "lor", "ixor", "lxor",
971 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
972 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
973 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
974 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
975 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
976 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
977 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
978 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
979 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
980 "", "new", "newarray", "anewarray", "arraylength", "athrow",
981 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
982 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
983 "", "", "", "", "", "",
984 "", "", "", "", "", "",
985 "", "", "", "", "", "",
986 "", "", "", "", "", "",
987 "", "", "", "", "", "",
988 "", "", "", "", "", "",
989 "", "", "", "", "", "",
990 "", "", "", "", "", "",
993 for(int i=0;i<size();i++) {
995 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
996 sb.append(i).append(": ");
997 sb.append(OP_NAMES[op[i]&0xff]);
999 if (arg[i] instanceof Type) s = ((Type)arg[i]).toString();
1000 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1001 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1002 else if (arg[i] != null) s = arg[i].toString();
1003 if (s != null) sb.append(" ").append(s);
1008 // Unused //////////////////////////////////////////////////////////////////////////////
1010 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
1011 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1012 public static byte negate(byte op) {
1014 case IFEQ: return IFNE;
1015 case IFNE: return IFEQ;
1016 case IFLT: return IFGE;
1017 case IFGE: return IFLT;
1018 case IFGT: return IFLE;
1019 case IFLE: return IFGT;
1020 case IF_ICMPEQ: return IF_ICMPNE;
1021 case IF_ICMPNE: return IF_ICMPEQ;
1022 case IF_ICMPLT: return IF_ICMPGE;
1023 case IF_ICMPGE: return IF_ICMPLT;
1024 case IF_ICMPGT: return IF_ICMPLE;
1025 case IF_ICMPLE: return IF_ICMPGT;
1026 case IF_ACMPEQ: return IF_ACMPNE;
1027 case IF_ACMPNE: return IF_ACMPEQ;
1030 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));