0001: /*
0002: * Bytecode Analysis Framework
0003: * Copyright (C) 2003-2007 University of Maryland
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public
0007: * License as published by the Free Software Foundation; either
0008: * version 2.1 of the License, or (at your option) any later version.
0009: *
0010: * This library is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * Lesser General Public License for more details.
0014: *
0015: * You should have received a copy of the GNU Lesser General Public
0016: * License along with this library; if not, write to the Free Software
0017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0018: */
0019:
0020: package edu.umd.cs.findbugs.ba;
0021:
0022: import java.util.Arrays;
0023: import java.util.BitSet;
0024: import java.util.Collection;
0025: import java.util.HashMap;
0026: import java.util.HashSet;
0027: import java.util.Iterator;
0028: import java.util.List;
0029: import java.util.Map;
0030: import java.util.Set;
0031: import java.util.TreeSet;
0032:
0033: import org.apache.bcel.Constants;
0034: import org.apache.bcel.classfile.Code;
0035: import org.apache.bcel.classfile.JavaClass;
0036: import org.apache.bcel.classfile.LineNumber;
0037: import org.apache.bcel.classfile.LineNumberTable;
0038: import org.apache.bcel.classfile.Method;
0039: import org.apache.bcel.generic.ConstantPoolGen;
0040: import org.apache.bcel.generic.INVOKESTATIC;
0041: import org.apache.bcel.generic.Instruction;
0042: import org.apache.bcel.generic.InvokeInstruction;
0043: import org.apache.bcel.generic.MethodGen;
0044:
0045: import edu.umd.cs.findbugs.SystemProperties;
0046: import edu.umd.cs.findbugs.TigerSubstitutes;
0047: import edu.umd.cs.findbugs.annotations.CheckForNull;
0048: import edu.umd.cs.findbugs.annotations.NonNull;
0049: import edu.umd.cs.findbugs.ba.ca.CallListDataflow;
0050: import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
0051: import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow;
0052: import edu.umd.cs.findbugs.ba.heap.LoadDataflow;
0053: import edu.umd.cs.findbugs.ba.heap.StoreDataflow;
0054: import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
0055: import edu.umd.cs.findbugs.ba.npe.ReturnPathTypeDataflow;
0056: import edu.umd.cs.findbugs.ba.npe.UsagesRequiringNonNullValues;
0057: import edu.umd.cs.findbugs.ba.npe2.DefinitelyNullSetDataflow;
0058: import edu.umd.cs.findbugs.ba.type.ExceptionSetFactory;
0059: import edu.umd.cs.findbugs.ba.type.TypeDataflow;
0060: import edu.umd.cs.findbugs.ba.vna.LoadedFieldSet;
0061: import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
0062: import edu.umd.cs.findbugs.bcel.BCELUtil;
0063: import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
0064: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
0065: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
0066: import edu.umd.cs.findbugs.classfile.Global;
0067: import edu.umd.cs.findbugs.classfile.MethodDescriptor;
0068: import edu.umd.cs.findbugs.classfile.ResourceNotFoundException;
0069: import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
0070: import edu.umd.cs.findbugs.classfile.engine.SelfMethodCalls;
0071: import edu.umd.cs.findbugs.classfile.engine.bcel.NonExceptionPostdominatorsAnalysis;
0072: import edu.umd.cs.findbugs.classfile.engine.bcel.NonImplicitExceptionPostDominatorsAnalysis;
0073: import edu.umd.cs.findbugs.classfile.engine.bcel.UnpackedBytecodeCallback;
0074: import edu.umd.cs.findbugs.classfile.engine.bcel.UnpackedCode;
0075: import edu.umd.cs.findbugs.util.MapCache;
0076: import edu.umd.cs.findbugs.util.MultiMap;
0077: import edu.umd.cs.findbugs.util.TopologicalSort;
0078: import edu.umd.cs.findbugs.util.TopologicalSort.OutEdges;
0079:
0080: /**
0081: * A ClassContext caches all of the auxiliary objects used to analyze
0082: * the methods of a class. That way, these objects don't need to
0083: * be created over and over again.
0084: *
0085: * @author David Hovemeyer
0086: */
0087: public class ClassContext {
0088: public static final boolean DEBUG = SystemProperties
0089: .getBoolean("classContext.debug");
0090:
0091: public static final boolean TIME_ANALYSES = SystemProperties
0092: .getBoolean("classContext.timeAnalyses");
0093:
0094: public static final boolean DUMP_DATAFLOW_ANALYSIS = SystemProperties
0095: .getBoolean("dataflow.dump");
0096:
0097: public static int depth;
0098:
0099: public static void indent() {
0100: for (int i = 0; i < depth; ++i)
0101: System.out.print(" ");
0102: }
0103:
0104: private final JavaClass jclass;
0105: private final ClassInfo classInfo;
0106: private final AnalysisContext analysisContext;
0107: private final Map<Class<?>, Map<MethodDescriptor, Object>> methodAnalysisObjectMap;
0108:
0109: /* ----------------------------------------------------------------------
0110: * Public methods
0111: * ---------------------------------------------------------------------- */
0112:
0113: /**
0114: * Constructor.
0115: *
0116: * @param jclass the JavaClass
0117: */
0118: public ClassContext(JavaClass jclass,
0119: AnalysisContext analysisContext) {
0120: this .jclass = jclass;
0121: this .analysisContext = analysisContext;
0122: this .methodAnalysisObjectMap = new HashMap<Class<?>, Map<MethodDescriptor, Object>>();
0123: try {
0124: classInfo = (ClassInfo) Global.getAnalysisCache()
0125: .getClassAnalysis(
0126: XClass.class,
0127: DescriptorFactory
0128: .createClassDescriptor(jclass));
0129: } catch (CheckedAnalysisException e) {
0130: throw new AssertionError("No ClassInfo for " + jclass);
0131: }
0132: }
0133:
0134: public Map<MethodDescriptor, Object> getObjectMap(
0135: Class<?> analysisClass) {
0136: Map<MethodDescriptor, Object> objectMap = methodAnalysisObjectMap
0137: .get(analysisClass);
0138: if (objectMap == null) {
0139: objectMap = new HashMap<MethodDescriptor, Object>();
0140: methodAnalysisObjectMap.put(analysisClass, objectMap);
0141: }
0142: return objectMap;
0143: }
0144:
0145: /**
0146: * Store a method analysis object.
0147: * Note that the cached analysis object could be a special value
0148: * (indicating null or an exception).
0149: *
0150: * @param analysisClass class the method analysis object belongs to
0151: * @param methodDescriptor method descriptor identifying the analyzed method
0152: * @param object the analysis object to cache
0153: */
0154: public void putMethodAnalysis(Class<?> analysisClass,
0155: MethodDescriptor methodDescriptor, Object object) {
0156: if (object == null) {
0157: throw new IllegalArgumentException();
0158: }
0159: Map<MethodDescriptor, Object> objectMap = getObjectMap(analysisClass);
0160: objectMap.put(methodDescriptor, object);
0161: }
0162:
0163: /**
0164: * Retrieve a method analysis object.
0165: *
0166: * @param analysisClass class the method analysis object should belong to
0167: * @param methodDescriptor method descriptor identifying the analyzed method
0168: * @return the analysis object
0169: * @throws CheckedAnalysisException
0170: */
0171: public Object getMethodAnalysis(Class<?> analysisClass,
0172: MethodDescriptor methodDescriptor)
0173: throws CheckedAnalysisException {
0174: Map<MethodDescriptor, Object> objectMap = getObjectMap(analysisClass);
0175: return objectMap.get(methodDescriptor);
0176: }
0177:
0178: public void purgeAllMethodAnalyses() {
0179: methodAnalysisObjectMap.clear();
0180: }
0181:
0182: /**
0183: * Purge all CFG-based method analyses for given method.
0184: *
0185: * @param methodDescriptor method descriptor identifying method to purge
0186: */
0187: public void purgeMethodAnalyses(MethodDescriptor methodDescriptor) {
0188: Set<Map.Entry<Class<?>, Map<MethodDescriptor, Object>>> entrySet = methodAnalysisObjectMap
0189: .entrySet();
0190: for (Iterator<Map.Entry<Class<?>, Map<MethodDescriptor, Object>>> i = entrySet
0191: .iterator(); i.hasNext();) {
0192: Map.Entry<Class<?>, Map<MethodDescriptor, Object>> entry = i
0193: .next();
0194:
0195: Class<?> cls = entry.getKey();
0196:
0197: // FIXME: hack
0198: if (!DataflowAnalysis.class.isAssignableFrom(cls)
0199: && !Dataflow.class.isAssignableFrom(cls)) {
0200: // There is really no need to purge analysis results
0201: // that aren't CFG-based.
0202: // Currently, only dataflow analyses need
0203: // to be purged.
0204: continue;
0205: }
0206:
0207: entry.getValue().remove(methodDescriptor);
0208: }
0209: }
0210:
0211: /**
0212: * Get the JavaClass.
0213: */
0214: public JavaClass getJavaClass() {
0215: return jclass;
0216: }
0217:
0218: public XClass getXClass() {
0219: return classInfo;
0220: }
0221:
0222: public ClassDescriptor getClassDescriptor() {
0223: return classInfo;
0224: }
0225:
0226: /**
0227: * Look up the Method represented by given MethodGen.
0228: *
0229: * @param methodGen a MethodGen
0230: * @return the Method represented by the MethodGen
0231: */
0232: public Method getMethod(MethodGen methodGen) {
0233: Method[] methodList = jclass.getMethods();
0234: for (Method method : methodList) {
0235: if (method.getName().equals(methodGen.getName())
0236: && method.getSignature().equals(
0237: methodGen.getSignature())
0238: && method.getAccessFlags() == methodGen
0239: .getAccessFlags()) {
0240: return method;
0241: }
0242: }
0243: return null;
0244: }
0245:
0246: @CheckForNull
0247: List<Method> methodsInCallOrder = null;
0248:
0249: public @NonNull
0250: List<Method> getMethodsInCallOrder() {
0251: if (methodsInCallOrder != null)
0252: return methodsInCallOrder;
0253: List<Method> methodList = Arrays.asList(getJavaClass()
0254: .getMethods());
0255: final Map<String, Method> map = new HashMap<String, Method>();
0256:
0257: for (Method m : methodList) {
0258: map.put(m.getName() + m.getSignature() + m.isStatic(), m);
0259: }
0260: final MultiMap<Method, Method> multiMap = SelfMethodCalls
0261: .getSelfCalls(getClassDescriptor(), map);
0262: OutEdges<Method> edges1 = new OutEdges<Method>() {
0263:
0264: public Collection<Method> getOutEdges(Method method) {
0265: return multiMap.get(method);
0266: }
0267: };
0268: if (false) {
0269: OutEdges<Method> edges2 = new OutEdges<Method>() {
0270: final ConstantPoolGen cpg = getConstantPoolGen();
0271: final String this ClassName = getJavaClass()
0272: .getClassName();
0273:
0274: public Collection<Method> getOutEdges(Method method) {
0275: HashSet<Method> result = new HashSet<Method>();
0276: try {
0277: CFG cfg = getCFG(method);
0278: for (Iterator<Location> i = cfg
0279: .locationIterator(); i.hasNext();) {
0280: Instruction ins = i.next().getHandle()
0281: .getInstruction();
0282: if (ins instanceof InvokeInstruction) {
0283: InvokeInstruction inv = (InvokeInstruction) ins;
0284: String className = inv
0285: .getClassName(cpg);
0286: if (!this ClassName.equals(className))
0287: continue;
0288: String signature = inv
0289: .getSignature(cpg);
0290: if (signature.indexOf('L') < 0
0291: && signature.indexOf('[') < 0)
0292: continue;
0293: String methodKey = inv
0294: .getMethodName(cpg)
0295: + signature
0296: + (inv instanceof INVOKESTATIC);
0297: Method method2 = map.get(methodKey);
0298: if (method2 != null)
0299: result.add(method2);
0300: }
0301: }
0302: } catch (CFGBuilderException e) {
0303: AnalysisContext.logError(
0304: "Error getting methods called by "
0305: + this ClassName + "."
0306: + method.getName() + ":"
0307: + method.getSignature(), e);
0308: }
0309: return result;
0310: }
0311: };
0312: }
0313: methodsInCallOrder = TopologicalSort.sortByCallGraph(
0314: methodList, edges1);
0315:
0316: assert methodList.size() == methodsInCallOrder.size();
0317: return methodsInCallOrder;
0318: }
0319:
0320: /**
0321: * Get the AnalysisContext.
0322: */
0323: public AnalysisContext getAnalysisContext() {
0324: return analysisContext;
0325: }
0326:
0327: /**
0328: * Get the RepositoryLookupFailureCallback.
0329: *
0330: * @return the RepositoryLookupFailureCallback
0331: */
0332: public RepositoryLookupFailureCallback getLookupFailureCallback() {
0333: return analysisContext.getLookupFailureCallback();
0334: }
0335:
0336: /**
0337: * Get a MethodGen object for given method.
0338: *
0339: * @param method the method
0340: * @return the MethodGen object for the method, or null
0341: * if the method has no Code attribute (and thus cannot be analyzed)
0342: * or if the method seems unprofitable to analyze
0343: */
0344: @CheckForNull
0345: public MethodGen getMethodGen(Method method) {
0346: return getMethodAnalysisNoException(MethodGen.class, method);
0347: }
0348:
0349: /**
0350: * Get a CFG for given method.
0351: * If pruning options are in effect, pruning will be done.
0352: * Because the CFG pruning can involve interprocedural analysis,
0353: * it is done on a best-effort basis, so the CFG returned might
0354: * not actually be pruned.
0355: *
0356: * @param method the method
0357: * @return the CFG
0358: * @throws CFGBuilderException if a CFG cannot be constructed for the method
0359: */
0360: public CFG getCFG(Method method) throws CFGBuilderException {
0361: return getMethodAnalysisNoDataflowAnalysisException(CFG.class,
0362: method);
0363: }
0364:
0365: /**
0366: * Get the ConstantPoolGen used to create the MethodGens
0367: * for this class.
0368: *
0369: * @return the ConstantPoolGen
0370: */
0371: public @NonNull
0372: ConstantPoolGen getConstantPoolGen() {
0373: return getClassAnalysisNoException(ConstantPoolGen.class);
0374: }
0375:
0376: /**
0377: * Get a UsagesRequiringNonNullValues for given method.
0378: *
0379: * @param method the method
0380: * @return the UsagesRequiringNonNullValues
0381: */
0382: public UsagesRequiringNonNullValues getUsagesRequiringNonNullValues(
0383: Method method) throws DataflowAnalysisException,
0384: CFGBuilderException {
0385: return getMethodAnalysis(UsagesRequiringNonNullValues.class,
0386: method);
0387: }
0388:
0389: /**
0390: * Get a ValueNumberDataflow for given method.
0391: *
0392: * @param method the method
0393: * @return the ValueNumberDataflow
0394: */
0395: public ValueNumberDataflow getValueNumberDataflow(Method method)
0396: throws DataflowAnalysisException, CFGBuilderException {
0397: return getMethodAnalysis(ValueNumberDataflow.class, method);
0398: }
0399:
0400: /**
0401: * Get an IsNullValueDataflow for given method.
0402: *
0403: * @param method the method
0404: * @return the IsNullValueDataflow
0405: */
0406: public IsNullValueDataflow getIsNullValueDataflow(Method method)
0407: throws DataflowAnalysisException, CFGBuilderException {
0408: return getMethodAnalysis(IsNullValueDataflow.class, method);
0409: }
0410:
0411: /**
0412: * Get a TypeDataflow for given method.
0413: *
0414: * @param method the method
0415: * @return the TypeDataflow
0416: */
0417: public TypeDataflow getTypeDataflow(Method method)
0418: throws DataflowAnalysisException, CFGBuilderException {
0419: return getMethodAnalysis(TypeDataflow.class, method);
0420: }
0421:
0422: /**
0423: * Get a DepthFirstSearch for given method.
0424: *
0425: * @param method the method
0426: * @return the DepthFirstSearch
0427: */
0428: public DepthFirstSearch getDepthFirstSearch(Method method)
0429: throws CFGBuilderException {
0430: return getMethodAnalysisNoDataflowAnalysisException(
0431: DepthFirstSearch.class, method);
0432: }
0433:
0434: /**
0435: * Get a ReverseDepthFirstSearch for given method.
0436: *
0437: * @param method the method
0438: * @return the ReverseDepthFirstSearch
0439: */
0440: public ReverseDepthFirstSearch getReverseDepthFirstSearch(
0441: Method method) throws CFGBuilderException {
0442: return getMethodAnalysisNoDataflowAnalysisException(
0443: ReverseDepthFirstSearch.class, method);
0444: }
0445:
0446: static MapCache<XMethod, BitSet> cachedBitsets = new MapCache<XMethod, BitSet>(
0447: 64);
0448: static MapCache<XMethod, Set<Integer>> cachedLoopExits = new MapCache<XMethod, Set<Integer>>(
0449: 13);
0450:
0451: /**
0452: * Get a BitSet representing the bytecodes that are used in the given method.
0453: * This is useful for prescreening a method for the existence of particular instructions.
0454: * Because this step doesn't require building a MethodGen, it is very
0455: * fast and memory-efficient. It may allow a Detector to avoid some
0456: * very expensive analysis, which is a Big Win for the user.
0457: *
0458: * @param method the method
0459: * @return the BitSet containing the opcodes which appear in the method,
0460: * or null if the method has no code
0461: */
0462: @CheckForNull
0463: public BitSet getBytecodeSet(Method method) {
0464: return getBytecodeSet(jclass, method);
0465: }
0466:
0467: /**
0468: * Get a BitSet representing the bytecodes that are used in the given method.
0469: * This is useful for prescreening a method for the existence of particular instructions.
0470: * Because this step doesn't require building a MethodGen, it is very
0471: * fast and memory-efficient. It may allow a Detector to avoid some
0472: * very expensive analysis, which is a Big Win for the user.
0473: *
0474: * @param method the method
0475: * @return the BitSet containing the opcodes which appear in the method,
0476: * or null if the method has no code
0477: */
0478: @CheckForNull
0479: static public BitSet getBytecodeSet(JavaClass clazz, Method method) {
0480:
0481: XMethod xmethod = XFactory.createXMethod(clazz, method);
0482: if (cachedBitsets.containsKey(xmethod)) {
0483: return cachedBitsets.get(xmethod);
0484: }
0485: Code code = method.getCode();
0486: if (code == null)
0487: return null;
0488:
0489: byte[] instructionList = code.getCode();
0490:
0491: // Create callback
0492: UnpackedBytecodeCallback callback = new UnpackedBytecodeCallback(
0493: instructionList.length);
0494:
0495: // Scan the method.
0496: BytecodeScanner scanner = new BytecodeScanner();
0497: scanner.scan(instructionList, callback);
0498:
0499: UnpackedCode unpackedCode = callback.getUnpackedCode();
0500: BitSet result = null;
0501: if (unpackedCode != null)
0502: result = unpackedCode.getBytecodeSet();
0503: cachedBitsets.put(xmethod, result);
0504: return result;
0505: }
0506:
0507: @CheckForNull
0508: static public Set<Integer> getLoopExitBranches(Method method,
0509: MethodGen methodGen) {
0510:
0511: XMethod xmethod = XFactory.createXMethod(methodGen);
0512: if (cachedLoopExits.containsKey(xmethod)) {
0513: return cachedLoopExits.get(xmethod);
0514: }
0515: Code code = method.getCode();
0516: if (code == null)
0517: return null;
0518:
0519: byte[] instructionList = code.getCode();
0520:
0521: Set<Integer> result = new HashSet<Integer>();
0522: for (int i = 0; i < instructionList.length; i++)
0523: if (checkForBranchExit(instructionList, i))
0524: result.add(i);
0525: if (result.size() == 0)
0526: result = TigerSubstitutes.emptySet(); // alas, emptySet() is @since 1.5
0527:
0528: cachedLoopExits.put(xmethod, result);
0529: return result;
0530: }
0531:
0532: static short getBranchOffset(byte[] codeBytes, int pos) {
0533: int branchByte1 = 0xff & codeBytes[pos];
0534: int branchByte2 = 0xff & codeBytes[pos + 1];
0535: int branchOffset = (short) (branchByte1 << 8 | branchByte2);
0536: return (short) branchOffset;
0537:
0538: }
0539:
0540: static boolean checkForBranchExit(byte[] codeBytes, int pos) {
0541: if (pos < 0 || pos + 2 >= codeBytes.length)
0542: return false;
0543: switch (0xff & codeBytes[pos]) {
0544: case Constants.IF_ACMPEQ:
0545: case Constants.IF_ACMPNE:
0546: case Constants.IF_ICMPEQ:
0547: case Constants.IF_ICMPGE:
0548: case Constants.IF_ICMPGT:
0549: case Constants.IF_ICMPLE:
0550: case Constants.IF_ICMPLT:
0551: case Constants.IF_ICMPNE:
0552: break;
0553: default:
0554: return false;
0555: }
0556: int branchTarget = pos + getBranchOffset(codeBytes, pos + 1);
0557: if (branchTarget - 3 < pos || branchTarget >= codeBytes.length)
0558: return false;
0559: if ((codeBytes[branchTarget - 3] & 0xff) != Constants.GOTO)
0560: return false;
0561: int backBranchTarget = branchTarget
0562: + getBranchOffset(codeBytes, branchTarget - 2);
0563: if (backBranchTarget <= pos && backBranchTarget + 12 >= pos)
0564: return true;
0565: return false;
0566: }
0567:
0568: /**
0569: * Get array mapping bytecode offsets to opcodes for given method.
0570: * Array elements containing zero are either not valid instruction offsets,
0571: * or contain a NOP instruction. (It is convenient not to distinguish
0572: * these cases.)
0573: *
0574: * @param method the method
0575: * @return map of bytecode offsets to opcodes, or null if the method has no code
0576: */
0577: public short[] getOffsetToOpcodeMap(Method method) {
0578: UnpackedCode unpackedCode = getMethodAnalysisNoException(
0579: UnpackedCode.class, method);
0580: return unpackedCode != null ? unpackedCode
0581: .getOffsetToBytecodeMap() : null;
0582: }
0583:
0584: /**
0585: * Get dataflow for LockAnalysis for given method.
0586: *
0587: * @param method the method
0588: * @return the LockDataflow
0589: */
0590: public LockDataflow getLockDataflow(Method method)
0591: throws CFGBuilderException, DataflowAnalysisException {
0592: return getMethodAnalysis(LockDataflow.class, method);
0593: }
0594:
0595: /**
0596: * Get LockChecker for method.
0597: * This is like LockDataflow, but may be able to avoid performing
0598: * the actual dataflow analyses if the method doesn't contain
0599: * explicit monitorenter/monitorexit instructions.
0600: *
0601: * @param method the method
0602: * @return the LockChecker
0603: * @throws CFGBuilderException
0604: * @throws DataflowAnalysisException
0605: */
0606: public LockChecker getLockChecker(Method method)
0607: throws CFGBuilderException, DataflowAnalysisException {
0608: return getMethodAnalysis(LockChecker.class, method);
0609: }
0610:
0611: /**
0612: * Get ReturnPathDataflow for method.
0613: *
0614: * @param method the method
0615: * @return the ReturnPathDataflow
0616: */
0617: public ReturnPathDataflow getReturnPathDataflow(Method method)
0618: throws CFGBuilderException, DataflowAnalysisException {
0619: return getMethodAnalysis(ReturnPathDataflow.class, method);
0620: }
0621:
0622: /**
0623: * Get DominatorsAnalysis for given method,
0624: * where exception edges are ignored.
0625: *
0626: * @param method the method
0627: * @return the DominatorsAnalysis
0628: */
0629: public DominatorsAnalysis getNonExceptionDominatorsAnalysis(
0630: Method method) throws CFGBuilderException,
0631: DataflowAnalysisException {
0632: return getMethodAnalysis(DominatorsAnalysis.class, method);
0633: }
0634:
0635: /**
0636: * Get DominatorsAnalysis for given method,
0637: * where implicit exception edges are ignored.
0638: *
0639: * @param method the method
0640: * @return the DominatorsAnalysis
0641: */
0642: public PostDominatorsAnalysis getNonImplicitExceptionDominatorsAnalysis(
0643: Method method) throws CFGBuilderException,
0644: DataflowAnalysisException {
0645: return getMethodAnalysis(
0646: NonImplicitExceptionPostDominatorsAnalysis.class,
0647: method);
0648: }
0649:
0650: /**
0651: * Get PostDominatorsAnalysis for given method,
0652: * where exception edges are ignored.
0653: *
0654: * @param method the method
0655: * @return the PostDominatorsAnalysis
0656: */
0657: public PostDominatorsAnalysis getNonExceptionPostDominatorsAnalysis(
0658: Method method) throws CFGBuilderException,
0659: DataflowAnalysisException {
0660: return getMethodAnalysis(
0661: NonExceptionPostdominatorsAnalysis.class, method);
0662: }
0663:
0664: /**
0665: * Get ExceptionSetFactory for given method.
0666: *
0667: * @param method the method
0668: * @return the ExceptionSetFactory
0669: */
0670: public ExceptionSetFactory getExceptionSetFactory(Method method) {
0671: return getMethodAnalysisNoException(ExceptionSetFactory.class,
0672: method);
0673: }
0674:
0675: /**
0676: * Get array of type signatures of parameters for given method.
0677: *
0678: * @param method the method
0679: * @return an array of type signatures indicating the types
0680: * of the method's parameters
0681: */
0682: public String[] getParameterSignatureList(Method method) {
0683: return getMethodAnalysisNoException(
0684: (Class<String[]>) new String[0].getClass(), method);// XXX
0685: }
0686:
0687: /**
0688: * Get the set of fields loaded by given method.
0689: *
0690: * @param method the method
0691: * @return the set of fields loaded by the method
0692: */
0693: public LoadedFieldSet getLoadedFieldSet(Method method) {
0694: return getMethodAnalysisNoException(LoadedFieldSet.class,
0695: method);
0696: }
0697:
0698: /**
0699: * Get LiveLocalStoreAnalysis dataflow for given method.
0700: *
0701: * @param method the method
0702: * @return the Dataflow object for LiveLocalStoreAnalysis on the method
0703: */
0704: public LiveLocalStoreDataflow getLiveLocalStoreDataflow(
0705: Method method) throws DataflowAnalysisException,
0706: CFGBuilderException {
0707: return getMethodAnalysis(LiveLocalStoreDataflow.class, method);
0708: }
0709:
0710: /**
0711: * Get BlockType dataflow for given method.
0712: *
0713: * @param method the method
0714: * @return the Dataflow object for BlockTypeAnalysis on the method
0715: */
0716: public BlockTypeDataflow getBlockTypeDataflow(Method method)
0717: throws DataflowAnalysisException, CFGBuilderException {
0718: return getMethodAnalysis(BlockTypeDataflow.class, method);
0719: }
0720:
0721: // /**
0722: // * Get the assigned field map for the class.
0723: // *
0724: // * @return the AssignedFieldMap
0725: // * @throws ClassNotFoundException if a class lookup prevents
0726: // * the class's superclasses from being searched for
0727: // * assignable fields
0728: // */
0729: // public AssignedFieldMap getAssignedFieldMap() throws ClassNotFoundException {
0730: // return getClassAnalysisPossibleClassNotFoundException(AssignedFieldMap.class);
0731: // }
0732:
0733: /**
0734: * Get AssertionMethods for class.
0735: *
0736: * @return the AssertionMethods
0737: */
0738: public AssertionMethods getAssertionMethods() {
0739: return getClassAnalysisNoException(AssertionMethods.class);
0740: }
0741:
0742: /**
0743: * Get ConstantDataflow for method.
0744: *
0745: * @param method the method
0746: * @return the ConstantDataflow
0747: * @throws CFGBuilderException
0748: * @throws DataflowAnalysisException
0749: */
0750: public ConstantDataflow getConstantDataflow(Method method)
0751: throws CFGBuilderException, DataflowAnalysisException {
0752: return getMethodAnalysis(ConstantDataflow.class, method);
0753: }
0754:
0755: /**
0756: * Get load dataflow.
0757: *
0758: * @param method the method
0759: * @return the LoadDataflow
0760: * @throws CFGBuilderException
0761: * @throws DataflowAnalysisException
0762: */
0763: public LoadDataflow getLoadDataflow(Method method)
0764: throws CFGBuilderException, DataflowAnalysisException {
0765: return getMethodAnalysis(LoadDataflow.class, method);
0766: }
0767:
0768: /**
0769: * Get store dataflow.
0770: *
0771: * @param method the method
0772: * @return the StoreDataflow
0773: * @throws CFGBuilderException
0774: * @throws DataflowAnalysisException
0775: */
0776: public StoreDataflow getStoreDataflow(Method method)
0777: throws CFGBuilderException, DataflowAnalysisException {
0778: return getMethodAnalysis(StoreDataflow.class, method);
0779: }
0780:
0781: /**
0782: * Get CallListDataflow for method.
0783: *
0784: * @param method the method
0785: * @return the CallListDataflow
0786: * @throws CFGBuilderException
0787: * @throws DataflowAnalysisException
0788: */
0789: public CallListDataflow getCallListDataflow(Method method)
0790: throws CFGBuilderException, DataflowAnalysisException {
0791: return getMethodAnalysis(CallListDataflow.class, method);
0792: }
0793:
0794: public static BitSet linesMentionedMultipleTimes(Method method) {
0795:
0796: BitSet lineMentionedMultipleTimes = new BitSet();
0797: Code code = method.getCode();
0798: if (code == null || code.getExceptionTable() == null)
0799: return lineMentionedMultipleTimes;
0800: BitSet foundOnce = new BitSet();
0801: LineNumberTable lineNumberTable = method.getLineNumberTable();
0802: int lineNum = -1;
0803: if (lineNumberTable != null)
0804: for (LineNumber line : lineNumberTable.getLineNumberTable()) {
0805: int newLine = line.getLineNumber();
0806: if (newLine == lineNum || newLine == -1)
0807: continue;
0808: lineNum = newLine;
0809: if (foundOnce.get(lineNum)) {
0810: lineMentionedMultipleTimes.set(lineNum);
0811: } else {
0812: foundOnce.set(lineNum);
0813: }
0814: }
0815: return lineMentionedMultipleTimes;
0816: }
0817:
0818: /**
0819: * Get the UnconditionalValueDerefDataflow for a method.
0820: *
0821: * @param method the method
0822: * @return the UnconditionalValueDerefDataflow
0823: * @throws CFGBuilderException
0824: * @throws DataflowAnalysisException
0825: */
0826: public UnconditionalValueDerefDataflow getUnconditionalValueDerefDataflow(
0827: Method method) throws CFGBuilderException,
0828: DataflowAnalysisException {
0829: return getMethodAnalysis(UnconditionalValueDerefDataflow.class,
0830: method);
0831: }
0832:
0833: /**
0834: * Get a CompactLocationNumbering for a method.
0835: *
0836: * @param method a method
0837: * @return the CompactLocationNumbering for the method
0838: * @throws CFGBuilderException
0839: */
0840: public CompactLocationNumbering getCompactLocationNumbering(
0841: Method method) throws CFGBuilderException {
0842: return getMethodAnalysisNoDataflowAnalysisException(
0843: CompactLocationNumbering.class, method);
0844: }
0845:
0846: /**
0847: * Get DefinitelyNullSetDataflow for a method.
0848: *
0849: * @param method a method
0850: * @return the DefinitelyNullSetDataflow for the method
0851: * @throws DataflowAnalysisException
0852: * @throws CFGBuilderException
0853: */
0854: public DefinitelyNullSetDataflow getDefinitelyNullSetDataflow(
0855: Method method) throws CFGBuilderException,
0856: DataflowAnalysisException {
0857: return getMethodAnalysis(DefinitelyNullSetDataflow.class,
0858: method);
0859: }
0860:
0861: /**
0862: * Get ReturnPathTypeDataflow for a method.
0863: *
0864: * @param method the method
0865: * @return the ReturnPathTypeDataflow for the method
0866: * @throws CFGBuilderException
0867: * @throws DataflowAnalysisException
0868: */
0869: public ReturnPathTypeDataflow getReturnPathTypeDataflow(
0870: Method method) throws CFGBuilderException,
0871: DataflowAnalysisException {
0872: return getMethodAnalysis(ReturnPathTypeDataflow.class, method);
0873: }
0874:
0875: public void dumpSimpleDataflowInformation(Method method) {
0876: try {
0877: dumpDataflowInformation(method, getCFG(method),
0878: getValueNumberDataflow(method),
0879: getIsNullValueDataflow(method), null, null);
0880: } catch (DataflowAnalysisException e) {
0881: AnalysisContext.logError(
0882: "Could not dump data information for "
0883: + getJavaClass().getClassName() + "."
0884: + method.getName(), e);
0885: } catch (CFGBuilderException e) {
0886: AnalysisContext.logError(
0887: "Could not dump data information for "
0888: + getJavaClass().getClassName() + "."
0889: + method.getName(), e);
0890:
0891: }
0892: }
0893:
0894: public void dumpDataflowInformation(Method method) {
0895: try {
0896: dumpDataflowInformation(method, getCFG(method),
0897: getValueNumberDataflow(method),
0898: getIsNullValueDataflow(method),
0899: getUnconditionalValueDerefDataflow(method),
0900: getTypeDataflow(method));
0901: } catch (DataflowAnalysisException e) {
0902: AnalysisContext.logError(
0903: "Could not dump data information for "
0904: + getJavaClass().getClassName() + "."
0905: + method.getName(), e);
0906: } catch (CFGBuilderException e) {
0907: AnalysisContext.logError(
0908: "Could not dump data information for "
0909: + getJavaClass().getClassName() + "."
0910: + method.getName(), e);
0911:
0912: }
0913: }
0914:
0915: /**
0916: * @param method
0917: * @param cfg
0918: * @param vnd
0919: * @param inv
0920: * @param dataflow
0921: * @param typeDataflow TODO
0922: * @throws DataflowAnalysisException
0923: */
0924: public static void dumpDataflowInformation(Method method, CFG cfg,
0925: ValueNumberDataflow vnd, IsNullValueDataflow inv,
0926: @CheckForNull
0927: UnconditionalValueDerefDataflow dataflow, @CheckForNull
0928: TypeDataflow typeDataflow) throws DataflowAnalysisException {
0929: System.out
0930: .println("\n\n{ UnconditionalValueDerefAnalysis analysis for "
0931: + method.getName());
0932: TreeSet<Location> tree = new TreeSet<Location>();
0933:
0934: for (Iterator<Location> locs = cfg.locationIterator(); locs
0935: .hasNext();) {
0936: Location loc = locs.next();
0937: tree.add(loc);
0938: }
0939: for (Location loc : tree) {
0940: System.out.println();
0941: if (dataflow != null)
0942: System.out.println("\n Pre: "
0943: + dataflow.getFactAfterLocation(loc));
0944: System.out.println("Vna: " + vnd.getFactAtLocation(loc));
0945: System.out.println("inv: " + inv.getFactAtLocation(loc));
0946: if (typeDataflow != null)
0947: System.out.println("type: "
0948: + typeDataflow.getFactAtLocation(loc));
0949: System.out.println("Location: " + loc);
0950: if (dataflow != null)
0951: System.out.println("Post: "
0952: + dataflow.getFactAtLocation(loc));
0953: System.out.println("Vna: " + vnd.getFactAfterLocation(loc));
0954: System.out.println("inv: " + inv.getFactAfterLocation(loc));
0955: if (typeDataflow != null)
0956: System.out.println("type: "
0957: + typeDataflow.getFactAfterLocation(loc));
0958: }
0959: System.out.println("}\n\n");
0960: }
0961:
0962: /**
0963: * @param method
0964: * @param cfg
0965: * @param typeDataflow
0966: * @throws DataflowAnalysisException
0967: */
0968: public static void dumpTypeDataflow(Method method, CFG cfg,
0969: TypeDataflow typeDataflow) throws DataflowAnalysisException {
0970: System.out.println("\n\n{ Type analysis for "
0971: + cfg.getMethodGen().getClassName() + "."
0972: + method.getName());
0973: TreeSet<Location> tree = new TreeSet<Location>();
0974:
0975: for (Iterator<Location> locs = cfg.locationIterator(); locs
0976: .hasNext();) {
0977: Location loc = locs.next();
0978: tree.add(loc);
0979: }
0980: for (Location loc : tree) {
0981: System.out.println("\n Pre: "
0982: + typeDataflow.getFactAtLocation(loc));
0983: System.out.println("Location: " + loc);
0984: System.out.println("Post: "
0985: + typeDataflow.getFactAfterLocation(loc));
0986: }
0987: System.out.println("}\n\n");
0988: }
0989:
0990: /* ----------------------------------------------------------------------
0991: * Helper methods for getting an analysis object from the analysis cache.
0992: * ---------------------------------------------------------------------- */
0993:
0994: private <Analysis> Analysis getMethodAnalysisNoException(
0995: Class<Analysis> analysisClass, Method method) {
0996: try {
0997: return getMethodAnalysis(analysisClass, method);
0998: } catch (CheckedAnalysisException e) {
0999: IllegalStateException ise = new IllegalStateException(
1000: "should not happen");
1001: ise.initCause(e);
1002: throw ise;
1003: }
1004: }
1005:
1006: private <Analysis> Analysis getMethodAnalysisNoDataflowAnalysisException(
1007: Class<Analysis> analysisClass, Method method)
1008: throws CFGBuilderException {
1009: try {
1010: return getMethodAnalysis(analysisClass, method);
1011: } catch (CFGBuilderException e) {
1012: throw e;
1013: } catch (CheckedAnalysisException e) {
1014: IllegalStateException ise = new IllegalStateException(
1015: "should not happen");
1016: ise.initCause(e);
1017: throw ise;
1018: }
1019:
1020: }
1021:
1022: private <Analysis> Analysis getMethodAnalysis(
1023: Class<Analysis> analysisClass, Method method)
1024: throws DataflowAnalysisException, CFGBuilderException {
1025: try {
1026: MethodDescriptor methodDescriptor = BCELUtil
1027: .getMethodDescriptor(jclass, method);
1028: return Global.getAnalysisCache().getMethodAnalysis(
1029: analysisClass, methodDescriptor);
1030: } catch (DataflowAnalysisException e) {
1031: throw e;
1032: } catch (CFGBuilderException e) {
1033: throw e;
1034: } catch (CheckedAnalysisException e) {
1035: Throwable cause = e.getCause();
1036: if (cause instanceof CFGBuilderException) {
1037: throw (CFGBuilderException) cause;
1038: }
1039: System.out.println("Bad CAE: " + e.getClass().getName()
1040: + " for " + analysisClass.getName() + " of "
1041: + method);
1042: e.printStackTrace(System.out);
1043: IllegalStateException ise = new IllegalStateException(
1044: "should not happen");
1045: ise.initCause(e);
1046: throw ise;
1047: }
1048: }
1049:
1050: private <Analysis> Analysis getClassAnalysis(
1051: Class<Analysis> analysisClass)
1052: throws CheckedAnalysisException {
1053: ClassDescriptor classDescriptor = BCELUtil
1054: .getClassDescriptor(jclass);
1055: return Global.getAnalysisCache().getClassAnalysis(
1056: analysisClass, classDescriptor);
1057: }
1058:
1059: private <Analysis> Analysis getClassAnalysisNoException(
1060: Class<Analysis> analysisClass) {
1061: try {
1062: return getClassAnalysis(analysisClass);
1063: } catch (CheckedAnalysisException e) {
1064: IllegalStateException ise = new IllegalStateException(
1065: "should not happen");
1066: ise.initCause(e);
1067: throw ise;
1068: }
1069: }
1070:
1071: private <Analysis> Analysis getClassAnalysisPossibleClassNotFoundException(
1072: Class<Analysis> analysisClass)
1073: throws ClassNotFoundException {
1074: try {
1075: return Global.getAnalysisCache().getClassAnalysis(
1076: analysisClass, BCELUtil.getClassDescriptor(jclass));
1077: } catch (ResourceNotFoundException e) {
1078: throw e.toClassNotFoundException();
1079: } catch (CheckedAnalysisException e) {
1080: throw new AnalysisException("Unexpected exception", e);
1081: }
1082: }
1083: }
1084:
1085: // vim:ts=3
|