/*
 * 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.annotations.SuppressFBWarnings;
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
/** * 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.
Params:
  • jclass – the JavaClass
/** * 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).
Params:
  • analysisClass – class the method analysis object belongs to
  • methodDescriptor – method descriptor identifying the analyzed method
  • object – the analysis object to cache
/** * 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.
Params:
  • analysisClass – class the method analysis object should belong to
  • methodDescriptor – method descriptor identifying the analyzed method
Returns:the analysis 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.
Params:
  • methodDescriptor – method descriptor identifying method to purge
/** * 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.
/** * 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.
Params:
  • methodGen – a MethodGen
Returns:the Method represented by the MethodGen
/** * 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.
/** * Get the AnalysisContext. */
public AnalysisContext getAnalysisContext() { return analysisContext; }
Get the RepositoryLookupFailureCallback.
Returns:the RepositoryLookupFailureCallback
/** * Get the RepositoryLookupFailureCallback. * * @return the RepositoryLookupFailureCallback */
public RepositoryLookupFailureCallback getLookupFailureCallback() { return analysisContext.getLookupFailureCallback(); }
Get a MethodGen object for given method.
Params:
  • method – the method
Returns: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
/** * 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.
Params:
  • method – the method
Throws:
Returns:the CFG
/** * 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.
Returns:the ConstantPoolGen
/** * 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.
Params:
  • method – the method
Returns:the UsagesRequiringNonNullValues
/** * 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.
Params:
  • method – the method
Returns:the ValueNumberDataflow
/** * 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.
Params:
  • method – the method
Returns:the IsNullValueDataflow
/** * 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.
Params:
  • method – the method
Returns:the TypeDataflow
/** * 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.
Params:
  • method – the method
Returns:the DepthFirstSearch
/** * 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.
Params:
  • method – the method
Returns:the ReverseDepthFirstSearch
/** * 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.
Params:
  • method – the method
Returns:the BitSet containing the opcodes which appear in the method, or null if the method has no code
/** * 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.
Params:
  • method – the method
Returns:the BitSet containing the opcodes which appear in the method, or null if the method has no code
/** * 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; } 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; } 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.)
Params:
  • method – the method
Returns:map of bytecode offsets to opcodes, or null if the method has no code
/** * 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 */
@CheckForNull @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS") public short[] getOffsetToOpcodeMap(Method method) { UnpackedCode unpackedCode = getMethodAnalysisNoException(UnpackedCode.class, method); return unpackedCode != null ? unpackedCode.getOffsetToBytecodeMap() : null; }
Get dataflow for LockAnalysis for given method.
Params:
  • method – the method
Returns:the LockDataflow
/** * 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.
Params:
  • method – the method
Throws:
Returns:the LockChecker
/** * 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.
Params:
  • method – the method
Returns:the ReturnPathDataflow
/** * 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.
Params:
  • method – the method
Returns:the DominatorsAnalysis
/** * 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.
Params:
  • method – the method
Returns:the DominatorsAnalysis
/** * 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.
Params:
  • method – the method
Returns:the PostDominatorsAnalysis
/** * 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.
Params:
  • method – the method
Returns:the ExceptionSetFactory
/** * 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.
Params:
  • method – the method
Returns:an array of type signatures indicating the types of the method's parameters
/** * 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.
Params:
  • method – the method
Returns:the set of fields loaded by the 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.
Params:
  • method – the method
Returns:the Dataflow object for LiveLocalStoreAnalysis on the 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.
Params:
  • method – the method
Returns:the Dataflow object for BlockTypeAnalysis on the 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.
Returns:the AssertionMethods
/** * Get AssertionMethods for class. * * @return the AssertionMethods */
public AssertionMethods getAssertionMethods() { return getClassAnalysisNoException(AssertionMethods.class); }
Get ConstantDataflow for method.
Params:
  • method – the method
Throws:
Returns:the ConstantDataflow
/** * 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.
Params:
  • method – the method
Throws:
Returns:the LoadDataflow
/** * 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.
Params:
  • method – the method
Throws:
Returns:the StoreDataflow
/** * 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.
Params:
  • method – the method
Throws:
Returns:the CallListDataflow
/** * 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.
Params:
  • method – the method
Throws:
Returns:the UnconditionalValueDerefDataflow
/** * 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.
Params:
  • method – a method
Throws:
Returns:the CompactLocationNumbering for the 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.
Params:
  • method – the method
Throws:
Returns:the ReturnPathTypeDataflow for the 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); } } 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"); } 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"); } 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); // } // } }