001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003-2005 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import org.apache.bcel.Constants;
023: import org.apache.bcel.generic.IFNONNULL;
024: import org.apache.bcel.generic.IFNULL;
025: import org.apache.bcel.generic.IF_ACMPEQ;
026: import org.apache.bcel.generic.IF_ACMPNE;
027: import org.apache.bcel.generic.Instruction;
028: import org.apache.bcel.generic.InstructionHandle;
029: import org.apache.bcel.generic.MethodGen;
030:
031: import edu.umd.cs.findbugs.SystemProperties;
032: import edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters;
033: import edu.umd.cs.findbugs.annotations.NonNull;
034:
035: @javax.annotation.ParametersAreNonnullByDefault
036: @DefaultAnnotationForParameters(NonNull.class)
037: public class ResourceValueAnalysis<Resource> extends
038: FrameDataflowAnalysis<ResourceValue, ResourceValueFrame>
039: implements EdgeTypes {
040:
041: private static final boolean DEBUG = SystemProperties
042: .getBoolean("dataflow.debug");
043:
044: private MethodGen methodGen;
045: private CFG cfg;
046: private ResourceTracker<Resource> resourceTracker;
047: private Resource resource;
048: private ResourceValueFrameModelingVisitor visitor;
049: private boolean ignoreImplicitExceptions;
050:
051: public ResourceValueAnalysis(MethodGen methodGen, CFG cfg,
052: DepthFirstSearch dfs,
053: ResourceTracker<Resource> resourceTracker, Resource resource) {
054:
055: super (dfs);
056: this .methodGen = methodGen;
057: this .cfg = cfg;
058: this .resourceTracker = resourceTracker;
059: this .resource = resource;
060: this .visitor = resourceTracker.createVisitor(resource,
061: methodGen.getConstantPool());
062:
063: this .ignoreImplicitExceptions = resourceTracker
064: .ignoreImplicitExceptions(resource);
065: }
066:
067: public ResourceValueFrame createFact() {
068: ResourceValueFrame fact = new ResourceValueFrame(methodGen
069: .getMaxLocals());
070: fact.setTop();
071: return fact;
072: }
073:
074: public void initEntryFact(ResourceValueFrame result) {
075: result.setValid();
076: result.clearStack();
077: final int numSlots = result.getNumSlots();
078: for (int i = 0; i < numSlots; ++i) {
079: boolean slotContainsInstance = resourceTracker
080: .isParamInstance(resource, i);
081: result.setValue(i, slotContainsInstance ? ResourceValue
082: .instance() : ResourceValue.notInstance());
083: }
084: }
085:
086: public void meetInto(ResourceValueFrame fact, Edge edge,
087: ResourceValueFrame result) throws DataflowAnalysisException {
088: BasicBlock source = edge.getSource();
089: BasicBlock dest = edge.getTarget();
090:
091: ResourceValueFrame tmpFact = null;
092:
093: if (edge.isExceptionEdge()) {
094: // If this edge throws only implicit exceptions
095: // (as determined by TypeAnalysis and PruneInfeasibleExceptionEdges),
096: // and the resource tracker says to ignore implicit exceptions
097: // for this resource, ignore it.
098: if (AnalysisContext.currentAnalysisContext()
099: .getBoolProperty(
100: AnalysisFeatures.ACCURATE_EXCEPTIONS)
101: && ignoreImplicitExceptions
102: && !edge.isFlagSet(EXPLICIT_EXCEPTIONS_FLAG))
103: return;
104:
105: // The ResourceTracker may veto the exception edge
106: if (resourceTracker.ignoreExceptionEdge(edge, resource,
107: methodGen.getConstantPool()))
108: return;
109:
110: if (fact.getStatus() == ResourceValueFrame.OPEN) {
111: // If status is OPEN, downgrade to OPEN_ON_EXCEPTION_PATH
112: tmpFact = modifyFrame(fact, null);
113: tmpFact
114: .setStatus(ResourceValueFrame.OPEN_ON_EXCEPTION_PATH);
115: }
116:
117: if (fact.isValid()) {
118: // Special case: if the instruction that closes the resource
119: // throws an exception, we consider the resource to be successfully
120: // closed anyway.
121: InstructionHandle exceptionThrower = source
122: .getExceptionThrower();
123: BasicBlock fallThroughSuccessor = cfg
124: .getSuccessorWithEdgeType(source,
125: FALL_THROUGH_EDGE);
126: if (DEBUG && fallThroughSuccessor == null)
127: System.out.println("Null fall through successor!");
128: if (fallThroughSuccessor != null
129: && resourceTracker.isResourceClose(
130: fallThroughSuccessor, exceptionThrower,
131: methodGen.getConstantPool(), resource,
132: fact)) {
133: tmpFact = modifyFrame(fact, tmpFact);
134: tmpFact.setStatus(ResourceValueFrame.CLOSED);
135: if (DEBUG)
136: System.out.print("(failed attempt to close)");
137: }
138: }
139:
140: if (dest.isExceptionHandler()) {
141: // Clear stack, push value for exception
142: if (fact.isValid()) {
143: tmpFact = modifyFrame(fact, tmpFact);
144: tmpFact.clearStack();
145: tmpFact.pushValue(ResourceValue.notInstance());
146: }
147: }
148: }
149:
150: // Make the resource nonexistent if it is compared against null
151: int edgeType = edge.getType();
152: if (edgeType == IFCMP_EDGE || edgeType == FALL_THROUGH_EDGE) {
153: InstructionHandle lastInSourceHandle = source
154: .getLastInstruction();
155: if (lastInSourceHandle != null) {
156: Instruction lastInSource = lastInSourceHandle
157: .getInstruction();
158: boolean isNullCheck = false;
159: boolean isNonNullCheck = false;
160: // This check catches null == X, null != X
161: if (lastInSource instanceof IF_ACMPEQ
162: || lastInSource instanceof IF_ACMPNE) {
163: Location l = new Location(lastInSourceHandle,
164: source);
165: InstructionHandle ih = l.getHandle();
166: // Get instruction that pushed topmost
167: InstructionHandle ihPrev = ih == null ? null : ih
168: .getPrev();
169: // Get next-topmost that pushed next-topmost
170: InstructionHandle ihPrevPrev = ihPrev == null ? null
171: : ihPrev.getPrev();
172: int prevPush = 0;
173: if (ihPrev != null) {
174: prevPush = ihPrev.getInstruction()
175: .produceStack(
176: methodGen.getConstantPool());
177: }
178: int prevPrevPush = 0;
179: if (ihPrevPrev != null) {
180: prevPrevPush = ihPrevPrev.getInstruction()
181: .produceStack(
182: methodGen.getConstantPool());
183: }
184: // If instructions exist and both push one word onto the
185: // stack and the next-topmost pushes null...
186: if (ihPrev != null
187: && ihPrevPrev != null
188: && prevPush == 1
189: && prevPrevPush == 1
190: && ihPrevPrev.getInstruction().getOpcode() == Constants.ACONST_NULL) {
191: // Topmost item on stack is being compared with null
192: // (the null itself is next-topmost on the stack)
193: isNullCheck = lastInSource instanceof IF_ACMPEQ;
194: isNonNullCheck = lastInSource instanceof IF_ACMPNE;
195: }
196: }
197: // This check catches X == null, X != null
198: else if (lastInSource instanceof IFNULL
199: || lastInSource instanceof IFNONNULL) {
200: isNullCheck = lastInSource instanceof IFNULL;
201: isNonNullCheck = lastInSource instanceof IFNONNULL;
202: }
203: if (isNullCheck || isNonNullCheck) {
204: // Get the frame at the if statement
205: ResourceValueFrame startFrame = getStartFact(source);
206: if (startFrame.isValid()) {
207: // The source block has a valid start fact.
208: // That means it is safe to inspect the frame at the If instruction.
209: ResourceValueFrame frameAtIf = getFactAtLocation(new Location(
210: lastInSourceHandle, source));
211: ResourceValue topValue = frameAtIf
212: .getValue(frameAtIf.getNumSlots() - 1);
213:
214: if (topValue.isInstance()) {
215: if ((isNullCheck && edgeType == IFCMP_EDGE)
216: || (isNonNullCheck && edgeType == FALL_THROUGH_EDGE)) {
217: //System.out.println("**** making resource nonexistent on edge "+edge.getId());
218: tmpFact = modifyFrame(fact, tmpFact);
219: tmpFact
220: .setStatus(ResourceValueFrame.NONEXISTENT);
221: }
222: }
223: }
224: }
225: }
226: }
227:
228: if (tmpFact != null)
229: fact = tmpFact;
230:
231: mergeInto(fact, result);
232: }
233:
234: @Override
235: protected void mergeInto(ResourceValueFrame frame,
236: ResourceValueFrame result) throws DataflowAnalysisException {
237: // Merge slots
238: super .mergeInto(frame, result);
239:
240: // Merge status
241: result.setStatus(Math
242: .min(result.getStatus(), frame.getStatus()));
243: }
244:
245: @Override
246: protected void mergeValues(ResourceValueFrame otherFrame,
247: ResourceValueFrame resultFrame, int slot)
248: throws DataflowAnalysisException {
249: ResourceValue value = ResourceValue.merge(resultFrame
250: .getValue(slot), otherFrame.getValue(slot));
251: resultFrame.setValue(slot, value);
252: }
253:
254: @Override
255: public void transferInstruction(InstructionHandle handle,
256: BasicBlock basicBlock, ResourceValueFrame fact)
257: throws DataflowAnalysisException {
258:
259: visitor.setFrameAndLocation(fact, new Location(handle,
260: basicBlock));
261: visitor.transferInstruction(handle, basicBlock);
262:
263: }
264:
265: }
266:
267: // vim:ts=4
|