import java.io.*;
import java.util.*;
-// FEATURE: Support WIDE bytecodes
-
/** A class representing a method in a generated classfile
@see ClassGen#addMethod */
public class MethodGen implements CGConst {
break;
case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
+ if(n >= maxLocals) maxLocals = n + 1;
if(n >= 0 && n <= 3) {
byte base = 0;
switch(op) {
}
op = (byte)((base&0xff) + n);
} else {
- if(n >= maxLocals) maxLocals = n + 1;
arg = N(n);
}
break;
@see MethodGen.TSI
@see MethodGen.LSI
*/
- public static class SI {
+ public static abstract class SI {
public final Object[] targets;
public Object defaultTarget;
public int size() { return targets.length; }
public int getTarget(int pos) { return ((Integer)targets[pos]).intValue(); }
- public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
+ public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); }
+
+ abstract int length();
}
/** This class represents the arguments to the TABLESWITCH bytecode */
}
public void setTargetForVal(int val, Object o) { setTarget(val-lo,o); }
public void setTargetForVal(int val, int n) { setTarget(val-lo,n); }
+
+ int length() { return 12 + targets.length * 4; } // 4bytes/target, hi, lo, default
}
/** This class represents the arguments to the LOOKUPSWITCH bytecode */
this.vals = new int[size];
}
public final void setVal(int pos, int val) { vals[pos] = val; }
+
+ int length() { return 8 + targets.length * 8; } // key/val per target, default, count
}
/** This class represents the arguments to byecodes that take two integer arguments. */
public int i2;
public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
}
+
+ public static class Wide {
+ public final byte op;
+ public final int varNum;
+ public final int n;
+ Wide(byte op, int varNum) { this(op,varNum,0); }
+ Wide(byte op, int varNum, int n) { this.op = op; this.varNum = varNum; this.n = n; }
+ }
/** Sets the maximum number of locals in the function to <i>maxLocals</i>. NOTE: This defaults to 0 and is automatically increased as
necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */
private void _finish() throws IOException {
if(size == FINISHED) return;
+ cp.stable();
+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutput o = new DataOutputStream(baos);
}
switch(op) {
+ // Speical caculations
case GOTO:
- case JSR:
- p += 3;
- break;
+ case JSR: {
+ int arg = ((Integer)this.arg[i]).intValue();
+ if(arg < i && p - maxpc[arg] <= 32768) p += 3;
+ else p += 5;
+ continue;
+ }
case NOP:
if(EMIT_NOPS) p++;
- break;
+ continue;
case LOOKUPSWITCH:
case TABLESWITCH: {
SI si = (SI) arg[i];
Object[] targets = si.targets;
for(j=0;j<targets.length;j++) targets[j] = resolveTarget(targets[j]);
si.defaultTarget = resolveTarget(si.defaultTarget);
- p += 1 + 3 + 4; // opcode itself, padding, default
- if(op == TABLESWITCH) p += 4 + 4 + targets.length * 4; // lo, hi, targets
- else p += 4 + targets.length * 4 * 2; // count, key,val * targets
- if(op == LOOKUPSWITCH) {
+ p += 1 + 3 + si.length(); // opcode itself, padding, data
+ if(op == LOOKUPSWITCH) { // verify sanity of lookupswitch vals
int[] vals = ((LSI)si).vals;
for(j=1;j<vals.length;j++)
if(vals[j] <= vals[j-1])
throw new IllegalStateException("out of order/duplicate lookupswitch values");
}
+ continue;
+ }
+ // May need widening
+ case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD:
+ case FSTORE: case DLOAD: case DSTORE: case ALOAD: case ASTORE:
+ case RET: {
+ int arg = ((Integer)this.arg[i]).intValue();
+ if(arg > 255) {
+ this.op[i] = WIDE;
+ this.arg[i] = new Wide(op,arg);
+ }
+ break;
+ }
+ case IINC: {
+ Pair pair = (Pair) this.arg[i];
+ if(pair.i1 > 255 || pair.i2 < -128 || pair.i2 > 127) {
+ this.op[i] = WIDE;
+ this.arg[i] = new Wide(IINC,pair.i1,pair.i2);
+ }
break;
}
case LDC:
j = ((CPGen.Ent)arg[i]).getIndex();
if(j >= 256) this.op[i] = op = LDC_W;
- // fall through
- default:
- if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
- p += 1 + j;
break;
+ default:
}
+ if((j = (opdata&OP_ARG_LENGTH_MASK)) == 7) throw new Error("shouldn't be here");
+ p += 1 + j;
}
// Pass2 - Widen instructions if they can possibly be too short
else p += 4 + si.size() * 4 * 2; // count, key,val * targets
break;
}
+ case WIDE:
+ p += 2 + (((Wide)arg[i]).op == IINC ? 4 : 2);
+ break;
default: {
int l = OP_DATA[op&0xff] & OP_ARG_LENGTH_MASK;
if(l == 7) throw new Error("shouldn't be here");
}
}
}
-
int codeSize = p;
if(codeSize >= 65536) throw new ClassGen.Exn("method too large in size");
byte op = this.op[i];
int opdata = OP_DATA[op&0xff];
if(op == NOP && !EMIT_NOPS) continue;
-
- o.writeByte(op&0xff);
+ o.writeByte(op);
int argLength = opdata & OP_ARG_LENGTH_MASK;
if(argLength == 0) continue; // skip if no args
}
break;
}
- case WIDE:
- throw new Error("WIDE instruction not yet supported");
+ case WIDE: {
+ Wide wide = (Wide) arg;
+ o.writeByte(wide.op);
+ o.writeShort(wide.varNum);
+ if(wide.op == IINC) o.writeShort(wide.n);
+ break;
+ }
default:
if((opdata & OP_BRANCH_FLAG) != 0) {