/* * 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 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.CheckForNull; import edu.umd.cs.findbugs.annotations.SuppressWarnings; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.ba.Hierarchy2; import edu.umd.cs.findbugs.ba.XClass; import edu.umd.cs.findbugs.ba.XField; import edu.umd.cs.findbugs.ba.XMethod; import edu.umd.cs.findbugs.classfile.CheckedAnalysisException; import edu.umd.cs.findbugs.classfile.ClassDescriptor; import edu.umd.cs.findbugs.classfile.DescriptorFactory; import edu.umd.cs.findbugs.classfile.FieldDescriptor; import edu.umd.cs.findbugs.classfile.Global; import edu.umd.cs.findbugs.classfile.MethodDescriptor; import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName; import edu.umd.cs.findbugs.util.ClassName; 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 final int[] prevOpcode = new int[32]; private int currentPosInPrevOpcodeBuffer; private int sizePrevOpcodeBuffer; private int defaultSwitchOffset; private @SlashedClassName String classConstantOperand; private ClassDescriptor referencedClass; private XClass referencedXClass; private MethodDescriptor referencedMethod; private XMethod referencedXMethod; private FieldDescriptor referencedField; private XField referencedXField; private String dottedClassConstantOperand; private String nameConstantOperand; private String sigConstantOperand; 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 = "./."; static String replaceSlashesWithDots(String c) { return c.replace('/', '.'); } /** * 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 public ClassDescriptor getClassDescriptorOperand() { if (referencedClass == null) throw new IllegalStateException("getClassDescriptorOperand called but value not available"); return referencedClass; } public @CheckForNull XClass getXClassOperand() { return getReferencedXClass(); } public MethodDescriptor getMethodDescriptorOperand() { if (nameConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getMethodDescriptorOperand called but value not available"); if (referencedMethod == null) { referencedMethod = DescriptorFactory.instance().getMethodDescriptor(classConstantOperand, nameConstantOperand, sigConstantOperand, opcode == INVOKESTATIC); } return referencedMethod; } public @CheckForNull XMethod getXMethodOperand() { if (getReferencedXClass() != null && referencedXMethod == null) { referencedXMethod = Hierarchy2.findInvocationLeastUpperBound(getReferencedXClass(), nameConstantOperand, sigConstantOperand, opcode == INVOKESTATIC, opcode == INVOKEINTERFACE); } return referencedXMethod; } public FieldDescriptor getFieldDescriptorOperand() { if (nameConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getFieldDescriptorOperand called but value not available"); if (referencedField == null) { referencedField = DescriptorFactory.instance().getFieldDescriptor(classConstantOperand, nameConstantOperand, sigConstantOperand, opcode == GETSTATIC || opcode == PUTSTATIC); } return referencedField; } public @CheckForNull XField getXFieldOperand() { if (getReferencedXClass() != null && referencedXField == null) referencedXField = getReferencedXClass().findField(nameConstantOperand, sigConstantOperand, opcode == GETSTATIC || opcode == PUTSTATIC); return referencedXField; } /** * If the current opcode has a class operand, get the associated class * constant, dot-formatted */ public String getDottedClassConstantOperand() { if (dottedClassConstantOperand != null) { assert dottedClassConstantOperand != NOT_AVAILABLE; return dottedClassConstantOperand; } if (classConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getDottedClassConstantOperand called but value not available"); dottedClassConstantOperand = ClassName.toDottedClassName(classConstantOperand); return dottedClassConstantOperand; } /** * If the current opcode has a reference constant operand, get its string * representation */ @Deprecated public String getRefConstantOperand() { if (refConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getRefConstantOperand called but value not available"); if (refConstantOperand == null) { String dottedClassConstantOperand = getDottedClassConstantOperand(); StringBuilder ref = new StringBuilder(dottedClassConstantOperand.length() + nameConstantOperand.length() + sigConstantOperand.length() + 5); ref.append(dottedClassConstantOperand).append(".").append(nameConstantOperand).append(" : ") .append(replaceSlashesWithDots(sigConstantOperand)); 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, slash-formatted */ public String getSigConstantOperand() { if (sigConstantOperand == NOT_AVAILABLE) throw new IllegalStateException("getSigConstantOperand called but value not available"); return sigConstantOperand; } /** * If the current opcode has a class constant operand, get the classname, * slash-formatted. */ public @SlashedClassName 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() { assert getOpcode() != LDC || getConstantRefOperand() instanceof ConstantInteger; return intConstant; } public long getLongConstant() { assert getOpcode() != LDC2_W || getConstantRefOperand() instanceof ConstantLong; return longConstant; } 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 < 0) throw new IllegalArgumentException("offset (" + offset + ") must be nonnegative"); if (offset >= prevOpcode.length || offset > sizePrevOpcodeBuffer) return NOP; int pos = currentPosInPrevOpcodeBuffer - offset; if (pos < 0) pos += prevOpcode.length; return prevOpcode[pos]; } public boolean isWideOpcode() { return opcodeIsWide; } /** * 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("getSwitchLabels called but value not available"); return switchLabels; } private void resetState() { classConstantOperand = nameConstantOperand = sigConstantOperand = stringConstantOperand = refConstantOperand = NOT_AVAILABLE; refFieldIsStatic = false; constantRefOperand = null; registerOperand = -1; isRegisterLoad = false; isRegisterStore = false; branchOffset = branchTarget = branchFallThrough = defaultSwitchOffset = INVALID_OFFSET; switchOffsets = switchLabels = null; dottedClassConstantOperand = null; referencedClass = null; setReferencedXClass(null); referencedMethod = null; referencedXMethod = null; referencedField = null; referencedXField = 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; switchOffsets[o] = byteStream.readInt(); i += 4; } sortByOffset(switchOffsets, switchLabels); } else if (opcode == WIDE) { opcodeIsWide = true; opcode = byteStream.readUnsignedByte(); i++; switch (opcode) { case ILOAD: case FLOAD: case ALOAD: case LLOAD: case DLOAD: case ISTORE: case FSTORE: case ASTORE: case LSTORE: case DSTORE: case RET: registerOperand = byteStream.readUnsignedShort(); i += 2; break; case IINC: registerOperand = byteStream.readUnsignedShort(); i += 2; intConstant = byteStream.readShort(); i += 2; break; default: throw new IllegalStateException("bad wide bytecode: " + OPCODE_NAMES[opcode]); } } else throw new IllegalStateException("bad unpredicatable bytecode: " + OPCODE_NAMES[opcode]); } else { if (byteStreamArgCount < 0) throw new IllegalStateException("bad length for bytecode: " + OPCODE_NAMES[opcode]); for (int k = 0; k < TYPE_OF_OPERANDS[opcode].length; k++) { int v; int t = TYPE_OF_OPERANDS[opcode][k]; int m = MEANING_OF_OPERANDS[opcode][k]; boolean unsigned = (m == M_CP || m == M_R || m == M_UINT); switch (t) { case T_BYTE: if (unsigned) v = byteStream.readUnsignedByte(); else v = byteStream.readByte(); /* * System.out.print("Read byte " + v); * System.out.println(" with meaning" + m); */ i++; break; case T_SHORT: if (unsigned) v = byteStream.readUnsignedShort(); else v = byteStream.readShort(); i += 2; break; case T_INT: v = byteStream.readInt(); i += 4; break; default: throw new IllegalStateException(); } switch (m) { case M_BR: branchOffset = v; branchTarget = v + PC; branchFallThrough = i; break; case M_CP: constantRefOperand = getConstantPool().getConstant(v); if (constantRefOperand instanceof ConstantClass) { ConstantClass clazz = (ConstantClass) constantRefOperand; classConstantOperand = getStringFromIndex(clazz.getNameIndex()); referencedClass = DescriptorFactory.createClassDescriptor(classConstantOperand); } else if (constantRefOperand instanceof ConstantInteger) intConstant = ((ConstantInteger) constantRefOperand).getBytes(); else if (constantRefOperand instanceof ConstantLong) longConstant = ((ConstantLong) constantRefOperand).getBytes(); else if (constantRefOperand instanceof ConstantFloat) floatConstant = ((ConstantFloat) constantRefOperand).getBytes(); else if (constantRefOperand instanceof ConstantDouble) doubleConstant = ((ConstantDouble) constantRefOperand).getBytes(); else if (constantRefOperand instanceof ConstantString) { int s = ((ConstantString) constantRefOperand).getStringIndex(); stringConstantOperand = getStringFromIndex(s); } else if (constantRefOperand instanceof ConstantCP) { ConstantCP cp = (ConstantCP) constantRefOperand; ConstantClass clazz = (ConstantClass) getConstantPool().getConstant(cp.getClassIndex()); classConstantOperand = getStringFromIndex(clazz.getNameIndex()); referencedClass = DescriptorFactory.createClassDescriptor(classConstantOperand); referencedXClass = null; ConstantNameAndType sig = (ConstantNameAndType) getConstantPool().getConstant( cp.getNameAndTypeIndex()); nameConstantOperand = getStringFromIndex(sig.getNameIndex()); sigConstantOperand = getStringFromIndex(sig.getSignatureIndex()); refConstantOperand = null; } break; case M_R: registerOperand = v; break; case M_UINT: case M_INT: intConstant = v; } } } switch (opcode) { case IINC: isRegisterLoad = true; isRegisterStore = true; break; case ILOAD_0: case ILOAD_1: case ILOAD_2: case ILOAD_3: registerOperand = opcode - ILOAD_0; isRegisterLoad = true; break; case ALOAD_0: case ALOAD_1: case ALOAD_2: case ALOAD_3: registerOperand = opcode - ALOAD_0; isRegisterLoad = true; break; case FLOAD_0: case FLOAD_1: case FLOAD_2: case FLOAD_3: registerOperand = opcode - FLOAD_0; isRegisterLoad = true; break; case DLOAD_0: case DLOAD_1: case DLOAD_2: case DLOAD_3: registerOperand = opcode - DLOAD_0; isRegisterLoad = true; break; case LLOAD_0: case LLOAD_1: case LLOAD_2: case LLOAD_3: registerOperand = opcode - LLOAD_0; isRegisterLoad = true; break; case ILOAD: case FLOAD: case ALOAD: case LLOAD: case DLOAD: isRegisterLoad = true; break; case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: registerOperand = opcode - ISTORE_0; isRegisterStore = true; break; case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: registerOperand = opcode - ASTORE_0; isRegisterStore = true; break; case FSTORE_0: case FSTORE_1: case FSTORE_2: case FSTORE_3: registerOperand = opcode - FSTORE_0; isRegisterStore = true; break; case DSTORE_0: case DSTORE_1: case DSTORE_2: case DSTORE_3: registerOperand = opcode - DSTORE_0; isRegisterStore = true; break; case LSTORE_0: case LSTORE_1: case LSTORE_2: case LSTORE_3: registerOperand = opcode - LSTORE_0; isRegisterStore = true; break; case ISTORE: case FSTORE: case ASTORE: case LSTORE: case DSTORE: isRegisterStore = true; break; } switch (opcode) { case ILOAD: case FLOAD: case ALOAD: case LLOAD: case DLOAD: // registerKind = opcode - ILOAD; break; case ISTORE: case FSTORE: case ASTORE: case LSTORE: case DSTORE: // registerKind = opcode - ISTORE; break; case RET: // registerKind = R_REF; break; case GETSTATIC: case PUTSTATIC: refFieldIsStatic = true; break; case GETFIELD: case PUTFIELD: refFieldIsStatic = false; break; } nextPC = i; if (beforeOpcode(opcode)) sawOpcode(opcode); afterOpcode(opcode); if (opcode == TABLESWITCH) { sawInt(switchLow); sawInt(switchHigh); int prevOffset = i - PC; for (int o = 0; o <= switchHigh - switchLow; o++) { sawBranchTo(switchOffsets[o] + PC); prevOffset = switchOffsets[o]; } sawBranchTo(defaultSwitchOffset + PC); } else if (opcode == LOOKUPSWITCH) { sawInt(switchOffsets.length); int prevOffset = i - PC; for (int o = 0; o < switchOffsets.length; o++) { sawBranchTo(switchOffsets[o] + PC); prevOffset = switchOffsets[o]; sawInt(switchLabels[o]); } sawBranchTo(defaultSwitchOffset + PC); } else for (int k = 0; k < TYPE_OF_OPERANDS[opcode].length; k++) { int m = MEANING_OF_OPERANDS[opcode][k]; switch (m) { case M_BR: sawBranchTo(branchOffset + PC); break; case M_CP: if (constantRefOperand instanceof ConstantInteger) sawInt(intConstant); else if (constantRefOperand instanceof ConstantLong) sawLong(longConstant); else if (constantRefOperand instanceof ConstantFloat) sawFloat(floatConstant); else if (constantRefOperand instanceof ConstantDouble) sawDouble(doubleConstant); else if (constantRefOperand instanceof ConstantString) sawString(stringConstantOperand); else if (constantRefOperand instanceof ConstantFieldref) sawField(); else if (constantRefOperand instanceof ConstantMethodref) sawMethod(); else if (constantRefOperand instanceof ConstantInterfaceMethodref) sawIMethod(); else if (constantRefOperand instanceof ConstantClass) sawClass(); break; case M_R: sawRegister(registerOperand); break; case M_INT: sawInt(intConstant); break; } } } } catch (IOException e) { AnalysisContext.logError("Error while dismantling bytecode", e); assert false; } try { byteStream.close(); } catch (IOException e) { assert false; } } public void sawDouble(double seen) { } public void sawFloat(float seen) { } public void sawRegister(int r) { } public void sawInt(int seen) { } public void sawLong(long seen) { } public void sawBranchTo(int targetPC) { } /** return false if we should skip calling sawOpcode */ public boolean beforeOpcode(int seen) { return true; } public void afterOpcode(int seen) { } public void sawOpcode(int seen) { } public void sawString(String seen) { } public void sawField() { } public void sawMethod() { } public void sawIMethod() { } public void sawClass() { } static private NumberFormat formatter = NumberFormat.getIntegerInstance(); static { formatter.setMinimumIntegerDigits(4); formatter.setGroupingUsed(false); } public void printOpCode(int seen) { System.out.print(" " + this.getClass().getSimpleName() + ": [" + formatter.format(getPC()) + "] " + OPCODE_NAMES[seen]); if ((seen == INVOKEVIRTUAL) || (seen == INVOKESPECIAL) || (seen == INVOKEINTERFACE) || (seen == INVOKESTATIC)) System.out.print(" " + getClassConstantOperand() + "." + getNameConstantOperand() + " " + getSigConstantOperand()); else if (seen == LDC || seen == LDC_W || seen == LDC2_W) { Constant c = getConstantRefOperand(); if (c instanceof ConstantString) System.out.print(" \"" + getStringConstantOperand() + "\""); else if (c instanceof ConstantClass) System.out.print(" " + getClassConstantOperand()); else System.out.print(" " + c); } else if ((seen == ALOAD) || (seen == ASTORE)) System.out.print(" " + getRegisterOperand()); else if ((seen == GOTO) || (seen == GOTO_W) || (seen == IF_ACMPEQ) || (seen == IF_ACMPNE) || (seen == IF_ICMPEQ) || (seen == IF_ICMPGE) || (seen == IF_ICMPGT) || (seen == IF_ICMPLE) || (seen == IF_ICMPLT) || (seen == IF_ICMPNE) || (seen == IFEQ) || (seen == IFGE) || (seen == IFGT) || (seen == IFLE) || (seen == IFLT) || (seen == IFNE) || (seen == IFNONNULL) || (seen == IFNULL)) System.out.print(" " + getBranchTarget()); else if ((seen == NEW) || (seen == INSTANCEOF)) System.out.print(" " + getClassConstantOperand()); else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) { System.out.print(" ["); int switchPC = getPC(); int[] offsets = getSwitchOffsets(); for (int offset : offsets) { System.out.print((switchPC + offset) + ","); } System.out.print((switchPC + getDefaultSwitchOffset()) + "]"); } System.out.println(); } /** * @return Returns the nextPC. */ public int getNextPC() { return nextPC; } public int getNextOpcode() { return codeBytes[nextPC] & 0xff; } public boolean isReturn(int opcode) { switch (opcode) { case IRETURN: case ARETURN: case LRETURN: case DRETURN: case FRETURN: case RETURN: return true; default: return false; } } public static boolean areOppositeBranches(int opcode1, int opcode2) { if (!isBranch(opcode1)) throw new IllegalArgumentException(OPCODE_NAMES[opcode1] + " isn't a branch"); if (!isBranch(opcode2)) throw new IllegalArgumentException(OPCODE_NAMES[opcode2] + " isn't a branch"); switch (opcode1) { case IF_ACMPEQ: case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPLE: case IF_ICMPGT: case IF_ICMPGE: case IFNE: case IFEQ: case IFLT: case IFLE: case IFGT: case IFGE: return ((opcode1 + 1) ^ 1) == opcode2 + 1; case IFNONNULL: return opcode2 == IFNULL; case IFNULL: return opcode2 == IFNONNULL; default: return false; } } public boolean isRegisterStore(int opcode) { switch (opcode) { case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: case FSTORE_0: case FSTORE_1: case FSTORE_2: case FSTORE_3: case DSTORE_0: case DSTORE_1: case DSTORE_2: case DSTORE_3: case LSTORE_0: case LSTORE_1: case LSTORE_2: case LSTORE_3: case ISTORE: case FSTORE: case ASTORE: case LSTORE: case DSTORE: return true; default: return false; } } /** * @param referencedXClass * The referencedXClass to set. */ private void setReferencedXClass(XClass referencedXClass) { this.referencedXClass = referencedXClass; } /** * @return Returns the referencedXClass. */ private XClass getReferencedXClass() { if (referencedXClass == null && referencedClass != null) try { referencedXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, referencedClass); } catch (CheckedAnalysisException e) { assert true; } return referencedXClass; } }