📄 dismantlebytecode.java
字号:
/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003,2004 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */package edu.umd.cs.findbugs.visitclass;import java.io.ByteArrayInputStream;import java.io.DataInputStream;import java.io.IOException;import java.text.NumberFormat;import java.util.HashMap;import org.apache.bcel.classfile.Code;import org.apache.bcel.classfile.CodeException;import org.apache.bcel.classfile.Constant;import org.apache.bcel.classfile.ConstantCP;import org.apache.bcel.classfile.ConstantClass;import org.apache.bcel.classfile.ConstantDouble;import org.apache.bcel.classfile.ConstantFieldref;import org.apache.bcel.classfile.ConstantFloat;import org.apache.bcel.classfile.ConstantInteger;import org.apache.bcel.classfile.ConstantInterfaceMethodref;import org.apache.bcel.classfile.ConstantLong;import org.apache.bcel.classfile.ConstantMethodref;import org.apache.bcel.classfile.ConstantNameAndType;import org.apache.bcel.classfile.ConstantString;import org.apache.bcel.classfile.LineNumberTable;import edu.umd.cs.findbugs.annotations.SuppressWarnings;abstract public class DismantleBytecode extends AnnotationVisitor { private int opcode; private boolean opcodeIsWide; private int PC,nextPC; private int branchOffset; private int branchTarget; private int branchFallThrough; private int[] switchOffsets; private int[] switchLabels; private int[] prevOpcode = new int[32]; private int currentPosInPrevOpcodeBuffer; private int sizePrevOpcodeBuffer; private int defaultSwitchOffset; private String classConstantOperand; private String dottedClassConstantOperand; private String nameConstantOperand; private String sigConstantOperand; private String dottedSigConstantOperand; private String stringConstantOperand; private String refConstantOperand; private boolean refFieldIsStatic; private Constant constantRefOperand; private int intConstant; private long longConstant; private float floatConstant; private double doubleConstant; private int registerOperand; private boolean isRegisterLoad; private boolean isRegisterStore; private static final int INVALID_OFFSET = Integer.MIN_VALUE; private static final String NOT_AVAILABLE = "none"; /* protected static final int R_INT = 0; protected static final int R_LONG = 1; protected static final int R_FLOAT = 2; protected static final int R_DOUBLE = 3; protected static final int R_REF = 4; protected int registerKind; */ private static HashMap<String, String> replaceSlashesWithDotsCache = new HashMap<String, String>(); synchronized static String replaceSlashesWithDots(String c) { String result = replaceSlashesWithDotsCache.get(c); if (result != null) return result; result = c.replace('/', '.'); replaceSlashesWithDotsCache.put(c, result); return result; } /** * Meaning of bytecode operands */ public static final byte M_INT = 1; public static final byte M_UINT = 2; public static final byte M_CP = 3; public static final byte M_R = 4; public static final byte M_BR = 5; public static final byte M_PAD = 6; /** * Meaning of bytecode operands */ static final byte[][] MEANING_OF_OPERANDS = { // 0 1 2 3 4 5 6 7 8 9 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {M_INT}, {M_INT}, {M_CP}, {M_CP}, {M_CP}, {M_R}, {M_R}, {M_R}, {M_R}, {M_R}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {M_R}, {M_R}, {M_R}, {M_R}, {M_R}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},// 130 1 2 3 4 5 6 7 8 9 {}, {}, {M_R, M_INT}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {M_R},// 170 1 2 3 4 5 6 7 8 9 {}, {}, {}, {}, {}, {}, {}, {}, {M_CP}, {M_CP}, {M_CP}, {M_CP}, {M_CP}, {M_CP}, {M_CP}, {M_CP, M_PAD, M_PAD}, {}, {M_CP}, {M_UINT}, {M_CP},// 190 1 2 3 4 5 6 7 8 9 {}, {}, {M_CP}, {M_CP}, {}, {}, {M_PAD}, {M_CP, M_UINT}, {M_BR}, {M_BR}, {M_BR}, {M_BR}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }; protected byte[] codeBytes; protected LineNumberTable lineNumberTable; // Accessors /** If the current opcode has a class operand, get the associated class constant, dot-formatted */ public String getDottedClassConstantOperand() { if (dottedClassConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getDottedClassConstantOperand called but value not available"); return dottedClassConstantOperand; } /** If the current opcode has a reference constant operand, get its string representation */ public String getRefConstantOperand() { if (refConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getRefConstantOperand called but value not available"); if (refConstantOperand == null) { StringBuffer ref = new StringBuffer(dottedClassConstantOperand.length() + nameConstantOperand.length() + dottedSigConstantOperand.length() + 5); ref.append(dottedClassConstantOperand) .append(".") .append(nameConstantOperand) .append(" : ") .append(dottedSigConstantOperand); refConstantOperand = ref.toString(); } return refConstantOperand; } /** If the current opcode has a reference constant operand, get its name */ public String getNameConstantOperand() { if (nameConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getNameConstantOperand called but value not available"); return nameConstantOperand; } /** If the current opcode has a reference constant operand, get its signature, dot-formatted */ @Deprecated public String getDottedSigConstantOperand() { if (dottedSigConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getDottedSigConstantOperand called but value not available"); return dottedSigConstantOperand; } /** If the current opcode has a reference constant operand, get its signature, slash-formatted */ public String getSigConstantOperand() { if (sigConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getSigConstantOperand called but value not available"); return sigConstantOperand; } public String getClassConstantOperand() { if (classConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getClassConstantOperand called but value not available"); return classConstantOperand; } /** If the current opcode has a string constant operand, get its name */ public String getStringConstantOperand() { if (stringConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getStringConstantOperand called but value not available"); return stringConstantOperand; } public Constant getConstantRefOperand() { if (constantRefOperand == null) throw new IllegalStateException("getConstantRefOperand called but value not available"); return constantRefOperand; } public boolean isRegisterLoad() { return isRegisterLoad; } public boolean isRegisterStore() { return isRegisterStore; } public int getRegisterOperand() { if (registerOperand == -1) throw new IllegalStateException("getRegisterOperand called but value not available"); return registerOperand; } public int getIntConstant() { return intConstant; } public int getBranchOffset() { if (branchOffset == INVALID_OFFSET) throw new IllegalStateException("getBranchOffset called but value not available"); return branchOffset; } public int getBranchTarget() { if (branchTarget == INVALID_OFFSET) throw new IllegalStateException("getBranchTarget called but value not available"); return branchTarget; } public int getBranchFallThrough() { if (branchFallThrough == INVALID_OFFSET) throw new IllegalStateException("getBranchFallThrough called but value not available"); return branchFallThrough; } public int getDefaultSwitchOffset() { if (defaultSwitchOffset == INVALID_OFFSET) throw new IllegalStateException("getDefaultSwitchOffset called but value not available"); return defaultSwitchOffset; } public boolean getRefFieldIsStatic() { return refFieldIsStatic; } public int getPC() { return PC; } /** * return previous opcode; * @param offset 0 for current opcode, 1 for one before that, etc. */ public int getPrevOpcode(int offset) { if (offset >= prevOpcode.length || offset > sizePrevOpcodeBuffer) return NOP; int pos = currentPosInPrevOpcodeBuffer - offset; if (pos < 0) pos += prevOpcode.length; return prevOpcode[pos]; } /** * Return whether or not given opcode is a branch instruction. * * @param opcode the opcode * @return true if instruction is a branch, false if not */ public static boolean isBranch(int opcode) { byte[] operands = MEANING_OF_OPERANDS[opcode]; return operands.length > 0 && operands[0] == M_BR; } /** * Return whether or not given opcode is a switch instruction. * * @param opcode the opcode * @return true if instruction is a switch, false if not */ public static boolean isSwitch(int opcode) { return opcode == LOOKUPSWITCH || opcode == TABLESWITCH; } @SuppressWarnings("EI") public int[] getSwitchOffsets() { if (switchOffsets == null) throw new IllegalStateException("getSwitchOffsets called but value not available"); return switchOffsets; } @SuppressWarnings("EI") public int[] getSwitchLabels() { if (switchLabels == null) throw new IllegalStateException("getSwitchOffsets called but value not available"); return switchLabels; } private void resetState() { dottedClassConstantOperand = classConstantOperand = nameConstantOperand = sigConstantOperand = dottedSigConstantOperand = stringConstantOperand = refConstantOperand = NOT_AVAILABLE; refFieldIsStatic = false; constantRefOperand = null; registerOperand = -1; isRegisterLoad = false; isRegisterStore = false; branchOffset = branchTarget = branchFallThrough = defaultSwitchOffset = INVALID_OFFSET; switchOffsets = switchLabels = null; } private static void sortByOffset(int[] switchOffsets, int[] switchLabels) { int npairs = switchOffsets.length; // Sort by offset for (int j = 0; j < npairs; j++) { int min = j; for (int k = j + 1; k < npairs; k++) if (switchOffsets[min] > switchOffsets[k]) min = k; if (min > j) { int tmp = switchOffsets[min]; switchOffsets[min] = switchOffsets[j]; switchOffsets[j] = tmp; tmp = switchLabels[min]; switchLabels[min] = switchLabels[j]; switchLabels[j] = tmp; } } } public int getMaxPC() { return codeBytes.length-1; } public int getCodeByte(int offset) { return 0xff & codeBytes[offset]; } public int getOpcode() { return opcode; } public boolean atCatchBlock() { for(CodeException e : getCode().getExceptionTable()) if (e.getHandlerPC() == getPC()) return true; return false; } @Override public void visit(Code obj) { sizePrevOpcodeBuffer = 0; currentPosInPrevOpcodeBuffer = prevOpcode.length-1; int switchLow = 1000000; int switchHigh = -1000000; codeBytes = obj.getCode(); DataInputStream byteStream = new DataInputStream(new ByteArrayInputStream(codeBytes)); lineNumberTable = obj.getLineNumberTable(); try { for (int i = 0; i < codeBytes.length;) { resetState(); PC = i; opcodeIsWide = false; opcode = byteStream.readUnsignedByte(); sizePrevOpcodeBuffer++; currentPosInPrevOpcodeBuffer++; if (currentPosInPrevOpcodeBuffer >= prevOpcode.length) currentPosInPrevOpcodeBuffer = 0; prevOpcode[currentPosInPrevOpcodeBuffer] = opcode; i++; // System.out.println(OPCODE_NAMES[opCode]); int byteStreamArgCount = NO_OF_OPERANDS[opcode]; if (byteStreamArgCount == UNPREDICTABLE) { if (opcode == LOOKUPSWITCH) { int pad = 4 - (i & 3); if (pad == 4) pad = 0; int count = pad; while (count > 0) count -= byteStream.skipBytes(count); i += pad; defaultSwitchOffset = byteStream.readInt(); branchOffset = defaultSwitchOffset; branchTarget = branchOffset + PC; i += 4; int npairs = byteStream.readInt(); i += 4; switchOffsets = new int[npairs]; switchLabels = new int[npairs]; for (int o = 0; o < npairs; o++) { switchLabels[o] = byteStream.readInt(); switchOffsets[o] = byteStream.readInt(); i += 8; } sortByOffset(switchOffsets, switchLabels); } else if (opcode == TABLESWITCH) { int pad = 4 - (i & 3); if (pad == 4) pad = 0; int count = pad; while (count > 0) count -= byteStream.skipBytes(count); i += pad; defaultSwitchOffset = byteStream.readInt(); branchOffset = defaultSwitchOffset; branchTarget = branchOffset + PC; i += 4; switchLow = byteStream.readInt(); i += 4; switchHigh = byteStream.readInt(); i += 4; int npairs = switchHigh - switchLow + 1; switchOffsets = new int[npairs]; switchLabels = new int[npairs]; for (int o = 0; o < npairs; o++) { switchLabels[o] = o + switchLow;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -