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(args.length + (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) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
149 if ((opdata&OP_CPENT_FLAG)!=0) {
150 ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
152 Object key = ent.key();
158 case CONSTANT_INTEGER:
161 case CONSTANT_DOUBLE:
162 case CONSTANT_STRING:
166 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
173 if (tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
178 if (tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
184 if (tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
187 throw new Error("should never happen");
191 // treat everything else (including branches for now) as plain old ints
193 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
194 if (argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
195 else if (argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
196 else throw new Error("should never happen");
197 if ((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
205 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
206 for(int i=0;i<size();i++) {
211 Switch si = (Switch) arg[i];
213 int pos = map[si.getDefaultTarget()];
214 if (pos < 0) throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
215 si.setDefaultTarget(pos);
217 for(int j=0;j<si.size();j++) {
218 pos = map[si.getTarget(j)];
219 if (pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
225 if (OP_BRANCH(op[i])) {
226 int pos = map[((Integer)arg[i]).intValue()];
227 if (pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
236 // Exception Table //////////////////////////////////////////////////////////////////////////////
242 final Type.Class type; // null type means all exceptions (for finally)
244 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
245 int startPC = in.readUnsignedShort();
246 int endPC = in.readUnsignedShort();
247 int handlerPC = in.readUnsignedShort();
248 int index = in.readUnsignedShort();
249 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
250 int max = bytecodeMap.length;
251 if (startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
252 if (endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
253 if (handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
254 this.start = bytecodeMap[startPC];
255 this.end = bytecodeMap[endPC];
256 this.handler = bytecodeMap[handlerPC];
258 ExnTableEnt(int start, int end, int handler, Type.Class type) {
261 this.handler = handler;
264 void finish(ConstantPool cp) { if (type != null) cp.add(type); }
265 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
266 o.writeShort(pc[start]);
267 o.writeShort(end==pc.length ? endPC : pc[end]);
268 o.writeShort(pc[handler]);
269 o.writeShort(type == null ? 0 : cp.getIndex(type));
273 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
274 @param start The instruction to start at (inclusive)
275 @param end The instruction to end at (exclusive)
276 @param handler The instruction of the excepton handler
277 @param type The type of exception that is to be handled (MUST inherit from Throwable)
279 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
280 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
283 /** Adds a exception type that can be thrown from this method
284 NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown
285 @param type The type of exception that can be thrown
287 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
289 private final void grow() { if (size == capacity) grow(size+1); }
290 private final void grow(int newCap) {
291 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
292 if (newCap <= capacity) return;
293 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
295 byte[] op2 = new byte[newCap];
296 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
299 Object[] arg2 = new Object[newCap];
300 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
306 // Accessors //////////////////////////////////////////////////////////////////////////////
308 /** Returns the size (in instructions) of this method
309 @return The size of the method (in instructions)
311 public final int size() { return size; }
313 // These two are optimized for speed, they don't call set() below
314 /** Add a bytecode (with no argument) to the method */
315 public final int add(byte op) {
317 if (s == capacity) grow();
322 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
323 public final void set(int pos, byte op) { this.op[pos] = op; }
325 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
326 @return The position of the new bytecode
328 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
329 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
330 @return The position of the new bytecode
333 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
334 /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
335 @return The position of the new bytecode
336 @see #add(byte, Object)
338 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
340 /** Gets the bytecode at position <i>pos</i>
341 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
343 public final byte get(int pos) { return op[pos]; }
345 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
346 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
347 interal form when they are added. The value returned from this method for these instruction can be reused, but there
348 is no way to retrieve the original object
349 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
351 public final Object getArg(int pos) { return arg[pos]; }
353 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is equivalent to set(pos, op, new Integer(arg)), but optimized to prevent the allocation when possible.
354 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
355 @see #setArg(int, Object) */
356 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
357 /** Sets the argument for <i>pos</i> to <i>arg</i>.
358 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
360 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
362 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
363 This is equivalent to set(pos, op, arg?1:0)
364 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
366 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
368 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
369 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
370 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
371 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
373 public final void set(int pos, byte op, int n) {
378 case -1: op = ICONST_M1; break OUTER;
379 case 0: op = ICONST_0; break OUTER;
380 case 1: op = ICONST_1; break OUTER;
381 case 2: op = ICONST_2; break OUTER;
382 case 3: op = ICONST_3; break OUTER;
383 case 4: op = ICONST_4; break OUTER;
384 case 5: op = ICONST_5; break OUTER;
386 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
387 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
390 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
391 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
392 if (n >= maxLocals) maxLocals = n + 1;
393 if (n >= 0 && n <= 3) {
396 case ILOAD: base = ILOAD_0; break;
397 case ISTORE: base = ISTORE_0; break;
398 case LLOAD: base = LLOAD_0; break;
399 case LSTORE: base = LSTORE_0; break;
400 case FLOAD: base = FLOAD_0; break;
401 case FSTORE: base = FSTORE_0; break;
402 case DLOAD: base = DLOAD_0; break;
403 case DSTORE: base = DSTORE_0; break;
404 case ALOAD: base = ALOAD_0; break;
405 case ASTORE: base = ASTORE_0; break;
407 op = (byte)((base&0xff) + n);
420 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
421 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
423 public final void set(int pos, byte op, Object arg) {
425 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
426 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
427 // set(int, byte, int) always handles these ops itself
428 set(pos, op, ((Integer)arg).intValue());
431 // set(int, byte, int) always handles these opts itself
432 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
433 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
435 if (arg instanceof Long) {
436 long l = ((Long)arg).longValue();
437 if (l == 0L || l == 1L) {
438 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
439 this.arg[pos] = null;
443 } else if (arg instanceof Double) {
448 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
453 public static abstract class Switch {
454 public final Object[] targets;
455 public Object defaultTarget;
457 Switch(int size) { targets = new Object[size]; }
458 public void setTarget(int pos, Object val) { targets[pos] = val; }
459 public void setTarget(int pos, int val) { targets[pos] = N(val); }
460 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
461 public void setDefaultTarget(Object o) { defaultTarget = o; }
462 public int size() { return targets.length; }
464 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
465 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
467 abstract int length();
469 public static class Table extends Switch {
472 public Table(int lo, int hi) {
477 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
478 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
480 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
483 public static class Lookup extends Table {
484 public final int[] vals;
485 public Lookup(int size) {
487 this.vals = new int[size];
489 public final void setVal(int pos, int val) { vals[pos] = val; }
491 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
495 /** This class represents the arguments to byecodes that take two integer arguments. */
496 public static class Pair {
499 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
502 public static class MultiANewArray {
503 public Type.Class type;
505 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
508 public static class Wide {
509 public final byte op;
510 public final int varNum;
512 Wide(byte op, int varNum) { this(op, varNum, 0); }
513 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
516 /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
517 necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
518 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
519 /** Sets the maxinum size of th stack for this function to <i>maxStack</i>. This defaults to 16< */
520 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
522 /** Computes the final bytecode for this method.
523 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
524 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
526 void finish(ConstantPool cp) {
527 cp.addUtf8(method.name);
528 cp.addUtf8(method.getDescriptor());
530 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
531 cp.add(e.nextElement());
533 if (size == NO_CODE) return;
534 for(int i=0;i<exnTable.size();i++)
535 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
537 // We'll set these correctly later
538 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
539 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
541 codeAttrs.finish(cp);
543 cparg = new ConstantPool.Ent[size];
545 for(int i=0, p=0;i<size;i++) {
561 cparg[i] = cp.add(arg[i]);
563 case INVOKEINTERFACE:
564 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
567 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
573 private Object resolveTarget(Object arg) {
575 if (arg instanceof PhantomTarget) {
576 target = ((PhantomTarget)arg).getTarget();
577 if (target == -1) throw new IllegalStateException("unresolved phantom target");
580 target = ((Integer)arg).intValue();
582 if (target < 0 || target >= size)
583 throw new IllegalStateException("invalid target address " + target + "/" + size);
587 private void generateCode(ConstantPool cp) {
590 } catch(IOException e) {
591 throw new Error("should never happen");
595 private void _generateCode(ConstantPool cp) throws IOException {
596 ByteArrayOutputStream baos = new ByteArrayOutputStream();
597 DataOutput o = new DataOutputStream(baos);
599 int[] pc = new int[size];
603 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
604 for(i=0, p=0;i<size;i++) {
605 byte op = this.op[i];
606 int opdata = OP_DATA[op&0xff];
610 if ((opdata & OP_BRANCH_FLAG)!= 0) {
612 arg[i] = resolveTarget(arg[i]);
613 } catch(RuntimeException e) {
614 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
620 // Speical caculations
623 int arg = ((Integer)this.arg[i]).intValue();
624 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
633 Switch si = (Switch) arg[i];
634 Object[] targets = si.targets;
635 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
636 si.defaultTarget = resolveTarget(si.defaultTarget);
637 p += 1 + 3 + si.length(); // opcode itself, padding, data
638 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
639 int[] vals = ((Switch.Lookup)si).vals;
640 for(j=1;j<vals.length;j++)
641 if (vals[j] <= vals[j-1])
642 throw new IllegalStateException("out of order/duplicate lookupswitch values");
647 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
650 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
651 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
653 int arg = ((Integer)this.arg[i]).intValue();
656 this.arg[i] = new Wide(op, arg);
663 Pair pair = (Pair) this.arg[i];
664 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
666 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
673 j = cp.getIndex(cparg[i]);
675 this.op[i] = op = LDC_W;
682 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
686 // Pass2 - Widen instructions if they can possibly be too short
687 for(i=0;i<size;i++) {
691 int arg = ((Integer)this.arg[i]).intValue();
692 int diff = maxpc[arg] - maxpc[i];
693 if (diff < -32768 || diff > 32767)
694 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
700 // Pass3 - Calculate actual pc
701 for(i=0, p=0;i<size;i++) {
702 byte op = this.op[i];
710 Switch si = (Switch) arg[i];
711 p++; // opcode itself
712 p = (p + 3) & ~3; // padding
714 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
715 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
719 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
722 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
723 if (l == 7) throw new Error("shouldn't be here");
730 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
732 o.writeShort(maxStack);
733 o.writeShort(maxLocals);
734 o.writeInt(codeSize);
736 // Pass 4 - Actually write the bytecodes
737 for(i=0;i<size;i++) {
738 byte op = this.op[i];
739 int opdata = OP_DATA[op&0xff];
740 if (op == NOP && !EMIT_NOPS) continue;
742 int argLength = opdata & OP_ARG_LENGTH_MASK;
744 if (argLength == 0) continue; // skip if no args
747 Object arg = this.arg[i];
751 Pair pair = (Pair) arg;
752 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
753 o.writeByte(pair.i1);
754 o.writeByte(pair.i2);
759 Switch si = (Switch) arg;
761 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
762 o.writeInt(pc[si.getDefaultTarget()] - mypc);
763 if (op == LOOKUPSWITCH) {
764 int[] vals = ((Switch.Lookup)si).vals;
765 o.writeInt(si.size());
766 for(int j=0;j<si.size();j++) {
768 o.writeInt(pc[si.getTarget(j)] - mypc);
771 Switch.Table tsi = (Switch.Table) si;
774 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
779 Wide wide = (Wide) arg;
780 o.writeByte(wide.op);
781 o.writeShort(wide.varNum);
782 if (wide.op == IINC) o.writeShort(wide.n);
785 case MULTIANEWARRAY: {
786 o.writeShort(cp.getIndex(cparg[i]));
787 int v = ((MultiANewArray) arg).dims;
788 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
792 case INVOKEINTERFACE:
793 o.writeShort(cp.getIndex(cparg[i]));
794 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
798 if ((opdata & OP_BRANCH_FLAG) != 0) {
799 int v = pc[((Integer)arg).intValue()] - pc[i];
800 if (argLength == 2) {
801 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
803 } else if (argLength == 4) {
806 throw new Error("should never happen");
808 } else if ((opdata & OP_CPENT_FLAG) != 0) {
809 int v = cp.getIndex(cparg[i]);
810 if (argLength == 1) o.writeByte(v);
811 else if (argLength == 2) o.writeShort(v);
812 else throw new Error("should never happen");
813 } else if (argLength == 7) {
814 throw new Error("should never happen - variable length instruction not explicitly handled");
816 int iarg = ((Integer)arg).intValue();
817 if (argLength == 1) {
818 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128)) throw new ClassFile.Exn("overflow of s/u1 option");
820 } else if (argLength == 2) {
821 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768)) throw new ClassFile.Exn("overflow of s/u2 option");
824 throw new Error("should never happen");
831 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
833 o.writeShort(exnTable.size());
834 for(i=0;i<exnTable.size();i++)
835 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
837 codeAttrs.dump(o,cp);
840 byte[] codeAttribute = baos.toByteArray();
841 attrs.put("Code", codeAttribute);
844 void generateExceptions(ConstantPool cp) throws IOException {
845 if (thrownExceptions.size() > 0) {
846 ByteArrayOutputStream baos = new ByteArrayOutputStream();
847 DataOutputStream o = new DataOutputStream(baos);
848 o.writeShort(thrownExceptions.size());
849 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
850 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
852 attrs.put("Exceptions", baos.toByteArray());
856 void dump(DataOutput o, ConstantPool cp) throws IOException {
857 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
858 generateExceptions(cp);
861 o.writeShort(cp.getUtf8Index(method.name));
862 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
866 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
867 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
868 public static byte negate(byte op) {
870 case IFEQ: return IFNE;
871 case IFNE: return IFEQ;
872 case IFLT: return IFGE;
873 case IFGE: return IFLT;
874 case IFGT: return IFLE;
875 case IFLE: return IFGT;
876 case IF_ICMPEQ: return IF_ICMPNE;
877 case IF_ICMPNE: return IF_ICMPEQ;
878 case IF_ICMPLT: return IF_ICMPGE;
879 case IF_ICMPGE: return IF_ICMPLT;
880 case IF_ICMPGT: return IF_ICMPLE;
881 case IF_ICMPLE: return IF_ICMPGT;
882 case IF_ACMPEQ: return IF_ACMPNE;
883 case IF_ACMPNE: return IF_ACMPEQ;
886 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
890 /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written.
891 This class is more or less a mutable integer */
892 public static class PhantomTarget {
893 private int target = -1;
894 public void setTarget(int target) { this.target = target; }
895 public int getTarget() { return target; }
898 private static Integer N(int n) { return new Integer(n); }
899 private static Long N(long n) { return new Long(n); }
900 private static Float N(float f) { return new Float(f); }
901 private static Double N(double d) { return new Double(d); }
902 private static int max(int a, int b) { return a > b ? a : b; }
904 private static final int OP_BRANCH_FLAG = 1<<3;
905 private static final int OP_CPENT_FLAG = 1<<4;
906 private static final int OP_UNSIGNED_FLAG = 1<<5;
907 private static final int OP_VALID_FLAG = 1<<6;
908 private static final int OP_ARG_LENGTH_MASK = 7;
909 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
910 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
911 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
912 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
913 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
915 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
916 private static final byte[] OP_DATA = {
917 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
918 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
919 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
920 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
921 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
922 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
923 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
924 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
925 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
926 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
927 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
928 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
929 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
930 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
931 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
932 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
935 // Debugging //////////////////////////////////////////////////////////////////////////////
937 public void debugToString(StringBuffer sb, String constructorName) {
938 // This is intentionally a local variable so it can be removed by gcclass
939 final String[] OP_NAMES = new String[]{
940 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
941 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
942 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
943 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
944 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
945 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
946 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
947 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
948 "faload", "daload", "aaload", "baload", "caload", "saload",
949 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
950 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
951 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
952 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
953 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
954 "bastore", "castore", "sastore", "pop", "pop2", "dup",
955 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
956 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
957 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
958 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
959 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
960 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
961 "iand", "land", "ior", "lor", "ixor", "lxor",
962 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
963 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
964 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
965 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
966 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
967 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
968 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
969 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
970 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
971 "", "new", "newarray", "anewarray", "arraylength", "athrow",
972 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
973 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
974 "", "", "", "", "", "",
975 "", "", "", "", "", "",
976 "", "", "", "", "", "",
977 "", "", "", "", "", "",
978 "", "", "", "", "", "",
979 "", "", "", "", "", "",
980 "", "", "", "", "", "",
981 "", "", "", "", "", "",
985 sb.append(" ").append(ClassFile.flagsToString(flags,false));
986 sb.append(method.debugToString());
987 if (thrownExceptions.size() > 0) {
989 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
990 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
991 sb.setLength(sb.length()-1);
994 if ((flags & (NATIVE|ABSTRACT))==0) {
996 for(int i=0;i<size();i++) {
998 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
999 sb.append(i).append(": ");
1000 sb.append(OP_NAMES[op[i]&0xff]);
1002 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1003 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1004 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1005 else if (arg[i] != null) s = arg[i].toString();
1006 if (s != null) sb.append(" ").append(s);