use of com.mebigfatguy.fbcontrib.collect.Statistics in project fb-contrib by mebigfatguy.
the class PossiblyRedundantMethodCalls method sawOpcode.
/**
* implements the visitor to look for repetitive calls to the same method on the same object using the same constant parameters. These methods must return a
* value.
*
* @param seen
* the opcode of the currently parsed instruction
*/
@Override
public void sawOpcode(int seen) {
String userValue = null;
try {
stack.precomputation(this);
int pc = getPC();
if (branchTargets.get(pc)) {
localMethodCalls.clear();
fieldMethodCalls.clear();
branchTargets.clear(pc);
}
if (((seen >= Const.IFEQ) && (seen <= Const.GOTO)) || ((seen >= Const.IFNULL) && (seen <= Const.GOTO_W))) {
branchTargets.set(getBranchTarget());
} else if ((seen == Const.TABLESWITCH) || (seen == Const.LOOKUPSWITCH)) {
int[] offsets = getSwitchOffsets();
for (int offset : offsets) {
branchTargets.set(offset + pc);
}
branchTargets.set(getDefaultSwitchOffset() + pc);
} else if (OpcodeUtils.isAStore(seen)) {
localMethodCalls.remove(Integer.valueOf(RegisterUtils.getAStoreReg(this, seen)));
} else if (seen == Const.PUTFIELD) {
String fieldSource = "";
if (stack.getStackDepth() > 0) {
OpcodeStack.Item item = stack.getStackItem(0);
fieldSource = (String) item.getUserValue();
if (fieldSource == null) {
fieldSource = "";
}
}
fieldMethodCalls.remove(new FieldInfo(fieldSource, getNameConstantOperand()));
} else if (seen == Const.GETFIELD) {
if (stack.getStackDepth() > 0) {
OpcodeStack.Item item = stack.getStackItem(0);
userValue = (String) item.getUserValue();
if (userValue == null) {
int reg = item.getRegisterNumber();
if (reg >= 0) {
userValue = String.valueOf(reg);
} else {
XField xf = item.getXField();
if (xf != null) {
userValue = xf.getName();
}
}
}
}
} else if ((seen == Const.INVOKEVIRTUAL) || (seen == Const.INVOKEINTERFACE) || (seen == Const.INVOKESTATIC)) {
String className = getClassConstantOperand();
String methodName = getNameConstantOperand();
String signature = getSigConstantOperand();
int parmCount = SignatureUtils.getNumParameters(signature);
int reg = -1;
XField field = null;
MethodCall mc = null;
String fieldSource = null;
if (seen == Const.INVOKESTATIC) {
XMethod xm = XFactory.createXMethod(getDottedClassConstantOperand(), methodName, signature, true);
String genericSignature = xm.getSourceSignature();
if ((genericSignature != null) && genericSignature.endsWith(">;")) {
return;
}
} else if (stack.getStackDepth() > parmCount) {
OpcodeStack.Item obj = stack.getStackItem(parmCount);
reg = obj.getRegisterNumber();
field = obj.getXField();
if (reg >= 0) {
mc = localMethodCalls.get(Integer.valueOf(reg));
MethodInfo mi = Statistics.getStatistics().getMethodStatistics(className, getNameConstantOperand(), signature);
if ((mi != null) && mi.getModifiesState()) {
clearFieldMethods(String.valueOf(reg));
return;
}
} else if (field != null) {
fieldSource = (String) obj.getUserValue();
if (fieldSource == null) {
fieldSource = "";
}
mc = fieldMethodCalls.get(new FieldInfo(fieldSource, field.getName()));
MethodInfo mi = Statistics.getStatistics().getMethodStatistics(className, getNameConstantOperand(), signature);
if ((mi != null) && mi.getModifiesState()) {
clearFieldMethods(fieldSource);
return;
}
}
}
int neededStackSize = parmCount + ((seen == Const.INVOKESTATIC) ? 0 : 1);
if (stack.getStackDepth() >= neededStackSize) {
Object[] parmConstants = new Object[parmCount];
for (int i = 0; i < parmCount; i++) {
OpcodeStack.Item parm = stack.getStackItem(i);
parmConstants[i] = parm.getConstant();
if (parm.getSignature().startsWith(Values.SIG_ARRAY_PREFIX)) {
if (!Values.ZERO.equals(parm.getConstant())) {
return;
}
XField f = parm.getXField();
if (f != null) {
// Two different fields holding a 0 length array should be considered different
parmConstants[i] = f.getName() + ':' + parmConstants[i];
}
}
if (parmConstants[i] == null) {
return;
}
}
if (seen == Const.INVOKESTATIC) {
mc = staticMethodCalls.get(className);
} else if ((reg < 0) && (field == null)) {
return;
}
if (mc != null) {
if (!signature.endsWith(Values.SIG_VOID) && methodName.equals(mc.getName()) && signature.equals(mc.getSignature()) && !isRiskyName(className, methodName) && !commonMethods.contains(new FQMethod(getClassConstantOperand(), methodName, signature))) {
Object[] parms = mc.getParms();
if (Arrays.equals(parms, parmConstants)) {
int ln = getLineNumber(pc);
if ((ln != mc.getLineNumber()) || (Math.abs(pc - mc.getPC()) < 10)) {
Statistics statistics = Statistics.getStatistics();
MethodInfo mi = statistics.getMethodStatistics(getClassConstantOperand(), methodName, signature);
bugReporter.reportBug(new BugInstance(this, BugType.PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS.name(), getBugPriority(methodName, mi)).addClass(this).addMethod(this).addSourceLine(this).addString(methodName + signature));
}
}
}
if (seen == Const.INVOKESTATIC) {
staticMethodCalls.remove(className);
} else {
if (reg >= 0) {
localMethodCalls.remove(Integer.valueOf(reg));
} else if (fieldSource != null) {
fieldMethodCalls.remove(new FieldInfo(fieldSource, field.getName()));
}
}
} else {
int ln = getLineNumber(pc);
if (seen == Const.INVOKESTATIC) {
staticMethodCalls.put(className, new MethodCall(methodName, signature, parmConstants, pc, ln));
} else {
if (reg >= 0) {
localMethodCalls.put(Integer.valueOf(reg), new MethodCall(methodName, signature, parmConstants, pc, ln));
} else if (field != null) {
OpcodeStack.Item obj = stack.getStackItem(parmCount);
fieldSource = (String) obj.getUserValue();
if (fieldSource == null) {
fieldSource = "";
}
fieldMethodCalls.put(new FieldInfo(fieldSource, field.getName()), new MethodCall(methodName, signature, parmConstants, pc, ln));
}
}
}
}
} else if (OpcodeUtils.isReturn(seen)) {
localMethodCalls.clear();
fieldMethodCalls.clear();
branchTargets.clear(pc);
}
} finally {
stack.sawOpcode(this, seen);
if ((userValue != null) && (stack.getStackDepth() > 0)) {
OpcodeStack.Item item = stack.getStackItem(0);
item.setUserValue(userValue);
}
}
}
Aggregations