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 only. A method can throw exceptions not declared to be thrown
295 @param type The type of exception that can be thrown
297 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
299 private final void grow() { if (size == capacity) grow(size+1); }
300 private final void grow(int newCap) {
301 if (capacity == NO_CODE) throw new IllegalStateException("method can't have code");
302 if (newCap <= capacity) return;
303 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
305 byte[] op2 = new byte[newCap];
306 if (capacity != 0) System.arraycopy(op, 0, op2, 0, size);
309 Object[] arg2 = new Object[newCap];
310 if (capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
316 // Accessors //////////////////////////////////////////////////////////////////////////////
318 /** Returns the size (in instructions) of this method
319 @return The size of the method (in instructions)
321 public final int size() { return size; }
323 // These two are optimized for speed, they don't call set() below
324 /** Add a bytecode (with no argument) to the method */
325 public final int add(byte op) {
327 if (s == capacity) grow();
333 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
334 public final void set(int pos, byte op) { this.op[pos] = op; }
336 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
337 @return The position of the new bytecode
339 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
341 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
342 @return The position of the new bytecode
345 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
347 /** Adds a bytecode with an integer argument. This is equivalent
348 * to add(op, new Integer(arg)), but optimized to prevent the
349 * allocation when possible
350 @return The position of the new bytecode
351 @see #add(byte, Object)
353 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
355 /** Gets the bytecode at position <i>pos</i>
356 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
358 public final byte get(int pos) { return op[pos]; }
360 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
361 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
362 interal form when they are added. The value returned from this method for these instruction can be reused, but there
363 is no way to retrieve the original object
364 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
366 public final Object getArg(int pos) { return arg[pos]; }
368 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
369 * equivalent to set(pos, op, new Integer(arg)), but optimized to
370 * prevent the allocation when possible.
371 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
372 @see #setArg(int, Object) */
373 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
375 /** Sets the argument for <i>pos</i> to <i>arg</i>.
376 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
377 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
379 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
380 This is equivalent to set(pos, op, arg?1:0)
381 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
383 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
385 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
386 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
387 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
388 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
390 public final void set(int pos, byte op, int n) {
395 case -1: op = ICONST_M1; break OUTER;
396 case 0: op = ICONST_0; break OUTER;
397 case 1: op = ICONST_1; break OUTER;
398 case 2: op = ICONST_2; break OUTER;
399 case 3: op = ICONST_3; break OUTER;
400 case 4: op = ICONST_4; break OUTER;
401 case 5: op = ICONST_5; break OUTER;
403 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
404 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
407 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
408 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
409 if (n >= maxLocals) maxLocals = n + 1;
410 if (n >= 0 && n <= 3) {
413 case ILOAD: base = ILOAD_0; break;
414 case ISTORE: base = ISTORE_0; break;
415 case LLOAD: base = LLOAD_0; break;
416 case LSTORE: base = LSTORE_0; break;
417 case FLOAD: base = FLOAD_0; break;
418 case FSTORE: base = FSTORE_0; break;
419 case DLOAD: base = DLOAD_0; break;
420 case DSTORE: base = DSTORE_0; break;
421 case ALOAD: base = ALOAD_0; break;
422 case ASTORE: base = ASTORE_0; break;
424 op = (byte)((base&0xff) + n);
437 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
438 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
440 public final void set(int pos, byte op, Object arg) {
442 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
443 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
444 // set(int, byte, int) always handles these ops itself
445 set(pos, op, ((Integer)arg).intValue());
448 // set(int, byte, int) always handles these opts itself
449 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
450 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
452 if (arg instanceof Long) {
453 long l = ((Long)arg).longValue();
454 if (l == 0L || l == 1L) {
455 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
456 this.arg[pos] = null;
460 } else if (arg instanceof Double) {
465 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
470 /** Sets the maximum number of locals in the function to
471 <i>maxLocals</i>. NOTE: This defaults to 0 and is
472 automatically increased as necessary when *LOAD/*STORE
473 bytecodes are added. You do not need to call this function in
475 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
477 /** Sets the maxinum size of th stack for this function to
478 * <i>maxStack</i>. This defaults to 16< */
479 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
482 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
484 public static abstract class Switch {
485 public final Object[] targets;
486 public Object defaultTarget;
488 Switch(int size) { targets = new Object[size]; }
489 public void setTarget(int pos, Object val) { targets[pos] = val; }
490 public void setTarget(int pos, int val) { targets[pos] = N(val); }
491 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
492 public void setDefaultTarget(Object o) { defaultTarget = o; }
493 public int size() { return targets.length; }
495 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
496 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
498 abstract int length();
500 public static class Table extends Switch {
503 public Table(int lo, int hi) {
508 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
509 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
511 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
514 public static class Lookup extends Switch {
515 public final int[] vals;
516 public Lookup(int size) {
518 this.vals = new int[size];
520 public final void setVal(int pos, int val) { vals[pos] = val; }
522 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
526 /** This class represents the arguments to byecodes that take two integer arguments. */
527 public static class Pair {
530 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
533 public static class MultiANewArray {
534 public Type.Class type;
536 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
539 public static class Wide {
540 public final byte op;
541 public final int varNum;
543 Wide(byte op, int varNum) { this(op, varNum, 0); }
544 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
548 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
550 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
551 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
552 public static byte negate(byte op) {
554 case IFEQ: return IFNE;
555 case IFNE: return IFEQ;
556 case IFLT: return IFGE;
557 case IFGE: return IFLT;
558 case IFGT: return IFLE;
559 case IFLE: return IFGT;
560 case IF_ICMPEQ: return IF_ICMPNE;
561 case IF_ICMPNE: return IF_ICMPEQ;
562 case IF_ICMPLT: return IF_ICMPGE;
563 case IF_ICMPGE: return IF_ICMPLT;
564 case IF_ICMPGT: return IF_ICMPLE;
565 case IF_ICMPLE: return IF_ICMPGT;
566 case IF_ACMPEQ: return IF_ACMPNE;
567 case IF_ACMPNE: return IF_ACMPEQ;
570 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
574 private Object resolveTarget(Object arg) {
576 if (arg instanceof PhantomTarget) {
577 target = ((PhantomTarget)arg).getTarget();
578 if (target == -1) throw new IllegalStateException("unresolved phantom target");
581 target = ((Integer)arg).intValue();
583 if (target < 0 || target >= size)
584 throw new IllegalStateException("invalid target address " + target + "/" + size);
588 /** Computes the final bytecode for this method.
589 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
590 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
592 void finish(ConstantPool cp) {
593 cp.addUtf8(method.name);
594 cp.addUtf8(method.getDescriptor());
596 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
597 cp.add(e.nextElement());
599 if (size == NO_CODE) return;
600 for(int i=0;i<exnTable.size();i++)
601 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
603 // We'll set these correctly later
604 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
605 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
607 codeAttrs.finish(cp);
609 cparg = new ConstantPool.Ent[size];
611 for(int i=0, p=0;i<size;i++) {
627 cparg[i] = cp.add(arg[i]);
629 case INVOKEINTERFACE:
630 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
633 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
639 private void generateCode(ConstantPool cp) {
642 } catch(IOException e) {
643 throw new Error("should never happen");
647 private void _generateCode(ConstantPool cp) throws IOException {
648 ByteArrayOutputStream baos = new ByteArrayOutputStream();
649 DataOutput o = new DataOutputStream(baos);
651 int[] pc = new int[size];
655 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
656 for(i=0, p=0;i<size;i++) {
657 byte op = this.op[i];
658 int opdata = OP_DATA[op&0xff];
662 if ((opdata & OP_BRANCH_FLAG)!= 0) {
664 arg[i] = resolveTarget(arg[i]);
665 } catch(RuntimeException e) {
666 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
672 // Speical caculations
675 int arg = ((Integer)this.arg[i]).intValue();
676 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
685 Switch si = (Switch) arg[i];
686 Object[] targets = si.targets;
687 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
688 si.defaultTarget = resolveTarget(si.defaultTarget);
689 p += 1 + 3 + si.length(); // opcode itself, padding, data
690 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
691 int[] vals = ((Switch.Lookup)si).vals;
692 for(j=1;j<vals.length;j++)
693 if (vals[j] <= vals[j-1])
694 throw new IllegalStateException("out of order/duplicate lookupswitch values");
699 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
702 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
703 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
705 int arg = ((Integer)this.arg[i]).intValue();
708 this.arg[i] = new Wide(op, arg);
715 Pair pair = (Pair) this.arg[i];
716 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
718 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
725 j = cp.getIndex(cparg[i]);
727 this.op[i] = op = LDC_W;
734 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
738 // Pass2 - Widen instructions if they can possibly be too short
739 for(i=0;i<size;i++) {
743 int arg = ((Integer)this.arg[i]).intValue();
744 int diff = maxpc[arg] - maxpc[i];
745 if (diff < -32768 || diff > 32767)
746 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
752 // Pass3 - Calculate actual pc
753 for(i=0, p=0;i<size;i++) {
754 byte op = this.op[i];
762 Switch si = (Switch) arg[i];
763 p++; // opcode itself
764 p = (p + 3) & ~3; // padding
766 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
767 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
771 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
774 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
775 if (l == 7) throw new Error("shouldn't be here");
782 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
784 o.writeShort(maxStack);
785 o.writeShort(maxLocals);
786 o.writeInt(codeSize);
788 // Pass 4 - Actually write the bytecodes
789 for(i=0;i<size;i++) {
790 byte op = this.op[i];
791 int opdata = OP_DATA[op&0xff];
792 if (op == NOP && !EMIT_NOPS) continue;
794 int argLength = opdata & OP_ARG_LENGTH_MASK;
796 if (argLength == 0) continue; // skip if no args
799 Object arg = this.arg[i];
803 Pair pair = (Pair) arg;
804 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
805 o.writeByte(pair.i1);
806 o.writeByte(pair.i2);
811 Switch si = (Switch) arg;
813 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
814 o.writeInt(pc[si.getDefaultTarget()] - mypc);
815 if (op == LOOKUPSWITCH) {
816 int[] vals = ((Switch.Lookup)si).vals;
817 o.writeInt(si.size());
818 for(int j=0;j<si.size();j++) {
820 o.writeInt(pc[si.getTarget(j)] - mypc);
823 Switch.Table tsi = (Switch.Table) si;
826 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
831 Wide wide = (Wide) arg;
832 o.writeByte(wide.op);
833 o.writeShort(wide.varNum);
834 if (wide.op == IINC) o.writeShort(wide.n);
837 case MULTIANEWARRAY: {
838 o.writeShort(cp.getIndex(cparg[i]));
839 int v = ((MultiANewArray) arg).dims;
840 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
844 case INVOKEINTERFACE:
845 o.writeShort(cp.getIndex(cparg[i]));
846 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
850 if ((opdata & OP_BRANCH_FLAG) != 0) {
851 int v = pc[((Integer)arg).intValue()] - pc[i];
852 if (argLength == 2) {
853 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
855 } else if (argLength == 4) {
858 throw new Error("should never happen");
860 } else if ((opdata & OP_CPENT_FLAG) != 0) {
861 int v = cp.getIndex(cparg[i]);
862 if (argLength == 1) o.writeByte(v);
863 else if (argLength == 2) o.writeShort(v);
864 else throw new Error("should never happen");
865 } else if (argLength == 7) {
866 throw new Error("should never happen - variable length instruction not explicitly handled");
868 int iarg = ((Integer)arg).intValue();
869 if (argLength == 1) {
870 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
871 throw new ClassFile.Exn("overflow of s/u1 option");
873 } else if (argLength == 2) {
874 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
875 throw new ClassFile.Exn("overflow of s/u2 option");
878 throw new Error("should never happen");
885 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
887 o.writeShort(exnTable.size());
888 for(i=0;i<exnTable.size();i++)
889 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
891 codeAttrs.dump(o,cp);
894 byte[] codeAttribute = baos.toByteArray();
895 attrs.put("Code", codeAttribute);
898 void generateExceptions(ConstantPool cp) throws IOException {
899 if (thrownExceptions.size() > 0) {
900 ByteArrayOutputStream baos = new ByteArrayOutputStream();
901 DataOutputStream o = new DataOutputStream(baos);
902 o.writeShort(thrownExceptions.size());
903 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
904 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
906 attrs.put("Exceptions", baos.toByteArray());
910 void dump(DataOutput o, ConstantPool cp) throws IOException {
911 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
912 generateExceptions(cp);
915 o.writeShort(cp.getUtf8Index(method.name));
916 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
921 /** Class that represents a target that isn't currently know. The
922 target MUST be set with setTarget() before the classfile is
923 written. This class is more or less a mutable integer */
924 public static class PhantomTarget {
925 private int target = -1;
926 public void setTarget(int target) { this.target = target; }
927 public int getTarget() { return target; }
930 private static Integer N(int n) { return new Integer(n); }
931 private static Long N(long n) { return new Long(n); }
932 private static Float N(float f) { return new Float(f); }
933 private static Double N(double d) { return new Double(d); }
934 private static int max(int a, int b) { return a > b ? a : b; }
936 private static final int OP_BRANCH_FLAG = 1<<3;
937 private static final int OP_CPENT_FLAG = 1<<4;
938 private static final int OP_UNSIGNED_FLAG = 1<<5;
939 private static final int OP_VALID_FLAG = 1<<6;
940 private static final int OP_ARG_LENGTH_MASK = 7;
941 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
942 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
943 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
944 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
945 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
947 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
948 private static final byte[] OP_DATA = {
949 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
950 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
951 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
952 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
953 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
954 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
955 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
956 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
957 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
958 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
959 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
960 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
961 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
962 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
963 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
964 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
967 // Debugging //////////////////////////////////////////////////////////////////////////////
969 public void debugToString(StringBuffer sb, String constructorName) {
970 // This is intentionally a local variable so it can be removed by gcclass
971 final String[] OP_NAMES = new String[]{
972 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
973 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
974 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
975 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
976 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
977 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
978 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
979 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
980 "faload", "daload", "aaload", "baload", "caload", "saload",
981 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
982 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
983 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
984 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
985 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
986 "bastore", "castore", "sastore", "pop", "pop2", "dup",
987 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
988 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
989 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
990 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
991 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
992 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
993 "iand", "land", "ior", "lor", "ixor", "lxor",
994 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
995 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
996 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
997 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
998 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
999 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
1000 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
1001 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
1002 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
1003 "", "new", "newarray", "anewarray", "arraylength", "athrow",
1004 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
1005 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
1006 "", "", "", "", "", "",
1007 "", "", "", "", "", "",
1008 "", "", "", "", "", "",
1009 "", "", "", "", "", "",
1010 "", "", "", "", "", "",
1011 "", "", "", "", "", "",
1012 "", "", "", "", "", "",
1013 "", "", "", "", "", "",
1017 sb.append(" ").append(ClassFile.flagsToString(flags,false));
1018 sb.append(method.debugToString());
1019 if (thrownExceptions.size() > 0) {
1020 sb.append("throws");
1021 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
1022 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1023 sb.setLength(sb.length()-1);
1026 if ((flags & (NATIVE|ABSTRACT))==0) {
1028 for(int i=0;i<size();i++) {
1030 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1031 sb.append(i).append(": ");
1032 sb.append(OP_NAMES[op[i]&0xff]);
1034 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1035 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1036 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1037 else if (arg[i] != null) s = arg[i].toString();
1038 if (s != null) sb.append(" ").append(s);