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 public void debugToString(StringBuffer sb, String constructorName) {
30 // This is intentionally a local variable so it can be removed by gcclass
31 final String[] OP_NAMES = new String[]{
32 "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2",
33 "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0",
34 "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush",
35 "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload",
36 "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3",
37 "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1",
38 "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3",
39 "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload",
40 "faload", "daload", "aaload", "baload", "caload", "saload",
41 "istore", "lstore", "fstore", "dstore", "astore", "istore_0",
42 "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2",
43 "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0",
44 "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2",
45 "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore",
46 "bastore", "castore", "sastore", "pop", "pop2", "dup",
47 "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap",
48 "iadd", "ladd", "fadd", "dadd", "isub", "lsub",
49 "fsub", "dsub", "imul", "lmul", "fmul", "dmul",
50 "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem",
51 "frem", "drem", "ineg", "lneg", "fneg", "dneg",
52 "ishl", "lshl", "ishr", "lshr", "iushr", "lushr",
53 "iand", "land", "ior", "lor", "ixor", "lxor",
54 "iinc", "i2l", "i2f", "i2d", "l2i", "l2f",
55 "l2d", "f2i", "f2l", "f2d", "d2i", "d2l",
56 "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl",
57 "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt",
58 "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt",
59 "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto",
60 "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn",
61 "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic",
62 "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface",
63 "", "new", "newarray", "anewarray", "arraylength", "athrow",
64 "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray",
65 "ifnull", "ifnonnull", "goto_w", "jsr_w", "", "",
66 "", "", "", "", "", "",
67 "", "", "", "", "", "",
68 "", "", "", "", "", "",
69 "", "", "", "", "", "",
70 "", "", "", "", "", "",
71 "", "", "", "", "", "",
72 "", "", "", "", "", "",
73 "", "", "", "", "", "",
77 sb.append(" ").append(ClassFile.flagsToString(flags,false));
78 sb.append(method.debugToString());
79 if(thrownExceptions.size() > 0) {
81 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
82 sb.append(" ").append(((Type.Class)e.nextElement()).debugToString()).append(",");
83 sb.setLength(sb.length()-1);
86 if((flags & (NATIVE|ABSTRACT))==0) {
88 for(int i=0;i<size();i++) {
90 for(int j=i==0?1:i;j<10000;j*=10) sb.append(" ");
91 sb.append(i).append(": ");
92 sb.append(OP_NAMES[op[i]&0xff]);
94 if(arg[i] instanceof Type) s = ((Type)arg[i]).debugToString();
95 else if(arg[i] instanceof Type.Class.Member) s = ((Type.Class.Member)arg[i]).toString();
96 else if(arg[i] instanceof String) s = "\"" + s + "\"";
97 else if(arg[i] != null) s = arg[i].toString();
98 if(s != null) sb.append(" ").append(s);
107 MethodGen(Type.Class c, DataInput in, ConstantPool cp, boolean ownerInterface) throws IOException {
108 this.flags = in.readShort();
109 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new ClassFile.ClassReadExn("invalid flags");
110 String name = cp.getUtf8KeyByIndex(in.readShort());
111 this.method = c.method(name+cp.getUtf8KeyByIndex(in.readShort()));
112 this.attrs = new ClassFile.AttrGen(in,cp);
114 if((flags & (NATIVE|ABSTRACT))==0) {
115 byte[] codeAttr = (byte[]) attrs.get("Code");
116 if(codeAttr == null) throw new ClassFile.ClassReadExn("code attr expected");
117 DataInputStream ci = new DataInputStream(new ByteArrayInputStream(codeAttr));
118 maxStack = ci.readUnsignedShort();
119 maxLocals = ci.readUnsignedShort();
120 int codeLen = ci.readInt();
121 int[] bytecodeMap = parseCode(ci,codeLen,cp);
122 int numExns = ci.readUnsignedShort();
124 exnTable.addElement(new ExnTableEnt(ci,cp,bytecodeMap));
125 codeAttrs = new ClassFile.AttrGen(ci,cp);
126 // FEATURE: Support these
127 // NOTE: Until we can support them properly we HAVE to delete them,
128 // they'll be incorrect after we rewrite the constant pool, etc
129 codeAttrs.remove("LineNumberTable");
130 codeAttrs.remove("LocalVariableTable");
133 codeAttrs = new ClassFile.AttrGen();
136 if(attrs.contains("Exceptions")) {
137 DataInputStream ei = new DataInputStream(new ByteArrayInputStream((byte[]) attrs.get("Exceptions")));
138 int exnCount = ei.readUnsignedShort();
139 while(exnCount-- > 0) {
140 Type.Class t = (Type.Class) cp.getKeyByIndex(ei.readUnsignedShort());
141 thrownExceptions.put(t,t);
146 final int[] parseCode(DataInputStream in, int codeLen, ConstantPool cp) throws IOException {
147 int[] map = new int[codeLen];
149 for(pc=0;pc<map.length;pc++) map[pc] = -1;
150 for(pc=0;pc<codeLen;) {
151 byte op = in.readByte();
152 int opdata = OP_DATA[op&0xff];
153 //System.err.println("Processing " + Integer.toString(op&0xff,16) + " at " + pc);
154 if((opdata&OP_VALID_FLAG)==0) throw new ClassFile.ClassReadExn("invalid bytecode " + (op&0xff));
155 int argLength = opdata & OP_ARG_LENGTH_MASK;
158 pc += 1 + (argLength == 7 ? 0 : argLength);
159 if(argLength == 0) { add(op); continue; }
163 arg = new Pair(in.readUnsignedByte(),in.readByte());
168 for(;(pc&3) != 0;pc++) if(in.readByte() != 0) throw new ClassFile.ClassReadExn("invalid padding");
169 int def = in.readInt() + mypc;
171 if(op == LOOKUPSWITCH) {
172 LSI lsi = new LSI(in.readInt());
174 for(int i=0;i<lsi.size();i++) {
175 lsi.setVal(i,in.readInt());
176 lsi.setTarget(i,in.readInt() + mypc);
181 int lo = in.readInt();
182 int hi = in.readInt();
184 TSI tsi = new TSI(lo,hi);
185 for(int i=0;i<tsi.size();i++) { tsi.setTarget(i,in.readInt() + mypc); pc += 4; }
188 si.setDefaultTarget(def);
192 byte wideop = in.readByte();
194 ? new Wide(wideop,in.readUnsignedShort(),in.readShort())
195 : new Wide(wideop,in.readUnsignedShort());
196 pc += wideop == IINC ? 5 : 3;
200 arg = new MultiANewArray((Type.Class)cp.getKeyByIndex(in.readUnsignedShort()),in.readUnsignedByte());
202 case INVOKEINTERFACE: {
203 ConstantPool.Ent ent = cp.getByIndex(in.readUnsignedShort());
204 if(ent.tag != CONSTANT_INTERFACEMETHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode");
205 arg = ((ConstantPool.InterfaceMethodKey)ent.key()).method;
206 if(in.readByte() == 0 || in.readByte() != 0) throw new ClassFile.ClassReadExn("illegal count or 0 arg to invokeinterface");
210 if((opdata&OP_CPENT_FLAG)!=0) {
211 ConstantPool.Ent ent = cp.getByIndex(argLength == 2 ? in.readUnsignedShort() : argLength == 1 ? in.readUnsignedByte() : -1);
213 Object key = ent.key();
219 case CONSTANT_INTEGER:
222 case CONSTANT_DOUBLE:
223 case CONSTANT_STRING:
227 throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
234 if(tag != CONSTANT_FIELDREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
239 if(tag != CONSTANT_METHODREF) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
245 if(tag != CONSTANT_CLASS) throw new ClassFile.ClassReadExn("illegal argument to bytecode 0x" + Integer.toString(op&0xff,16));
248 throw new Error("should never happen");
252 // treat everything else (including branches for now) as plain old ints
254 boolean unsigned = (opdata&OP_UNSIGNED_FLAG)!=0;
255 if(argLength == 1) n = unsigned ? in.readUnsignedByte() : in.readByte();
256 else if(argLength == 2) n = unsigned ? in.readUnsignedShort() : in.readShort();
257 else throw new Error("should never happen");
258 if((opdata&OP_BRANCH_FLAG)!=0) n += mypc;
266 throw new ClassFile.ClassReadExn("didn't read enough code (" + pc + "/" + codeLen + " in " + method.name + ")");
267 for(int i=0;i<size();i++) {
274 int pos = map[si.getDefaultTarget()];
275 if(pos < 0) throw new ClassFile.ClassReadExn("default target points to invalid bytecode: " + si.getDefaultTarget());
276 si.setDefaultTarget(pos);
278 for(int j=0;j<si.size();j++) {
279 pos = map[si.getTarget(j)];
280 if(pos < 0) throw new ClassFile.ClassReadExn("target points to invalid bytecode");
286 if(OP_BRANCH(op[i])) {
287 int pos = map[((Integer)arg[i]).intValue()];
288 if(pos < 0) throw new ClassFile.ClassReadExn("branch points to invalid bytecode");
297 MethodGen(Type.Class c, String name, Type ret, Type[] args, int flags, boolean ownerInterface) {
298 if ((flags & ~VALID_METHOD_FLAGS) != 0) throw new IllegalArgumentException("invalid flags");
299 this.method = c.method(name, ret, args);
302 attrs = new ClassFile.AttrGen();
303 codeAttrs = new ClassFile.AttrGen();
305 if(ownerInterface || (flags & (ABSTRACT|NATIVE)) != 0) size = capacity = -1;
307 maxLocals = Math.max(args.length + (flags&STATIC)==0 ? 1 : 0, 4);
314 final Type.Class type; // null type means all exceptions (for finally)
316 ExnTableEnt(DataInput in, ConstantPool cp, int[] bytecodeMap) throws IOException {
317 int startPC = in.readUnsignedShort();
318 int endPC = in.readUnsignedShort();
319 int handlerPC = in.readUnsignedShort();
320 int index = in.readUnsignedShort();
321 this.type = index == 0 ? null : (Type.Class) cp.getKeyByIndex(index);
322 int max = bytecodeMap.length;
323 if(startPC >= max || bytecodeMap[startPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
324 if(endPC >= max || bytecodeMap[endPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
325 if(handlerPC >= max || bytecodeMap[handlerPC] < 0) throw new ClassFile.ClassReadExn("invalid startPC");
326 this.start = bytecodeMap[startPC];
327 this.end = bytecodeMap[endPC];
328 this.handler = bytecodeMap[handlerPC];
330 ExnTableEnt(int start, int end, int handler, Type.Class type) {
333 this.handler = handler;
336 void finish(ConstantPool cp) { if(type != null) cp.add(type); }
337 void dump(DataOutput o, int[] pc, int endPC, ConstantPool cp) throws IOException {
338 o.writeShort(pc[start]);
339 o.writeShort(end==pc.length ? endPC : pc[end]);
340 o.writeShort(pc[handler]);
341 o.writeShort(type == null ? 0 : cp.getIndex(type));
345 /** Adds an exception handler for the range [<i>start</i>, <i>end</i>) pointing to <i>handler</i>
346 @param start The instruction to start at (inclusive)
347 @param end The instruction to end at (exclusive)
348 @param handler The instruction of the excepton handler
349 @param type The type of exception that is to be handled (MUST inherit from Throwable)
351 public final void addExceptionHandler(int start, int end, int handler, Type.Class type) {
352 exnTable.addElement(new ExnTableEnt(start, end, handler, type));
355 /** Adds a exception type that can be thrown from this method
356 NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown
357 @param type The type of exception that can be thrown
359 public final void addThrow(Type.Class type) { thrownExceptions.put(type, type); }
361 private final void grow() { if(size == capacity) grow(size+1); }
362 private final void grow(int newCap) {
363 if(capacity == NO_CODE) throw new IllegalStateException("method can't have code");
364 if(newCap <= capacity) return;
365 newCap = Math.max(newCap, capacity == 0 ? 256 : capacity*2);
367 byte[] op2 = new byte[newCap];
368 if(capacity != 0) System.arraycopy(op, 0, op2, 0, size);
371 Object[] arg2 = new Object[newCap];
372 if(capacity != 0) System.arraycopy(arg, 0, arg2, 0, size);
378 /** Returns the size (in instructions) of this method
379 @return The size of the method (in instructions)
381 public final int size() { return size; }
383 // These two are optimized for speed, they don't call set() below
384 /** Add a bytecode (with no argument) to the method */
385 public final int add(byte op) {
387 if(s == capacity) grow();
392 /** Set the bytecode at position <i>pos</i> to <i>op</i> */
393 public final void set(int pos, byte op) { this.op[pos] = op; }
395 /** Adds a bytecode, <i>op</i>, with argument <i>arg</i> to the method
396 @return The position of the new bytecode
398 public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
399 /** Adds a bytecode with a boolean argument - equivalent to add(op, arg?1:0);
400 @return The position of the new bytecode
403 public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
404 /** Adds a bytecode with an integer argument. This is equivalent to add(op, new Integer(arg)), but optimized to prevent the allocation when possible
405 @return The position of the new bytecode
406 @see #add(byte, Object)
408 public final int add(byte op, int arg) { if(capacity == size) grow(); set(size, op, arg); return size++; }
410 /** Gets the bytecode at position <i>pos</i>
411 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
413 public final byte get(int pos) { return op[pos]; }
415 /** Gets the bytecode at position <i>pos</i>. NOTE: This isn't necessarily the same object that was set with add or set.
416 Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient
417 interal form when they are added. The value returned from this method for these instruction can be reused, but there
418 is no way to retrieve the original object
419 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
421 public final Object getArg(int pos) { return arg[pos]; }
423 /** 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.
424 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
425 @see #setArg(int, Object) */
426 public final void setArg(int pos, int arg) { set(pos, op[pos], N(arg)); }
427 /** Sets the argument for <i>pos</i> to <i>arg</i>.
428 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
430 public final void setArg(int pos, Object arg) { set(pos, op[pos], arg); }
432 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
433 This is equivalent to set(pos, op, arg?1:0)
434 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
436 public final void set(int pos, byte op, boolean arg) { set(pos, op, arg?1:0); }
438 // This MUST handle x{LOAD, STORE} and LDC with an int arg WITHOUT falling back to set(int, byte, Object)
439 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>n</i> respectivly.
440 This is equivalent to set(pos, op, new Integer(n)), but optimized to prevent the allocation when possible.
441 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
443 public final void set(int pos, byte op, int n) {
448 case -1: op = ICONST_M1; break OUTER;
449 case 0: op = ICONST_0; break OUTER;
450 case 1: op = ICONST_1; break OUTER;
451 case 2: op = ICONST_2; break OUTER;
452 case 3: op = ICONST_3; break OUTER;
453 case 4: op = ICONST_4; break OUTER;
454 case 5: op = ICONST_5; break OUTER;
456 if(n >= -128 && n <= 127) { op = BIPUSH; arg = N(n); }
457 else if(n >= -32768 && n <= 32767) { op = SIPUSH; arg = N(n); }
460 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
461 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
462 if(n >= maxLocals) maxLocals = n + 1;
463 if(n >= 0 && n <= 3) {
466 case ILOAD: base = ILOAD_0; break;
467 case ISTORE: base = ISTORE_0; break;
468 case LLOAD: base = LLOAD_0; break;
469 case LSTORE: base = LSTORE_0; break;
470 case FLOAD: base = FLOAD_0; break;
471 case FSTORE: base = FSTORE_0; break;
472 case DLOAD: base = DLOAD_0; break;
473 case DSTORE: base = DSTORE_0; break;
474 case ALOAD: base = ALOAD_0; break;
475 case ASTORE: base = ASTORE_0; break;
477 op = (byte)((base&0xff) + n);
490 /** Sets the bytecode and argument at <i>pos</i> to <i>op</i> and <i>arg</i> respectivly.
491 @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size()
493 public final void set(int pos, byte op, Object arg) {
495 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
496 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
497 // set(int, byte, int) always handles these ops itself
498 set(pos, op, ((Integer)arg).intValue());
501 // set(int, byte, int) always handles these opts itself
502 if(arg instanceof Integer) { set(pos, op, ((Integer)arg).intValue()); return; }
503 if(arg instanceof Boolean) { set(pos, op, ((Boolean)arg).booleanValue()); return; }
505 if(arg instanceof Long) {
506 long l = ((Long)arg).longValue();
507 if(l == 0L || l == 1L) {
508 this.op[pos] = l == 0L ? LCONST_0 : LCONST_1;
509 this.arg[pos] = null;
513 } else if(arg instanceof Double) {
518 if((OP_DATA[op&0xff]&OP_VALID_FLAG) == 0) throw new IllegalArgumentException("unknown bytecode");
523 /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes
527 public static abstract class SI {
528 public final Object[] targets;
529 public Object defaultTarget;
531 SI(int size) { targets = new Object[size]; }
532 public void setTarget(int pos, Object val) { targets[pos] = val; }
533 public void setTarget(int pos, int val) { targets[pos] = N(val); }
534 public void setDefaultTarget(int val) { setDefaultTarget(N(val)); }
535 public void setDefaultTarget(Object o) { defaultTarget = o; }
536 public int size() { return targets.length; }
538 public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
539 public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
541 abstract int length();
544 /** This class represents the arguments to the TABLESWITCH bytecode */
545 public static class TSI extends SI {
548 public TSI(int lo, int hi) {
553 public void setTargetForVal(int val, Object o) { setTarget(val-lo, o); }
554 public void setTargetForVal(int val, int n) { setTarget(val-lo, n); }
556 int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
559 /** This class represents the arguments to the LOOKUPSWITCH bytecode */
560 public static class LSI extends SI {
561 public final int[] vals;
562 public LSI(int size) {
564 this.vals = new int[size];
566 public final void setVal(int pos, int val) { vals[pos] = val; }
568 int length() { return 8 + targets.length * 8; } // key/val per target, default, count
571 /** This class represents the arguments to byecodes that take two integer arguments. */
572 public static class Pair {
575 public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
578 public static class MultiANewArray {
579 public Type.Class type;
581 public MultiANewArray(Type.Class type, int dims) { this.type = type; this.dims = dims; }
584 public static class Wide {
585 public final byte op;
586 public final int varNum;
588 Wide(byte op, int varNum) { this(op, varNum, 0); }
589 Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
592 /** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
593 necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
594 public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
595 /** Sets the maxinum size of th stack for this function to <i>maxStack</i>. This defaults to 16< */
596 public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
598 /** Computes the final bytecode for this method.
599 @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc)
600 @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc)
602 void finish(ConstantPool cp) {
603 cp.addUtf8(method.name);
604 cp.addUtf8(method.getDescriptor());
606 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
607 cp.add(e.nextElement());
609 if(size == NO_CODE) return;
610 for(int i=0;i<exnTable.size();i++)
611 ((ExnTableEnt)exnTable.elementAt(i)).finish(cp);
613 // We'll set these correctly later
614 if((flags & (NATIVE|ABSTRACT))==0) attrs.put("Code","");
615 if(thrownExceptions.size() > 0) attrs.put("Exceptions","");
617 codeAttrs.finish(cp);
619 cparg = new ConstantPool.Ent[size];
621 for(int i=0, p=0;i<size;i++) {
637 cparg[i] = cp.add(arg[i]);
639 case INVOKEINTERFACE:
640 cparg[i] = cp.add(new ConstantPool.InterfaceMethodKey((Type.Class.Method)arg[i]));
643 cparg[i] = cp.add(((MultiANewArray)arg[i]).type);
649 private Object resolveTarget(Object arg) {
651 if(arg instanceof PhantomTarget) {
652 target = ((PhantomTarget)arg).getTarget();
653 if(target == -1) throw new IllegalStateException("unresolved phantom target");
656 target = ((Integer)arg).intValue();
658 if(target < 0 || target >= size)
659 throw new IllegalStateException("invalid target address " + target + "/" + size);
663 private void generateCode(ConstantPool cp) {
666 } catch(IOException e) {
667 throw new Error("should never happen");
671 private void _generateCode(ConstantPool cp) throws IOException {
672 ByteArrayOutputStream baos = new ByteArrayOutputStream();
673 DataOutput o = new DataOutputStream(baos);
675 int[] pc = new int[size];
679 // Pass1 - Calculate maximum pc of each bytecode, widen some insns, resolve any unresolved jumps, etc
680 for(i=0, p=0;i<size;i++) {
681 byte op = this.op[i];
682 int opdata = OP_DATA[op&0xff];
686 if((opdata & OP_BRANCH_FLAG)!= 0) {
688 arg[i] = resolveTarget(arg[i]);
689 } catch(RuntimeException e) {
690 System.err.println("WARNING: Error resolving target for " + Integer.toHexString(op&0xff));
696 // Speical caculations
699 int arg = ((Integer)this.arg[i]).intValue();
700 if(arg < i && p - maxpc[arg] <= 32768) p += 3;
710 Object[] targets = si.targets;
711 for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
712 si.defaultTarget = resolveTarget(si.defaultTarget);
713 p += 1 + 3 + si.length(); // opcode itself, padding, data
714 if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
715 int[] vals = ((LSI)si).vals;
716 for(j=1;j<vals.length;j++)
717 if(vals[j] <= vals[j-1])
718 throw new IllegalStateException("out of order/duplicate lookupswitch values");
723 p += ((Wide)arg[i]).op == IINC ? 5 : 3;
726 case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
727 case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
729 int arg = ((Integer)this.arg[i]).intValue();
732 this.arg[i] = new Wide(op, arg);
739 Pair pair = (Pair) this.arg[i];
740 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
742 this.arg[i] = new Wide(IINC, pair.i1, pair.i2);
749 j = cp.getIndex(cparg[i]);
751 this.op[i] = op = LDC_W;
758 if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here " + Integer.toString(op&0xff,16));
762 // Pass2 - Widen instructions if they can possibly be too short
763 for(i=0;i<size;i++) {
767 int arg = ((Integer)this.arg[i]).intValue();
768 int diff = maxpc[arg] - maxpc[i];
769 if(diff < -32768 || diff > 32767)
770 op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
776 // Pass3 - Calculate actual pc
777 for(i=0, p=0;i<size;i++) {
778 byte op = this.op[i];
787 p++; // opcode itself
788 p = (p + 3) & ~3; // padding
790 if(op == TABLESWITCH) p += 4 + 4 + si.size() * 4; // lo, hi, targets
791 else p += 4 + si.size() * 4 * 2; // count, key, val * targets
795 p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
798 int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
799 if(l == 7) throw new Error("shouldn't be here");
806 if(codeSize >= 65536) throw new ClassFile.Exn("method too large in size");
808 o.writeShort(maxStack);
809 o.writeShort(maxLocals);
810 o.writeInt(codeSize);
812 // Pass 4 - Actually write the bytecodes
813 for(i=0;i<size;i++) {
814 byte op = this.op[i];
815 int opdata = OP_DATA[op&0xff];
816 if(op == NOP && !EMIT_NOPS) continue;
818 int argLength = opdata & OP_ARG_LENGTH_MASK;
820 if(argLength == 0) continue; // skip if no args
823 Object arg = this.arg[i];
827 Pair pair = (Pair) arg;
828 if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) throw new ClassFile.Exn("overflow of iinc arg");
829 o.writeByte(pair.i1);
830 o.writeByte(pair.i2);
837 for(p = pc[i]+1;(p&3)!=0;p++) o.writeByte(0);
838 o.writeInt(pc[si.getDefaultTarget()] - mypc);
839 if(op == LOOKUPSWITCH) {
840 int[] vals = ((LSI)si).vals;
841 o.writeInt(si.size());
842 for(int j=0;j<si.size();j++) {
844 o.writeInt(pc[si.getTarget(j)] - mypc);
850 for(int j=0;j<tsi.size();j++) o.writeInt(pc[tsi.getTarget(j)] - mypc);
855 Wide wide = (Wide) arg;
856 o.writeByte(wide.op);
857 o.writeShort(wide.varNum);
858 if(wide.op == IINC) o.writeShort(wide.n);
861 case MULTIANEWARRAY: {
862 o.writeShort(cp.getIndex(cparg[i]));
863 int v = ((MultiANewArray) arg).dims;
864 if(v >= 256) throw new ClassFile.Exn("overflow of dimensions in multianewarray");
868 case INVOKEINTERFACE:
869 o.writeShort(cp.getIndex(cparg[i]));
870 o.writeByte(((Type.Class.Method)arg).argTypes.length + 1);
874 if((opdata & OP_BRANCH_FLAG) != 0) {
875 int v = pc[((Integer)arg).intValue()] - pc[i];
877 if(v < -32768 || v > 32767) throw new ClassFile.Exn("overflow of s2 offset");
879 } else if(argLength == 4) {
882 throw new Error("should never happen");
884 } else if((opdata & OP_CPENT_FLAG) != 0) {
885 int v = cp.getIndex(cparg[i]);
886 if(argLength == 1) o.writeByte(v);
887 else if(argLength == 2) o.writeShort(v);
888 else throw new Error("should never happen");
889 } else if(argLength == 7) {
890 throw new Error("should never happen - variable length instruction not explicitly handled");
892 int iarg = ((Integer)arg).intValue();
894 if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 256 : (iarg < -128 || iarg >= 128)) throw new ClassFile.Exn("overflow of s/u1 option");
896 } else if(argLength == 2) {
897 if((opdata & OP_UNSIGNED_FLAG) != 0 ? iarg >= 65536 : (iarg < -32768 || iarg >= 32768)) throw new ClassFile.Exn("overflow of s/u2 option");
900 throw new Error("should never happen");
907 //if(baos.size() - 8 != codeSize) throw new Error("we didn't output what we were supposed to");
909 o.writeShort(exnTable.size());
910 for(i=0;i<exnTable.size();i++)
911 ((ExnTableEnt)exnTable.elementAt(i)).dump(o, pc, codeSize, cp);
913 codeAttrs.dump(o,cp);
916 byte[] codeAttribute = baos.toByteArray();
917 attrs.put("Code", codeAttribute);
920 void generateExceptions(ConstantPool cp) throws IOException {
921 if(thrownExceptions.size() > 0) {
922 ByteArrayOutputStream baos = new ByteArrayOutputStream();
923 DataOutputStream o = new DataOutputStream(baos);
924 o.writeShort(thrownExceptions.size());
925 for(Enumeration e = thrownExceptions.keys();e.hasMoreElements();)
926 o.writeShort(cp.getIndex(thrownExceptions.get(e.nextElement())));
928 attrs.put("Exceptions", baos.toByteArray());
932 void dump(DataOutput o, ConstantPool cp) throws IOException {
933 if((flags & (NATIVE|ABSTRACT))==0) generateCode(cp);
934 generateExceptions(cp);
937 o.writeShort(cp.getUtf8Index(method.name));
938 o.writeShort(cp.getUtf8Index(method.getDescriptor()));
942 /** Negates the IF* instruction, <i>op</i> (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc)
943 @exception IllegalArgumentException if <i>op</i> isn't an IF* instruction */
944 public static byte negate(byte op) {
946 case IFEQ: return IFNE;
947 case IFNE: return IFEQ;
948 case IFLT: return IFGE;
949 case IFGE: return IFLT;
950 case IFGT: return IFLE;
951 case IFLE: return IFGT;
952 case IF_ICMPEQ: return IF_ICMPNE;
953 case IF_ICMPNE: return IF_ICMPEQ;
954 case IF_ICMPLT: return IF_ICMPGE;
955 case IF_ICMPGE: return IF_ICMPLT;
956 case IF_ICMPGT: return IF_ICMPLE;
957 case IF_ICMPLE: return IF_ICMPGT;
958 case IF_ACMPEQ: return IF_ACMPNE;
959 case IF_ACMPNE: return IF_ACMPEQ;
962 throw new IllegalArgumentException("Can't negate " + Integer.toHexString(op));
966 /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written.
967 This class is more or less a mutable integer */
968 public static class PhantomTarget {
969 private int target = -1;
970 public void setTarget(int target) { this.target = target; }
971 public int getTarget() { return target; }
974 private static Integer N(int n) { return new Integer(n); }
975 private static Long N(long n) { return new Long(n); }
976 private static Float N(float f) { return new Float(f); }
977 private static Double N(double d) { return new Double(d); }
978 private static int max(int a, int b) { return a > b ? a : b; }
980 private static final int OP_BRANCH_FLAG = 1<<3;
981 private static final int OP_CPENT_FLAG = 1<<4;
982 private static final int OP_UNSIGNED_FLAG = 1<<5;
983 private static final int OP_VALID_FLAG = 1<<6;
984 private static final int OP_ARG_LENGTH_MASK = 7;
985 private static final boolean OP_VALID(byte op) { return (OP_DATA[op&0xff] & OP_VALID_FLAG) != 0; }
986 private static final int OP_ARG_LENGTH(byte op) { return (OP_DATA[op&0xff]&OP_ARG_LENGTH_MASK); }
987 private static final boolean OP_CPENT(byte op) { return (OP_DATA[op&0xff]&OP_CPENT_FLAG) != 0; }
988 private static final boolean OP_BRANCH(byte op) { return (OP_DATA[op&0xff]&OP_BRANCH_FLAG ) != 0; }
989 private static final boolean OP_UNSIGNED(byte op) { return (OP_DATA[op&0xff]&OP_UNSIGNED_FLAG ) != 0; }
991 // Run perl -x src/org/ibex/classgen/CGConst.java to generate this
992 private static final byte[] OP_DATA = {
993 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
994 0x41, 0x42, 0x51, 0x52, 0x52, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
995 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
996 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x61, 0x61, 0x61, 0x61, 0x61, 0x40, 0x40, 0x40, 0x40, 0x40,
997 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
998 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
999 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1000 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1001 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
1002 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1003 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x41, 0x47, 0x47, 0x40, 0x40, 0x40, 0x40,
1004 0x40, 0x40, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x54, 0x01, 0x52, 0x41, 0x52, 0x40, 0x40,
1005 0x52, 0x52, 0x40, 0x40, 0x47, 0x53, 0x4a, 0x4a, 0x4c, 0x4c, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1006 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1007 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1008 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01