Bugfixes, code and documentation improvements.

This commit is contained in:
Stephan Gocht 2016-01-13 02:07:11 +01:00
parent 0387bd1591
commit 2869af24df
11 changed files with 346 additions and 49 deletions

View File

@ -11,6 +11,7 @@ Require-Bundle: com.ibm.wala.shrike,
Bundle-ActivationPolicy: lazy
Export-Package: .,
com.ibm.wala.analysis.arraybounds,
com.ibm.wala.analysis.exceptionanalysis,
com.ibm.wala.analysis.nullpointer,
com.ibm.wala.analysis.pointers,
com.ibm.wala.analysis.reflection,
@ -43,6 +44,7 @@ Export-Package: .,
com.ibm.wala.ipa.cfg,
com.ibm.wala.ipa.cfg.exceptionpruning,
com.ibm.wala.ipa.cfg.exceptionpruning.filter,
com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural,
com.ibm.wala.ipa.cha,
com.ibm.wala.ipa.modref,
com.ibm.wala.ipa.slicer,

View File

@ -24,6 +24,12 @@ import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
/**
* Wrapper to store multiple intraprocedural analysis for a call graph.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class CGIntraproceduralExceptionAnalysis {
private Map<CGNode, IntraproceduralExceptionAnalysis> analysis;
private Set<TypeReference> exceptions;
@ -34,10 +40,10 @@ public class CGIntraproceduralExceptionAnalysis {
this.callGraph = cg;
this.exceptions = new LinkedHashSet<>();
this.analysis = new LinkedHashMap<>();
for (CGNode node : cg) {
if (node.getIR() == null || node.getIR().isEmptyIR()) {
for (CGNode node : cg) {
if (node.getIR() == null || node.getIR().isEmptyIR()) {
analysis.put(node, IntraproceduralExceptionAnalysis.newDummy());
} else {
} else {
IntraproceduralExceptionAnalysis intraEA;
intraEA = new IntraproceduralExceptionAnalysis(node, filter.getFilter(node), cha, pointerAnalysis);
analysis.put(node, intraEA);

View File

@ -26,7 +26,7 @@ public class Exception2BitvectorTransformer {
return values;
}
public Exception2BitvectorTransformer(Set<TypeReference> exceptions) {
public Exception2BitvectorTransformer(Set<TypeReference> exceptions) {
createValues(exceptions);
for (TypeReference exception : exceptions) {
BitVector bv = new BitVector(values.getSize());
@ -48,7 +48,7 @@ public class Exception2BitvectorTransformer {
result.set(pos);
} else {
throw new IllegalArgumentException("Got exception I don't know about,"
+ "make sure only to use exceptions given to the constructor ");
+ "make sure only to use exceptions given to the constructor ");
}
}
return result;

View File

@ -10,8 +10,12 @@
*******************************************************************************/
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;
@ -19,26 +23,72 @@ 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 <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) {
CGIntraproceduralExceptionAnalysis intraResult = new CGIntraproceduralExceptionAnalysis(callgraph, pointerAnalysis, cha, 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);
@ -55,16 +105,125 @@ public class ExceptionAnalysis {
try {
solver.solve(null);
} catch (CancelException e) {
throw new RuntimeException("Internal Error: Got Cancel Exception, "
+ "but didn't use Progressmonitor!", 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;
}
}
public void solve(IProgressMonitor monitor) throws CancelException {
solver.solve(monitor);
}
/**
* @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);
@ -72,4 +231,15 @@ public class ExceptionAnalysis {
return null;
}
}
/**
* @return the used filter
*/
public InterproceduralExceptionFilter<SSAInstruction> getFilter() {
if (!isSolved) {
throw new IllegalStateException("You need to use .solve() first!");
}
return filter;
}
}

View File

@ -0,0 +1,62 @@
/*******************************************************************************
* 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.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.EdgeFilter;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
/**
* Converter to use the results of the exception analysis with an edge filter.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ExceptionAnalysis2EdgeFilter implements EdgeFilter<ISSABasicBlock> {
private ExceptionAnalysis analysis;
private CGNode node;
public ExceptionAnalysis2EdgeFilter(ExceptionAnalysis analysis, CGNode node) {
this.analysis = analysis;
this.node = node;
}
@Override
public boolean hasNormalEdge(ISSABasicBlock src, ISSABasicBlock dst) {
boolean originalEdge = node.getIR().getControlFlowGraph().getNormalSuccessors(src).contains(dst);
boolean result = originalEdge;
SSAInstruction instruction = IntraproceduralExceptionAnalysis.getThrowingInstruction(src);
if (instruction != null) {
if (analysis.getFilter().getFilter(node).alwaysThrowsException(instruction)) {
result = false;
}
}
return result;
}
@Override
public boolean hasExceptionalEdge(ISSABasicBlock src, ISSABasicBlock dst) {
boolean originalEdge = node.getIR().getControlFlowGraph().getExceptionalSuccessors(src).contains(dst);
boolean result = originalEdge;
if (dst.isCatchBlock()) {
if (!analysis.catchesException(node, src, dst)) {
result = false;
}
} else {
assert dst.isExitBlock();
result = analysis.hasUncaughtExceptions(node, src);
}
return result;
}
}

View File

@ -70,19 +70,18 @@ public class ExceptionTransferFunctionProvider implements ITransferFunctionProvi
Iterator<CallSiteReference> callsites = cg.getPossibleSites(src, dst);
BitVector filtered = new BitVector(transformer.getValues().getSize());
if (callsites.hasNext()) {
CallSiteReference callsite = callsites.next();
Set<TypeReference> caught = new LinkedHashSet<>();
caught.addAll(intraResult.getAnalysis(src).getCaughtExceptions(callsite));
Set<TypeReference> caught = new LinkedHashSet<>();
caught.addAll(intraResult.getAnalysis(src).getCaughtExceptions(callsite));
while (callsites.hasNext()) {
callsite = callsites.next();
caught.retainAll(intraResult.getAnalysis(src).getCaughtExceptions(callsite));
}
filtered = transformer.computeBitVector(caught);
return new BitVectorMinusVector(filtered);
} else {

View File

@ -30,8 +30,8 @@ public class InitializedBitVectorSolver extends BitVectorSolver<CGNode> {
protected BitVectorVariable makeEdgeVariable(CGNode src, CGNode dst) {
return newBV();
}
private BitVectorVariable newBV(){
private BitVectorVariable newBV() {
/*
* If we do not initialize BitVectorVariable, with a BitVector, it contains
* null, which may crash in combination with {@link BitVectorMinusVector}
@ -39,6 +39,6 @@ public class InitializedBitVectorSolver extends BitVectorSolver<CGNode> {
*/
BitVectorVariable result = new BitVectorVariable();
result.addAll(new BitVector());
return result;
return result;
}
}

View File

@ -15,6 +15,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.classLoader.CallSiteReference;
@ -35,6 +36,7 @@ import com.ibm.wala.ssa.SSAInstruction.Visitor;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.intset.IntIterator;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.ssa.InstructionByIIndexMap;
public class IntraproceduralExceptionAnalysis {
private Set<TypeReference> exceptions;
@ -45,6 +47,7 @@ public class IntraproceduralExceptionAnalysis {
private ExceptionFilter<SSAInstruction> filter;
private IR ir;
private boolean dummy = false;
private Map<SSAInstruction, Boolean> allExceptionsCaught;
public static IntraproceduralExceptionAnalysis newDummy() {
return new IntraproceduralExceptionAnalysis();
@ -104,10 +107,8 @@ public class IntraproceduralExceptionAnalysis {
this.node = node;
this.exceptions = new LinkedHashSet<>();
this.possiblyCaughtExceptions = new LinkedHashSet<>();
this.allExceptionsCaught = new InstructionByIIndexMap<>();
compute();
this.filter = null;
this.pointerAnalysis = null;
this.node = null;
}
/**
@ -119,25 +120,26 @@ public class IntraproceduralExceptionAnalysis {
if (ir != null) {
for (ISSABasicBlock block : ir.getControlFlowGraph()) {
SSAInstruction throwingInstruction = getThrowingInstruction(block);
if (throwingInstruction != null) {
if (throwingInstruction != null && throwingInstruction.isPEI()) {
Set<TypeReference> thrownExceptions = collectThrownExceptions(throwingInstruction);
Set<TypeReference> caughtExceptions = collectCaughtExceptions(block);
Set<TypeReference> filteredExceptions = collectFilteredExceptions(throwingInstruction);
thrownExceptions.removeAll(filteredExceptions);
thrownExceptions.removeAll(caughtExceptions);
this.allExceptionsCaught.put(throwingInstruction, thrownExceptions.isEmpty());
exceptions.addAll(thrownExceptions);
}
if (block.isCatchBlock()) {
Iterator<TypeReference> it = block.getCaughtExceptionTypes();
while (it.hasNext()) {
Iterator<TypeReference> it = block.getCaughtExceptionTypes();
while (it.hasNext()) {
possiblyCaughtExceptions.add(it.next());
}
}
}
}
Set<TypeReference> subClasses = new LinkedHashSet<>();
for (TypeReference caught : possiblyCaughtExceptions) {
for (IClass iclass : this.classHierachy.computeSubClasses(caught)) {
@ -150,6 +152,7 @@ public class IntraproceduralExceptionAnalysis {
/**
* Return all exceptions that could be returned from getCaughtExceptions
*
* @return all exceptions that could be returned from getCaughtExceptions
*/
public Set<TypeReference> getPossiblyCaughtExceptions() {
@ -194,7 +197,7 @@ public class IntraproceduralExceptionAnalysis {
* @return a set of exceptions, which might be thrown from this instruction
* within this method
*/
private Set<TypeReference> collectThrownExceptions(SSAInstruction throwingInstruction) {
public Set<TypeReference> collectThrownExceptions(SSAInstruction throwingInstruction) {
final LinkedHashSet<TypeReference> result = new LinkedHashSet<>();
result.addAll(throwingInstruction.getExceptionTypes());
@ -243,11 +246,11 @@ public class IntraproceduralExceptionAnalysis {
* @return an instruction which may throw exceptions, or null if this block
* can't throw exceptions
*/
private SSAInstruction getThrowingInstruction(ISSABasicBlock block) {
public static SSAInstruction getThrowingInstruction(ISSABasicBlock block) {
SSAInstruction result = null;
if (block.getLastInstructionIndex() >= 0) {
SSAInstruction lastInstruction = block.getLastInstruction();
if (lastInstruction != null) {
if (lastInstruction != null && lastInstruction.isPEI()) {
result = lastInstruction;
}
}
@ -282,7 +285,6 @@ public class IntraproceduralExceptionAnalysis {
return result;
}
/**
* Returns all exceptions for the given call site in the given call graph
* node, which will be caught.
@ -317,6 +319,11 @@ public class IntraproceduralExceptionAnalysis {
return result;
}
public boolean hasUncaughtExceptions(SSAInstruction instruction) {
Boolean allCaught = this.allExceptionsCaught.get(instruction);
return (allCaught == null ? true : !allCaught.booleanValue());
}
/**
* Returns all exceptions which might be created and thrown but not caught or
* filtered. (So this does not contain exceptions from invoked methods.)

View File

@ -1,2 +1,8 @@
/**
* This package contains an exception analysis. For an interprocedural
* exception analysis use {@link com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis}.
* If you need a CFG without unnecessary exception edges use {@link com.ibm.wala.ipa.cfg.PrunedCFG}
* in combination with {@link com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis2EdgeFilter}
*/
package com.ibm.wala.analysis.exceptionanalysis;

View File

@ -1,7 +1,7 @@
package com.ibm.wala.ipa.cfg.exceptionpruning;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import com.ibm.wala.classLoader.IClass;
@ -29,37 +29,49 @@ public class ExceptionMatcher {
filteredExceptions, cha);
return matcher.areAllExceptionsIgnored();
}
/**
* Returns all exceptions of thrownExceptions which are not filtered by filteredExceptions
* @param thrownExceptions
* @param filteredExceptions
* @param cha
* @return all exceptions of thrownExceptions which are not filtered by filteredExceptions
*/
public static Set<TypeReference> retainedExceptions(Collection<TypeReference> thrownExceptions,
Collection<FilteredException> filteredExceptions, ClassHierarchy cha){
final ExceptionMatcher matcher = new ExceptionMatcher(thrownExceptions,
filteredExceptions, cha);
return matcher.getRetainedExceptions();
}
private Set<TypeReference> ignoreExact;
private Set<TypeReference> ignoreSubclass;
private final Set<TypeReference> retainedExceptions;
private ClassHierarchy cha;
private final boolean areAllExceptionsIgnored;
private ExceptionMatcher(Collection<TypeReference> thrownExceptions,
Collection<FilteredException> filteredExceptions, ClassHierarchy cha) {
this.ignoreExact = new HashSet<>();
this.ignoreSubclass = new HashSet<>();
this.ignoreExact = new LinkedHashSet<>();
this.ignoreSubclass = new LinkedHashSet<>();
this.cha = cha;
this.retainedExceptions = new LinkedHashSet<>();
this.fillIgnore(filteredExceptions);
this.areAllExceptionsIgnored = this
.allExceptionsIgnored(thrownExceptions);
this.computeRetainedExceptions(thrownExceptions);
this.areAllExceptionsIgnored = this.retainedExceptions.isEmpty();
this.free();
}
private boolean allExceptionsIgnored(
Collection<TypeReference> thrownExceptions) {
boolean allExceptionsIgnored = true;
for (final TypeReference exception : thrownExceptions) {
allExceptionsIgnored &= this.isFiltered(exception);
if (!allExceptionsIgnored) {
break;
}
}
return allExceptionsIgnored;
private void computeRetainedExceptions(Collection<TypeReference> thrownExceptions){
for (final TypeReference exception : thrownExceptions) {
if (!this.isFiltered(exception)) {
this.retainedExceptions.add(exception);
}
}
}
private boolean areAllExceptionsIgnored() {
@ -108,4 +120,8 @@ public class ExceptionMatcher {
return isFiltered;
}
public Set<TypeReference> getRetainedExceptions() {
return retainedExceptions;
}
}

View File

@ -0,0 +1,29 @@
/*******************************************************************************
* 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.ipa.cfg.exceptionpruning.filter;
import java.util.Collection;
import java.util.Collections;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
public class DummyFilter<Instruction> implements ExceptionFilter<Instruction>{
@Override
public boolean alwaysThrowsException(Instruction instruction) {
return false;
}
@Override
public Collection<FilteredException> filteredExceptions(Instruction instruction) {
return Collections.emptyList();
}
}