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 private 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;
29 // Constructors //////////////////////////////////////////////////////////////////////////////
31 MethodGen(Type.Class.Method method, int flags) {
32 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
36 attrs = new ClassFile.AttrGen();
37 codeAttrs = new ClassFile.AttrGen();
39 if (((flags & INTERFACE) != 0) || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
41 maxLocals = Math.max(method.getNumArgs() + (flags&STATIC)==0 ? 1 : 0, 4);
44 MethodGen(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
45 this.flags = in.readShort();
46 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
47 String name = cp.getUtf8KeyByIndex(in.readShort());
48 this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
49 this.attrs = new ClassFile.AttrGen(in,cp);
51 if ((flags & (NATIVE|ABSTRACT))==0) {
52 byte[] codeAttr = (byte[]) attrs.get("Code");
53 if (codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
54 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
55 maxStack = ci.readUnsignedShort();
56 maxLocals = ci.readUnsignedShort();
57 int codeLen = ci.readInt();
58 int[] bytecodeMap = parseCode(ci,codeLen,cp);
59 int numExns = ci.readUnsignedShort();
61 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
62 codeAttrs = new ClassFile.AttrGen(ci,cp);
63 // FEATURE: Support these
64 // NOTE: Until we can support them properly we HAVE to delete them,
65 // they'll be incorrect after we rewrite the constant pool, etc
66 codeAttrs.remove("LineNumberTable");
67 codeAttrs.remove("LocalVariableTable");
70 codeAttrs = new ClassFile.AttrGen();
73 if (attrs.contains("Exceptions")) {
74 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
75 int exnCount = ei.readUnsignedShort();
76 while(exnCount-- > 0) {
77 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
78 thrownExceptions.put(t,t);
83 // Parsing //////////////////////////////////////////////////////////////////////////////
85 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
86 int[] map = new int[codeLen];
88 for(pc=0;pc<map.length;pc++) map[pc] = -1;
89 for(pc=0;pc<codeLen;) {
90 byte op = in.readByte();
91 int opdata = OP_DATA[op&0xff];
92 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
93 if ((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
94 int argLength = opdata & OP_ARG_LENGTH_MASK;
97 pc += 1 + (argLength == 7 ? 0 : argLength);
98 if (argLength == 0) { add(op); continue; }
102 arg = new Pair(in.readUnsignedByte(),in.readByte());
107 for(;(pc&3) != 0;pc++) if (in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
108 int def = in.readInt() + mypc;
110 if (op == LOOKUPSWITCH) {
111 Switch.Lookup lsi = new Switch.Lookup(in.readInt());
113 for(int i=0;i<lsi.size();i++) {
114 lsi.setVal(i,in.readInt());
115 lsi.setTarget(i,in.readInt() + mypc);
120 int lo = in.readInt();
121 int hi = in.readInt();
123 Switch.Table tsi = new Switch.Table(lo,hi);
124 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
127 si.setDefaultTarget(def);
131 byte wideop = in.readByte();
133 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
134 : new Wide(wideop,in.readUnsignedShort());
135 pc += wideop == IINC ? 5 : 3;
139 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
141 case INVOKEINTERFACE: {
142 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
143 if (ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
144 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
145 if (in.readByte() == 0 || in.readByte() != 0)
146 throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
150 if ((opdata&OP_CPENT_FLAG)!=0) {
151 ConstantPool.Ent ent =
152 cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
154 Object key = ent.key();
160 case CONSTANT_INTEGER:
163 case CONSTANT_DOUBLE:
164 case CONSTANT_STRING:
168 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
169 Integer.toString(op&0xff,16));
176 if (tag != CONSTANT_FIELDREF)
177 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
178 Integer.toString(op&0xff,16));
183 if (tag != CONSTANT_METHODREF)
184 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
185 Integer.toString(op&0xff,16));
191 if (tag != CONSTANT_CLASS)
192 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" +
193 Integer.toString(op&0xff,16));
196 throw new Error("should never happen");
200 // treat everything else (including branches for now) as plain old ints
202 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
203 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
204 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
205 else throw new Error("should never happen");
206 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
214 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
215 for(int i=0;i<size();i++) {
220 Switch si = (Switch) arg[i];
222 int pos = map[si.getDefaultTarget()];
224 throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
225 si.setDefaultTarget(pos);
227 for(int j=0;j<si.size();j++) {
228 pos = map[si.getTarget(j)];
229 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
235 if (OP_BRANCH(op[i])) {
236 int pos = map[((Integer)arg[i]).intValue()];
237 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
246 // Exception Table //////////////////////////////////////////////////////////////////////////////
252 final Type.Class type; // null type means all exceptions (for finally)
254 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
255 int startPC = in.readUnsignedShort();
256 int endPC = in.readUnsignedShort();
257 int handlerPC = in.readUnsignedShort();
258 int index = in.readUnsignedShort();
259 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
260 int max = bytecodeMap.length;
261 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
262 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
263 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
264 this.start = bytecodeMap[startPC];
265 this.end = bytecodeMap[endPC];
266 this.handler = bytecodeMap[handlerPC];
268 ExnTableEnt(int start, int end, int handler, Type.Class type) {
271 this.handler = handler;
274 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
275 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
276 o.writeShort(pc[start]);
277 o.writeShort(end==pc.length ? endPC : pc[end]);
278 o.writeShort(pc[handler]);
279 o.writeShort(type == null ? 0 : cp.getIndex(type));
283 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
284 @param start The instruction to start at (inclusive)
285 @param end The instruction to end at (exclusive)
286 @param handler The instruction of the excepton handler
287 @param type The type of exception that is to be handled (MUST inherit from Throwable)
289 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
290 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
293 /** Adds a exception type that can be thrown from this method
294 NOTE: This isn't enforced by the JVM. This is for reference
295 only. A method can throw exceptions not declared to be thrown
296 @param type The type of exception that can be thrown
298 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
300 private final void grow() { if (size == capacity) grow(size+1); }
301 private final void grow(int newCap) {
302 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
303 if (newCap <= capacity) return;
304 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
306 byte[] op2 = new byte[newCap];
307 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
310 Object[] arg2 = new Object[newCap];
311 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
317 // Accessors //////////////////////////////////////////////////////////////////////////////
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.getDescriptor());
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.getDescriptor()));
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 debugToString(StringBuffer sb, String constructorName) {
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 "", "", "", "", "", "",
994 sb.append(" ").append(ClassFile.flagsToString(flags,false));
995 sb.append(method.debugToString());
996 if (thrownExceptions.size() > 0) {
998 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
999 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1000 sb.setLength(sb.length()-1);
1003 if ((flags & (NATIVE|ABSTRACT))==0) {
1005 for(int i=0;i<size();i++) {
1007 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1008 sb.append(i).append(": ");
1009 sb.append(OP_NAMES[op[i]&0xff]);
1011 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1012 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1013 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1014 else if (arg[i] != null) s = arg[i].toString();
1015 if (s != null) sb.append(" ").append(s);
1024 // Unused //////////////////////////////////////////////////////////////////////////////
1026 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
1027 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
1028 public static byte negate(byte op) {
1030 case IFEQ: return IFNE;
1031 case IFNE: return IFEQ;
1032 case IFLT: return IFGE;
1033 case IFGE: return IFLT;
1034 case IFGT: return IFLE;
1035 case IFLE: return IFGT;
1036 case IF_ICMPEQ: return IF_ICMPNE;
1037 case IF_ICMPNE: return IF_ICMPEQ;
1038 case IF_ICMPLT: return IF_ICMPGE;
1039 case IF_ICMPGE: return IF_ICMPLT;
1040 case IF_ICMPGT: return IF_ICMPLE;
1041 case IF_ICMPLE: return IF_ICMPGT;
1042 case IF_ACMPEQ: return IF_ACMPNE;
1043 case IF_ACMPNE: return IF_ACMPEQ;
1046 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));