package edu.umd.cs.findbugs.ba;
import org.apache.bcel.Constants;
import org.apache.bcel.generic.IFNONNULL;
import org.apache.bcel.generic.IFNULL;
import org.apache.bcel.generic.IF_ACMPEQ;
import org.apache.bcel.generic.IF_ACMPNE;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.SystemProperties;
@javax.annotation.ParametersAreNonnullByDefault
public class ResourceValueAnalysis<Resource> extends FrameDataflowAnalysis<ResourceValue, ResourceValueFrame> implements
EdgeTypes {
private static final boolean DEBUG = SystemProperties.getBoolean("dataflow.debug");
private final MethodGen methodGen;
private final CFG cfg;
private final ResourceTracker<Resource> resourceTracker;
private final Resource resource;
private final ResourceValueFrameModelingVisitor visitor;
private final boolean ignoreImplicitExceptions;
public ResourceValueAnalysis(MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, ResourceTracker<Resource> resourceTracker,
Resource resource) {
super(dfs);
this.methodGen = methodGen;
this.cfg = cfg;
this.resourceTracker = resourceTracker;
this.resource = resource;
this.visitor = resourceTracker.createVisitor(resource, methodGen.getConstantPool());
this.ignoreImplicitExceptions = resourceTracker.ignoreImplicitExceptions(resource);
}
@Override
public ResourceValueFrame createFact() {
ResourceValueFrame fact = new ResourceValueFrame(methodGen.getMaxLocals());
fact.setTop();
return fact;
}
@Override
public void initEntryFact(ResourceValueFrame result) {
result.setValid();
result.clearStack();
final int numSlots = result.getNumSlots();
for (int i = 0; i < numSlots; ++i) {
boolean slotContainsInstance = resourceTracker.isParamInstance(resource, i);
result.setValue(i, slotContainsInstance ? ResourceValue.instance() : ResourceValue.notInstance());
}
}
@Override
public void meetInto(ResourceValueFrame fact, Edge edge, ResourceValueFrame result) throws DataflowAnalysisException {
BasicBlock source = edge.getSource();
BasicBlock dest = edge.getTarget();
ResourceValueFrame tmpFact = null;
if (edge.isExceptionEdge()) {
if (AnalysisContext.currentAnalysisContext().getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS)
&& ignoreImplicitExceptions && !edge.isFlagSet(EXPLICIT_EXCEPTIONS_FLAG)) {
return;
}
if (resourceTracker.ignoreExceptionEdge(edge, resource, methodGen.getConstantPool())) {
return;
}
if (fact.getStatus() == ResourceValueFrame.OPEN) {
tmpFact = modifyFrame(fact, null);
tmpFact.setStatus(ResourceValueFrame.OPEN_ON_EXCEPTION_PATH);
}
if (fact.isValid()) {
InstructionHandle exceptionThrower = source.getExceptionThrower();
BasicBlock fallThroughSuccessor = cfg.getSuccessorWithEdgeType(source, FALL_THROUGH_EDGE);
if (DEBUG && fallThroughSuccessor == null) {
System.out.println("Null fall through successor!");
}
if (fallThroughSuccessor != null
&& resourceTracker.isResourceClose(fallThroughSuccessor, exceptionThrower, methodGen.getConstantPool(),
resource, fact)) {
tmpFact = modifyFrame(fact, tmpFact);
tmpFact.setStatus(ResourceValueFrame.CLOSED);
if (DEBUG) {
System.out.print("(failed attempt to close)");
}
}
}
if (dest.isExceptionHandler()) {
if (fact.isValid()) {
tmpFact = modifyFrame(fact, tmpFact);
tmpFact.clearStack();
tmpFact.pushValue(ResourceValue.notInstance());
}
}
}
int edgeType = edge.getType();
if (edgeType == IFCMP_EDGE || edgeType == FALL_THROUGH_EDGE) {
InstructionHandle lastInSourceHandle = source.getLastInstruction();
if (lastInSourceHandle != null) {
Instruction lastInSource = lastInSourceHandle.getInstruction();
boolean isNullCheck = false;
boolean isNonNullCheck = false;
if (lastInSource instanceof IF_ACMPEQ || lastInSource instanceof IF_ACMPNE) {
Location l = new Location(lastInSourceHandle, source);
InstructionHandle ih = l.getHandle();
InstructionHandle ihPrev = ih.getPrev();
InstructionHandle ihPrevPrev = ihPrev == null ? null : ihPrev.getPrev();
int prevPush = 0;
if (ihPrev != null) {
prevPush = ihPrev.getInstruction().produceStack(methodGen.getConstantPool());
}
int prevPrevPush = 0;
if (ihPrevPrev != null) {
prevPrevPush = ihPrevPrev.getInstruction().produceStack(methodGen.getConstantPool());
}
if (ihPrev != null && ihPrevPrev != null && prevPush == 1 && prevPrevPush == 1
&& ihPrevPrev.getInstruction().getOpcode() == Constants.ACONST_NULL) {
isNullCheck = lastInSource instanceof IF_ACMPEQ;
isNonNullCheck = lastInSource instanceof IF_ACMPNE;
}
}
else if (lastInSource instanceof IFNULL || lastInSource instanceof IFNONNULL) {
isNullCheck = lastInSource instanceof IFNULL;
isNonNullCheck = lastInSource instanceof IFNONNULL;
}
if (isNullCheck || isNonNullCheck) {
ResourceValueFrame startFrame = getStartFact(source);
if (startFrame.isValid()) {
ResourceValueFrame frameAtIf = getFactAtLocation(new Location(lastInSourceHandle, source));
ResourceValue topValue = frameAtIf.getValue(frameAtIf.getNumSlots() - 1);
if (topValue.isInstance()) {
if ((isNullCheck && edgeType == IFCMP_EDGE) || (isNonNullCheck && edgeType == FALL_THROUGH_EDGE)) {
tmpFact = modifyFrame(fact, tmpFact);
tmpFact.setStatus(ResourceValueFrame.NONEXISTENT);
}
}
}
}
}
}
if (tmpFact != null) {
fact = tmpFact;
}
mergeInto(fact, result);
}
@Override
protected void mergeInto(ResourceValueFrame frame, ResourceValueFrame result) throws DataflowAnalysisException {
super.mergeInto(frame, result);
result.setStatus(Math.min(result.getStatus(), frame.getStatus()));
}
@Override
protected void mergeValues(ResourceValueFrame otherFrame, ResourceValueFrame resultFrame, int slot)
throws DataflowAnalysisException {
ResourceValue value = ResourceValue.merge(resultFrame.getValue(slot), otherFrame.getValue(slot));
resultFrame.setValue(slot, value);
}
@Override
public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, ResourceValueFrame fact)
throws DataflowAnalysisException {
visitor.setFrameAndLocation(fact, new Location(handle, basicBlock));
visitor.transferInstruction(handle, basicBlock);
}
}