|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.java.util.jar.pack; |
|
|
|
import java.io.IOException; |
|
import java.util.Arrays; |
|
import static com.sun.java.util.jar.pack.Constants.*; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class Instruction { |
|
protected byte[] bytes; |
|
protected int pc; |
|
protected int bc; |
|
protected int w; |
|
protected int length; |
|
|
|
protected boolean special; |
|
|
|
protected Instruction(byte[] bytes, int pc, int bc, int w, int length) { |
|
reset(bytes, pc, bc, w, length); |
|
} |
|
private void reset(byte[] bytes, int pc, int bc, int w, int length) { |
|
this.bytes = bytes; |
|
this.pc = pc; |
|
this.bc = bc; |
|
this.w = w; |
|
this.length = length; |
|
} |
|
|
|
public int getBC() { |
|
return bc; |
|
} |
|
public boolean isWide() { |
|
return w != 0; |
|
} |
|
public byte[] getBytes() { |
|
return bytes; |
|
} |
|
public int getPC() { |
|
return pc; |
|
} |
|
public int getLength() { |
|
return length; |
|
} |
|
public int getNextPC() { |
|
return pc + length; |
|
} |
|
|
|
public Instruction next() { |
|
int npc = pc + length; |
|
if (npc == bytes.length) |
|
return null; |
|
else |
|
return Instruction.at(bytes, npc, this); |
|
} |
|
|
|
public boolean isNonstandard() { |
|
return isNonstandard(bc); |
|
} |
|
|
|
public void setNonstandardLength(int length) { |
|
assert(isNonstandard()); |
|
this.length = length; |
|
} |
|
|
|
|
|
|
|
public Instruction forceNextPC(int nextpc) { |
|
int llength = nextpc - pc; |
|
return new Instruction(bytes, pc, -1, -1, llength); |
|
} |
|
|
|
public static Instruction at(byte[] bytes, int pc) { |
|
return Instruction.at(bytes, pc, null); |
|
} |
|
|
|
public static Instruction at(byte[] bytes, int pc, Instruction reuse) { |
|
int bc = getByte(bytes, pc); |
|
int prefix = -1; |
|
int w = 0; |
|
int length = BC_LENGTH[w][bc]; |
|
if (length == 0) { |
|
|
|
switch (bc) { |
|
case _wide: |
|
bc = getByte(bytes, pc+1); |
|
w = 1; |
|
length = BC_LENGTH[w][bc]; |
|
if (length == 0) { |
|
|
|
length = 1; |
|
} |
|
break; |
|
case _tableswitch: |
|
return new TableSwitch(bytes, pc); |
|
case _lookupswitch: |
|
return new LookupSwitch(bytes, pc); |
|
default: |
|
|
|
length = 1; |
|
break; |
|
} |
|
} |
|
assert(length > 0); |
|
assert(pc+length <= bytes.length); |
|
|
|
if (reuse != null && !reuse.special) { |
|
reuse.reset(bytes, pc, bc, w, length); |
|
return reuse; |
|
} |
|
return new Instruction(bytes, pc, bc, w, length); |
|
} |
|
|
|
|
|
public byte getCPTag() { |
|
return BC_TAG[w][bc]; |
|
} |
|
|
|
|
|
public int getCPIndex() { |
|
int indexLoc = BC_INDEX[w][bc]; |
|
if (indexLoc == 0) return -1; |
|
assert(w == 0); |
|
if (length == 2) |
|
return getByte(bytes, pc+indexLoc); |
|
else |
|
return getShort(bytes, pc+indexLoc); |
|
} |
|
|
|
public void setCPIndex(int cpi) { |
|
int indexLoc = BC_INDEX[w][bc]; |
|
assert(indexLoc != 0); |
|
if (length == 2) |
|
setByte(bytes, pc+indexLoc, cpi); |
|
else |
|
setShort(bytes, pc+indexLoc, cpi); |
|
assert(getCPIndex() == cpi); |
|
} |
|
|
|
public ConstantPool.Entry getCPRef(ConstantPool.Entry[] cpMap) { |
|
int index = getCPIndex(); |
|
return (index < 0) ? null : cpMap[index]; |
|
} |
|
|
|
|
|
public int getLocalSlot() { |
|
int slotLoc = BC_SLOT[w][bc]; |
|
if (slotLoc == 0) return -1; |
|
if (w == 0) |
|
return getByte(bytes, pc+slotLoc); |
|
else |
|
return getShort(bytes, pc+slotLoc); |
|
} |
|
|
|
|
|
public int getBranchLabel() { |
|
int branchLoc = BC_BRANCH[w][bc]; |
|
if (branchLoc == 0) return -1; |
|
assert(w == 0); |
|
assert(length == 3 || length == 5); |
|
int offset; |
|
if (length == 3) |
|
offset = (short)getShort(bytes, pc+branchLoc); |
|
else |
|
offset = getInt(bytes, pc+branchLoc); |
|
assert(offset+pc >= 0); |
|
assert(offset+pc <= bytes.length); |
|
return offset+pc; |
|
} |
|
|
|
public void setBranchLabel(int targetPC) { |
|
int branchLoc = BC_BRANCH[w][bc]; |
|
assert(branchLoc != 0); |
|
if (length == 3) |
|
setShort(bytes, pc+branchLoc, targetPC-pc); |
|
else |
|
setInt(bytes, pc+branchLoc, targetPC-pc); |
|
assert(targetPC == getBranchLabel()); |
|
} |
|
|
|
// Return the trailing constant in the instruction (as a signed value). |
|
|
|
public int getConstant() { |
|
int conLoc = BC_CON[w][bc]; |
|
if (conLoc == 0) return 0; |
|
switch (length - conLoc) { |
|
case 1: return (byte) getByte(bytes, pc+conLoc); |
|
case 2: return (short) getShort(bytes, pc+conLoc); |
|
} |
|
assert(false); |
|
return 0; |
|
} |
|
|
|
public void setConstant(int con) { |
|
int conLoc = BC_CON[w][bc]; |
|
assert(conLoc != 0); |
|
switch (length - conLoc) { |
|
case 1: setByte(bytes, pc+conLoc, con); break; |
|
case 2: setShort(bytes, pc+conLoc, con); break; |
|
} |
|
assert(con == getConstant()); |
|
} |
|
|
|
public abstract static class Switch extends Instruction { |
|
|
|
public abstract int getCaseCount(); |
|
public abstract int getCaseValue(int n); |
|
public abstract int getCaseLabel(int n); |
|
public abstract void setCaseCount(int caseCount); |
|
public abstract void setCaseValue(int n, int value); |
|
public abstract void setCaseLabel(int n, int targetPC); |
|
protected abstract int getLength(int caseCount); |
|
|
|
public int getDefaultLabel() { return intAt(0)+pc; } |
|
public void setDefaultLabel(int targetPC) { setIntAt(0, targetPC-pc); } |
|
|
|
protected int apc; |
|
protected int intAt(int n) { return getInt(bytes, apc + n*4); } |
|
protected void setIntAt(int n, int x) { setInt(bytes, apc + n*4, x); } |
|
protected Switch(byte[] bytes, int pc, int bc) { |
|
super(bytes, pc, bc, 0, 0); |
|
this.apc = alignPC(pc+1); |
|
this.special = true; |
|
length = getLength(getCaseCount()); |
|
} |
|
public int getAlignedPC() { return apc; } |
|
public String toString() { |
|
String s = super.toString(); |
|
s += " Default:"+labstr(getDefaultLabel()); |
|
int caseCount = getCaseCount(); |
|
for (int i = 0; i < caseCount; i++) { |
|
s += "\n\tCase "+getCaseValue(i)+":"+labstr(getCaseLabel(i)); |
|
} |
|
return s; |
|
} |
|
public static int alignPC(int apc) { |
|
while (apc % 4 != 0) ++apc; |
|
return apc; |
|
} |
|
} |
|
|
|
public static class TableSwitch extends Switch { |
|
|
|
public int getLowCase() { return intAt(1); } |
|
public int getHighCase() { return intAt(2); } |
|
public int getCaseCount() { return intAt(2)-intAt(1)+1; } |
|
public int getCaseValue(int n) { return getLowCase()+n; } |
|
public int getCaseLabel(int n) { return intAt(3+n)+pc; } |
|
|
|
public void setLowCase(int val) { setIntAt(1, val); } |
|
public void setHighCase(int val) { setIntAt(2, val); } |
|
public void setCaseLabel(int n, int tpc) { setIntAt(3+n, tpc-pc); } |
|
public void setCaseCount(int caseCount) { |
|
setHighCase(getLowCase() + caseCount - 1); |
|
length = getLength(caseCount); |
|
} |
|
public void setCaseValue(int n, int val) { |
|
if (n != 0) throw new UnsupportedOperationException(); |
|
int caseCount = getCaseCount(); |
|
setLowCase(val); |
|
setCaseCount(caseCount); |
|
} |
|
|
|
TableSwitch(byte[] bytes, int pc) { |
|
super(bytes, pc, _tableswitch); |
|
} |
|
protected int getLength(int caseCount) { |
|
return (apc-pc) + (3 + caseCount) * 4; |
|
} |
|
} |
|
|
|
public static class LookupSwitch extends Switch { |
|
|
|
public int getCaseCount() { return intAt(1); } |
|
public int getCaseValue(int n) { return intAt(2+n*2+0); } |
|
public int getCaseLabel(int n) { return intAt(2+n*2+1)+pc; } |
|
|
|
public void setCaseCount(int caseCount) { |
|
setIntAt(1, caseCount); |
|
length = getLength(caseCount); |
|
} |
|
public void setCaseValue(int n, int val) { setIntAt(2+n*2+0, val); } |
|
public void setCaseLabel(int n, int tpc) { setIntAt(2+n*2+1, tpc-pc); } |
|
|
|
LookupSwitch(byte[] bytes, int pc) { |
|
super(bytes, pc, _lookupswitch); |
|
} |
|
protected int getLength(int caseCount) { |
|
return (apc-pc) + (2 + caseCount*2) * 4; |
|
} |
|
} |
|
|
|
|
|
public boolean equals(Object o) { |
|
return (o != null) && (o.getClass() == Instruction.class) |
|
&& equals((Instruction) o); |
|
} |
|
|
|
public int hashCode() { |
|
int hash = 3; |
|
hash = 11 * hash + Arrays.hashCode(this.bytes); |
|
hash = 11 * hash + this.pc; |
|
hash = 11 * hash + this.bc; |
|
hash = 11 * hash + this.w; |
|
hash = 11 * hash + this.length; |
|
return hash; |
|
} |
|
|
|
public boolean equals(Instruction that) { |
|
if (this.pc != that.pc) return false; |
|
if (this.bc != that.bc) return false; |
|
if (this.w != that.w) return false; |
|
if (this.length != that.length) return false; |
|
for (int i = 1; i < length; i++) { |
|
if (this.bytes[this.pc+i] != that.bytes[that.pc+i]) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
static String labstr(int pc) { |
|
if (pc >= 0 && pc < 100000) |
|
return ((100000+pc)+"").substring(1); |
|
return pc+""; |
|
} |
|
public String toString() { |
|
return toString(null); |
|
} |
|
public String toString(ConstantPool.Entry[] cpMap) { |
|
String s = labstr(pc) + ": "; |
|
if (bc >= _bytecode_limit) { |
|
s += Integer.toHexString(bc); |
|
return s; |
|
} |
|
if (w == 1) s += "wide "; |
|
String bcname = (bc < BC_NAME.length)? BC_NAME[bc]: null; |
|
if (bcname == null) { |
|
return s+"opcode#"+bc; |
|
} |
|
s += bcname; |
|
int tag = getCPTag(); |
|
if (tag != 0) s += " "+ConstantPool.tagName(tag)+":"; |
|
int idx = getCPIndex(); |
|
if (idx >= 0) s += (cpMap == null) ? ""+idx : "="+cpMap[idx].stringValue(); |
|
int slt = getLocalSlot(); |
|
if (slt >= 0) s += " Local:"+slt; |
|
int lab = getBranchLabel(); |
|
if (lab >= 0) s += " To:"+labstr(lab); |
|
int con = getConstant(); |
|
if (con != 0) s += " Con:"+con; |
|
return s; |
|
} |
|
|
|
|
|
//public static byte constantPoolTagFor(int bc) { return BC_TAG[0][bc]; } |
|
|
|
/// Fetching values from byte arrays: |
|
|
|
public int getIntAt(int off) { |
|
return getInt(bytes, pc+off); |
|
} |
|
public int getShortAt(int off) { |
|
return getShort(bytes, pc+off); |
|
} |
|
public int getByteAt(int off) { |
|
return getByte(bytes, pc+off); |
|
} |
|
|
|
|
|
public static int getInt(byte[] bytes, int pc) { |
|
return (getShort(bytes, pc+0) << 16) + (getShort(bytes, pc+2) << 0); |
|
} |
|
public static int getShort(byte[] bytes, int pc) { |
|
return (getByte(bytes, pc+0) << 8) + (getByte(bytes, pc+1) << 0); |
|
} |
|
public static int getByte(byte[] bytes, int pc) { |
|
return bytes[pc] & 0xFF; |
|
} |
|
|
|
|
|
public static void setInt(byte[] bytes, int pc, int x) { |
|
setShort(bytes, pc+0, x >> 16); |
|
setShort(bytes, pc+2, x >> 0); |
|
} |
|
public static void setShort(byte[] bytes, int pc, int x) { |
|
setByte(bytes, pc+0, x >> 8); |
|
setByte(bytes, pc+1, x >> 0); |
|
} |
|
public static void setByte(byte[] bytes, int pc, int x) { |
|
bytes[pc] = (byte)x; |
|
} |
|
|
|
// some bytecode classifiers |
|
|
|
|
|
public static boolean isNonstandard(int bc) { |
|
return BC_LENGTH[0][bc] < 0; |
|
} |
|
|
|
public static int opLength(int bc) { |
|
int l = BC_LENGTH[0][bc]; |
|
assert(l > 0); |
|
return l; |
|
} |
|
public static int opWideLength(int bc) { |
|
int l = BC_LENGTH[1][bc]; |
|
assert(l > 0); |
|
return l; |
|
} |
|
|
|
public static boolean isLocalSlotOp(int bc) { |
|
return (bc < BC_SLOT[0].length && BC_SLOT[0][bc] > 0); |
|
} |
|
|
|
public static boolean isBranchOp(int bc) { |
|
return (bc < BC_BRANCH[0].length && BC_BRANCH[0][bc] > 0); |
|
} |
|
|
|
public static boolean isCPRefOp(int bc) { |
|
if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return true; |
|
if (bc >= _xldc_op && bc < _xldc_limit) return true; |
|
if (bc == _invokespecial_int || bc == _invokestatic_int) return true; |
|
return false; |
|
} |
|
|
|
public static byte getCPRefOpTag(int bc) { |
|
if (bc < BC_INDEX[0].length && BC_INDEX[0][bc] > 0) return BC_TAG[0][bc]; |
|
if (bc >= _xldc_op && bc < _xldc_limit) return CONSTANT_LoadableValue; |
|
if (bc == _invokestatic_int || bc == _invokespecial_int) return CONSTANT_InterfaceMethodref; |
|
return CONSTANT_None; |
|
} |
|
|
|
public static boolean isFieldOp(int bc) { |
|
return (bc >= _getstatic && bc <= _putfield); |
|
} |
|
|
|
public static boolean isInvokeInitOp(int bc) { |
|
return (bc >= _invokeinit_op && bc < _invokeinit_limit); |
|
} |
|
|
|
public static boolean isSelfLinkerOp(int bc) { |
|
return (bc >= _self_linker_op && bc < _self_linker_limit); |
|
} |
|
|
|
/// Format definitions. |
|
|
|
private static final byte[][] BC_LENGTH = new byte[2][0x100]; |
|
private static final byte[][] BC_INDEX = new byte[2][0x100]; |
|
private static final byte[][] BC_TAG = new byte[2][0x100]; |
|
private static final byte[][] BC_BRANCH = new byte[2][0x100]; |
|
private static final byte[][] BC_SLOT = new byte[2][0x100]; |
|
private static final byte[][] BC_CON = new byte[2][0x100]; |
|
private static final String[] BC_NAME = new String[0x100]; |
|
private static final String[][] BC_FORMAT = new String[2][_bytecode_limit]; |
|
static { |
|
for (int i = 0; i < _bytecode_limit; i++) { |
|
BC_LENGTH[0][i] = -1; |
|
BC_LENGTH[1][i] = -1; |
|
} |
|
def("b", _nop, _dconst_1); |
|
def("bx", _bipush); |
|
def("bxx", _sipush); |
|
def("bk", _ldc); |
|
def("bkk", _ldc_w, _ldc2_w); |
|
def("blwbll", _iload, _aload); |
|
def("b", _iload_0, _saload); |
|
def("blwbll", _istore, _astore); |
|
def("b", _istore_0, _lxor); |
|
def("blxwbllxx", _iinc); |
|
def("b", _i2l, _dcmpg); |
|
def("boo", _ifeq, _jsr); |
|
def("blwbll", _ret); |
|
def("", _tableswitch, _lookupswitch); |
|
def("b", _ireturn, _return); |
|
def("bkf", _getstatic, _putfield); |
|
def("bkm", _invokevirtual, _invokestatic); |
|
def("bkixx", _invokeinterface); |
|
def("bkyxx", _invokedynamic); |
|
def("bkc", _new); |
|
def("bx", _newarray); |
|
def("bkc", _anewarray); |
|
def("b", _arraylength, _athrow); |
|
def("bkc", _checkcast, _instanceof); |
|
def("b", _monitorenter, _monitorexit); |
|
def("", _wide); |
|
def("bkcx", _multianewarray); |
|
def("boo", _ifnull, _ifnonnull); |
|
def("boooo", _goto_w, _jsr_w); |
|
for (int i = 0; i < _bytecode_limit; i++) { |
|
//System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]); |
|
|
|
if (BC_LENGTH[0][i] == -1) { |
|
continue; |
|
} |
|
|
|
|
|
if (BC_LENGTH[1][i] == -1) |
|
BC_LENGTH[1][i] = (byte)(1+BC_LENGTH[0][i]); |
|
} |
|
|
|
String names = |
|
"nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3 iconst_4 "+ |
|
"iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2 dconst_0 dconst_1 "+ |
|
"bipush sipush ldc ldc_w ldc2_w iload lload fload dload aload iload_0 "+ |
|
"iload_1 iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1 "+ |
|
"fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1 aload_2 "+ |
|
"aload_3 iaload laload faload daload aaload baload caload saload istore "+ |
|
"lstore fstore dstore astore istore_0 istore_1 istore_2 istore_3 lstore_0 "+ |
|
"lstore_1 lstore_2 lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 "+ |
|
"dstore_1 dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore "+ |
|
"lastore fastore dastore aastore bastore castore sastore pop pop2 dup "+ |
|
"dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd isub lsub "+ |
|
"fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem lrem frem drem "+ |
|
"ineg lneg fneg dneg ishl lshl ishr lshr iushr lushr iand land ior lor "+ |
|
"ixor lxor iinc i2l i2f i2d l2i l2f l2d f2i f2l f2d d2i d2l d2f i2b i2c "+ |
|
"i2s lcmp fcmpl fcmpg dcmpl dcmpg ifeq ifne iflt ifge ifgt ifle if_icmpeq "+ |
|
"if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne "+ |
|
"goto jsr ret tableswitch lookupswitch ireturn lreturn freturn dreturn "+ |
|
"areturn return getstatic putstatic getfield putfield invokevirtual "+ |
|
"invokespecial invokestatic invokeinterface invokedynamic new newarray "+ |
|
"anewarray arraylength athrow checkcast instanceof monitorenter "+ |
|
"monitorexit wide multianewarray ifnull ifnonnull goto_w jsr_w "; |
|
for (int bc = 0; names.length() > 0; bc++) { |
|
int sp = names.indexOf(' '); |
|
BC_NAME[bc] = names.substring(0, sp); |
|
names = names.substring(sp+1); |
|
} |
|
} |
|
public static String byteName(int bc) { |
|
String iname; |
|
if (bc < BC_NAME.length && BC_NAME[bc] != null) { |
|
iname = BC_NAME[bc]; |
|
} else if (isSelfLinkerOp(bc)) { |
|
int idx = (bc - _self_linker_op); |
|
boolean isSuper = (idx >= _self_linker_super_flag); |
|
if (isSuper) idx -= _self_linker_super_flag; |
|
boolean isAload = (idx >= _self_linker_aload_flag); |
|
if (isAload) idx -= _self_linker_aload_flag; |
|
int origBC = _first_linker_op + idx; |
|
assert(origBC >= _first_linker_op && origBC <= _last_linker_op); |
|
iname = BC_NAME[origBC]; |
|
iname += (isSuper ? "_super" : "_this"); |
|
if (isAload) iname = "aload_0&" + iname; |
|
iname = "*"+iname; |
|
} else if (isInvokeInitOp(bc)) { |
|
int idx = (bc - _invokeinit_op); |
|
switch (idx) { |
|
case _invokeinit_self_option: |
|
iname = "*invokespecial_init_this"; break; |
|
case _invokeinit_super_option: |
|
iname = "*invokespecial_init_super"; break; |
|
default: |
|
assert(idx == _invokeinit_new_option); |
|
iname = "*invokespecial_init_new"; break; |
|
} |
|
} else { |
|
switch (bc) { |
|
case _ildc: iname = "*ildc"; break; |
|
case _fldc: iname = "*fldc"; break; |
|
case _ildc_w: iname = "*ildc_w"; break; |
|
case _fldc_w: iname = "*fldc_w"; break; |
|
case _dldc2_w: iname = "*dldc2_w"; break; |
|
case _cldc: iname = "*cldc"; break; |
|
case _cldc_w: iname = "*cldc_w"; break; |
|
case _qldc: iname = "*qldc"; break; |
|
case _qldc_w: iname = "*qldc_w"; break; |
|
case _byte_escape: iname = "*byte_escape"; break; |
|
case _ref_escape: iname = "*ref_escape"; break; |
|
case _end_marker: iname = "*end"; break; |
|
default: iname = "*bc#"+bc; break; |
|
} |
|
} |
|
return iname; |
|
} |
|
private static int BW = 4; |
|
private static void def(String fmt, int bc) { |
|
def(fmt, bc, bc); |
|
} |
|
private static void def(String fmt, int from_bc, int to_bc) { |
|
String[] fmts = { fmt, null }; |
|
if (fmt.indexOf('w') > 0) { |
|
fmts[1] = fmt.substring(fmt.indexOf('w')); |
|
fmts[0] = fmt.substring(0, fmt.indexOf('w')); |
|
} |
|
for (int w = 0; w <= 1; w++) { |
|
fmt = fmts[w]; |
|
if (fmt == null) continue; |
|
int length = fmt.length(); |
|
int index = Math.max(0, fmt.indexOf('k')); |
|
int tag = CONSTANT_None; |
|
int branch = Math.max(0, fmt.indexOf('o')); |
|
int slot = Math.max(0, fmt.indexOf('l')); |
|
int con = Math.max(0, fmt.indexOf('x')); |
|
if (index > 0 && index+1 < length) { |
|
switch (fmt.charAt(index+1)) { |
|
case 'c': tag = CONSTANT_Class; break; |
|
case 'k': tag = CONSTANT_LoadableValue; break; |
|
case 'f': tag = CONSTANT_Fieldref; break; |
|
case 'm': tag = CONSTANT_Methodref; break; |
|
case 'i': tag = CONSTANT_InterfaceMethodref; break; |
|
case 'y': tag = CONSTANT_InvokeDynamic; break; |
|
} |
|
assert(tag != CONSTANT_None); |
|
} else if (index > 0 && length == 2) { |
|
assert(from_bc == _ldc); |
|
tag = CONSTANT_LoadableValue; |
|
} |
|
for (int bc = from_bc; bc <= to_bc; bc++) { |
|
BC_FORMAT[w][bc] = fmt; |
|
assert(BC_LENGTH[w][bc] == -1); |
|
BC_LENGTH[w][bc] = (byte) length; |
|
BC_INDEX[w][bc] = (byte) index; |
|
BC_TAG[w][bc] = (byte) tag; |
|
assert(!(index == 0 && tag != CONSTANT_None)); |
|
BC_BRANCH[w][bc] = (byte) branch; |
|
BC_SLOT[w][bc] = (byte) slot; |
|
assert(branch == 0 || slot == 0); |
|
assert(branch == 0 || index == 0); |
|
assert(slot == 0 || index == 0); |
|
BC_CON[w][bc] = (byte) con; |
|
} |
|
} |
|
} |
|
|
|
public static void opcodeChecker(byte[] code, ConstantPool.Entry[] cpMap, |
|
Package.Version clsVersion) throws FormatException { |
|
Instruction i = at(code, 0); |
|
while (i != null) { |
|
int opcode = i.getBC(); |
|
if (opcode < _nop || opcode > _jsr_w) { |
|
String message = "illegal opcode: " + opcode + " " + i; |
|
throw new FormatException(message); |
|
} |
|
ConstantPool.Entry e = i.getCPRef(cpMap); |
|
if (e != null) { |
|
byte tag = i.getCPTag(); |
|
boolean match = e.tagMatches(tag); |
|
if (!match && |
|
(i.bc == _invokespecial || i.bc == _invokestatic) && |
|
e.tagMatches(CONSTANT_InterfaceMethodref) && |
|
clsVersion.greaterThan(Constants.JAVA7_MAX_CLASS_VERSION)) { |
|
match = true; |
|
} |
|
if (!match) { |
|
String message = "illegal reference, expected type=" |
|
+ ConstantPool.tagName(tag) + ": " |
|
+ i.toString(cpMap); |
|
throw new FormatException(message); |
|
} |
|
} |
|
i = i.next(); |
|
} |
|
} |
|
static class FormatException extends IOException { |
|
private static final long serialVersionUID = 3175572275651367015L; |
|
|
|
FormatException(String message) { |
|
super(message); |
|
} |
|
} |
|
} |