1 package org.ibex.classgen;
6 /** A class representing a method in a generated classfile
7 @see ClassFile#addMethod */
8 public class MethodGen 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) {
33 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
37 attrs = new ClassFile.AttrGen();
38 codeAttrs = new ClassFile.AttrGen();
40 if (((flags & INTERFACE) != 0) || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
42 maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
45 MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
46 this.flags = in.readShort();
47 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
48 String name = cp.getUtf8KeyByIndex(in.readShort());
49 this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
50 this.attrs = new ClassFile.AttrGen(in,cp);
52 if ((flags & (NATIVE|ABSTRACT))==0) {
53 byte[] codeAttr = (byte[]) attrs.get("Code");
54 if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
55 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
56 maxStack = ci.readUnsignedShort();
57 maxLocals = ci.readUnsignedShort();
58 int codeLen = ci.readInt();
59 int[] bytecodeMap = parseCode(ci,codeLen,cp);
60 int numExns = ci.readUnsignedShort();
62 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
63 codeAttrs = new ClassFile.AttrGen(ci,cp);
64 // FEATURE: Support these
65 // NOTE: Until we can support them properly we HAVE to delete them,
66 // they'll be incorrect after we rewrite the constant pool, etc
67 codeAttrs.remove("LineNumberTable");
68 codeAttrs.remove("LocalVariableTable");
71 codeAttrs = new ClassFile.AttrGen();
74 if (attrs.contains("Exceptions")) {
75 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
76 int exnCount = ei.readUnsignedShort();
77 while(exnCount-- > 0) {
78 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
79 thrownExceptions.put(t,t);
84 // Parsing //////////////////////////////////////////////////////////////////////////////
86 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
87 int[] map = new int[codeLen];
89 for(pc=0;pc<map.length;pc++) map[pc] = -1;
90 for(pc=0;pc<codeLen;) {
91 byte op = in.readByte();
92 int opdata = OP_DATA[op&0xff];
93 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
94 if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
95 int argLength = opdata & OP_ARG_LENGTH_MASK;
98 pc += 1 + (argLength == 7 ? 0 : argLength);
99 if (argLength == 0) { add(op); continue; }
103 arg = new Pair(in.readUnsignedByte(),in.readByte());
108 for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
109 int def = in.readInt() + mypc;
111 if (op == LOOKUPSWITCH) {
112 Switch.Lookup lsi = new Switch.Lookup(in.readInt());
114 for(int i=0;i<lsi.size();i++) {
115 lsi.setVal(i,in.readInt());
116 lsi.setTarget(i,in.readInt() + mypc);
121 int lo = in.readInt();
122 int hi = in.readInt();
124 Switch.Table tsi = new Switch.Table(lo,hi);
125 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
128 si.setDefaultTarget(def);
132 byte wideop = in.readByte();
134 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
135 : new Wide(wideop,in.readUnsignedShort());
136 pc += wideop == IINC ? 5 : 3;
140 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
142 case INVOKEINTERFACE: {
143 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
144 if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
145 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
146 if (in.readByte() == 0 || in.readByte() != 0)
147 throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
151 if ((opdata&OP_CPENT_FLAG)!=0) {
152 ConstantPool.Ent ent =
153 cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
155 Object key = ent.key();
161 case CONSTANT_INTEGER:
164 case CONSTANT_DOUBLE:
165 case CONSTANT_STRING:
169 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
170 Integer.toString(op&0xff,16));
177 if (tag != CONSTANT_FIELDREF)
178 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
179 Integer.toString(op&0xff,16));
184 if (tag != CONSTANT_METHODREF)
185 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
186 Integer.toString(op&0xff,16));
192 if (tag != CONSTANT_CLASS)
193 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
194 Integer.toString(op&0xff,16));
197 throw new Error("should never happen");
201 // treat everything else (including branches for now) as plain old ints
203 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
204 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
205 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
206 else throw new Error("should never happen");
207 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
215 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
216 for(int i=0;i<size();i++) {
221 Switch si = (Switch) arg[i];
223 int pos = map[si.getDefaultTarget()];
225 throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
226 si.setDefaultTarget(pos);
228 for(int j=0;j<si.size();j++) {
229 pos = map[si.getTarget(j)];
230 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
236 if (OP_BRANCH(op[i])) {
237 int pos = map[((Integer)arg[i]).intValue()];
238 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
247 // Exception Table //////////////////////////////////////////////////////////////////////////////
253 final Type.Class type; // null type means all exceptions (for finally)
255 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
256 int startPC = in.readUnsignedShort();
257 int endPC = in.readUnsignedShort();
258 int handlerPC = in.readUnsignedShort();
259 int index = in.readUnsignedShort();
260 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
261 int max = bytecodeMap.length;
262 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
263 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
264 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
265 this.start = bytecodeMap[startPC];
266 this.end = bytecodeMap[endPC];
267 this.handler = bytecodeMap[handlerPC];
269 ExnTableEnt(int start, int end, int handler, Type.Class type) {
272 this.handler = handler;
275 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
276 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
277 o.writeShort(pc[start]);
278 o.writeShort(end==pc.length ? endPC : pc[end]);
279 o.writeShort(pc[handler]);
280 o.writeShort(type == null ? 0 : cp.getIndex(type));
284 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
285 @param start The instruction to start at (inclusive)
286 @param end The instruction to end at (exclusive)
287 @param handler The instruction of the excepton handler
288 @param type The type of exception that is to be handled (MUST inherit from Throwable)
290 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
291 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
294 /** Adds a exception type that can be thrown from this method
295 NOTE: This isn't enforced by the JVM. This is for reference
296 only. A method can throw exceptions not declared to be thrown
297 @param type The type of exception that can be thrown
299 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
301 private final void grow() { if (size == capacity) grow(size+1); }
302 private final void grow(int newCap) {
303 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
304 if (newCap <= capacity) return;
305 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
307 byte[] op2 = new byte[newCap];
308 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
311 Object[] arg2 = new Object[newCap];
312 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
318 // Accessors //////////////////////////////////////////////////////////////////////////////
320 /** Returns the size (in instructions) of this method
321 @return The size of the method (in instructions)
323 public final int size() { return size; }
325 // These two are optimized for speed, they don't call set() below
326 /** Add a bytecode (with no argument) to the method */
327 public final int add(byte op) {
329 if (s == capacity) grow();
335 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
336 public final void set(int pos, byte op) { this.op[pos] = op; }
338 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
339 @return The position of the new bytecode
341 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
343 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
344 @return The position of the new bytecode
347 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
349 /** Adds a bytecode with an integer argument. This is equivalent
350 * to add(op, new Integer(arg)), but optimized to prevent the
351 * allocation when possible
352 @return The position of the new bytecode
353 @see #add(byte, Object)
355 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
357 /** Gets the bytecode at position <i>pos</i>
358 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
360 public final byte get(int pos) { return op[pos]; }
362 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
363 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
364 interal form when they are added. The value returned from this method for these instruction can be reused, but there
365 is no way to retrieve the original object
366 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
368 public final Object getArg(int pos) { return arg[pos]; }
370 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
371 * equivalent to set(pos, op, new Integer(arg)), but optimized to
372 * prevent the allocation when possible.
373 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
374 @see #setArg(int, Object) */
375 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
377 /** Sets the argument for <i>pos</i> to <i>arg</i>.
378 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
379 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
381 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
382 This is equivalent to set(pos, op, arg?1:0)
383 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
385 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
387 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
388 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
389 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
390 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
392 public final void set(int pos, byte op, int n) {
397 case -1: op = ICONST_M1; break OUTER;
398 case 0: op = ICONST_0; break OUTER;
399 case 1: op = ICONST_1; break OUTER;
400 case 2: op = ICONST_2; break OUTER;
401 case 3: op = ICONST_3; break OUTER;
402 case 4: op = ICONST_4; break OUTER;
403 case 5: op = ICONST_5; break OUTER;
405 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
406 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
409 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
410 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
411 if (n >= maxLocals) maxLocals = n + 1;
412 if (n >= 0 && n <= 3) {
415 case ILOAD: base = ILOAD_0; break;
416 case ISTORE: base = ISTORE_0; break;
417 case LLOAD: base = LLOAD_0; break;
418 case LSTORE: base = LSTORE_0; break;
419 case FLOAD: base = FLOAD_0; break;
420 case FSTORE: base = FSTORE_0; break;
421 case DLOAD: base = DLOAD_0; break;
422 case DSTORE: base = DSTORE_0; break;
423 case ALOAD: base = ALOAD_0; break;
424 case ASTORE: base = ASTORE_0; break;
426 op = (byte)((base&0xff) + n);
439 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
440 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
442 public final void set(int pos, byte op, Object arg) {
444 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
445 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
446 // set(int, byte, int) always handles these ops itself
447 set(pos, op, ((Integer)arg).intValue());
450 // set(int, byte, int) always handles these opts itself
451 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
452 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
454 if (arg instanceof Long) {
455 long l = ((Long)arg).longValue();
456 if (l == 0L || l == 1L) {
457 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
458 this.arg[pos] = null;
462 } else if (arg instanceof Double) {
467 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
472 /** Sets the maximum number of locals in the function to
473 <i>maxLocals</i>. NOTE: This defaults to 0 and is
474 automatically increased as necessary when *LOAD/*STORE
475 bytecodes are added. You do not need to call this function in
477 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
479 /** Sets the maxinum size of th stack for this function to
480 * <i>maxStack</i>. This defaults to 16< */
481 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
484 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
486 public static abstract class Switch {
487 public final Object[] targets;
488 public Object defaultTarget;
490 Switch(int size) { targets = new Object[size]; }
491 public void setTarget(int pos, Object val) { targets[pos] = val; }
492 public void setTarget(int pos, int val) { targets[pos] = N(val); }
493 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
494 public void setDefaultTarget(Object o) { defaultTarget = o; }
495 public int size() { return targets.length; }
497 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
498 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
500 abstract int length();
502 public static class Table extends Switch {
505 public Table(int lo, int hi) {
510 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
511 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
513 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
516 public static class Lookup extends Switch {
517 public final int[] vals;
518 public Lookup(int size) {
520 this.vals = new int[size];
522 public final void setVal(int pos, int val) { vals[pos] = val; }
524 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
528 /** This class represents the arguments to byecodes that take two integer arguments. */
529 public static class Pair {
532 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
535 public static class MultiANewArray {
536 public Type.Class type;
538 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
541 public static class Wide {
542 public final byte op;
543 public final int varNum;
545 Wide(byte op, int varNum) { this(op, varNum, 0); }
546 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
550 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
552 private Object resolveTarget(Object arg) {
554 if (arg instanceof PhantomTarget) {
555 target = ((PhantomTarget)arg).getTarget();
556 if (target == -1) throw new IllegalStateException("unresolved phantom target");
559 target = ((Integer)arg).intValue();
561 if (target < 0 || target >= size)
562 throw new IllegalStateException("invalid target address " + target + "/" + size);
566 /** Computes the final bytecode for this method.
567 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
568 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
570 void finish(ConstantPool cp) {
571 cp.addUtf8(method.name);
572 cp.addUtf8(method.getDescriptor());
574 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
575 cp.add(e.nextElement());
577 if (size == NO_CODE) return;
578 for(int i=0;i<exnTable.size();i++)
579 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
581 // We'll set these correctly later
582 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
583 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
585 codeAttrs.finish(cp);
587 cparg = new ConstantPool.Ent[size];
589 for(int i=0, p=0;i<size;i++) {
605 cparg[i] = cp.add(arg[i]);
607 case INVOKEINTERFACE:
608 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
611 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
617 private void generateCode(ConstantPool cp) {
620 } catch(IOException e) {
621 throw new Error("should never happen");
625 private void _generateCode(ConstantPool cp) throws IOException {
626 ByteArrayOutputStream baos = new ByteArrayOutputStream();
627 DataOutput o = new DataOutputStream(baos);
629 int[] pc = new int[size];
633 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
634 for(i=0, p=0;i<size;i++) {
635 byte op = this.op[i];
636 int opdata = OP_DATA[op&0xff];
640 if ((opdata & OP_BRANCH_FLAG)!= 0) {
642 arg[i] = resolveTarget(arg[i]);
643 } catch(RuntimeException e) {
644 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
650 // Speical caculations
653 int arg = ((Integer)this.arg[i]).intValue();
654 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
663 Switch si = (Switch) arg[i];
664 Object[] targets = si.targets;
665 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
666 si.defaultTarget = resolveTarget(si.defaultTarget);
667 p += 1 + 3 + si.length(); // opcode itself, padding, data
668 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
669 int[] vals = ((Switch.Lookup)si).vals;
670 for(j=1;j<vals.length;j++)
671 if (vals[j] <= vals[j-1])
672 throw new IllegalStateException("out of order/duplicate lookupswitch values");
677 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
680 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
681 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
683 int arg = ((Integer)this.arg[i]).intValue();
686 this.arg[i] = new Wide(op, arg);
693 Pair pair = (Pair) this.arg[i];
694 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
696 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
703 j = cp.getIndex(cparg[i]);
705 this.op[i] = op = LDC_W;
712 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
716 // Pass2 - Widen instructions if they can possibly be too short
717 for(i=0;i<size;i++) {
721 int arg = ((Integer)this.arg[i]).intValue();
722 int diff = maxpc[arg] - maxpc[i];
723 if (diff < -32768 || diff > 32767)
724 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
730 // Pass3 - Calculate actual pc
731 for(i=0, p=0;i<size;i++) {
732 byte op = this.op[i];
740 Switch si = (Switch) arg[i];
741 p++; // opcode itself
742 p = (p + 3) & ~3; // padding
744 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
745 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
749 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
752 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
753 if (l == 7) throw new Error("shouldn't be here");
760 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
762 o.writeShort(maxStack);
763 o.writeShort(maxLocals);
764 o.writeInt(codeSize);
766 // Pass 4 - Actually write the bytecodes
767 for(i=0;i<size;i++) {
768 byte op = this.op[i];
769 int opdata = OP_DATA[op&0xff];
770 if (op == NOP && !EMIT_NOPS) continue;
772 int argLength = opdata & OP_ARG_LENGTH_MASK;
774 if (argLength == 0) continue; // skip if no args
777 Object arg = this.arg[i];
781 Pair pair = (Pair) arg;
782 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
783 o.writeByte(pair.i1);
784 o.writeByte(pair.i2);
789 Switch si = (Switch) arg;
791 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
792 o.writeInt(pc[si.getDefaultTarget()] - mypc);
793 if (op == LOOKUPSWITCH) {
794 int[] vals = ((Switch.Lookup)si).vals;
795 o.writeInt(si.size());
796 for(int j=0;j<si.size();j++) {
798 o.writeInt(pc[si.getTarget(j)] - mypc);
801 Switch.Table tsi = (Switch.Table) si;
804 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
809 Wide wide = (Wide) arg;
810 o.writeByte(wide.op);
811 o.writeShort(wide.varNum);
812 if (wide.op == IINC) o.writeShort(wide.n);
815 case MULTIANEWARRAY: {
816 o.writeShort(cp.getIndex(cparg[i]));
817 int v = ((MultiANewArray) arg).dims;
818 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
822 case INVOKEINTERFACE:
823 o.writeShort(cp.getIndex(cparg[i]));
824 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
828 if ((opdata & OP_BRANCH_FLAG) != 0) {
829 int v = pc[((Integer)arg).intValue()] - pc[i];
830 if (argLength == 2) {
831 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
833 } else if (argLength == 4) {
836 throw new Error("should never happen");
838 } else if ((opdata & OP_CPENT_FLAG) != 0) {
839 int v = cp.getIndex(cparg[i]);
840 if (argLength == 1) o.writeByte(v);
841 else if (argLength == 2) o.writeShort(v);
842 else throw new Error("should never happen");
843 } else if (argLength == 7) {
844 throw new Error("should never happen - variable length instruction not explicitly handled");
846 int iarg = ((Integer)arg).intValue();
847 if (argLength == 1) {
848 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
849 throw new ClassFile.Exn("overflow of s/u1 option");
851 } else if (argLength == 2) {
852 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
853 throw new ClassFile.Exn("overflow of s/u2 option");
856 throw new Error("should never happen");
863 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
865 o.writeShort(exnTable.size());
866 for(i=0;i<exnTable.size();i++)
867 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
869 codeAttrs.dump(o,cp);
872 byte[] codeAttribute = baos.toByteArray();
873 attrs.put("Code", codeAttribute);
876 void generateExceptions(ConstantPool cp) throws IOException {
877 if (thrownExceptions.size() > 0) {
878 ByteArrayOutputStream baos = new ByteArrayOutputStream();
879 DataOutputStream o = new DataOutputStream(baos);
880 o.writeShort(thrownExceptions.size());
881 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
882 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
884 attrs.put("Exceptions", baos.toByteArray());
888 void dump(DataOutput o, ConstantPool cp) throws IOException {
889 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
890 generateExceptions(cp);
893 o.writeShort(cp.getUtf8Index(method.name));
894 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
899 /** Class that represents a target that isn't currently know. The
900 target MUST be set with setTarget() before the classfile is
901 written. This class is more or less a mutable integer */
902 public static class PhantomTarget {
903 private int target = -1;
904 public void setTarget(int target) { this.target = target; }
905 public int getTarget() { return target; }
908 private static Integer N(int n) { return new Integer(n); }
909 private static Long N(long n) { return new Long(n); }
910 private static Float N(float f) { return new Float(f); }
911 private static Double N(double d) { return new Double(d); }
912 private static int max(int a, int b) { return a > b ? a : b; }
914 private static final int OP_BRANCH_FLAG = 1<<3;
915 private static final int OP_CPENT_FLAG = 1<<4;
916 private static final int OP_UNSIGNED_FLAG = 1<<5;
917 private static final int OP_VALID_FLAG = 1<<6;
918 private static final int OP_ARG_LENGTH_MASK = 7;
919 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
920 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
921 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
922 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
923 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
925 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
926 private static final byte[] OP_DATA = {
927 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
928 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
929 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
930 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 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, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
935 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
936 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
937 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
938 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
939 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 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,
942 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
945 // Debugging //////////////////////////////////////////////////////////////////////////////
947 public void debugToString(StringBuffer sb, String constructorName) {
948 // This is intentionally a local variable so it can be removed by gcclass
949 final String[] OP_NAMES = new String[]{
950 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
951 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
952 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
953 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
954 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
955 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
956 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
957 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
958 "faload", "daload", "aaload", "baload", "caload", "saload",
959 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
960 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
961 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
962 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
963 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
964 "bastore", "castore", "sastore", "pop", "pop2", "dup",
965 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
966 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
967 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
968 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
969 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
970 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
971 "iand", "land", "ior", "lor", "ixor", "lxor",
972 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
973 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
974 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
975 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
976 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
977 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
978 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
979 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
980 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
981 "", "new", "newarray", "anewarray", "arraylength", "athrow",
982 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
983 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
984 "", "", "", "", "", "",
985 "", "", "", "", "", "",
986 "", "", "", "", "", "",
987 "", "", "", "", "", "",
988 "", "", "", "", "", "",
989 "", "", "", "", "", "",
990 "", "", "", "", "", "",
991 "", "", "", "", "", "",
995 sb.append(" ").append(ClassFile.flagsToString(flags,false));
996 sb.append(method.debugToString());
997 if (thrownExceptions.size() > 0) {
999 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
1000 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1001 sb.setLength(sb.length()-1);
1004 if ((flags & (NATIVE|ABSTRACT))==0) {
1006 for(int i=0;i<size();i++) {
1008 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1009 sb.append(i).append(": ");
1010 sb.append(OP_NAMES[op[i]&0xff]);
1012 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1013 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1014 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1015 else if (arg[i] != null) s = arg[i].toString();
1016 if (s != null) sb.append(" ").append(s);
1025 // Unused //////////////////////////////////////////////////////////////////////////////
1027 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
1028 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1029 public static byte negate(byte op) {
1031 case IFEQ: return IFNE;
1032 case IFNE: return IFEQ;
1033 case IFLT: return IFGE;
1034 case IFGE: return IFLT;
1035 case IFGT: return IFLE;
1036 case IFLE: return IFGT;
1037 case IF_ICMPEQ: return IF_ICMPNE;
1038 case IF_ICMPNE: return IF_ICMPEQ;
1039 case IF_ICMPLT: return IF_ICMPGE;
1040 case IF_ICMPGE: return IF_ICMPLT;
1041 case IF_ICMPGT: return IF_ICMPLE;
1042 case IF_ICMPLE: return IF_ICMPGT;
1043 case IF_ACMPEQ: return IF_ACMPNE;
1044 case IF_ACMPNE: return IF_ACMPEQ;
1047 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));