| /* |
| * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.java.util.jar.pack; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import static com.sun.java.util.jar.pack.Constants.*; |
| |
| /** |
| * A parsed bytecode instruction. |
| * Provides accessors to various relevant bits. |
| * @author John Rose |
| */ |
| class Instruction { |
| protected byte[] bytes; // bytecodes |
| protected int pc; // location of this instruction |
| protected int bc; // opcode of this instruction |
| protected int w; // 0 if normal, 1 if a _wide prefix at pc |
| protected int length; // bytes in this instruction |
| |
| 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; |
| } |
| |
| |
| /** A fake instruction at this pc whose next() will be at nextpc. */ |
| 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) { |
| // Hard cases: |
| switch (bc) { |
| case _wide: |
| bc = getByte(bytes, pc+1); |
| w = 1; |
| length = BC_LENGTH[w][bc]; |
| if (length == 0) { |
| // unknown instruction; treat as one byte |
| length = 1; |
| } |
| break; |
| case _tableswitch: |
| return new TableSwitch(bytes, pc); |
| case _lookupswitch: |
| return new LookupSwitch(bytes, pc); |
| default: |
| // unknown instruction; treat as one byte |
| length = 1; |
| break; |
| } |
| } |
| assert(length > 0); |
| assert(pc+length <= bytes.length); |
| // Speed hack: Instruction.next reuses self if possible. |
| if (reuse != null && !reuse.special) { |
| reuse.reset(bytes, pc, bc, w, length); |
| return reuse; |
| } |
| return new Instruction(bytes, pc, bc, w, length); |
| } |
| |
| // Return the constant pool reference type, or 0 if none. |
| public byte getCPTag() { |
| return BC_TAG[w][bc]; |
| } |
| |
| // Return the constant pool index, or -1 if none. |
| 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); // _ldc opcode only |
| 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); // _ldc opcode only |
| 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]; |
| } |
| |
| // Return the slot of the affected local, or -1 if none. |
| 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); |
| } |
| |
| // Return the target of the branch, or -1 if none. |
| 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). |
| // Return 0 if there is none. |
| 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 { |
| // Each case is a (value, label) pair, indexed 0 <= n < caseCount |
| 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; // aligned pc (table base) |
| 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, /*w*/0, /*length*/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 { |
| // apc: (df, lo, hi, (hi-lo+1)*(label)) |
| 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); // keep invariant |
| } |
| |
| 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 { |
| // apc: (df, nc, nc*(case, label)) |
| 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; |
| } |
| } |
| |
| /** Two instructions are equal if they have the same bytes. */ |
| 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. |
| |
| static private final byte[][] BC_LENGTH = new byte[2][0x100]; |
| static private final byte[][] BC_INDEX = new byte[2][0x100]; |
| static private final byte[][] BC_TAG = new byte[2][0x100]; |
| static private final byte[][] BC_BRANCH = new byte[2][0x100]; |
| static private final byte[][] BC_SLOT = new byte[2][0x100]; |
| static private final byte[][] BC_CON = new byte[2][0x100]; |
| static private final String[] BC_NAME = new String[0x100]; // debug only |
| static private final String[][] BC_FORMAT = new String[2][_bytecode_limit]; // debug only |
| 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); // do not pack |
| def("bkk", _ldc_w, _ldc2_w); // do not pack |
| 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); // pack oo |
| def("blwbll", _ret); |
| def("", _tableswitch, _lookupswitch); // pack all ints, omit padding |
| def("b", _ireturn, _return); |
| def("bkf", _getstatic, _putfield); // pack kf (base=Field) |
| def("bkm", _invokevirtual, _invokestatic); // pack kn (base=Method) |
| def("bkixx", _invokeinterface); // pack ki (base=IMethod), omit xx |
| def("bkyxx", _invokedynamic); // pack ky (base=Any), omit xx |
| def("bkc", _new); // pack kc |
| def("bx", _newarray); |
| def("bkc", _anewarray); // pack kc |
| def("b", _arraylength, _athrow); |
| def("bkc", _checkcast, _instanceof); // pack kc |
| def("b", _monitorenter, _monitorexit); |
| def("", _wide); |
| def("bkcx", _multianewarray); // pack kc |
| def("boo", _ifnull, _ifnonnull); // pack oo |
| def("boooo", _goto_w, _jsr_w); // pack oooo |
| for (int i = 0; i < _bytecode_limit; i++) { |
| //System.out.println(i+": l="+BC_LENGTH[0][i]+" i="+BC_INDEX[0][i]); |
| //assert(BC_LENGTH[0][i] != -1); |
| if (BC_LENGTH[0][i] == -1) { |
| continue; // unknown opcode |
| } |
| |
| // Have a complete mapping, to support spurious _wide prefixes. |
| 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; // width of classification field |
| 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; // _ldc opcode only |
| } |
| 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); // not both branch & local |
| assert(branch == 0 || index == 0); // not both branch & cp |
| assert(slot == 0 || index == 0); // not both local & cp |
| 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); |
| } |
| } |
| } |