/*
* 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;
}
}