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) 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();
323 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
324 public final void set(int pos, byte op) { this.op[pos] = op; }
326 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
327 @return The position of the new bytecode
329 public final int add(byte op, Object arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
331 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
332 @return The position of the new bytecode
335 public final int add(byte op, boolean arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
337 /** Adds a bytecode with an integer argument. This is equivalent
338 * to add(op, new Integer(arg)), but optimized to prevent the
339 * allocation when possible
340 @return The position of the new bytecode
341 @see #add(byte, Object)
343 public final int add(byte op, int arg) { if (capacity == size) grow(); set(size, op, arg); return size++; }
345 /** Gets the bytecode at position <i>pos</i>
346 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
348 public final byte get(int pos) { return op[pos]; }
350 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
351 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
352 interal form when they are added. The value returned from this method for these instruction can be reused, but there
353 is no way to retrieve the original object
354 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
356 public final Object getArg(int pos) { return arg[pos]; }
358 /** Sets the argument for <i>pos</i> to <i>arg</i>. This is
359 * equivalent to set(pos, op, new Integer(arg)), but optimized to
360 * prevent the allocation when possible.
361 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
362 @see #setArg(int, Object) */
363 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
365 /** Sets the argument for <i>pos</i> to <i>arg</i>.
366 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() */
367 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
369 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
370 This is equivalent to set(pos, op, arg?1:0)
371 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
373 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
375 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
376 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
377 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
378 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
380 public final void set(int pos, byte op, int n) {
385 case -1: op = ICONST_M1; break OUTER;
386 case 0: op = ICONST_0; break OUTER;
387 case 1: op = ICONST_1; break OUTER;
388 case 2: op = ICONST_2; break OUTER;
389 case 3: op = ICONST_3; break OUTER;
390 case 4: op = ICONST_4; break OUTER;
391 case 5: op = ICONST_5; break OUTER;
393 if (n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
394 else if (n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
397 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
398 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
399 if (n >= maxLocals) maxLocals = n + 1;
400 if (n >= 0 && n <= 3) {
403 case ILOAD: base = ILOAD_0; break;
404 case ISTORE: base = ISTORE_0; break;
405 case LLOAD: base = LLOAD_0; break;
406 case LSTORE: base = LSTORE_0; break;
407 case FLOAD: base = FLOAD_0; break;
408 case FSTORE: base = FSTORE_0; break;
409 case DLOAD: base = DLOAD_0; break;
410 case DSTORE: base = DSTORE_0; break;
411 case ALOAD: base = ALOAD_0; break;
412 case ASTORE: base = ASTORE_0; break;
414 op = (byte)((base&0xff) + n);
427 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
428 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
430 public final void set(int pos, byte op, Object arg) {
432 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
433 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
434 // set(int, byte, int) always handles these ops itself
435 set(pos, op, ((Integer)arg).intValue());
438 // set(int, byte, int) always handles these opts itself
439 if (arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
440 if (arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
442 if (arg instanceof Long) {
443 long l = ((Long)arg).longValue();
444 if (l == 0L || l == 1L) {
445 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
446 this.arg[pos] = null;
450 } else if (arg instanceof Double) {
455 if ((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
460 /** Sets the maximum number of locals in the function to
461 <i>maxLocals</i>. NOTE: This defaults to 0 and is
462 automatically increased as necessary when *LOAD/*STORE
463 bytecodes are added. You do not need to call this function in
465 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
467 /** Sets the maxinum size of th stack for this function to
468 * <i>maxStack</i>. This defaults to 16< */
469 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
472 // Bytecode-Specific inner classes ////////////////////////////////////////////////////////////////////////////////
474 public static abstract class Switch {
475 public final Object[] targets;
476 public Object defaultTarget;
478 Switch(int size) { targets = new Object[size]; }
479 public void setTarget(int pos, Object val) { targets[pos] = val; }
480 public void setTarget(int pos, int val) { targets[pos] = N(val); }
481 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
482 public void setDefaultTarget(Object o) { defaultTarget = o; }
483 public int size() { return targets.length; }
485 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
486 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
488 abstract int length();
490 public static class Table extends Switch {
493 public Table(int lo, int hi) {
498 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
499 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
501 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
504 public static class Lookup extends Switch {
505 public final int[] vals;
506 public Lookup(int size) {
508 this.vals = new int[size];
510 public final void setVal(int pos, int val) { vals[pos] = val; }
512 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
516 /** This class represents the arguments to byecodes that take two integer arguments. */
517 public static class Pair {
520 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
523 public static class MultiANewArray {
524 public Type.Class type;
526 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
529 public static class Wide {
530 public final byte op;
531 public final int varNum;
533 Wide(byte op, int varNum) { this(op, varNum, 0); }
534 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
538 // Emitting Bits //////////////////////////////////////////////////////////////////////////////
540 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
541 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
542 public static byte negate(byte op) {
544 case IFEQ: return IFNE;
545 case IFNE: return IFEQ;
546 case IFLT: return IFGE;
547 case IFGE: return IFLT;
548 case IFGT: return IFLE;
549 case IFLE: return IFGT;
550 case IF_ICMPEQ: return IF_ICMPNE;
551 case IF_ICMPNE: return IF_ICMPEQ;
552 case IF_ICMPLT: return IF_ICMPGE;
553 case IF_ICMPGE: return IF_ICMPLT;
554 case IF_ICMPGT: return IF_ICMPLE;
555 case IF_ICMPLE: return IF_ICMPGT;
556 case IF_ACMPEQ: return IF_ACMPNE;
557 case IF_ACMPNE: return IF_ACMPEQ;
560 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
564 private Object resolveTarget(Object arg) {
566 if (arg instanceof PhantomTarget) {
567 target = ((PhantomTarget)arg).getTarget();
568 if (target == -1) throw new IllegalStateException("unresolved phantom target");
571 target = ((Integer)arg).intValue();
573 if (target < 0 || target >= size)
574 throw new IllegalStateException("invalid target address " + target + "/" + size);
578 /** Computes the final bytecode for this method.
579 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
580 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
582 void finish(ConstantPool cp) {
583 cp.addUtf8(method.name);
584 cp.addUtf8(method.getDescriptor());
586 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
587 cp.add(e.nextElement());
589 if (size == NO_CODE) return;
590 for(int i=0;i<exnTable.size();i++)
591 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
593 // We'll set these correctly later
594 if ((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
595 if (thrownExceptions.size() > 0) attrs.put("Exceptions","");
597 codeAttrs.finish(cp);
599 cparg = new ConstantPool.Ent[size];
601 for(int i=0, p=0;i<size;i++) {
617 cparg[i] = cp.add(arg[i]);
619 case INVOKEINTERFACE:
620 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
623 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
629 private void generateCode(ConstantPool cp) {
632 } catch(IOException e) {
633 throw new Error("should never happen");
637 private void _generateCode(ConstantPool cp) throws IOException {
638 ByteArrayOutputStream baos = new ByteArrayOutputStream();
639 DataOutput o = new DataOutputStream(baos);
641 int[] pc = new int[size];
645 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
646 for(i=0, p=0;i<size;i++) {
647 byte op = this.op[i];
648 int opdata = OP_DATA[op&0xff];
652 if ((opdata & OP_BRANCH_FLAG)!= 0) {
654 arg[i] = resolveTarget(arg[i]);
655 } catch(RuntimeException e) {
656 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
662 // Speical caculations
665 int arg = ((Integer)this.arg[i]).intValue();
666 if (arg < i && p - maxpc[arg] <= 32768) p += 3;
675 Switch si = (Switch) arg[i];
676 Object[] targets = si.targets;
677 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
678 si.defaultTarget = resolveTarget(si.defaultTarget);
679 p += 1 + 3 + si.length(); // opcode itself, padding, data
680 if (op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
681 int[] vals = ((Switch.Lookup)si).vals;
682 for(j=1;j<vals.length;j++)
683 if (vals[j] <= vals[j-1])
684 throw new IllegalStateException("out of order/duplicate lookupswitch values");
689 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
692 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
693 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
695 int arg = ((Integer)this.arg[i]).intValue();
698 this.arg[i] = new Wide(op, arg);
705 Pair pair = (Pair) this.arg[i];
706 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
708 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
715 j = cp.getIndex(cparg[i]);
717 this.op[i] = op = LDC_W;
724 if ((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
728 // Pass2 - Widen instructions if they can possibly be too short
729 for(i=0;i<size;i++) {
733 int arg = ((Integer)this.arg[i]).intValue();
734 int diff = maxpc[arg] - maxpc[i];
735 if (diff < -32768 || diff > 32767)
736 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
742 // Pass3 - Calculate actual pc
743 for(i=0, p=0;i<size;i++) {
744 byte op = this.op[i];
752 Switch si = (Switch) arg[i];
753 p++; // opcode itself
754 p = (p + 3) & ~3; // padding
756 if (op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
757 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
761 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
764 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
765 if (l == 7) throw new Error("shouldn't be here");
772 if (codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
774 o.writeShort(maxStack);
775 o.writeShort(maxLocals);
776 o.writeInt(codeSize);
778 // Pass 4 - Actually write the bytecodes
779 for(i=0;i<size;i++) {
780 byte op = this.op[i];
781 int opdata = OP_DATA[op&0xff];
782 if (op == NOP && !EMIT_NOPS) continue;
784 int argLength = opdata & OP_ARG_LENGTH_MASK;
786 if (argLength == 0) continue; // skip if no args
789 Object arg = this.arg[i];
793 Pair pair = (Pair) arg;
794 if (pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
795 o.writeByte(pair.i1);
796 o.writeByte(pair.i2);
801 Switch si = (Switch) arg;
803 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
804 o.writeInt(pc[si.getDefaultTarget()] - mypc);
805 if (op == LOOKUPSWITCH) {
806 int[] vals = ((Switch.Lookup)si).vals;
807 o.writeInt(si.size());
808 for(int j=0;j<si.size();j++) {
810 o.writeInt(pc[si.getTarget(j)] - mypc);
813 Switch.Table tsi = (Switch.Table) si;
816 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
821 Wide wide = (Wide) arg;
822 o.writeByte(wide.op);
823 o.writeShort(wide.varNum);
824 if (wide.op == IINC) o.writeShort(wide.n);
827 case MULTIANEWARRAY: {
828 o.writeShort(cp.getIndex(cparg[i]));
829 int v = ((MultiANewArray) arg).dims;
830 if (v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
834 case INVOKEINTERFACE:
835 o.writeShort(cp.getIndex(cparg[i]));
836 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
840 if ((opdata & OP_BRANCH_FLAG) != 0) {
841 int v = pc[((Integer)arg).intValue()] - pc[i];
842 if (argLength == 2) {
843 if (v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
845 } else if (argLength == 4) {
848 throw new Error("should never happen");
850 } else if ((opdata & OP_CPENT_FLAG) != 0) {
851 int v = cp.getIndex(cparg[i]);
852 if (argLength == 1) o.writeByte(v);
853 else if (argLength == 2) o.writeShort(v);
854 else throw new Error("should never happen");
855 } else if (argLength == 7) {
856 throw new Error("should never happen - variable length instruction not explicitly handled");
858 int iarg = ((Integer)arg).intValue();
859 if (argLength == 1) {
860 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128))
861 throw new ClassFile.Exn("overflow of s/u1 option");
863 } else if (argLength == 2) {
864 if ((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768))
865 throw new ClassFile.Exn("overflow of s/u2 option");
868 throw new Error("should never happen");
875 //if (baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
877 o.writeShort(exnTable.size());
878 for(i=0;i<exnTable.size();i++)
879 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
881 codeAttrs.dump(o,cp);
884 byte[] codeAttribute = baos.toByteArray();
885 attrs.put("Code", codeAttribute);
888 void generateExceptions(ConstantPool cp) throws IOException {
889 if (thrownExceptions.size() > 0) {
890 ByteArrayOutputStream baos = new ByteArrayOutputStream();
891 DataOutputStream o = new DataOutputStream(baos);
892 o.writeShort(thrownExceptions.size());
893 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
894 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
896 attrs.put("Exceptions", baos.toByteArray());
900 void dump(DataOutput o, ConstantPool cp) throws IOException {
901 if ((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
902 generateExceptions(cp);
905 o.writeShort(cp.getUtf8Index(method.name));
906 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
911 /** Class that represents a target that isn't currently know. The
912 target MUST be set with setTarget() before the classfile is
913 written. This class is more or less a mutable integer */
914 public static class PhantomTarget {
915 private int target = -1;
916 public void setTarget(int target) { this.target = target; }
917 public int getTarget() { return target; }
920 private static Integer N(int n) { return new Integer(n); }
921 private static Long N(long n) { return new Long(n); }
922 private static Float N(float f) { return new Float(f); }
923 private static Double N(double d) { return new Double(d); }
924 private static int max(int a, int b) { return a > b ? a : b; }
926 private static final int OP_BRANCH_FLAG = 1<<3;
927 private static final int OP_CPENT_FLAG = 1<<4;
928 private static final int OP_UNSIGNED_FLAG = 1<<5;
929 private static final int OP_VALID_FLAG = 1<<6;
930 private static final int OP_ARG_LENGTH_MASK = 7;
931 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
932 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
933 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
934 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
935 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
937 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
938 private static final byte[] OP_DATA = {
939 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
940 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
941 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
942 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
943 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
944 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
945 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
946 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
947 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
948 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
949 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
950 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
951 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
952 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
953 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
954 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
957 // Debugging //////////////////////////////////////////////////////////////////////////////
959 public void debugToString(StringBuffer sb, String constructorName) {
960 // This is intentionally a local variable so it can be removed by gcclass
961 final String[] OP_NAMES = new String[]{
962 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
963 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
964 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
965 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
966 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
967 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
968 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
969 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
970 "faload", "daload", "aaload", "baload", "caload", "saload",
971 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
972 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
973 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
974 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
975 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
976 "bastore", "castore", "sastore", "pop", "pop2", "dup",
977 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
978 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
979 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
980 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
981 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
982 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
983 "iand", "land", "ior", "lor", "ixor", "lxor",
984 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
985 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
986 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
987 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
988 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
989 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
990 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
991 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
992 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
993 "", "new", "newarray", "anewarray", "arraylength", "athrow",
994 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
995 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
996 "", "", "", "", "", "",
997 "", "", "", "", "", "",
998 "", "", "", "", "", "",
999 "", "", "", "", "", "",
1000 "", "", "", "", "", "",
1001 "", "", "", "", "", "",
1002 "", "", "", "", "", "",
1003 "", "", "", "", "", "",
1007 sb.append(" ").append(ClassFile.flagsToString(flags,false));
1008 sb.append(method.debugToString());
1009 if (thrownExceptions.size() > 0) {
1010 sb.append("throws");
1011 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
1012 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
1013 sb.setLength(sb.length()-1);
1016 if ((flags & (NATIVE|ABSTRACT))==0) {
1018 for(int i=0;i<size();i++) {
1020 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
1021 sb.append(i).append(": ");
1022 sb.append(OP_NAMES[op[i]&0xff]);
1024 if (arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
1025 else if (arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
1026 else if (arg[i] instanceof String) s = "\"" + s + "\"";
1027 else if (arg[i] != null) s = arg[i].toString();
1028 if (s != null) sb.append(" ").append(s);