WALA/com.ibm.wala.core/src/com/ibm/wala/analysis/exceptionanalysis/ExceptionAnalysis.java

246 lines
9.1 KiB
Java

/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.analysis.exceptionanalysis;
import java.util.Iterator;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.analysis.nullpointer.IntraproceduralNullPointerAnalysis;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.dataflow.graph.BitVectorFramework;
import com.ibm.wala.dataflow.graph.BitVectorSolver;
import com.ibm.wala.fixpoint.BitVectorVariable;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionMatcher;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.DummyFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.IgnoreExceptionsInterFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.InterproceduralExceptionFilter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction.Visitor;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.impl.InvertedGraph;
/**
*
* This class analyzes the exceptional control flow. Use
* {@link ExceptionAnalysis2EdgeFilter} to remove infeasible edges.
*
* In a first step an intraprocedural analysis is performed, to collect the
* thrown exceptions and collect the exceptions caught, per invoke instruction.
* The results of the intraprocedural analysis are used for a GenKill data flow
* analysis on the call graph. (Each node generates intraprocedural thrown
* exceptions and along invoke edges, caught exceptions are removed.)
*
* Notice: Only exceptions, which are part of the analysis scope are considered.
*
* @author Stephan Gocht {@code <stephan@gobro.de>}
*
*/
public class ExceptionAnalysis {
private BitVectorSolver<CGNode> solver;
private Exception2BitvectorTransformer transformer;
private InterproceduralExceptionFilter<SSAInstruction> filter;
private ClassHierarchy cha;
private CGIntraproceduralExceptionAnalysis intraResult;
private CallGraph cg;
private boolean isSolved = false;
public ExceptionAnalysis(CallGraph callgraph, PointerAnalysis<InstanceKey> pointerAnalysis, ClassHierarchy cha) {
this(callgraph, pointerAnalysis, cha, null);
}
/**
* @param callgraph
* @param pointerAnalysis
* @param cha
* @param filter
* a filter to include results of other analysis (like
* {@link ArrayOutOfBoundsAnalysis} or
* {@link IntraproceduralNullPointerAnalysis}) or to ignore
* exceptions completely.
*/
public ExceptionAnalysis(CallGraph callgraph, PointerAnalysis<InstanceKey> pointerAnalysis, ClassHierarchy cha,
InterproceduralExceptionFilter<SSAInstruction> filter) {
this.cha = cha;
this.cg = callgraph;
if (filter == null) {
this.filter = new IgnoreExceptionsInterFilter<>(new DummyFilter<SSAInstruction>());
} else {
this.filter = filter;
}
intraResult = new CGIntraproceduralExceptionAnalysis(callgraph, pointerAnalysis, cha, this.filter);
transformer = new Exception2BitvectorTransformer(intraResult.getExceptions());
ExceptionTransferFunctionProvider transferFunctionProvider = new ExceptionTransferFunctionProvider(intraResult, callgraph,
transformer);
Graph<CGNode> graph = new InvertedGraph<CGNode>(callgraph);
BitVectorFramework<CGNode, TypeReference> problem = new BitVectorFramework<>(graph, transferFunctionProvider,
transformer.getValues());
solver = new InitializedBitVectorSolver(problem);
solver.initForFirstSolve();
}
public void solve() {
try {
solver.solve(null);
} catch (CancelException e) {
throw new RuntimeException("Internal Error: Got Cancel Exception, " + "but didn't use Progressmonitor!", e);
}
this.isSolved = true;
}
public void solve(IProgressMonitor monitor) throws CancelException {
solver.solve(monitor);
this.isSolved = true;
}
public boolean catchesException(CGNode node, ISSABasicBlock throwBlock, ISSABasicBlock catchBlock) {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
if (node.getIR().getControlFlowGraph().getExceptionalSuccessors(throwBlock).contains(catchBlock) && catchBlock.isCatchBlock()) {
SSAInstruction instruction = IntraproceduralExceptionAnalysis.getThrowingInstruction(throwBlock);
assert instruction != null;
Iterator<TypeReference> caughtExceptions = catchBlock.getCaughtExceptionTypes();
Set<TypeReference> thrownExceptions = this.getExceptions(node, instruction);
boolean isCaught = false;
while (caughtExceptions.hasNext() && !isCaught) {
TypeReference caughtException = caughtExceptions.next();
for (TypeReference thrownException : thrownExceptions) {
isCaught |= cha.isAssignableFrom(cha.lookupClass(caughtException), cha.lookupClass(thrownException));
if (isCaught)
break;
}
}
return isCaught;
} else {
return false;
}
}
/**
* @param node
* @param block
* @return if the block has uncaught exceptions
*/
public boolean hasUncaughtExceptions(CGNode node, ISSABasicBlock block) {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
SSAInstruction instruction = IntraproceduralExceptionAnalysis.getThrowingInstruction(block);
if (instruction != null) {
Set<TypeReference> exceptions = this.getExceptions(node, instruction);
boolean allCaught = true;
for (TypeReference thrownException : exceptions) {
boolean isCaught = false;
for (ISSABasicBlock catchBlock : node.getIR().getControlFlowGraph().getExceptionalSuccessors(block)) {
Iterator<TypeReference> caughtExceptions = catchBlock.getCaughtExceptionTypes();
while (caughtExceptions.hasNext() && !isCaught) {
TypeReference caughtException = caughtExceptions.next();
isCaught |= cha.isAssignableFrom(cha.lookupClass(caughtException), cha.lookupClass(thrownException));
if (isCaught)
break;
}
if (isCaught)
break;
}
allCaught &= isCaught;
if (!allCaught)
break;
}
return !allCaught;
} else {
return false;
}
}
/**
* Returns all exceptions, which may be raised by this instruction. This
* includes exceptions from throw and invoke statements.
*
* @param node
* @param instruction
* @return all exceptions, which may be raised by this instruction
*/
public Set<TypeReference> getExceptions(final CGNode node, SSAInstruction instruction) {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
final Set<TypeReference> thrown = intraResult.getAnalysis(node).collectThrownExceptions(instruction);
instruction.visit(new Visitor() {
@Override
public void visitInvoke(SSAInvokeInstruction instruction) {
CallSiteReference site = instruction.getCallSite();
Set<CGNode> targets = cg.getPossibleTargets(node, site);
for (CGNode target : targets) {
thrown.addAll(getCGNodeExceptions(target));
}
}
});
Set<TypeReference> result = thrown;
if (filter != null) {
ExceptionFilter<SSAInstruction> nodeFilter = filter.getFilter(node);
result = ExceptionMatcher.retainedExceptions(thrown, nodeFilter.filteredExceptions(instruction), cha);
}
return result;
}
/**
*
* @param node
* @return all exceptions, which might be thrown by the method represented
* through the call graph node.
*/
public Set<TypeReference> getCGNodeExceptions(CGNode node) {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
BitVectorVariable nodeResult = solver.getOut(node);
if (nodeResult != null) {
return transformer.computeExceptions(nodeResult);
} else {
return null;
}
}
/**
* @return the used filter
*/
public InterproceduralExceptionFilter<SSAInstruction> getFilter() {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
return filter;
}
}