/*
* Bytecode Analysis Framework
* Copyright (C) 2003-2007 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 java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.MethodGen;
import edu.umd.cs.findbugs.AnalysisLocal;
import edu.umd.cs.findbugs.OpcodeStack.JumpInfo;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.ca.CallListDataflow;
import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow;
import edu.umd.cs.findbugs.ba.heap.LoadDataflow;
import edu.umd.cs.findbugs.ba.heap.StoreDataflow;
import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
import edu.umd.cs.findbugs.ba.npe.ReturnPathTypeDataflow;
import edu.umd.cs.findbugs.ba.npe.UsagesRequiringNonNullValues;
import edu.umd.cs.findbugs.ba.type.ExceptionSetFactory;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.vna.LoadedFieldSet;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.bcel.BCELUtil;
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.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
import edu.umd.cs.findbugs.classfile.engine.bcel.NonExceptionPostdominatorsAnalysis;
import edu.umd.cs.findbugs.classfile.engine.bcel.NonImplicitExceptionPostDominatorsAnalysis;
import edu.umd.cs.findbugs.classfile.engine.bcel.UnpackedBytecodeCallback;
import edu.umd.cs.findbugs.classfile.engine.bcel.UnpackedCode;
import edu.umd.cs.findbugs.util.MapCache;
/**
* A ClassContext caches all of the auxiliary objects used to analyze the
* methods of a class. That way, these objects don't need to be created over and
* over again.
*
* @author David Hovemeyer
*/
public class ClassContext {
public static final boolean DEBUG = SystemProperties.getBoolean("classContext.debug");
public static final boolean TIME_ANALYSES = SystemProperties.getBoolean("classContext.timeAnalyses");
public static final boolean DUMP_DATAFLOW_ANALYSIS = SystemProperties.getBoolean("dataflow.dump");
private final JavaClass jclass;
private final ClassInfo classInfo;
private final AnalysisContext analysisContext;
private final Map<Class<?>, Map<MethodDescriptor, Object>> methodAnalysisObjectMap;
/*
* ----------------------------------------------------------------------
* Public methods
* ----------------------------------------------------------------------
*/
@Override
public String toString() {
return jclass.getClassName();
}
/**
* Constructor.
*
* @param jclass
* the JavaClass
*/
public ClassContext(JavaClass jclass, AnalysisContext analysisContext) {
this.jclass = jclass;
this.analysisContext = analysisContext;
this.methodAnalysisObjectMap = new HashMap<Class<?>, Map<MethodDescriptor, Object>>();
try {
classInfo = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class,
DescriptorFactory.createClassDescriptor(jclass));
} catch (CheckedAnalysisException e) {
throw new AssertionError("No ClassInfo for " + jclass);
}
}
public Map<MethodDescriptor, Object> getObjectMap(Class<?> analysisClass) {
Map<MethodDescriptor, Object> objectMap = methodAnalysisObjectMap.get(analysisClass);
if (objectMap == null) {
if (analysisClass == ValueNumberDataflow.class)
objectMap = new MapCache<MethodDescriptor, Object>(300);
else if (Dataflow.class.isAssignableFrom(analysisClass))
objectMap = new MapCache<MethodDescriptor, Object>(500);
else
objectMap = new HashMap<MethodDescriptor, Object>();
methodAnalysisObjectMap.put(analysisClass, objectMap);
}
return objectMap;
}
/**
* Store a method analysis object. Note that the cached analysis object
* could be a special value (indicating null or an exception).
*
* @param analysisClass
* class the method analysis object belongs to
* @param methodDescriptor
* method descriptor identifying the analyzed method
* @param object
* the analysis object to cache
*/
public void putMethodAnalysis(Class<?> analysisClass, MethodDescriptor methodDescriptor, Object object) {
if (object == null) {
throw new IllegalArgumentException();
}
Map<MethodDescriptor, Object> objectMap = getObjectMap(analysisClass);
objectMap.put(methodDescriptor, object);
}
/**
* Retrieve a method analysis object.
*
* @param analysisClass
* class the method analysis object should belong to
* @param methodDescriptor
* method descriptor identifying the analyzed method
* @return the analysis object
*/
public Object getMethodAnalysis(Class<?> analysisClass, MethodDescriptor methodDescriptor) {
Map<MethodDescriptor, Object> objectMap = getObjectMap(analysisClass);
return objectMap.get(methodDescriptor);
}
public void purgeAllMethodAnalyses() {
methodAnalysisObjectMap.clear();
}
/**
* Purge all CFG-based method analyses for given method.
*
* @param methodDescriptor
* method descriptor identifying method to purge
*/
public void purgeMethodAnalyses(MethodDescriptor methodDescriptor) {
Set<Map.Entry<Class<?>, Map<MethodDescriptor, Object>>> entrySet = methodAnalysisObjectMap.entrySet();
for (Iterator<Map.Entry<Class<?>, Map<MethodDescriptor, Object>>> i = entrySet.iterator(); i.hasNext();) {
Map.Entry<Class<?>, Map<MethodDescriptor, Object>> entry = i.next();
Class<?> cls = entry.getKey();
// FIXME: hack
if (!DataflowAnalysis.class.isAssignableFrom(cls) && !Dataflow.class.isAssignableFrom(cls)) {
// There is really no need to purge analysis results
// that aren't CFG-based.
// Currently, only dataflow analyses need
// to be purged.
continue;
}
entry.getValue().remove(methodDescriptor);
}
}
/**
* Get the JavaClass.
*/
public JavaClass getJavaClass() {
return jclass;
}
public XClass getXClass() {
return classInfo;
}
public ClassDescriptor getClassDescriptor() {
return classInfo;
}
/**
* Look up the Method represented by given MethodGen.
*
* @param methodGen
* a MethodGen
* @return the Method represented by the MethodGen
*/
public Method getMethod(MethodGen methodGen) {
Method[] methodList = jclass.getMethods();
for (Method method : methodList) {
if (method.getName().equals(methodGen.getName()) && method.getSignature().equals(methodGen.getSignature())
&& method.getAccessFlags() == methodGen.getAccessFlags()) {
return method;
}
}
return null;
}
public String getFullyQualifiedMethodName(Method method) {
return getClassDescriptor().getDottedClassName() + "." + method.getName() + method.getSignature();
}
public @Nonnull
List<Method> getMethodsInCallOrder() {
Map<XMethod, Method> map = new HashMap<XMethod, Method>();
for (Method m : getJavaClass().getMethods()) {
XMethod xMethod = classInfo.findMethod(m.getName(), m.getSignature(), m.isStatic());
map.put(xMethod, m);
}
List<? extends XMethod> xmethodsInCallOrder = classInfo.getXMethodsInCallOrder();
List<Method> methodsInCallOrder = new ArrayList<Method>(xmethodsInCallOrder.size());
for (XMethod x : xmethodsInCallOrder) {
Method m = map.get(x);
if (m != null)
methodsInCallOrder.add(m);
}
return methodsInCallOrder;
}
/**
* Get the AnalysisContext.
*/
public AnalysisContext getAnalysisContext() {
return analysisContext;
}
/**
* Get the RepositoryLookupFailureCallback.
*
* @return the RepositoryLookupFailureCallback
*/
public RepositoryLookupFailureCallback getLookupFailureCallback() {
return analysisContext.getLookupFailureCallback();
}
/**
* Get a MethodGen object for given method.
*
* @param method
* the method
* @return the MethodGen object for the method, or null if the method has no
* Code attribute (and thus cannot be analyzed) or if the method
* seems unprofitable to analyze
*/
@CheckForNull
public MethodGen getMethodGen(Method method) {
return getMethodAnalysisNoException(MethodGen.class, method);
}
/**
* Get a CFG for given method. If pruning options are in effect, pruning
* will be done. Because the CFG pruning can involve interprocedural
* analysis, it is done on a best-effort basis, so the CFG returned might
* not actually be pruned.
*
* @param method
* the method
* @return the CFG
* @throws CFGBuilderException
* if a CFG cannot be constructed for the method
*/
public CFG getCFG(Method method) throws CFGBuilderException {
return getMethodAnalysisNoDataflowAnalysisException(CFG.class, method);
}
/**
* Get the ConstantPoolGen used to create the MethodGens for this class.
*
* @return the ConstantPoolGen
*/
public @Nonnull
ConstantPoolGen getConstantPoolGen() {
return getClassAnalysisNoException(ConstantPoolGen.class);
}
/**
* Get a UsagesRequiringNonNullValues for given method.
*
* @param method
* the method
* @return the UsagesRequiringNonNullValues
*/
public UsagesRequiringNonNullValues getUsagesRequiringNonNullValues(Method method) throws DataflowAnalysisException,
CFGBuilderException {
return getMethodAnalysis(UsagesRequiringNonNullValues.class, method);
}
/**
* Get a ValueNumberDataflow for given method.
*
* @param method
* the method
* @return the ValueNumberDataflow
*/
public ValueNumberDataflow getValueNumberDataflow(Method method) throws DataflowAnalysisException, CFGBuilderException {
return getMethodAnalysis(ValueNumberDataflow.class, method);
}
/**
* Get an IsNullValueDataflow for given method.
*
* @param method
* the method
* @return the IsNullValueDataflow
*/
public IsNullValueDataflow getIsNullValueDataflow(Method method) throws DataflowAnalysisException, CFGBuilderException {
return getMethodAnalysis(IsNullValueDataflow.class, method);
}
/**
* Get a TypeDataflow for given method.
*
* @param method
* the method
* @return the TypeDataflow
*/
public TypeDataflow getTypeDataflow(Method method) throws DataflowAnalysisException, CFGBuilderException {
return getMethodAnalysis(TypeDataflow.class, method);
}
/**
* Get a DepthFirstSearch for given method.
*
* @param method
* the method
* @return the DepthFirstSearch
*/
public DepthFirstSearch getDepthFirstSearch(Method method) throws CFGBuilderException {
return getMethodAnalysisNoDataflowAnalysisException(DepthFirstSearch.class, method);
}
/**
* Get a ReverseDepthFirstSearch for given method.
*
* @param method
* the method
* @return the ReverseDepthFirstSearch
*/
public ReverseDepthFirstSearch getReverseDepthFirstSearch(Method method) throws CFGBuilderException {
return getMethodAnalysisNoDataflowAnalysisException(ReverseDepthFirstSearch.class, method);
}
static final AnalysisLocal<MapCache<XMethod, BitSet>> cachedBitsets_AL
= new AnalysisLocal<MapCache<XMethod, BitSet>>() {
@Override
protected MapCache<XMethod, BitSet> initialValue() {
return new MapCache<XMethod, BitSet>(64);
}
};
static final AnalysisLocal<MapCache<XMethod, Set<Integer>>> cachedLoopExits_AL =
new AnalysisLocal<MapCache<XMethod, Set<Integer>>>() {
@Override
protected MapCache<XMethod, Set<Integer>> initialValue() {
return new MapCache<XMethod, Set<Integer>>(13);
}
};
/**
* Get a BitSet representing the bytecodes that are used in the given
* method. This is useful for prescreening a method for the existence of
* particular instructions. Because this step doesn't require building a
* MethodGen, it is very fast and memory-efficient. It may allow a Detector
* to avoid some very expensive analysis, which is a Big Win for the user.
*
* @param method
* the method
* @return the BitSet containing the opcodes which appear in the method, or
* null if the method has no code
*/
@CheckForNull
public BitSet getBytecodeSet(Method method) {
return getBytecodeSet(jclass, method);
}
/**
* Get a BitSet representing the bytecodes that are used in the given
* method. This is useful for prescreening a method for the existence of
* particular instructions. Because this step doesn't require building a
* MethodGen, it is very fast and memory-efficient. It may allow a Detector
* to avoid some very expensive analysis, which is a Big Win for the user.
*
* @param method
* the method
* @return the BitSet containing the opcodes which appear in the method, or
* null if the method has no code
*/
@CheckForNull
static public BitSet getBytecodeSet(JavaClass clazz, Method method) {
XMethod xmethod = XFactory.createXMethod(clazz, method);
if (cachedBitsets().containsKey(xmethod)) {
return cachedBitsets().get(xmethod);
}
Code code = method.getCode();
if (code == null)
return null;
byte[] instructionList = code.getCode();
// Create callback
UnpackedBytecodeCallback callback = new UnpackedBytecodeCallback(instructionList.length);
// Scan the method.
BytecodeScanner scanner = new BytecodeScanner();
scanner.scan(instructionList, callback);
UnpackedCode unpackedCode = callback.getUnpackedCode();
BitSet result = null;
if (unpackedCode != null)
result = unpackedCode.getBytecodeSet();
cachedBitsets().put(xmethod, result);
return result;
}
/**
* @return
*/
private static MapCache<XMethod, BitSet> cachedBitsets() {
return cachedBitsets_AL.get();
}
@Nonnull
static public Set<Integer> getLoopExitBranches(Method method, MethodGen methodGen) {
XMethod xmethod = XFactory.createXMethod(methodGen);
if (cachedLoopExits().containsKey(xmethod)) {
Set<Integer> result = cachedLoopExits().get(xmethod);
if (result == null) {
AnalysisContext.logError("Null cachedLoopExits for " + xmethod, new NullPointerException());
assert false;
return Collections.<Integer> emptySet();
}
return result;
}
Code code = method.getCode();
if (code == null) {
assert false;
return Collections.<Integer> emptySet();
}
byte[] instructionList = code.getCode();
Set<Integer> result = new HashSet<Integer>();
for (int i = 0; i < instructionList.length; i++)
if (checkForBranchExit(instructionList, i))
result.add(i);
if (result.size() == 0)
result = Collections.<Integer> emptySet();
cachedLoopExits().put(xmethod, result);
return result;
}
/**
* @return
*/
private static MapCache<XMethod, Set<Integer>> cachedLoopExits() {
return cachedLoopExits_AL.get();
}
static short getBranchOffset(byte[] codeBytes, int pos) {
int branchByte1 = 0xff & codeBytes[pos];
int branchByte2 = 0xff & codeBytes[pos + 1];
int branchOffset = (short) (branchByte1 << 8 | branchByte2);
return (short) branchOffset;
}
static boolean checkForBranchExit(byte[] codeBytes, int pos) {
if (pos < 0 || pos + 2 >= codeBytes.length)
return false;
switch (0xff & codeBytes[pos]) {
case Constants.IF_ACMPEQ:
case Constants.IF_ACMPNE:
case Constants.IF_ICMPEQ:
case Constants.IF_ICMPGE:
case Constants.IF_ICMPGT:
case Constants.IF_ICMPLE:
case Constants.IF_ICMPLT:
case Constants.IF_ICMPNE:
break;
default:
return false;
}
int branchTarget = pos + getBranchOffset(codeBytes, pos + 1);
if (branchTarget - 3 < pos || branchTarget >= codeBytes.length)
return false;
if ((codeBytes[branchTarget - 3] & 0xff) != Constants.GOTO)
return false;
int backBranchTarget = branchTarget + getBranchOffset(codeBytes, branchTarget - 2);
if (backBranchTarget <= pos && backBranchTarget + 12 >= pos)
return true;
return false;
}
/**
* Get array mapping bytecode offsets to opcodes for given method. Array
* elements containing zero are either not valid instruction offsets, or
* contain a NOP instruction. (It is convenient not to distinguish these
* cases.)
*
* @param method
* the method
* @return map of bytecode offsets to opcodes, or null if the method has no
* code
*/
public short[] getOffsetToOpcodeMap(Method method) {
UnpackedCode unpackedCode = getMethodAnalysisNoException(UnpackedCode.class, method);
return unpackedCode != null ? unpackedCode.getOffsetToBytecodeMap() : null;
}
/**
* Get dataflow for LockAnalysis for given method.
*
* @param method
* the method
* @return the LockDataflow
*/
public LockDataflow getLockDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(LockDataflow.class, method);
}
/**
* Get LockChecker for method. This is like LockDataflow, but may be able to
* avoid performing the actual dataflow analyses if the method doesn't
* contain explicit monitorenter/monitorexit instructions.
*
* @param method
* the method
* @return the LockChecker
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public LockChecker getLockChecker(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(LockChecker.class, method);
}
/**
* Get ReturnPathDataflow for method.
*
* @param method
* the method
* @return the ReturnPathDataflow
*/
public ReturnPathDataflow getReturnPathDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(ReturnPathDataflow.class, method);
}
/**
* Get DominatorsAnalysis for given method, where exception edges are
* ignored.
*
* @param method
* the method
* @return the DominatorsAnalysis
*/
public DominatorsAnalysis getNonExceptionDominatorsAnalysis(Method method) throws CFGBuilderException,
DataflowAnalysisException {
return getMethodAnalysis(DominatorsAnalysis.class, method);
}
/**
* Get DominatorsAnalysis for given method, where implicit exception edges
* are ignored.
*
* @param method
* the method
* @return the DominatorsAnalysis
*/
public PostDominatorsAnalysis getNonImplicitExceptionDominatorsAnalysis(Method method) throws CFGBuilderException,
DataflowAnalysisException {
return getMethodAnalysis(NonImplicitExceptionPostDominatorsAnalysis.class, method);
}
/**
* Get PostDominatorsAnalysis for given method, where exception edges are
* ignored.
*
* @param method
* the method
* @return the PostDominatorsAnalysis
*/
public PostDominatorsAnalysis getNonExceptionPostDominatorsAnalysis(Method method) throws CFGBuilderException,
DataflowAnalysisException {
return getMethodAnalysis(NonExceptionPostdominatorsAnalysis.class, method);
}
/**
* Get ExceptionSetFactory for given method.
*
* @param method
* the method
* @return the ExceptionSetFactory
*/
public ExceptionSetFactory getExceptionSetFactory(Method method) {
return getMethodAnalysisNoException(ExceptionSetFactory.class, method);
}
/**
* Get array of type signatures of parameters for given method.
*
* @param method
* the method
* @return an array of type signatures indicating the types of the method's
* parameters
*/
public String[] getParameterSignatureList(Method method) {
return getMethodAnalysisNoException(String[].class, method);
}
/**
* Get the set of fields loaded by given method.
*
* @param method
* the method
* @return the set of fields loaded by the method
*/
public LoadedFieldSet getLoadedFieldSet(Method method) {
return getMethodAnalysisNoException(LoadedFieldSet.class, method);
}
/**
* Get LiveLocalStoreAnalysis dataflow for given method.
*
* @param method
* the method
* @return the Dataflow object for LiveLocalStoreAnalysis on the method
*/
public LiveLocalStoreDataflow getLiveLocalStoreDataflow(Method method) throws DataflowAnalysisException, CFGBuilderException {
return getMethodAnalysis(LiveLocalStoreDataflow.class, method);
}
/**
* Get BlockType dataflow for given method.
*
* @param method
* the method
* @return the Dataflow object for BlockTypeAnalysis on the method
*/
public BlockTypeDataflow getBlockTypeDataflow(Method method) throws DataflowAnalysisException, CFGBuilderException {
return getMethodAnalysis(BlockTypeDataflow.class, method);
}
// /**
// * Get the assigned field map for the class.
// *
// * @return the AssignedFieldMap
// * @throws ClassNotFoundException if a class lookup prevents
// * the class's superclasses from being searched for
// * assignable fields
// */
// public AssignedFieldMap getAssignedFieldMap() throws
// ClassNotFoundException {
// return
// getClassAnalysisPossibleClassNotFoundException(AssignedFieldMap.class);
// }
/**
* Get AssertionMethods for class.
*
* @return the AssertionMethods
*/
public AssertionMethods getAssertionMethods() {
return getClassAnalysisNoException(AssertionMethods.class);
}
/**
* Get ConstantDataflow for method.
*
* @param method
* the method
* @return the ConstantDataflow
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public ConstantDataflow getConstantDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(ConstantDataflow.class, method);
}
/**
* Get load dataflow.
*
* @param method
* the method
* @return the LoadDataflow
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public LoadDataflow getLoadDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(LoadDataflow.class, method);
}
/**
* Get store dataflow.
*
* @param method
* the method
* @return the StoreDataflow
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public StoreDataflow getStoreDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(StoreDataflow.class, method);
}
/**
* Get CallListDataflow for method.
*
* @param method
* the method
* @return the CallListDataflow
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public CallListDataflow getCallListDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(CallListDataflow.class, method);
}
public BitSet linesMentionedMultipleTimes(Method method) {
IAnalysisCache analysisCache = Global.getAnalysisCache();
XMethod xMethod = XFactory.createXMethod(jclass, method);
JumpInfo jumpInfo = null;
try {
jumpInfo = analysisCache.getMethodAnalysis(JumpInfo.class, xMethod.getMethodDescriptor());
} catch (CheckedAnalysisException e) {
AnalysisContext.logError("Error getting jump information", e);
}
BitSet lineMentionedMultipleTimes = new BitSet();
BitSet pcInFinallyBlock = new BitSet();
Code code = method.getCode();
if (code == null)
return lineMentionedMultipleTimes;
CodeException[] exceptionTable = code.getExceptionTable();
if (exceptionTable == null || exceptionTable.length == 0)
return lineMentionedMultipleTimes;
int firstHandler = Integer.MAX_VALUE;
for (CodeException e : exceptionTable)
if (e.getCatchType() == 0) {
int pc = e.getHandlerPC();
firstHandler = Math.min(firstHandler, pc);
if (jumpInfo != null) {
int end = jumpInfo.getNextJump(pc + 1);
if (end >= pc)
pcInFinallyBlock.set(pc, end);
}
}
BitSet foundOnce = new BitSet();
BitSet afterHandler = new BitSet();
LineNumberTable lineNumberTable = method.getLineNumberTable();
int lineNum = -1;
int prevStartPc = -1;
if (lineNumberTable != null)
for (LineNumber line : lineNumberTable.getLineNumberTable()) {
int newLine = line.getLineNumber();
if (newLine == lineNum || newLine == -1)
continue;
if (prevStartPc >= 0) {
int nextPcInFinallyBlock = pcInFinallyBlock.nextSetBit(prevStartPc);
if (nextPcInFinallyBlock < line.getStartPC())
lineMentionedMultipleTimes.set(lineNum);
}
if (line.getStartPC() >= firstHandler)
afterHandler.set(lineNum);
lineNum = newLine;
prevStartPc = line.getStartPC();
if (foundOnce.get(lineNum)) {
lineMentionedMultipleTimes.set(lineNum);
} else {
foundOnce.set(lineNum);
}
}
lineMentionedMultipleTimes.and(afterHandler);
return lineMentionedMultipleTimes;
}
/**
* Get the UnconditionalValueDerefDataflow for a method.
*
* @param method
* the method
* @return the UnconditionalValueDerefDataflow
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public UnconditionalValueDerefDataflow getUnconditionalValueDerefDataflow(Method method) throws CFGBuilderException,
DataflowAnalysisException {
return getMethodAnalysis(UnconditionalValueDerefDataflow.class, method);
}
/**
* Get a CompactLocationNumbering for a method.
*
* @param method
* a method
* @return the CompactLocationNumbering for the method
* @throws CFGBuilderException
*/
public CompactLocationNumbering getCompactLocationNumbering(Method method) throws CFGBuilderException {
return getMethodAnalysisNoDataflowAnalysisException(CompactLocationNumbering.class, method);
}
/**
* Get ReturnPathTypeDataflow for a method.
*
* @param method
* the method
* @return the ReturnPathTypeDataflow for the method
* @throws CFGBuilderException
* @throws DataflowAnalysisException
*/
public ReturnPathTypeDataflow getReturnPathTypeDataflow(Method method) throws CFGBuilderException, DataflowAnalysisException {
return getMethodAnalysis(ReturnPathTypeDataflow.class, method);
}
public void dumpSimpleDataflowInformation(Method method) {
try {
dumpDataflowInformation(method, getCFG(method), getValueNumberDataflow(method), getIsNullValueDataflow(method), null,
null);
} catch (DataflowAnalysisException e) {
AnalysisContext.logError(
"Could not dump data information for " + getJavaClass().getClassName() + "." + method.getName(), e);
} catch (CFGBuilderException e) {
AnalysisContext.logError(
"Could not dump data information for " + getJavaClass().getClassName() + "." + method.getName(), e);
}
}
public void dumpDataflowInformation(Method method) {
try {
dumpDataflowInformation(method, getCFG(method), getValueNumberDataflow(method), getIsNullValueDataflow(method),
getUnconditionalValueDerefDataflow(method), getTypeDataflow(method));
} catch (DataflowAnalysisException e) {
AnalysisContext.logError(
"Could not dump data information for " + getJavaClass().getClassName() + "." + method.getName(), e);
} catch (CFGBuilderException e) {
AnalysisContext.logError(
"Could not dump data information for " + getJavaClass().getClassName() + "." + method.getName(), e);
}
}
/**
* @param method
* @param cfg
* @param vnd
* @param inv
* @param dataflow
* @param typeDataflow
* TODO
* @throws DataflowAnalysisException
*/
public static void dumpDataflowInformation(Method method, CFG cfg, ValueNumberDataflow vnd, IsNullValueDataflow inv,
@CheckForNull UnconditionalValueDerefDataflow dataflow, @CheckForNull TypeDataflow typeDataflow)
throws DataflowAnalysisException {
System.out.println("\n\n{ UnconditionalValueDerefAnalysis analysis for " + method.getName());
TreeSet<Location> tree = new TreeSet<Location>();
for (Iterator<Location> locs = cfg.locationIterator(); locs.hasNext();) {
Location loc = locs.next();
tree.add(loc);
}
for (Location loc : tree) {
System.out.println();
if (dataflow != null)
System.out.println("\n Pre: " + dataflow.getFactAfterLocation(loc));
System.out.println("Vna: " + vnd.getFactAtLocation(loc));
System.out.println("inv: " + inv.getFactAtLocation(loc));
if (typeDataflow != null)
System.out.println("type: " + typeDataflow.getFactAtLocation(loc));
System.out.println("Location: " + loc);
if (dataflow != null)
System.out.println("Post: " + dataflow.getFactAtLocation(loc));
System.out.println("Vna: " + vnd.getFactAfterLocation(loc));
System.out.println("inv: " + inv.getFactAfterLocation(loc));
if (typeDataflow != null)
System.out.println("type: " + typeDataflow.getFactAfterLocation(loc));
}
System.out.println("}\n\n");
}
/**
* @param method
* @param cfg
* @param typeDataflow
* @throws DataflowAnalysisException
*/
public static void dumpTypeDataflow(Method method, CFG cfg, TypeDataflow typeDataflow) throws DataflowAnalysisException {
System.out.println("\n\n{ Type analysis for " + cfg.getMethodGen().getClassName() + "." + method.getName()
+ method.getSignature());
TreeSet<Location> tree = new TreeSet<Location>();
for (Iterator<Location> locs = cfg.locationIterator(); locs.hasNext();) {
Location loc = locs.next();
tree.add(loc);
}
for (Location loc : tree) {
System.out.println("\n Pre: " + typeDataflow.getFactAtLocation(loc));
System.out.println("Location: " + loc);
System.out.println("Post: " + typeDataflow.getFactAfterLocation(loc));
}
System.out.println("}\n\n");
}
/**
* @param method
* @param cfg
* @param typeDataflow
* @throws DataflowAnalysisException
*/
public static void dumpLiveLocalStoreDataflow(MethodDescriptor method, CFG cfg, LiveLocalStoreDataflow dataflow)
throws DataflowAnalysisException {
System.out.println("\n\n{ LiveLocalStore analysis for " + method);
TreeSet<Location> tree = new TreeSet<Location>();
for (Iterator<Location> locs = cfg.locationIterator(); locs.hasNext();) {
Location loc = locs.next();
tree.add(loc);
}
for (Location loc : tree) {
System.out.println("\n Pre: " + dataflow.getFactAfterLocation(loc));
System.out.println("Location: " + loc);
System.out.println("Post: " + dataflow.getFactAtLocation(loc));
}
System.out.println("}\n\n");
}
/*
* ----------------------------------------------------------------------
* Helper methods for getting an analysis object from the analysis cache.
* ----------------------------------------------------------------------
*/
private <Analysis> Analysis getMethodAnalysisNoException(Class<Analysis> analysisClass, Method method) {
try {
return getMethodAnalysis(analysisClass, method);
} catch (CheckedAnalysisException e) {
IllegalStateException ise = new IllegalStateException("should not happen");
ise.initCause(e);
throw ise;
}
}
private <Analysis> Analysis getMethodAnalysisNoDataflowAnalysisException(Class<Analysis> analysisClass, Method method)
throws CFGBuilderException {
try {
return getMethodAnalysis(analysisClass, method);
} catch (CFGBuilderException e) {
throw e;
} catch (CheckedAnalysisException e) {
IllegalStateException ise = new IllegalStateException("should not happen");
ise.initCause(e);
throw ise;
}
}
private <Analysis> Analysis getMethodAnalysis(Class<Analysis> analysisClass, Method method) throws DataflowAnalysisException,
CFGBuilderException {
try {
MethodDescriptor methodDescriptor = BCELUtil.getMethodDescriptor(jclass, method);
return Global.getAnalysisCache().getMethodAnalysis(analysisClass, methodDescriptor);
} catch (DataflowAnalysisException e) {
throw e;
} catch (CFGBuilderException e) {
throw e;
} catch (CheckedAnalysisException e) {
Throwable cause = e.getCause();
if (cause instanceof CFGBuilderException) {
throw (CFGBuilderException) cause;
}
String message = "Should not happen: bad CAE: " + e.getClass().getName() + " for " + analysisClass.getName() + " of " + method;
IllegalStateException ise = new IllegalStateException(message);
ise.initCause(e);
throw ise;
}
}
private <Analysis> Analysis getClassAnalysis(Class<Analysis> analysisClass) throws CheckedAnalysisException {
ClassDescriptor classDescriptor = BCELUtil.getClassDescriptor(jclass);
return Global.getAnalysisCache().getClassAnalysis(analysisClass, classDescriptor);
}
private <Analysis> Analysis getClassAnalysisNoException(Class<Analysis> analysisClass) {
try {
return getClassAnalysis(analysisClass);
} catch (CheckedAnalysisException e) {
IllegalStateException ise = new IllegalStateException("should not happen");
ise.initCause(e);
throw ise;
}
}
// private<Analysis> Analysis
// getClassAnalysisPossibleClassNotFoundException(Class<Analysis>
// analysisClass)
// throws ClassNotFoundException {
// try {
// return Global.getAnalysisCache().getClassAnalysis(analysisClass,
// BCELUtil.getClassDescriptor(jclass));
// } catch (ResourceNotFoundException e) {
// throw e.toClassNotFoundException();
// } catch (CheckedAnalysisException e) {
// throw new AnalysisException("Unexpected exception", e);
// }
// }
}
// vim:ts=3