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 ClassFile cf;
14 private final String name;
15 private final Type ret;
16 private final Type[] args;
17 private final int flags;
18 private final ClassFile.AttrGen attrs;
19 private final ClassFile.AttrGen codeAttrs;
20 private final Vector exnTable = new Vector();
21 private final Hashtable thrownExceptions = new Hashtable();
23 private int maxStack = 16;
24 private int maxLocals;
30 private ConstantPool.Ent[] cparg;
32 public void debugToString(StringBuffer sb, String constructorName) {
33 // This is intentionally a local variable so it can be removed by gcclass
34 final String[] OP_NAMES = new String[]{
35 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
36 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
37 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
38 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
39 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
40 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
41 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
42 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
43 "faload", "daload", "aaload", "baload", "caload", "saload",
44 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
45 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
46 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
47 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
48 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
49 "bastore", "castore", "sastore", "pop", "pop2", "dup",
50 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
51 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
52 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
53 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
54 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
55 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
56 "iand", "land", "ior", "lor", "ixor", "lxor",
57 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
58 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
59 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
60 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
61 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
62 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
63 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
64 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
65 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
66 "", "new", "newarray", "anewarray", "arraylength", "athrow",
67 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
68 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
69 "", "", "", "", "", "",
70 "", "", "", "", "", "",
71 "", "", "", "", "", "",
72 "", "", "", "", "", "",
73 "", "", "", "", "", "",
74 "", "", "", "", "", "",
75 "", "", "", "", "", "",
76 "", "", "", "", "", "",
80 sb.append(" ").append(ClassFile.flagsToString(flags,false));
82 if (name.equals("<clinit>")) sb.append("static ");
84 if (name.equals("<init>"))
85 sb.append(constructorName);
87 sb.append(ret.debugToString()).append(" ").append(name);
89 for(int i=0; i<args.length; i++)
90 sb.append((i==0?"":", ")+args[i].debugToString());
92 if(thrownExceptions.size() > 0) {
94 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
95 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
96 sb.setLength(sb.length()-1);
100 if((flags & (NATIVE|ABSTRACT))==0) {
102 for(int i=0;i<size();i++) {
104 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
105 sb.append(i).append(": ");
106 sb.append(OP_NAMES[op[i]&0xff]);
108 if(arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
109 else if(arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
110 else if(arg[i] instanceof String) s = "\"" + s + "\"";
111 else if(arg[i] != null) s = arg[i].toString();
112 if(s != null) sb.append(" ").append(s);
121 MethodGen(ClassFile cf, DataInput in, ConstantPool cp, boolean ownerInterface) throws IOException {
123 flags = in.readShort();
124 if((flags & ~(PUBLIC|PRIVATE|PROTECTED|STATIC|FINAL|SYNCHRONIZED|NATIVE|ABSTRACT|STRICT)) != 0)
125 throw new ClassFile.ClassReadExn("invalid flags");
126 this.name = cp.getUtf8KeyByIndex(in.readShort());
127 //System.err.println("Processing " + name + "...");
128 Type.Class.Method m = cf.getType().method(name+cp.getUtf8KeyByIndex(in.readShort()));
129 this.ret = m.returnType;
130 this.args = new Type[m.getNumArgs()];
131 for(int i=0; i<args.length; i++) args[i] = m.getArgType(i);
132 this.attrs = new ClassFile.AttrGen(in,cp);
134 if((flags & (NATIVE|ABSTRACT))==0) {
135 byte[] codeAttr = (byte[]) attrs.get("Code");
136 if(codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
137 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
138 maxStack = ci.readUnsignedShort();
139 maxLocals = ci.readUnsignedShort();
140 int codeLen = ci.readInt();
141 int[] bytecodeMap = parseCode(ci,codeLen,cp);
142 int numExns = ci.readUnsignedShort();
144 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
145 codeAttrs = new ClassFile.AttrGen(ci,cp);
146 // FEATURE: Support these
147 // NOTE: Until we can support them properly we HAVE to delete them,
148 // they'll be incorrect after we rewrite the constant pool, etc
149 codeAttrs.remove("LineNumberTable");
150 codeAttrs.remove("LocalVariableTable");
153 codeAttrs = new ClassFile.AttrGen();
156 if(attrs.contains("Exceptions")) {
157 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
158 int exnCount = ei.readUnsignedShort();
159 while(exnCount-- > 0) {
160 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
161 thrownExceptions.put(t,t);
166 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
167 int[] map = new int[codeLen];
169 for(pc=0;pc<map.length;pc++) map[pc] = -1;
170 for(pc=0;pc<codeLen;) {
171 byte op = in.readByte();
172 int opdata = OP_DATA[op&0xff];
173 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
174 if((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
175 int argLength = opdata & OP_ARG_LENGTH_MASK;
178 pc += 1 + (argLength == 7 ? 0 : argLength);
179 if(argLength == 0) { add(op); continue; }
183 arg = new Pair(in.readUnsignedByte(),in.readByte());
188 for(;(pc&3) != 0;pc++) if(in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
189 int def = in.readInt() + mypc;
191 if(op == LOOKUPSWITCH) {
192 LSI lsi = new LSI(in.readInt());
194 for(int i=0;i<lsi.size();i++) {
195 lsi.setVal(i,in.readInt());
196 lsi.setTarget(i,in.readInt() + mypc);
201 int lo = in.readInt();
202 int hi = in.readInt();
204 TSI tsi = new TSI(lo,hi);
205 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
208 si.setDefaultTarget(def);
212 byte wideop = in.readByte();
214 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
215 : new Wide(wideop,in.readUnsignedShort());
216 pc += wideop == IINC ? 5 : 3;
220 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
222 case INVOKEINTERFACE: {
223 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
224 if(ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
225 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
226 if(in.readByte() == 0 || in.readByte() != 0) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
230 if((opdata&OP_CPENT_FLAG)!=0) {
231 ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
233 Object key = ent.key();
239 case CONSTANT_INTEGER:
242 case CONSTANT_DOUBLE:
243 case CONSTANT_STRING:
247 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
254 if(tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
259 if(tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
265 if(tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
268 throw new Error("should never happen");
272 // treat everything else (including branches for now) as plain old ints
274 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
275 if(argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
276 else if(argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
277 else throw new Error("should never happen");
278 if((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
285 if(pc != codeLen) throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + name + ")");
286 for(int i=0;i<size();i++) {
293 int pos = map[si.getDefaultTarget()];
294 if(pos < 0) throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
295 si.setDefaultTarget(pos);
297 for(int j=0;j<si.size();j++) {
298 pos = map[si.getTarget(j)];
299 if(pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
305 if(OP_BRANCH(op[i])) {
306 int pos = map[((Integer)arg[i]).intValue()];
307 if(pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
316 MethodGen(ClassFile cf, String name, Type ret, Type[] args, int flags, boolean ownerInterface) {
317 if((flags & ~(PUBLIC|PRIVATE|PROTECTED|STATIC|FINAL|SYNCHRONIZED|NATIVE|ABSTRACT|STRICT)) != 0)
318 throw new IllegalArgumentException("invalid flags");
325 attrs = new ClassFile.AttrGen();
326 codeAttrs = new ClassFile.AttrGen();
328 if(ownerInterface || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
330 maxLocals = Math.max(args.length + (flags&STATIC)==0 ? 1 : 0, 4);
337 final Type.Class type; // null type means all exceptions (for finally)
339 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
340 int startPC = in.readUnsignedShort();
341 int endPC = in.readUnsignedShort();
342 int handlerPC = in.readUnsignedShort();
343 int index = in.readUnsignedShort();
344 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
345 int max = bytecodeMap.length;
346 if(startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
347 if(endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
348 if(handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
349 this.start = bytecodeMap[startPC];
350 this.end = bytecodeMap[endPC];
351 this.handler = bytecodeMap[handlerPC];
353 ExnTableEnt(int start, int end, int handler, Type.Class type) {
356 this.handler = handler;
359 void finish(ConstantPool cp) { if(type != null) cp.add(type); }
360 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
361 o.writeShort(pc[start]);
362 o.writeShort(end==pc.length ? endPC : pc[end]);
363 o.writeShort(pc[handler]);
364 o.writeShort(type == null ? 0 : cp.getIndex(type));
368 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
369 @param start The instruction to start at (inclusive)
370 @param end The instruction to end at (exclusive)
371 @param handler The instruction of the excepton handler
372 @param type The type of exception that is to be handled (MUST inherit from Throwable)
374 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
375 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
378 /** Adds a exception type that can be thrown from this method
379 NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown
380 @param type The type of exception that can be thrown
382 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
384 private final void grow() { if(size == capacity) grow(size+1); }
385 private final void grow(int newCap) {
386 if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
387 if(newCap <= capacity) return;
388 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
390 byte[] op2 = new byte[newCap];
391 if(capacity != 0) System.arraycopy(op, 0, op2, 0, size);
394 Object[] arg2 = new Object[newCap];
395 if(capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
401 /** Returns the size (in instructions) of this method
402 @return The size of the method (in instructions)
404 public final int size() { return size; }
406 // These two are optimized for speed, they don't call set() below
407 /** Add a bytecode (with no argument) to the method */
408 public final int add(byte op) {
410 if(s == capacity) grow();
415 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
416 public final void set(int pos, byte op) { this.op[pos] = op; }
418 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
419 @return The position of the new bytecode
421 public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
422 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
423 @return The position of the new bytecode
426 public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
427 /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
428 @return The position of the new bytecode
429 @see #add(byte, Object)
431 public final int add(byte op, int arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
433 /** Gets the bytecode at position <i>pos</i>
434 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
436 public final byte get(int pos) { return op[pos]; }
438 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
439 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
440 interal form when they are added. The value returned from this method for these instruction can be reused, but there
441 is no way to retrieve the original object
442 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
444 public final Object getArg(int pos) { return arg[pos]; }
446 /** 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.
447 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
448 @see #setArg(int, Object) */
449 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
450 /** Sets the argument for <i>pos</i> to <i>arg</i>.
451 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
453 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
455 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
456 This is equivalent to set(pos, op, arg?1:0)
457 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
459 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
461 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
462 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
463 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
464 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
466 public final void set(int pos, byte op, int n) {
471 case -1: op = ICONST_M1; break OUTER;
472 case 0: op = ICONST_0; break OUTER;
473 case 1: op = ICONST_1; break OUTER;
474 case 2: op = ICONST_2; break OUTER;
475 case 3: op = ICONST_3; break OUTER;
476 case 4: op = ICONST_4; break OUTER;
477 case 5: op = ICONST_5; break OUTER;
479 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
480 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
483 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
484 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
485 if(n >= maxLocals) maxLocals = n + 1;
486 if(n >= 0 && n <= 3) {
489 case ILOAD: base = ILOAD_0; break;
490 case ISTORE: base = ISTORE_0; break;
491 case LLOAD: base = LLOAD_0; break;
492 case LSTORE: base = LSTORE_0; break;
493 case FLOAD: base = FLOAD_0; break;
494 case FSTORE: base = FSTORE_0; break;
495 case DLOAD: base = DLOAD_0; break;
496 case DSTORE: base = DSTORE_0; break;
497 case ALOAD: base = ALOAD_0; break;
498 case ASTORE: base = ASTORE_0; break;
500 op = (byte)((base&0xff) + n);
513 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
514 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
516 public final void set(int pos, byte op, Object arg) {
518 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
519 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
520 // set(int, byte, int) always handles these ops itself
521 set(pos, op, ((Integer)arg).intValue());
524 // set(int, byte, int) always handles these opts itself
525 if(arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
526 if(arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
528 if(arg instanceof Long) {
529 long l = ((Long)arg).longValue();
530 if(l == 0L || l == 1L) {
531 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
532 this.arg[pos] = null;
536 } else if(arg instanceof Double) {
541 if((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
546 /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
550 public static abstract class SI {
551 public final Object[] targets;
552 public Object defaultTarget;
554 SI(int size) { targets = new Object[size]; }
555 public void setTarget(int pos, Object val) { targets[pos] = val; }
556 public void setTarget(int pos, int val) { targets[pos] = N(val); }
557 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
558 public void setDefaultTarget(Object o) { defaultTarget = o; }
559 public int size() { return targets.length; }
561 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
562 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
564 abstract int length();
567 /** This class represents the arguments to the TABLESWITCH bytecode */
568 public static class TSI extends SI {
571 public TSI(int lo, int hi) {
576 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
577 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
579 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
582 /** This class represents the arguments to the LOOKUPSWITCH bytecode */
583 public static class LSI extends SI {
584 public final int[] vals;
585 public LSI(int size) {
587 this.vals = new int[size];
589 public final void setVal(int pos, int val) { vals[pos] = val; }
591 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
594 /** This class represents the arguments to byecodes that take two integer arguments. */
595 public static class Pair {
598 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
601 public static class MultiANewArray {
602 public Type.Class type;
604 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
607 public static class Wide {
608 public final byte op;
609 public final int varNum;
611 Wide(byte op, int varNum) { this(op, varNum, 0); }
612 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
615 /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
616 necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
617 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
618 /** Sets the maxinum size of th stack for this function to <i>maxStack</i>. This defaults to 16< */
619 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
621 /** Computes the final bytecode for this method.
622 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
623 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
625 void finish(ConstantPool cp) {
627 cp.addUtf8(Type.methodTypeDescriptor(args,ret));
629 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
630 cp.add(e.nextElement());
632 if(size == NO_CODE) return;
633 for(int i=0;i<exnTable.size();i++)
634 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
636 // We'll set these correctly later
637 if((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
638 if(thrownExceptions.size() > 0) attrs.put("Exceptions","");
640 codeAttrs.finish(cp);
642 cparg = new ConstantPool.Ent[size];
644 for(int i=0, p=0;i<size;i++) {
660 cparg[i] = cp.add(arg[i]);
662 case INVOKEINTERFACE:
663 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
666 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
672 private Object resolveTarget(Object arg) {
674 if(arg instanceof PhantomTarget) {
675 target = ((PhantomTarget)arg).getTarget();
676 if(target == -1) throw new IllegalStateException("unresolved phantom target");
679 target = ((Integer)arg).intValue();
681 if(target < 0 || target >= size)
682 throw new IllegalStateException("invalid target address " + target + "/" + size);
686 private void generateCode(ConstantPool cp) {
689 } catch(IOException e) {
690 throw new Error("should never happen");
694 private void _generateCode(ConstantPool cp) throws IOException {
695 ByteArrayOutputStream baos = new ByteArrayOutputStream();
696 DataOutput o = new DataOutputStream(baos);
698 int[] pc = new int[size];
702 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
703 for(i=0, p=0;i<size;i++) {
704 byte op = this.op[i];
705 int opdata = OP_DATA[op&0xff];
709 if((opdata & OP_BRANCH_FLAG)!= 0) {
711 arg[i] = resolveTarget(arg[i]);
712 } catch(RuntimeException e) {
713 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
719 // Speical caculations
722 int arg = ((Integer)this.arg[i]).intValue();
723 if(arg < i && p - maxpc[arg] <= 32768) p += 3;
733 Object[] targets = si.targets;
734 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
735 si.defaultTarget = resolveTarget(si.defaultTarget);
736 p += 1 + 3 + si.length(); // opcode itself, padding, data
737 if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
738 int[] vals = ((LSI)si).vals;
739 for(j=1;j<vals.length;j++)
740 if(vals[j] <= vals[j-1])
741 throw new IllegalStateException("out of order/duplicate lookupswitch values");
746 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
749 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
750 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
752 int arg = ((Integer)this.arg[i]).intValue();
755 this.arg[i] = new Wide(op, arg);
762 Pair pair = (Pair) this.arg[i];
763 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
765 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
772 j = cp.getIndex(cparg[i]);
774 this.op[i] = op = LDC_W;
781 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
785 // Pass2 - Widen instructions if they can possibly be too short
786 for(i=0;i<size;i++) {
790 int arg = ((Integer)this.arg[i]).intValue();
791 int diff = maxpc[arg] - maxpc[i];
792 if(diff < -32768 || diff > 32767)
793 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
799 // Pass3 - Calculate actual pc
800 for(i=0, p=0;i<size;i++) {
801 byte op = this.op[i];
810 p++; // opcode itself
811 p = (p + 3) & ~3; // padding
813 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
814 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
818 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
821 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
822 if(l == 7) throw new Error("shouldn't be here");
829 if(codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
831 o.writeShort(maxStack);
832 o.writeShort(maxLocals);
833 o.writeInt(codeSize);
835 // Pass 4 - Actually write the bytecodes
836 for(i=0;i<size;i++) {
837 byte op = this.op[i];
838 int opdata = OP_DATA[op&0xff];
839 if(op == NOP && !EMIT_NOPS) continue;
841 int argLength = opdata & OP_ARG_LENGTH_MASK;
843 if(argLength == 0) continue; // skip if no args
846 Object arg = this.arg[i];
850 Pair pair = (Pair) arg;
851 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
852 o.writeByte(pair.i1);
853 o.writeByte(pair.i2);
860 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
861 o.writeInt(pc[si.getDefaultTarget()] - mypc);
862 if(op == LOOKUPSWITCH) {
863 int[] vals = ((LSI)si).vals;
864 o.writeInt(si.size());
865 for(int j=0;j<si.size();j++) {
867 o.writeInt(pc[si.getTarget(j)] - mypc);
873 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
878 Wide wide = (Wide) arg;
879 o.writeByte(wide.op);
880 o.writeShort(wide.varNum);
881 if(wide.op == IINC) o.writeShort(wide.n);
884 case MULTIANEWARRAY: {
885 o.writeShort(cp.getIndex(cparg[i]));
886 int v = ((MultiANewArray) arg).dims;
887 if(v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
891 case INVOKEINTERFACE:
892 o.writeShort(cp.getIndex(cparg[i]));
893 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
897 if((opdata & OP_BRANCH_FLAG) != 0) {
898 int v = pc[((Integer)arg).intValue()] - pc[i];
900 if(v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
902 } else if(argLength == 4) {
905 throw new Error("should never happen");
907 } else if((opdata & OP_CPENT_FLAG) != 0) {
908 int v = cp.getIndex(cparg[i]);
909 if(argLength == 1) o.writeByte(v);
910 else if(argLength == 2) o.writeShort(v);
911 else throw new Error("should never happen");
912 } else if(argLength == 7) {
913 throw new Error("should never happen - variable length instruction not explicitly handled");
915 int iarg = ((Integer)arg).intValue();
917 if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128)) throw new ClassFile.Exn("overflow of s/u1 option");
919 } else if(argLength == 2) {
920 if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768)) throw new ClassFile.Exn("overflow of s/u2 option");
923 throw new Error("should never happen");
930 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
932 o.writeShort(exnTable.size());
933 for(i=0;i<exnTable.size();i++)
934 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
936 codeAttrs.dump(o,cp);
939 byte[] codeAttribute = baos.toByteArray();
940 attrs.put("Code", codeAttribute);
943 void generateExceptions(ConstantPool cp) throws IOException {
944 if(thrownExceptions.size() > 0) {
945 ByteArrayOutputStream baos = new ByteArrayOutputStream();
946 DataOutputStream o = new DataOutputStream(baos);
947 o.writeShort(thrownExceptions.size());
948 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
949 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
951 attrs.put("Exceptions", baos.toByteArray());
955 void dump(DataOutput o, ConstantPool cp) throws IOException {
956 if((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
957 generateExceptions(cp);
960 o.writeShort(cp.getUtf8Index(name));
961 o.writeShort(cp.getUtf8Index(Type.methodTypeDescriptor(args,ret)));
965 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
966 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
967 public static byte negate(byte op) {
969 case IFEQ: return IFNE;
970 case IFNE: return IFEQ;
971 case IFLT: return IFGE;
972 case IFGE: return IFLT;
973 case IFGT: return IFLE;
974 case IFLE: return IFGT;
975 case IF_ICMPEQ: return IF_ICMPNE;
976 case IF_ICMPNE: return IF_ICMPEQ;
977 case IF_ICMPLT: return IF_ICMPGE;
978 case IF_ICMPGE: return IF_ICMPLT;
979 case IF_ICMPGT: return IF_ICMPLE;
980 case IF_ICMPLE: return IF_ICMPGT;
981 case IF_ACMPEQ: return IF_ACMPNE;
982 case IF_ACMPNE: return IF_ACMPEQ;
985 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
989 /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written.
990 This class is more or less a mutable integer */
991 public static class PhantomTarget {
992 private int target = -1;
993 public void setTarget(int target) { this.target = target; }
994 public int getTarget() { return target; }
997 private static Integer N(int n) { return new Integer(n); }
998 private static Long N(long n) { return new Long(n); }
999 private static Float N(float f) { return new Float(f); }
1000 private static Double N(double d) { return new Double(d); }
1001 private static int max(int a, int b) { return a > b ? a : b; }
1003 private static final int OP_BRANCH_FLAG = 1<<3;
1004 private static final int OP_CPENT_FLAG = 1<<4;
1005 private static final int OP_UNSIGNED_FLAG = 1<<5;
1006 private static final int OP_VALID_FLAG = 1<<6;
1007 private static final int OP_ARG_LENGTH_MASK = 7;
1008 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
1009 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
1010 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
1011 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
1012 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
1014 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
1015 private static final byte[] OP_DATA = {
1016 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1017 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1018 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1019 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
1020 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1021 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1022 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1023 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1024 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1025 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1026 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
1027 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
1028 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1029 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1030 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1031 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01