/*
* Bytecode Analysis Framework
* Copyright (C) 2003-2005 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.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;
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters;
import edu.umd.cs.findbugs.annotations.NonNull;
@javax.annotation.ParametersAreNonnullByDefault
@DefaultAnnotationForParameters(NonNull.class)
public class ResourceValueAnalysis<Resource> extends FrameDataflowAnalysis<ResourceValue, ResourceValueFrame> implements
EdgeTypes {
private static final boolean DEBUG = SystemProperties.getBoolean("dataflow.debug");
private MethodGen methodGen;
private CFG cfg;
private ResourceTracker<Resource> resourceTracker;
private Resource resource;
private ResourceValueFrameModelingVisitor visitor;
private 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);
}
public ResourceValueFrame createFact() {
ResourceValueFrame fact = new ResourceValueFrame(methodGen.getMaxLocals());
fact.setTop();
return fact;
}
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());
}
}
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 this edge throws only implicit exceptions
// (as determined by TypeAnalysis and
// PruneInfeasibleExceptionEdges),
// and the resource tracker says to ignore implicit exceptions
// for this resource, ignore it.
if (AnalysisContext.currentAnalysisContext().getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS)
&& ignoreImplicitExceptions && !edge.isFlagSet(EXPLICIT_EXCEPTIONS_FLAG))
return;
// The ResourceTracker may veto the exception edge
if (resourceTracker.ignoreExceptionEdge(edge, resource, methodGen.getConstantPool()))
return;
if (fact.getStatus() == ResourceValueFrame.OPEN) {
// If status is OPEN, downgrade to OPEN_ON_EXCEPTION_PATH
tmpFact = modifyFrame(fact, null);
tmpFact.setStatus(ResourceValueFrame.OPEN_ON_EXCEPTION_PATH);
}
if (fact.isValid()) {
// Special case: if the instruction that closes the resource
// throws an exception, we consider the resource to be
// successfully
// closed anyway.
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()) {
// Clear stack, push value for exception
if (fact.isValid()) {
tmpFact = modifyFrame(fact, tmpFact);
tmpFact.clearStack();
tmpFact.pushValue(ResourceValue.notInstance());
}
}
}
// Make the resource nonexistent if it is compared against null
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;
// This check catches null == X, null != X
if (lastInSource instanceof IF_ACMPEQ || lastInSource instanceof IF_ACMPNE) {
Location l = new Location(lastInSourceHandle, source);
InstructionHandle ih = l.getHandle();
// Get instruction that pushed topmost
InstructionHandle ihPrev = ih == null ? null : ih.getPrev();
// Get next-topmost that pushed next-topmost
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 instructions exist and both push one word onto the
// stack and the next-topmost pushes null...
if (ihPrev != null && ihPrevPrev != null && prevPush == 1 && prevPrevPush == 1
&& ihPrevPrev.getInstruction().getOpcode() == Constants.ACONST_NULL) {
// Topmost item on stack is being compared with null
// (the null itself is next-topmost on the stack)
isNullCheck = lastInSource instanceof IF_ACMPEQ;
isNonNullCheck = lastInSource instanceof IF_ACMPNE;
}
}
// This check catches X == null, X != null
else if (lastInSource instanceof IFNULL || lastInSource instanceof IFNONNULL) {
isNullCheck = lastInSource instanceof IFNULL;
isNonNullCheck = lastInSource instanceof IFNONNULL;
}
if (isNullCheck || isNonNullCheck) {
// Get the frame at the if statement
ResourceValueFrame startFrame = getStartFact(source);
if (startFrame.isValid()) {
// The source block has a valid start fact.
// That means it is safe to inspect the frame at the If
// instruction.
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)) {
// System.out.println("**** making resource nonexistent on edge "+edge.getId());
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 {
// Merge slots
super.mergeInto(frame, result);
// Merge status
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);
}
}
// vim:ts=4