com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis Maven / Gradle / Ivy
/*
* 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 com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.analysis.nullpointer.IntraproceduralNullPointerAnalysis;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.classLoader.IClass;
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.SSAInstruction.Visitor;
import com.ibm.wala.ssa.SSAInvokeInstruction;
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;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
/**
* 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 }
*/
public class ExceptionAnalysis {
private final BitVectorSolver solver;
private final Exception2BitvectorTransformer transformer;
private final InterproceduralExceptionFilter filter;
private final ClassHierarchy cha;
private final CGIntraproceduralExceptionAnalysis intraResult;
private final CallGraph cg;
private boolean isSolved = false;
public ExceptionAnalysis(
CallGraph callgraph, PointerAnalysis pointerAnalysis, ClassHierarchy cha) {
this(callgraph, pointerAnalysis, cha, null);
}
/**
* @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 pointerAnalysis,
ClassHierarchy cha,
InterproceduralExceptionFilter filter) {
this.cha = cha;
this.cg = callgraph;
this.filter =
Objects.requireNonNullElseGet(
filter, () -> new IgnoreExceptionsInterFilter<>(new DummyFilter<>()));
intraResult =
new CGIntraproceduralExceptionAnalysis(callgraph, pointerAnalysis, cha, this.filter);
transformer = new Exception2BitvectorTransformer(intraResult.getExceptions());
ExceptionTransferFunctionProvider transferFunctionProvider =
new ExceptionTransferFunctionProvider(intraResult, callgraph, transformer);
Graph graph = new InvertedGraph<>(callgraph);
BitVectorFramework 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 caughtExceptions = catchBlock.getCaughtExceptionTypes();
Set thrownExceptions = this.getExceptions(node, instruction);
boolean isCaught = false;
while (caughtExceptions.hasNext() && !isCaught) {
TypeReference caughtException = caughtExceptions.next();
IClass caughtExceptionClass = cha.lookupClass(caughtException);
if (caughtExceptionClass == null) {
// for now, assume it is not caught
continue;
}
for (TypeReference thrownException : thrownExceptions) {
IClass thrownExceptionClass = cha.lookupClass(thrownException);
if (thrownExceptionClass == null) {
// for now, assume it is not caught
continue;
}
isCaught |= cha.isAssignableFrom(caughtExceptionClass, thrownExceptionClass);
if (isCaught) break;
}
}
return isCaught;
} else {
return false;
}
}
/**
* @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 exceptions = this.getExceptions(node, instruction);
boolean allCaught = true;
for (TypeReference thrownException : exceptions) {
boolean isCaught = false;
for (ISSABasicBlock catchBlock :
node.getIR().getControlFlowGraph().getExceptionalSuccessors(block)) {
Iterator 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.
*
* @return all exceptions, which may be raised by this instruction
*/
public Set getExceptions(final CGNode node, SSAInstruction instruction) {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
final Set thrown =
intraResult.getAnalysis(node).collectThrownExceptions(instruction);
instruction.visit(
new Visitor() {
@Override
public void visitInvoke(SSAInvokeInstruction instruction) {
CallSiteReference site = instruction.getCallSite();
Set targets = cg.getPossibleTargets(node, site);
for (CGNode target : targets) {
thrown.addAll(getCGNodeExceptions(target));
}
}
});
Set result = thrown;
if (filter != null) {
ExceptionFilter nodeFilter = filter.getFilter(node);
result =
ExceptionMatcher.retainedExceptions(
thrown, nodeFilter.filteredExceptions(instruction), cha);
}
return result;
}
/**
* @return all exceptions, which might be thrown by the method represented through the call graph
* node.
*/
public Set 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 getFilter() {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
return filter;
}
}