This commit is contained in:
Julian Dolby 2016-06-29 13:03:50 -04:00
commit 22ce3d1144
67 changed files with 5782 additions and 45 deletions

View File

@ -0,0 +1,226 @@
package arraybounds;
/**
*
* All array accesses in the following class are unnecessary and they will be
* detected correctly by the array bounds analysis.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class Detectable {
private int[] memberArr = new int[5];
/**
* Note: This is correct, even if memberArr is not final!
*
* @param i
* @return memberArr[i]
*/
public int memberLocalGet(int i) {
int[] arr = memberArr;
if (i >= 0 && i < arr.length) {
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
public int[] constantCreation() {
return new int[] { 3, 4, 5 };
}
public int get(int i, int[] arr) {
if (i >= 0 && i < arr.length) {
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
public void loop(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = 0;
}
}
public boolean equals(int[] a, int[] b) {
int lenA = a.length;
int lenB = b.length;
boolean result;
if (lenA == lenB) {
result = true;
for (int i = 0; i < lenA; i++) {
if (a[i] != b[i]) {
result = false;
}
}
} else {
result = false;
}
return result;
}
public void copy(int[] src, int[] dst) {
int lenSrc = src.length;
int lenDst = dst.length;
if (lenSrc < lenDst) {
for (int i = 0; i < lenSrc; i++) {
dst[i] = src[i];
}
} else {
throw new IllegalArgumentException();
}
}
/**
* swaps elements of a and b for all i: 0 <= i < min(a.length, b.length)
*
* @param a
* @param b
*/
public void swapWithMin(int[] a, int[] b) {
final int l1 = a.length;
final int l2 = b.length;
int length;
if (l1 < l2) {
length = l1;
} else {
length = l2;
}
for (int i = 0; i < length; i++) {
int tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
/**
* Invert the order of all elements of arr with index i: fromIndex <= i <
* toIndex.
*
* @param arr
* @param fromIndex
* @param toIndex
*/
public void partialInvert(int[] arr, int fromIndex, int toIndex) {
if (fromIndex >= 0 && toIndex <= arr.length && fromIndex < toIndex) {
for (int next = fromIndex; next < toIndex; ++next) {
int tmp = arr[toIndex - 1];
int i;
for (i = toIndex - 1; i > next; --i) {
arr[i] = arr[i - 1];
}
arr[i] = tmp; // i == next
}
}
}
/**
* The constant 3 is stored in a variable. The pi construction for the
* variable allows to detect, that the array access is in bound.
*
* Compare to {@link NotDetectable#dueToConstantPropagation(int[])}
*
* @param arr
* @return arr[3]
*/
public int nonFinalConstant(int[] arr) {
int i = 3;
if (i < arr.length) {
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
/**
* Workaround for {@link NotDetectable#constants(int[])}
*
* Note: It is important, that the variable five, is compared directly to the
* length, and that further computations are performed with this variable.
*
* @param arr
* @return arr[3]
*/
public int constantsWorkaround(int[] arr) {
int five = 5;
if (arr.length > five) {
return arr[five - 2];
} else {
throw new IllegalArgumentException();
}
}
/**
* Actually aliasing is only working, because it is removed during
* construction by wala.
*
* @param i
* @param arr
* @return arr[i]
*/
public int aliasing(int i, int[] arr) {
int[] a = arr;
int[] b = arr;
if (0 <= i && i < a.length) {
return b[i];
} else {
throw new IllegalArgumentException();
}
}
public String afterLoop(int[] arr) {
int len = arr.length - 1;
StringBuffer buffer = new StringBuffer();
int zero = 0;
if (zero < arr.length) {
int i = zero;
while (i < len) {
buffer.append(arr[i]);
buffer.append(", ");
i++;
}
buffer.append(arr[i]);
}
return buffer.toString();
}
public static void quickSort(int[] arr, int left, int right) {
if (0 <= left && right <= arr.length && left < right - 1) {
int pivot = arr[left];
int lhs = left + 1;
int rhs = right - 1;
while (lhs < rhs) {
while (arr[lhs] <= pivot && lhs < right - 1) {
lhs++;
}
while (arr[rhs] >= pivot && rhs > left) {
rhs--;
}
if (lhs < rhs) {
int tmp = arr[lhs];
arr[lhs] = arr[rhs];
arr[rhs] = tmp;
}
}
if (arr[lhs] < pivot) {
arr[left] = arr[lhs];
arr[lhs] = pivot;
}
quickSort(arr, left, lhs);
quickSort(arr, lhs, right);
}
}
}

View File

@ -0,0 +1,175 @@
package arraybounds;
/**
*
* All array accesses in the following class are unnecessary but they will not
* be detected correctly by the array bounds analysis.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class NotDetectable {
private final int[] memberArr = new int[5];
/**
* Member calls are not supported. See {@link Detectable#memberLocalGet(int)}
* for workaround.
*
* @param i
* @return memberArr[i]
*/
public int memberGet(int i) {
if (i >= 0 && i < memberArr.length) {
return memberArr[i];
} else {
throw new IllegalArgumentException();
}
}
private int getLength() {
return memberArr.length;
}
/**
* Interprocedural analysis is not supported.
*
* @param i
* @return memberArr[i]
*/
public int interproceduralGet(int i) {
if (i >= 0 && i < getLength()) {
return memberArr[i];
} else {
throw new IllegalArgumentException();
}
}
/**
* This example does not work: We know 5 > 3 and sometimes length > 5 > 3. In
* case of variables this conditional relation is resolved by introducing pi
* nodes. For constants pi nodes can be generated, but the pi variables will
* not be used (maybe due to constant propagation?). Additionally 5 != 3, so
* even if we would use pi-variables for 5, there would be no relation to 3: 0
* -(5)-> 5, 5 -(-5)-> 0, {5,length} -(0)-> 5', 0 -(3)-> 3, 3 -(-3)-> 0 Given
* the inequality graph above, we know that 5,5',3 are larger than 0 and 5
* larger 3 and length is larger than 5', but not 5' larger than 3. Which is
* not always the case in general anyway.
*
* This may be implemented by replacing each use of a constant dominated by a
* definition of a pi of a constant, with a fresh variable, that is connected
* to the inequality graph accordingly.
*
* For a workaround see {@link Detectable#nonFinalConstant(int[])}
*
*
*
* @param arr
* @return arr[3]
*/
public int constants(int[] arr) {
if (arr.length > 5) {
return arr[3];
} else {
throw new IllegalArgumentException();
}
}
/**
* As the variable i is final, constant propagation will prevent the detection
* (See also {@link NotDetectable#constants(int[])}), for a working example
* see {@link Detectable#nonFinalConstant(int[])}.
*
* @param arr
* @return arr[3]
*/
public int dueToConstantPropagation(int[] arr) {
final int i = 3;
if (i < arr.length) {
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
public int indirectComparison(int[] arr) {
int i = 3;
int e = i + 1;
if (e < arr.length) {
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
/**
* Neither modulo, multiplication or division with constants will work.
*
* Note: Any operation can be performed BEFORE comparing a variable to the
* array length.
*
* @param arr
* @param i
* @return arr[i]
*/
public int modulo(int[] arr, int i) {
if (0 <= i && i < arr.length) {
return arr[i % 2];
} else {
throw new IllegalArgumentException();
}
}
/**
* Neither subtraction of variables nor inverting variables will work.
*
* Note: Any operation can be performed BEFORE comparing a variable to the
* array length.
*
* @param arr
* @param i
* @return arr[i]
*/
public int variableSubtraction(int[] arr, int i) {
if (0 <= i && i < arr.length) {
int i2 = 0 - i;
int i3 = -i2;
return arr[i3];
} else {
throw new IllegalArgumentException();
}
}
/**
* The analysis can not detect, that forward is false every other iteration.
* So there is a positive and a negative loop. The positive loop is bound by
* the loop condition, while the negative loop is unbound, so index might be
* smaller than zero. This should result in the lower bound check beeing
* necessary.
*
* @param arr
* @return sum of all elements in arr
*/
public int nonMonotounous(int arr[]) {
int index = 0;
int sum = 0;
boolean forward = true;
while (index < arr.length) {
sum += arr[index];
if (forward) {
index += 2;
} else {
index -= 1;
}
forward = !forward;
}
return sum;
}
/**
* Multidimensional Arrays are not supported yet.
*/
public int multiDimensional(){
int arr[][] = new int[5][10];
return arr[2][3];
}
}

View File

@ -0,0 +1,80 @@
package arraybounds;
/**
*
* All array accesses in the following class are necessary and they will be
* detected correctly by the array bounds analysis.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class NotInBound {
public int phiOverwrite(int[] arr, boolean condition) {
if (arr.length > 0) {
int l = arr.length;
if (condition) {
l = 5;
}
return arr[l - 1];
} else {
throw new IllegalArgumentException();
}
}
public void offByOne(int[] arr) {
for (int i = 0; i <= arr.length; i++) {
arr[i] = 0;
}
}
public int unknownLength(int[] arr) {
return arr[4];
}
public int ambiguity(int[] arr1, int[] arr2, int i) {
int[] arr = arr2;
if (0 <= i && i < arr.length) {
arr = arr1;
return arr[i];
} else {
throw new IllegalArgumentException();
}
}
public int innerLoop(int[] arr) {
int sum = 0;
int i = 0;
while (i < arr.length) {
while (i < 6) {
i++;
sum += arr[i];
}
}
return sum;
}
public int comparedWrong(int[] arr, int index) {
if (index > 0 && index > arr.length) {
return arr[index];
} else {
throw new IllegalArgumentException();
}
}
public int nonTrackedOperationIsSafe(int[] arr, int index) {
if (index > 0 && index > arr.length) {
return arr[2*index];
} else {
throw new IllegalArgumentException();
}
}
public int unboundNegativeLoop(int[] arr, int index) {
int sum = 0;
for (int i = 5; i < arr.length; i--) {
sum += arr[i];
}
return sum;
}
}

View File

@ -0,0 +1,5 @@
package exceptionpruning;
public class OwnException extends RuntimeException {
}

View File

@ -0,0 +1,148 @@
package exceptionpruning;
public class TestPruning {
public void testTryCatchOwnException(int i) {
try {
switch (i) {
case 1:
invokeSingleThrowOwnException();
break;
case 2:
throw new OwnException();
case 3:
invokeSinglePassThrough();
break;
case 5:
invokeSingleRecursive(i);
break;
case 4:
invokeSingleRecursive2(i);
break;
}
} catch (OwnException e) {
}
}
public int testTryCatchImplicitException(int i) {
int res = 0;
try {
int[] a = new int[]{1,3,4};
switch (i) {
case 3:
invokeSingleImplicitException(a);
break;
case 4:
res = a[5];
}
} catch (ArrayIndexOutOfBoundsException e) {
}
return res;
}
public void testTryCatchSuper(int i) {
try {
switch (i) {
case 5:
invokeSingleThrowOwnException();
break;
case 6:
throw new OwnException();
case 7:
invokeAllPassThrough();
break;
}
} catch (RuntimeException e) {
}
}
public int testTryCatchMultipleExceptions(int i) {
int res = 0;
try {
int[] a = new int[]{1,3,4};
switch (i) {
case 7:
invokeAll();
break;
case 8:
throw new OwnException();
case 10:
res = a[5];
break;
case 11:
invokeSingleRecursive(i);
break;
case 12:
invokeSingleRecursive2(i);
break;
case 13:
invokeAllPassThrough();
break;
}
} catch (ArrayIndexOutOfBoundsException e) {
} catch (OwnException e) {
}
return res;
}
public void invokeAll() {
invokeSingleThrowOwnException();
invokeSingleImplicitException(null);
}
public void invokeAllPassThrough() {
invokeAll();
}
public void invokeSinglePassThrough() {
invokeSingleThrowOwnException();
}
public void invokeSingleThrowOwnException() {
throw new OwnException();
}
public int invokeSingleImplicitException(int[] a) {
// may throw NullPointerException implicit
return a[5];
}
public void invokeSingleRecursive(int i) {
if (i == 0) {
throw new OwnException();
} else {
invokeSingleRecursive(i - 1);
}
}
public void invokeSingleRecursive2(int i) {
if (i == 0) {
throw new OwnException();
} else {
invokeSingleRecursive2Helper(i - 1);
}
}
public void invokeSingleRecursive2Helper(int i) {
invokeSingleRecursive2(i);
}
public static void main(String[] args) {
TestPruning test = new TestPruning();
for (int i = 0; i < 50; i++) {
test.testTryCatchOwnException(i);
test.testTryCatchMultipleExceptions(i);
test.testTryCatchImplicitException(i);
test.testTryCatchSuper(i);
}
}
}

View File

@ -0,0 +1,151 @@
package com.ibm.wala.core.tests.arraybounds;
import java.io.IOException;
import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import static org.hamcrest.CoreMatchers.*;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis.UnnecessaryCheck;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ssa.AllIntegerDueToBranchePiPolicy;
import com.ibm.wala.ssa.DefaultIRFactory;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.IRFactory;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.config.AnalysisScopeReader;
/**
* The test data should be grouped, according to the behavior of the analysis.
* All array accesses of a class are to be detected as "in bound" or all are to
* be detected as "not in bound".
*
* This test will only check if all found accesses behave accordingly and if the
* number of array accesses is as expected.
*
* So there is no explicit check for specific lines.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ArrayboundsAnalysisTest {
private static ClassLoader CLASS_LOADER = ArrayboundsAnalysisTest.class.getClassLoader();
private static final String DETECTABLE_TESTDATA = "Larraybounds/Detectable";
private static final int DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 34;
private static final String NOT_DETECTABLE_TESTDATA = "Larraybounds/NotDetectable";
private static final int NOT_DETECTABLE_NUMBER_OF_ARRAY_ACCESS = 10;
private static final String NOT_IN_BOUND_TESTDATA = "Larraybounds/NotInBound";
private static final int NOT_IN_BOUND_TESTDATA_NUMBER_OF_ARRAY_ACCESS = 8;
private static IRFactory<IMethod> irFactory;
private static AnalysisOptions options;
private static AnalysisScope scope;
private static ClassHierarchy cha;
@Rule
public ErrorCollector collector = new ErrorCollector();
@BeforeClass
public static void init() throws IOException, ClassHierarchyException {
scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, null, CLASS_LOADER);
cha = ClassHierarchy.make(scope);
irFactory = new DefaultIRFactory();
options = new AnalysisOptions();
options.getSSAOptions().setPiNodePolicy(new AllIntegerDueToBranchePiPolicy());
}
public static IR getIr(IMethod method) {
return irFactory.makeIR(method, Everywhere.EVERYWHERE, options.getSSAOptions());
}
public static IClass getIClass(String name) {
final TypeReference typeRef = TypeReference.findOrCreate(scope.getApplicationLoader(), name);
return cha.lookupClass(typeRef);
}
@Test
public void detectable() {
IClass iClass = getIClass(DETECTABLE_TESTDATA);
assertAllSameNecessity(iClass, DETECTABLE_NUMBER_OF_ARRAY_ACCESS, equalTo(UnnecessaryCheck.BOTH));
}
@Test
public void notDetectable() {
IClass iClass = getIClass(NOT_DETECTABLE_TESTDATA);
assertAllSameNecessity(iClass, NOT_DETECTABLE_NUMBER_OF_ARRAY_ACCESS, not(equalTo(UnnecessaryCheck.BOTH)));
}
@Test
public void notInBound() {
IClass iClass = getIClass(NOT_IN_BOUND_TESTDATA);
assertAllSameNecessity(iClass, NOT_IN_BOUND_TESTDATA_NUMBER_OF_ARRAY_ACCESS, not(equalTo(UnnecessaryCheck.BOTH)));
}
public void assertAllSameNecessity(IClass iClass, int expectedNumberOfArrayAccesses, Matcher<UnnecessaryCheck> matcher) {
int numberOfArrayAccesses = 0;
for (IMethod method : iClass.getAllMethods()) {
if (method.getDeclaringClass().equals(iClass)) {
IR ir = getIr(method);
StringBuilder builder = new StringBuilder();
for (ISSABasicBlock block : ir.getControlFlowGraph()) {
for (SSAInstruction instruction : block) {
builder.append(instruction);
builder.append("\n");
}
}
String identifyer = method.getDeclaringClass().getName().toString() + "#" + method.getName().toString();
ArrayOutOfBoundsAnalysis analysis = new ArrayOutOfBoundsAnalysis(ir);
for (SSAArrayReferenceInstruction key : analysis.getBoundsCheckNecessary().keySet()) {
numberOfArrayAccesses++;
UnnecessaryCheck unnecessary = analysis.getBoundsCheckNecessary().get(key);
collector.checkThat("Unexpected necessity for bounds check in " + identifyer + ":" + method.getLineNumber(key.iindex),
unnecessary, matcher);
}
}
}
/*
* Possible reasons for this to fail are:
*
*
* *_NUMBER_OF_ARRAY_ACCESS is not set to the correct value (maybe the test
* data has changed).
*
* Not all methods of the class analyzed.
*
* There is a bug, so not all array accesses are found.
*/
collector.checkThat("Number of found array accesses is not as expected for " + iClass.getName().toString(),
numberOfArrayAccesses, equalTo(expectedNumberOfArrayAccesses));
}
@AfterClass
public static void free() throws IOException, ClassHierarchyException {
scope = null;
cha = null;
irFactory = null;
options = null;
}
}

View File

@ -0,0 +1,240 @@
package com.ibm.wala.core.tests.arraybounds;
import static org.hamcrest.CoreMatchers.*;
import java.io.IOException;
import java.util.LinkedHashSet;
import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.analysis.nullpointer.IntraproceduralNullPointerAnalysis;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
import com.ibm.wala.ipa.cfg.PrunedCFG;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter2EdgeFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.ArrayOutOfBoundFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.CombinedExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.NullPointerExceptionFilter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ssa.AllIntegerDueToBranchePiPolicy;
import com.ibm.wala.ssa.DefaultIRFactory;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.IRFactory;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.config.AnalysisScopeReader;
/**
* This test will check that:
* <ul>
* <li>no normal edge is removed
* <li>in case of an exceptional edge being removed, the instruction causing the
* edge is an instruction that is able to throw a NullpointerException or an
* ArrayIndexOutOfBoundException and no other exception may be thrown by this
* instruction.
* <li>the number of removed edges is as expected
* </ul>
*
* So there is no explicit check for specific lines.
*
* For an example how to use the exception pruning see
* {@link PruneArrayOutOfBoundExceptionEdge#computeCfgAndPrunedCFG(IMethod)}
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class PruneArrayOutOfBoundExceptionEdge {
private static ClassLoader CLASS_LOADER = PruneArrayOutOfBoundExceptionEdge.class.getClassLoader();
private static final String DETECTABLE_TESTDATA = "Larraybounds/Detectable";
/**
* The number of Basic Blocks, which have an exception edge, that should be
* removed. (#[array access] + #[other])
*/
private static final int DETECTABLE_EXPECTED_COUNT = 34 + 2;
private static final String NOT_DETECTABLE_TESTDATA = "Larraybounds/NotDetectable";
/**
* The number of Basic Blocks, which have an exception edge, that should be
* removed. (#[array access] + #[other])
*/
private static final int NOT_DETECTABLE_EXPECTED_COUNT = 0 + 3;
private static final String NOT_IN_BOUND_TESTDATA = "Larraybounds/NotInBound";
/**
* The number of Basic Blocks, which have an exception edge, that should be
* removed. (#[array access] + #[other])
*/
private static final int NOT_IN_BOUND_EXPECTED_COUNT = 0 + 1;
private static IRFactory<IMethod> irFactory;
private static AnalysisOptions options;
private static AnalysisScope scope;
private static ClassHierarchy cha;
@Rule
public ErrorCollector collector = new ErrorCollector();
@BeforeClass
public static void init() throws IOException, ClassHierarchyException {
scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, null, CLASS_LOADER);
cha = ClassHierarchy.make(scope);
irFactory = new DefaultIRFactory();
options = new AnalysisOptions();
options.getSSAOptions().setPiNodePolicy(new AllIntegerDueToBranchePiPolicy());
}
private static IR getIr(IMethod method) {
return irFactory.makeIR(method, Everywhere.EVERYWHERE, options.getSSAOptions());
}
public static Pair<SSACFG, PrunedCFG<SSAInstruction, ISSABasicBlock>> computeCfgAndPrunedCFG(IMethod method) {
IR ir = getIr(method);
SSACFG cfg = ir.getControlFlowGraph();
ArrayOutOfBoundsAnalysis arrayBoundsAnalysis = new ArrayOutOfBoundsAnalysis(ir);
IntraproceduralNullPointerAnalysis nullPointerAnalysis = new IntraproceduralNullPointerAnalysis(ir);
CombinedExceptionFilter<SSAInstruction> filter = new CombinedExceptionFilter<SSAInstruction>();
filter.add(new ArrayOutOfBoundFilter(arrayBoundsAnalysis));
filter.add(new NullPointerExceptionFilter(nullPointerAnalysis));
ExceptionFilter2EdgeFilter<ISSABasicBlock> edgeFilter = new ExceptionFilter2EdgeFilter<ISSABasicBlock>(filter, cha, cfg);
PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCfg = PrunedCFG.make(cfg, edgeFilter);
return Pair.make(cfg, prunedCfg);
}
private static IClass getIClass(String name) {
final TypeReference typeRef = TypeReference.findOrCreate(scope.getApplicationLoader(), name);
return cha.lookupClass(typeRef);
}
@Test
public void detectable() throws ClassNotFoundException {
IClass iClass = getIClass(DETECTABLE_TESTDATA);
checkRemovedEdges(iClass, DETECTABLE_EXPECTED_COUNT);
}
@Test
public void notDetectable() {
IClass iClass = getIClass(NOT_DETECTABLE_TESTDATA);
checkRemovedEdges(iClass, NOT_DETECTABLE_EXPECTED_COUNT);
}
@Test
public void notInBound() {
IClass iClass = getIClass(NOT_IN_BOUND_TESTDATA);
checkRemovedEdges(iClass, NOT_IN_BOUND_EXPECTED_COUNT);
}
private void checkRemovedEdges(IClass iClass, int expectedNumberOfArrayAccesses) {
int numberOfDeletedExceptionEdges = 0;
for (IMethod method : iClass.getAllMethods()) {
if (method.getDeclaringClass().equals(iClass)) {
String identifyer = method.getDeclaringClass().getName().toString() + "#" + method.getName().toString();
Pair<SSACFG, PrunedCFG<SSAInstruction, ISSABasicBlock>> cfgs = computeCfgAndPrunedCFG(method);
SSACFG cfg = cfgs.fst;
PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCfg = cfgs.snd;
for (ISSABasicBlock block : cfg) {
checkNormalSuccessors(cfg, prunedCfg, block);
boolean isEdgeRemoved = checkExceptionalSuccessors(block, cfg, prunedCfg, method, identifyer);
numberOfDeletedExceptionEdges += isEdgeRemoved ? 1 : 0;
}
}
}
/*
* Possible reasons for this to fail are:
*
*
* *_NUMBER_OF_BB_WITHOUT_EXCEPTION is not set to the correct value (maybe
* the test data has changed).
*
* Not all methods of the class analyzed.
*
* There is a bug, so not all edges are deleted.
*/
collector.checkThat("Number of deleted edges is not as expected for " + iClass.getName().toString(),
numberOfDeletedExceptionEdges, equalTo(expectedNumberOfArrayAccesses));
}
/**
* Check in case of an exceptional edge being removed, the instruction causing
* the edge is an instruction that is able to throw a NullpointerException or
* an ArrayIndexOutOfBoundException and no other exception may be thrown by
* this instruction.
*
* @param block
* @param cfg
* @param prunedCfg
* @param method
* @param identifyer
* @return if an edge of block was removed
*/
private boolean checkExceptionalSuccessors(ISSABasicBlock block, SSACFG cfg, PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCfg,
IMethod method, String identifyer) {
boolean isEdgeRemoved = false;
LinkedHashSet<ISSABasicBlock> exceptionalSuccessorCfg = new LinkedHashSet<ISSABasicBlock>(cfg.getExceptionalSuccessors(block));
LinkedHashSet<ISSABasicBlock> exceptionalSuccessorPruned = new LinkedHashSet<ISSABasicBlock>(
prunedCfg.getExceptionalSuccessors(block));
if (!exceptionalSuccessorCfg.equals(exceptionalSuccessorPruned)) {
isEdgeRemoved = true;
if (block.getLastInstructionIndex() >= 0) {
SSAInstruction lastInstruction = block.getLastInstruction();
lastInstruction.getExceptionTypes();
Matcher<Iterable<? super TypeReference>> matcher1 = anyOf(hasItem(equalTo(TypeReference.JavaLangNullPointerException)),
hasItem(equalTo(TypeReference.JavaLangArrayIndexOutOfBoundsException)));
collector.checkThat("Edge deleted but cause instruction can't throw NullPointerException"
+ "nor ArrayIndexOutOfBoundsException: " + identifyer + ":" + method.getLineNumber(lastInstruction.iindex),
lastInstruction.getExceptionTypes(), matcher1);
Matcher<Iterable<TypeReference>> matcher2 = everyItem(anyOf(equalTo(TypeReference.JavaLangNullPointerException),
equalTo(TypeReference.JavaLangArrayIndexOutOfBoundsException)));
collector.checkThat("Edge deleted but cause instruction throws other exceptions as NullPointerException"
+ "and ArrayIndexOutOfBoundsException: " + identifyer + ":" + method.getLineNumber(lastInstruction.iindex),
lastInstruction.getExceptionTypes(), matcher2);
} else {
collector.addError(new Throwable("Exceptional edge deleted, but no instruction as cause. - No last instruction."));
}
}
return isEdgeRemoved;
}
private void checkNormalSuccessors(SSACFG cfg, PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCfg, ISSABasicBlock block) {
LinkedHashSet<ISSABasicBlock> normalSuccessorCfg = new LinkedHashSet<ISSABasicBlock>(cfg.getNormalSuccessors(block));
LinkedHashSet<ISSABasicBlock> normalSuccessorPruned = new LinkedHashSet<ISSABasicBlock>(prunedCfg.getNormalSuccessors(block));
collector.checkThat("", normalSuccessorPruned, equalTo(normalSuccessorCfg));
}
@AfterClass
public static void free() throws IOException, ClassHierarchyException {
scope = null;
cha = null;
irFactory = null;
options = null;
}
}

View File

@ -0,0 +1,217 @@
package com.ibm.wala.core.tests.exceptionpruning;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis;
import com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis2EdgeFilter;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.Util;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cfg.EdgeFilter;
import com.ibm.wala.ipa.cfg.PrunedCFG;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter2EdgeFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.IgnoreExceptionsFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.CombinedInterproceduralExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.IgnoreExceptionsInterFilter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ssa.AllIntegerDueToBranchePiPolicy;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.ref.ReferenceCleanser;
/**
* This Test checks, if the number of deleted edges is correct for TestPruning,
* it is also doing a plausibility check for deleted edges (only edges after
* exceptional instructions should be deleted) and that no new edges are
* inserted.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ExceptionAnalysis2EdgeFilterTest {
private static ClassLoader CLASS_LOADER = ExceptionAnalysis2EdgeFilterTest.class.getClassLoader();
public static String REGRESSION_EXCLUSIONS = "Java60RegressionExclusions.txt";
private static ClassHierarchy cha;
private static CallGraph cg;
private static PointerAnalysis<InstanceKey> pointerAnalysis;
private static CombinedInterproceduralExceptionFilter<SSAInstruction> filter;
@Rule
public ErrorCollector collector = new ErrorCollector();
@BeforeClass
public static void init() throws IOException, ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException {
AnalysisOptions options;
AnalysisScope scope;
scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, new File(REGRESSION_EXCLUSIONS), CLASS_LOADER);
cha = ClassHierarchy.make(scope);
Iterable<Entrypoint> entrypoints = Util.makeMainEntrypoints(scope, cha, "Lexceptionpruning/TestPruning");
options = new AnalysisOptions(scope, entrypoints);
options.getSSAOptions().setPiNodePolicy(new AllIntegerDueToBranchePiPolicy());
ReferenceCleanser.registerClassHierarchy(cha);
AnalysisCache cache = new AnalysisCache();
ReferenceCleanser.registerCache(cache);
CallGraphBuilder builder = Util.makeZeroCFABuilder(options, cache, cha, scope);
cg = builder.makeCallGraph(options, null);
pointerAnalysis = builder.getPointerAnalysis();
/*
* We will ignore some exceptions to focus on the exceptions we want to
* raise (OwnException, ArrayIndexOutOfBoundException)
*/
filter = new CombinedInterproceduralExceptionFilter<SSAInstruction>();
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(TypeReference.JavaLangOutOfMemoryError)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangNullPointerException)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangExceptionInInitializerError)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangNegativeArraySizeException)));
}
@Test
public void test() {
HashMap<String, Integer> deletedExceptional = new HashMap<String, Integer>();
int deletedNormal = 0;
ExceptionAnalysis analysis = new ExceptionAnalysis(cg, pointerAnalysis, cha, filter);
analysis.solve();
for (CGNode node : cg) {
if (node.getIR() != null && !node.getIR().isEmptyIR()) {
EdgeFilter<ISSABasicBlock> exceptionAnalysedEdgeFilter = new ExceptionAnalysis2EdgeFilter(analysis, node);
SSACFG cfg_orig = node.getIR().getControlFlowGraph();
ExceptionFilter2EdgeFilter<ISSABasicBlock> filterOnlyEdgeFilter = new ExceptionFilter2EdgeFilter<ISSABasicBlock>(
filter.getFilter(node), cha, cfg_orig);
ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg = PrunedCFG.make(cfg_orig, filterOnlyEdgeFilter);
ControlFlowGraph<SSAInstruction, ISSABasicBlock> exceptionPruned = PrunedCFG.make(cfg_orig, exceptionAnalysedEdgeFilter);
for (ISSABasicBlock block : cfg) {
if (exceptionPruned.containsNode(block)) {
for (ISSABasicBlock normalSucc : cfg.getNormalSuccessors(block)) {
if (!exceptionPruned.getNormalSuccessors(block).contains(normalSucc)) {
checkRemovingNormalOk(node, cfg, block, normalSucc);
if (node.getMethod().getDeclaringClass().getName().getClassName().toString().equals("TestPruning")) {
deletedNormal += 1;
}
}
}
for (ISSABasicBlock exceptionalSucc : cfg.getExceptionalSuccessors(block)) {
if (!exceptionPruned.getExceptionalSuccessors(block).contains(exceptionalSucc)) {
if (node.getMethod().getDeclaringClass().getName().getClassName().toString().equals("TestPruning")) {
boolean count = true;
SSAInstruction instruction = block.getLastInstruction();
if (instruction instanceof SSAInvokeInstruction && ((SSAInvokeInstruction) instruction).isSpecial()) {
count = false;
}
if (count) {
Integer value = 0;
String key = node.getMethod().getName().toString();
if (deletedExceptional.containsKey(key)) {
value = deletedExceptional.get(key);
}
deletedExceptional.put(key, value + 1);
}
}
}
}
}
}
checkNoNewEdges(cfg, exceptionPruned);
}
}
assertEquals("Number of normal edges deleted wrong:", 0, deletedNormal);
for (String key : deletedExceptional.keySet()) {
int value = deletedExceptional.get(key);
String text = "Number of exceptional edges deleted wrong for " + key + ":";
if (key.equals("testTryCatchMultipleExceptions")) {
assertEquals(text, 12, value);
} else if (key.equals("testTryCatchOwnException")) {
assertEquals(text, 5, value);
} else if (key.equals("testTryCatchSuper")) {
assertEquals(text, 3, value);
} else if (key.equals("testTryCatchImplicitException")) {
assertEquals(text, 5, value);
} else if (key.equals("main")) {
assertEquals(text, 4, value);
} else {
assertEquals(text, 0, value);
}
}
}
private void checkRemovingNormalOk(CGNode node, ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg, ISSABasicBlock block,
ISSABasicBlock normalSucc) {
if (!block.getLastInstruction().isPEI() || !filter.getFilter(node).alwaysThrowsException(block.getLastInstruction())) {
specialCaseThrowFiltered(cfg, normalSucc);
} else {
assertTrue(block.getLastInstruction().isPEI());
assertTrue(filter.getFilter(node).alwaysThrowsException(block.getLastInstruction()));
}
}
/**
* If a filtered exception is thrown explicit with a throw command, all
* previous nodes, which only have normal edges to the throw statement will be
* deleted. They don't have a connection to the exit node anymore.
*
* So, if there is a throw statement as normal successor, evrything is fine.
*
* @param cfg
* @param normalSucc
*/
private void specialCaseThrowFiltered(ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg, ISSABasicBlock normalSucc) {
ISSABasicBlock next = normalSucc;
while (!(next.getLastInstruction() instanceof SSAThrowInstruction)) {
assertTrue(cfg.getNormalSuccessors(next).iterator().hasNext());
next = cfg.getNormalSuccessors(next).iterator().next();
}
}
private void checkNoNewEdges(ControlFlowGraph<SSAInstruction, ISSABasicBlock> original,
ControlFlowGraph<SSAInstruction, ISSABasicBlock> filtered) {
for (ISSABasicBlock block : filtered) {
for (ISSABasicBlock normalSucc : filtered.getNormalSuccessors(block)) {
assertTrue(original.getNormalSuccessors(block).contains(normalSucc));
}
for (ISSABasicBlock exceptionalSucc : filtered.getExceptionalSuccessors(block)) {
assertTrue(original.getExceptionalSuccessors(block).contains(exceptionalSucc));
}
}
}
}

View File

@ -0,0 +1,173 @@
package com.ibm.wala.core.tests.exceptionpruning;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.not;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import com.ibm.wala.analysis.exceptionanalysis.ExceptionAnalysis;
import com.ibm.wala.analysis.exceptionanalysis.IntraproceduralExceptionAnalysis;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.ipa.callgraph.AnalysisCache;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.CallGraphBuilderCancelException;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.Util;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.IgnoreExceptionsFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.CombinedInterproceduralExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural.IgnoreExceptionsInterFilter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ssa.AllIntegerDueToBranchePiPolicy;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.ref.ReferenceCleanser;
/**
* This class checks, if the number of exceptions which might occur intra and
* interprocedural is right. As well as the number of caught exceptions for each
* call site.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ExceptionAnalysisTest {
private static ClassLoader CLASS_LOADER = ExceptionAnalysisTest.class.getClassLoader();
public static String REGRESSION_EXCLUSIONS = "Java60RegressionExclusions.txt";
private static ClassHierarchy cha;
private static CallGraph cg;
private static PointerAnalysis<InstanceKey> pointerAnalysis;
private static CombinedInterproceduralExceptionFilter<SSAInstruction> filter;
@Rule
public ErrorCollector collector = new ErrorCollector();
@BeforeClass
public static void init() throws IOException, ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException {
AnalysisOptions options;
AnalysisScope scope;
scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, new File(REGRESSION_EXCLUSIONS), CLASS_LOADER);
cha = ClassHierarchy.make(scope);
Iterable<Entrypoint> entrypoints = Util.makeMainEntrypoints(scope, cha, "Lexceptionpruning/TestPruning");
options = new AnalysisOptions(scope, entrypoints);
options.getSSAOptions().setPiNodePolicy(new AllIntegerDueToBranchePiPolicy());
ReferenceCleanser.registerClassHierarchy(cha);
AnalysisCache cache = new AnalysisCache();
ReferenceCleanser.registerCache(cache);
CallGraphBuilder builder = Util.makeZeroCFABuilder(options, cache, cha, scope);
cg = builder.makeCallGraph(options, null);
pointerAnalysis = builder.getPointerAnalysis();
/*
* We will ignore some exceptions to focus on the exceptions we want to
* raise (OwnException, ArrayIndexOutOfBoundException)
*/
filter = new CombinedInterproceduralExceptionFilter<SSAInstruction>();
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(TypeReference.JavaLangOutOfMemoryError)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangNullPointerException)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangExceptionInInitializerError)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangExceptionInInitializerError)));
filter.add(new IgnoreExceptionsInterFilter<SSAInstruction>(new IgnoreExceptionsFilter(
TypeReference.JavaLangNegativeArraySizeException)));
}
@Test
public void testIntra() {
for (CGNode node : cg) {
IntraproceduralExceptionAnalysis analysis = new IntraproceduralExceptionAnalysis(node, filter.getFilter(node), cha,
pointerAnalysis);
if (node.getMethod().getDeclaringClass().getName().getClassName().toString().equals("TestPruning")) {
checkThrownExceptions(node, analysis);
checkCaughtExceptions(node, analysis);
}
}
}
private void checkCaughtExceptions(CGNode node, IntraproceduralExceptionAnalysis analysis) {
String text = "Number of caught exceptions did not match in " + node.getMethod().getName().toString()
+ ". The follwoing exceptions were caught: ";
Iterator<CallSiteReference> it = node.iterateCallSites();
while (it.hasNext()) {
Set<TypeReference> caught = analysis.getCaughtExceptions(it.next());
if (node.getMethod().getName().toString().matches("testTryCatch.*")) {
if (node.getMethod().getName().toString().equals("testTryCatchMultipleExceptions")) {
collector.checkThat(text + caught.toString(), caught.size(), equalTo(2));
} else if (node.getMethod().getName().toString().equals("testTryCatchSuper")) {
collector.checkThat(text + caught.toString(), caught.size(), not(anyOf(equalTo(0), equalTo(1), equalTo(2), equalTo(3))));
} else {
collector.checkThat(text + caught.toString(), caught.size(), equalTo(1));
}
} else {
collector.checkThat(text + caught.toString(), caught.size(), equalTo(0));
}
}
}
private void checkThrownExceptions(CGNode node, IntraproceduralExceptionAnalysis analysis) {
Set<TypeReference> exceptions = analysis.getExceptions();
String text = "Number of thrown exceptions did not match in " + node.getMethod().getName().toString()
+ ". The follwoing exceptions were thrown: " + exceptions.toString();
if (node.getMethod().getName().toString().matches("invokeSingle.*")
&& (!node.getMethod().getName().toString().equals("invokeSingleRecursive2Helper"))
&& (!node.getMethod().getName().toString().equals("invokeSinglePassThrough"))) {
collector.checkThat(text, exceptions.size(), equalTo(1));
} else {
collector.checkThat(text, exceptions.size(), equalTo(0));
}
}
@Test
public void testInterprocedural() {
ExceptionAnalysis analysis = new ExceptionAnalysis(cg, pointerAnalysis, cha, filter);
analysis.solve();
for (CGNode node : cg) {
if (node.getMethod().getDeclaringClass().getName().getClassName().toString().equals("TestPruning")) {
Set<TypeReference> exceptions = analysis.getCGNodeExceptions(node);
String text = "Number of thrown exceptions did not match in " + node.getMethod().getName().toString()
+ ". The follwoing exceptions were thrown: " + exceptions.toString();
if (node.getMethod().getName().toString().matches("invokeSingle.*")) {
collector.checkThat(text, exceptions.size(), equalTo(1));
} else if (node.getMethod().getName().toString().matches("testTryCatch.*")) {
collector.checkThat(text, exceptions.size(), equalTo(0));
} else if (node.getMethod().getName().toString().matches("invokeAll.*")) {
collector.checkThat(text, exceptions.size(), equalTo(2));
} else if (node.getMethod().getName().toString().equals("main")) {
collector.checkThat(text, exceptions.size(), equalTo(0));
} else {
String text2 = "Found method, i didn't know the expected number of exceptions for: "
+ node.getMethod().getName().toString();
collector.checkThat(text2, node.getMethod().getName().toString(), anyOf(equalTo("main"), equalTo("<init>")));
}
analysis.getCGNodeExceptions(node);
}
}
}
}

View File

@ -10,6 +10,9 @@ Require-Bundle: com.ibm.wala.shrike,
com.ibm.wala.util;bundle-version="1.0.0";visibility:=reexport
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,
com.ibm.wala.analysis.reflection.java7,
@ -39,6 +42,9 @@ Export-Package: .,
com.ibm.wala.ipa.callgraph.propagation.rta,
com.ibm.wala.ipa.callgraph.pruned,
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

@ -0,0 +1,316 @@
package com.ibm.wala.analysis.arraybounds;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.SoftFinalHyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.AdditiveEdgeWeight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
import com.ibm.wala.util.collections.Pair;
/**
* Some thoughts about implementation details, not mentioned in [1]:
*
* As it is written The paper describes, that the distance is equal to the
* shortest hyper path. But what if we don't know anything about a variable
* (i.e. it is returned by a method)? There will be no path at all, the distance
* should be unlimited.
*
* Initializing all nodes with -infinity instead of infinity, seems to work at
* first glance, as we also have hyper edges with more than one source, which
* cause the maximum to be propagated instead of minimum. However, this will not
* work, as loops will not get updated properly.
*
* We need to make sure, that only nodes, which are not connected to the source
* of shortest path computation are set to infinity. To do so, it is enough to
* set nodes, which don't have a predecessor to infinity. (Nodes in cycles will
* always have an ancestor, which is not part of the cycle. So all nodes are
* either connected to the source, or a node with no predecessor.)
*
* In this implementation this is done, by adding an infinity node and connect
* all lose ends to it (see
* {@link ArrayBoundsGraphBuilder#bundleDeadEnds(ArrayBoundsGraph)}). Note, that
* array length and the zero node are dead ends, if they are not the source of a
* shortest path computation. To prevent changing the inequality graph,
* depending on which source is used, a kind of trap door construct is used (See
* {@link ArrayBoundsGraph#createSourceVar(Integer)}).
*
* There are some variations, but these are minor changes to improve results:
* <ul>
* <li>handling of constants (see
* {@link ArrayBoundsGraph#addConstant(Integer, Integer)})
* <li>pi nodes (see {@link ArrayBoundsGraph#addPhi(Integer)})
* <li>array length nodes (see {@link ArrayBoundsGraph#arrayLength})
* </ul>
*
* [1] Bodík, Rastislav, Rajiv Gupta, and Vivek Sarkar.
* "ABCD: eliminating array bounds checks on demand." ACM SIGPLAN Notices. Vol.
* 35. No. 5. ACM, 2000.
*
* @author Stephan Gocht <stephan@gobro.de>
*/
public class ArrayBoundsGraph extends DirectedHyperGraph<Integer> {
/**
* We need a ssa variable representing zero. So we just use an integer,
* which is never produced by ssa generation
*/
public final static Integer ZERO = -1;
public final static Integer ZERO_HELPER = -3;
/**
* We need a ssa variable representing unlimited (values we don't know
* anything about). So we just use an integer, which is never produced by
* ssa generation
*/
public final static Integer UNLIMITED = -2;
/**
* Maps each array variable to a set of variables, which are used as Index
* for accessing that array
*/
private final HashMap<Integer, Set<Integer>> arrayAccess;
/**
* Maps each array variable to a node which is parent to all variables, that
* contain the array length
*/
private final HashMap<Integer, Integer> arrayLength;
private final HashSet<Integer> phis;
private final HashMap<Integer, Pair<Integer,Integer>> constants;
/**
* For simplicity we introduce extra variables, for arrayLength, to have a
* unique node representing the array length, even if the length is accessed
* more than once in the code.
*
* Start with -3 so it is unequal to other constants
*/
private Integer arrayCounter = -4;
public ArrayBoundsGraph() {
this.arrayAccess = new HashMap<>();
this.arrayLength = new HashMap<>();
this.constants = new HashMap<>();
this.phis = new HashSet<>();
this.addNode(UNLIMITED);
this.phis.add(UNLIMITED);
this.createSourceVar(ZERO);
this.addNode(ZERO_HELPER);
this.addEdge(ZERO, ZERO_HELPER);
//this.phis.add(ZERO_HELPER);
}
public void postProcessConstants() {
for (Integer constant:constants.keySet()) {
HyperNode<Integer> constantNode = this.getNodes().get(constant);
HyperNode<Integer> helper1 = this.getNodes().get(constants.get(constant).fst);
HyperNode<Integer> helper2 = this.getNodes().get(constants.get(constant).snd);
for (DirectedHyperEdge<Integer> edge:constantNode.getOutEdges()) {
if (!edge.getDestination().contains(helper2)) {
edge.getSource().remove(constant);
edge.getSource().add(helper1);
}
}
}
}
public void addAdditionEdge(Integer src, Integer dst, Integer value) {
this.addNode(src);
final HyperNode<Integer> srcNode = this.getNodes().get(src);
this.addNode(dst);
final HyperNode<Integer> dstNode = this.getNodes().get(dst);
Weight weight;
if (value == 0) {
weight = Weight.ZERO;
} else {
weight = new Weight(Type.NUMBER, value);
}
final EdgeWeight edgeWeight = new AdditiveEdgeWeight(weight);
final DirectedHyperEdge<Integer> edge = new DirectedHyperEdge<Integer>();
edge.getDestination().add(dstNode);
edge.getSource().add(srcNode);
edge.setWeight(edgeWeight);
this.getEdges().add(edge);
}
public void addArray(Integer array) {
this.getArrayNode(array);
}
/**
* Add variable as constant with value value.
*
* This will create the following construct: [zero] -(value)-> [h1] -0- >
* [variable] -(-value)-> [h2] -0-> [zero].
*
* The bidirectional linking, allows things like
*
* <pre>
* int[] a = new int[2]();
* a[0] = 1;
* </pre>
*
* to work properly. h1, h2 are helper nodes: [zero] and [variable] may have
* other predecessors, this will cause their in edges to be merged to a
* single hyper edge with weight zero. The helper nodes are inserted to keep
* the proper distance from [zero].
*
* @param variable
* @param value
*/
public void addConstant(Integer variable, Integer value) {
final Integer helper1 = this.generateNewVar();
final Integer helper2 = this.generateNewVar();
this.addAdditionEdge(ZERO_HELPER, helper1, value);
// this.addEdge(helper1, variable);
this.addAdditionEdge(variable, helper2, -value);
this.addEdge(helper2, ZERO_HELPER);
this.constants.put(variable, Pair.make(helper1, helper2));
}
public void addEdge(Integer src, Integer dst) {
this.addAdditionEdge(src, dst, 0);
}
public HyperNode<Integer> addNode(Integer value) {
HyperNode<Integer> result;
if (!this.getNodes().keySet().contains(value)) {
result = new HyperNode<Integer>(value);
this.getNodes().put(value, result);
} else {
result = this.getNodes().get(value);
}
return result;
}
public void addPhi(Integer dst) {
this.phis.add(dst);
}
public void addPi(Integer dst, Integer src1, Integer src2) {
this.addEdge(src1, dst);
this.addEdge(src2, dst);
}
/**
* Adds var as source var. A source var is a variable, which can be used as
* source for shortest path computation.
*
* This will create the following construct: [unlimited] -> [var] -> [var]
* -(unlimited)-> [unlimited]
*
* This is a trap door construct: if [var] is not set to 0 it will get the
* value unlimited, if [var] is set to 0 it will stay 0.
*
* @param var
*/
public void createSourceVar(Integer var) {
if (this.getNodes().keySet().contains(var)){
throw new AssertionError("Source variables should only be created once.");
}
SoftFinalHyperNode<Integer> node = new SoftFinalHyperNode<Integer>(var);
this.getNodes().put(var, node);
// final HyperNode<Integer> varNode = this.getNodes().get(var);
// final HyperNode<Integer> unlimitedNode = this.getNodes().get(UNLIMITED);
// final DirectedHyperEdge<Integer> edge = new DirectedHyperEdge<>();
// edge.setWeight(new AdditiveEdgeWeight(Weight.UNLIMITED));
// edge.getSource().add(varNode);
// edge.getDestination().add(unlimitedNode);
// this.getEdges().add(edge);
this.addEdge(UNLIMITED, var);
//this.addEdge(var, var);
}
public Integer generateNewVar() {
final int result = this.arrayCounter;
this.arrayCounter += -1;
return result;
}
public HashMap<Integer, Set<Integer>> getArrayAccess() {
return this.arrayAccess;
}
public HashMap<Integer, Integer> getArrayLength() {
return this.arrayLength;
}
public Integer getArrayNode(Integer array) {
Integer arrayVar;
if (!this.arrayLength.containsKey(array)) {
arrayVar = this.generateNewVar();
this.arrayLength.put(array, arrayVar);
this.createSourceVar(arrayVar);
} else {
arrayVar = this.arrayLength.get(array);
}
return arrayVar;
}
public HashSet<Integer> getPhis() {
return this.phis;
}
public void markAsArrayAccess(Integer array, Integer index) {
Set<Integer> indices;
if (!this.arrayAccess.containsKey(array)) {
indices = new HashSet<Integer>();
this.arrayAccess.put(array, indices);
} else {
indices = this.arrayAccess.get(array);
}
indices.add(index);
this.addArray(array);
}
/**
* Mark variable as length for array.
*
* @param array
* @param variable
*/
public void markAsArrayLength(Integer array, Integer variable) {
this.addEdge(this.getArrayNode(array), variable);
}
public void markAsDeadEnd(Integer variable) {
this.addEdge(UNLIMITED, variable);
}
public Weight getVariableWeight(Integer variable){
if (constants.containsKey(variable)) {
variable = constants.get(variable).fst;
}
return this.getNodes().get(variable).getWeight();
}
@Override
public void reset() {
super.reset();
this.getNodes().get(UNLIMITED).setWeight(Weight.UNLIMITED);
this.getNodes().get(UNLIMITED).setNewWeight(Weight.UNLIMITED);
}
}

View File

@ -0,0 +1,403 @@
package com.ibm.wala.analysis.arraybounds;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction.Operator;
import com.ibm.wala.ssa.DefUse;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACFG.BasicBlock;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstruction.Visitor;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
/**
* @see ArrayBoundsGraph
* @author Stephan Gocht <stephan@gobro.de>
*/
public class ArrayBoundsGraphBuilder {
private final IR ir;
/** Variables, which were already explored. */
private final HashSet<Integer> foundVariables;
private final DefUse defUse;
private final ArrayBoundsGraph lowerBoundGraph;
private final ArrayBoundsGraph upperBoundGraph;
private final Set<SSAArrayReferenceInstruction> arrayReferenceInstructions;
private final IBinaryOpInstruction.Operator SUB = IBinaryOpInstruction.Operator.SUB;
private final IBinaryOpInstruction.Operator ADD = IBinaryOpInstruction.Operator.ADD;
public ArrayBoundsGraphBuilder(IR ir) {
this.ir = ir;
this.foundVariables = new HashSet<Integer>();
this.defUse = new DefUse(ir);
this.arrayReferenceInstructions = new HashSet<>();
this.lowerBoundGraph = new ArrayBoundsGraph();
this.upperBoundGraph = new ArrayBoundsGraph();
this.findArrayAccess();
this.exploreIr();
this.addConstructionLength();
this.lowerBoundGraph.updateNodeEdges();
this.upperBoundGraph.updateNodeEdges();
this.lowerBoundGraph.postProcessConstants();
this.upperBoundGraph.postProcessConstants();
this.lowerBoundGraph.updateNodeEdges();
this.upperBoundGraph.updateNodeEdges();
this.bundleDeadEnds(this.lowerBoundGraph);
this.bundleDeadEnds(this.upperBoundGraph);
this.collapseNonPhiEdges(this.lowerBoundGraph);
this.collapseNonPhiEdges(this.upperBoundGraph);
this.lowerBoundGraph.updateNodeEdges();
this.upperBoundGraph.updateNodeEdges();
}
private void addConstructionLength() {
for (final Integer array : this.lowerBoundGraph.getArrayLength()
.keySet()) {
final Integer tmp = array;
final SSAInstruction instruction = this.defUse.getDef(array);
if (instruction != null) {
instruction.visit(new Visitor() {
@Override
public void visitNew(SSANewInstruction instruction) {
//We only support arrays with dimension 1
if (instruction.getNumberOfUses() == 1) {
final int constructionLength = instruction.getUse(0);
Integer arraysNode = ArrayBoundsGraphBuilder.this.lowerBoundGraph
.getArrayLength().get(tmp);
ArrayBoundsGraphBuilder.this.lowerBoundGraph.addEdge(
arraysNode, constructionLength);
arraysNode = ArrayBoundsGraphBuilder.this.upperBoundGraph
.getArrayLength().get(tmp);
ArrayBoundsGraphBuilder.this.upperBoundGraph.addEdge(
arraysNode, constructionLength);
ArrayBoundsGraphBuilder.this
.addPossibleConstant(constructionLength);
}
}
});
}
}
}
/**
* Case 1: piRestrictor restricts the pi variable for upper/ lower bounds graph
* Given this code below, we want to create a hyper edge
* {piParent, piRestrictor} --> {piVar}.
*
* If is op in {<, >} we now, that the distance from piRestrictor to piVar
* is +-1 as ( a < b ) <==> ( a <= b - 1), same with "<".
* To be more precise we introduce a helper node and add
* {piRestrictor} -- (-)1 --> {helper}
* {piParent, helper} --> {piVar}
*
* Case 2: no restriction is given by the branch (i.e. the operator is not equal)
* {piParent} --> {piVar}
*
* <code>if (piParent op piRestrictor) {piVar = piParent}</code>
*
* @param piVar
* @param piParent
* @param piRestrictor
* @param op
*/
private void addPiStructure(Integer piVar, Integer piParent,
Integer piRestrictor, Operator op) {
Integer helper;
switch (op) {
case EQ:
this.upperBoundGraph.addPi(piVar, piParent, piRestrictor);
this.lowerBoundGraph.addPi(piVar, piParent, piRestrictor);
break;
case NE:
this.upperBoundGraph.addEdge(piParent, piVar);
this.lowerBoundGraph.addEdge(piParent, piVar);
break;
case LE: // piVar <= piRestrictor
this.upperBoundGraph.addPi(piVar, piParent, piRestrictor);
this.lowerBoundGraph.addEdge(piParent, piVar);
break;
case GE: // piVar >= piRestrictor
this.lowerBoundGraph.addPi(piVar, piParent, piRestrictor);
this.upperBoundGraph.addEdge(piParent, piVar);
break;
case LT: // piVar < piRestrictor
helper = this.upperBoundGraph.generateNewVar();
this.upperBoundGraph.addAdditionEdge(piRestrictor, helper, -1);
this.upperBoundGraph.addPi(piVar, piParent, helper);
this.lowerBoundGraph.addEdge(piParent, piVar);
break;
case GT: // piVar > piRestrictor
helper = this.lowerBoundGraph.generateNewVar();
this.lowerBoundGraph.addAdditionEdge(piRestrictor, helper, 1);
this.lowerBoundGraph.addPi(piVar, piParent, helper);
this.upperBoundGraph.addEdge(piParent, piVar);
break;
}
}
private void addPossibleConstant(int handle) {
if (this.ir.getSymbolTable().isIntegerConstant(handle)) {
final int value = this.ir.getSymbolTable().getIntValue(handle);
this.lowerBoundGraph.addConstant(handle, value);
this.upperBoundGraph.addConstant(handle, value);
}
}
/**
* Connect all lose ends to the infinity node. See the description of
* {@link ArrayBoundsGraph} for why this is necessary.
*
* @param graph
*/
private void bundleDeadEnds(ArrayBoundsGraph graph) {
final Set<HyperNode<Integer>> nodes = new HashSet<>();
nodes.addAll(graph.getNodes().values());
for (final DirectedHyperEdge<Integer> edge : graph.getEdges()) {
for (final HyperNode<Integer> node : edge.getDestination()) {
nodes.remove(node);
}
}
for (final HyperNode<Integer> node : nodes) {
graph.markAsDeadEnd(node.getValue());
}
}
/**
* To make construction of the hyper-graph more easy, we always add single
* edges and fuse them into one hyper-edge. Where necessary (Everywhere but
* incoming edges of phi nodes.)
*
* @param graph
*/
private void collapseNonPhiEdges(ArrayBoundsGraph graph) {
final Map<HyperNode<Integer>, DirectedHyperEdge<Integer>> inEdges = new HashMap<HyperNode<Integer>, DirectedHyperEdge<Integer>>();
final Set<DirectedHyperEdge<Integer>> edges = new HashSet<>();
edges.addAll(graph.getEdges());
for (final DirectedHyperEdge<Integer> edge : edges) {
assert edge.getDestination().size() == 1;
final HyperNode<Integer> node = edge.getDestination().iterator()
.next();
if (!graph.getPhis().contains(node.getValue())) {
if (inEdges.containsKey(node)) {
final DirectedHyperEdge<Integer> inEdge = inEdges.get(node);
assert inEdge.getWeight().equals(edge.getWeight());
for (final HyperNode<Integer> src : edge.getSource()) {
inEdge.getSource().add(src);
}
graph.getEdges().remove(edge);
} else {
inEdges.put(node, edge);
}
}
}
}
/**
* Discovers predecessors and adds them to the graph.
*
* @param todo
* @param handle
*/
private void discoverPredecessors(final Stack<Integer> todo, int handle) {
final SSAInstruction def = this.defUse.getDef(handle);
if (def == null) {
this.addPossibleConstant(handle);
} else {
def.visit(new Visitor() {
@Override
public void visitArrayLength(
SSAArrayLengthInstruction instruction) {
ArrayBoundsGraphBuilder.this.lowerBoundGraph
.markAsArrayLength(instruction.getArrayRef(),
instruction.getDef());
ArrayBoundsGraphBuilder.this.upperBoundGraph
.markAsArrayLength(instruction.getArrayRef(),
instruction.getDef());
}
@Override
public void visitBinaryOp(SSABinaryOpInstruction instruction) {
if (instruction.getOperator() == ArrayBoundsGraphBuilder.this.SUB
|| instruction.getOperator() == ArrayBoundsGraphBuilder.this.ADD) {
final BinaryOpWithConstant op = BinaryOpWithConstant
.create(instruction,
ArrayBoundsGraphBuilder.this.ir);
if (op != null) {
todo.push(op.getOtherVar());
int value = op.getConstantValue();
if (instruction.getOperator() == ArrayBoundsGraphBuilder.this.SUB) {
value = -value;
}
ArrayBoundsGraphBuilder.this.lowerBoundGraph
.addAdditionEdge(op.getOtherVar(),
instruction.getDef(), value);
ArrayBoundsGraphBuilder.this.upperBoundGraph
.addAdditionEdge(op.getOtherVar(),
instruction.getDef(), value);
}
}
}
@Override
public void visitPhi(SSAPhiInstruction instruction) {
int phi = instruction.getDef();
ArrayBoundsGraphBuilder.this.lowerBoundGraph.addPhi(phi);
ArrayBoundsGraphBuilder.this.upperBoundGraph.addPhi(phi);
for (int i = 0; i < instruction.getNumberOfUses(); i++) {
int use = instruction.getUse(i);
todo.push(use);
ArrayBoundsGraphBuilder.this.lowerBoundGraph.addEdge(use, phi);
ArrayBoundsGraphBuilder.this.upperBoundGraph.addEdge(use, phi);
}
}
@Override
public void visitPi(SSAPiInstruction instruction) {
final SSAConditionalBranchInstruction branch = (SSAConditionalBranchInstruction) instruction
.getCause();
assert branch.getNumberOfUses() == 2;
final Integer piVar = instruction.getDef();
final Integer piParent = instruction.getUse(0);
final ConditionNormalizer cnd = new ConditionNormalizer(
branch, piParent, ArrayBoundsGraphBuilder.this
.isBranchTaken(instruction, branch));
final Integer piRestrictor = cnd.getRhs();
todo.push(piParent);
todo.push(piRestrictor);
ArrayBoundsGraphBuilder.this.addPiStructure(piVar,
piParent, piRestrictor, cnd.getOp());
}
});
}
}
private void exploreIr() {
final Set<Integer> variablesUsedAsIndex = new HashSet<Integer>();
for (final Set<Integer> variables : this.lowerBoundGraph
.getArrayAccess().values()) {
variablesUsedAsIndex.addAll(variables);
}
for (final Integer variable : variablesUsedAsIndex) {
this.startDFS(variable);
}
}
private void findArrayAccess() {
this.ir.visitNormalInstructions(new Visitor() {
@Override
public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
ArrayBoundsGraphBuilder.this.lowerBoundGraph.markAsArrayAccess(
instruction.getArrayRef(), instruction.getIndex());
ArrayBoundsGraphBuilder.this.upperBoundGraph.markAsArrayAccess(
instruction.getArrayRef(), instruction.getIndex());
ArrayBoundsGraphBuilder.this.arrayReferenceInstructions
.add(instruction);
}
@Override
public void visitArrayStore(SSAArrayStoreInstruction instruction) {
ArrayBoundsGraphBuilder.this.lowerBoundGraph.markAsArrayAccess(
instruction.getArrayRef(), instruction.getIndex());
ArrayBoundsGraphBuilder.this.upperBoundGraph.markAsArrayAccess(
instruction.getArrayRef(), instruction.getIndex());
ArrayBoundsGraphBuilder.this.arrayReferenceInstructions
.add(instruction);
}
});
}
public Set<SSAArrayReferenceInstruction> getArrayReferenceInstructions() {
return this.arrayReferenceInstructions;
}
public ArrayBoundsGraph getLowerBoundGraph() {
return this.lowerBoundGraph;
}
public ArrayBoundsGraph getUpperBoundGraph() {
return this.upperBoundGraph;
}
private boolean isBranchTaken(SSAPiInstruction pi,
SSAConditionalBranchInstruction cnd) {
final BasicBlock branchTargetBlock = this.ir.getControlFlowGraph()
.getBlockForInstruction(cnd.getTarget());
return branchTargetBlock.getNumber() == pi.getSuccessor();
}
/**
* Explore the DefUse-Chain with depth-first-search to add constraints to
* the given variable.
*/
private void startDFS(int index) {
final Stack<Integer> todo = new Stack<Integer>();
todo.push(index);
while (!todo.isEmpty()) {
final int next = todo.pop();
if (!this.foundVariables.contains(next)) {
this.foundVariables.add(next);
this.lowerBoundGraph.addNode(next);
this.upperBoundGraph.addNode(next);
this.discoverPredecessors(todo, next);
}
}
}
}

View File

@ -0,0 +1,140 @@
package com.ibm.wala.analysis.arraybounds;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.algorithms.ShortestPath;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.NormalOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.ReverseOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.util.ssa.InstructionByIIndexMap;
/**
* The array out of bounds analysis uses the inequality graph as described in
* [1]. And a shortest path computation as suggested ibid. as possible solver
* for the inequality graph.
*
* [1] Bodík, Rastislav, Rajiv Gupta, and Vivek Sarkar.
* "ABCD: eliminating array bounds checks on demand." ACM SIGPLAN Notices. Vol.
* 35. No. 5. ACM, 2000.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ArrayOutOfBoundsAnalysis {
public enum UnnecessaryCheck {
NONE, UPPER, LOWER, BOTH;
public UnnecessaryCheck union(UnnecessaryCheck other) {
final Set<UnnecessaryCheck> set = new HashSet<>();
set.add(this);
set.add(other);
set.remove(NONE);
if (set.contains(BOTH) || (set.contains(UPPER) && set.contains(LOWER))) {
return BOTH;
} else if (set.size() == 0) {
return NONE;
} else if (set.size() == 1) {
return set.iterator().next();
} else {
throw new RuntimeException("Case that should not happen, this method is implemented wrong.");
}
}
}
private ArrayBoundsGraph lowerBoundGraph;
private ArrayBoundsGraph upperBoundGraph;
/**
* List of variables, that are used for array access and if they are
* neccessary
*/
private final Map<SSAArrayReferenceInstruction, UnnecessaryCheck> boundsCheckUnnecessary;
/**
* Create and perform the array out of bounds analysis.
*
* Make sure, the given IR was created with pi nodes for each variable, that
* is part of a branch instruction! Otherwise the results will be poor.
*
* @param ir
*/
public ArrayOutOfBoundsAnalysis(IR ir) {
this.boundsCheckUnnecessary = new InstructionByIIndexMap<>();
this.buildInequalityGraphs(ir);
this.computeLowerBound();
this.computeUpperBounds();
this.lowerBoundGraph = null;
this.upperBoundGraph = null;
}
private void addUnnecessaryCheck(SSAArrayReferenceInstruction instruction, UnnecessaryCheck checkToAdd) {
final UnnecessaryCheck oldCheck = this.boundsCheckUnnecessary.get(instruction);
final UnnecessaryCheck newCheck = oldCheck.union(checkToAdd);
this.boundsCheckUnnecessary.put(instruction, newCheck);
}
private void buildInequalityGraphs(IR ir) {
ArrayBoundsGraphBuilder builder = new ArrayBoundsGraphBuilder(ir);
this.lowerBoundGraph = builder.getLowerBoundGraph();
this.upperBoundGraph = builder.getUpperBoundGraph();
for (final SSAArrayReferenceInstruction instruction : builder.getArrayReferenceInstructions()) {
this.boundsCheckUnnecessary.put(instruction, UnnecessaryCheck.NONE);
}
builder = null;
}
/**
* compute lower bound
*/
private void computeLowerBound() {
final HyperNode<Integer> zero = this.lowerBoundGraph.getNodes().get(ArrayBoundsGraph.ZERO);
ShortestPath.compute(this.lowerBoundGraph, zero, new NormalOrder());
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
Weight weight = this.lowerBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() >= 0) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.LOWER);
}
}
}
/**
* compute upper bound for each array
*/
private void computeUpperBounds() {
final Map<Integer, Integer> arrayLengths = this.upperBoundGraph.getArrayLength();
for (final Integer array : arrayLengths.keySet()) {
final HyperNode<Integer> arrayNode = this.upperBoundGraph.getNodes().get(arrayLengths.get(array));
ShortestPath.compute(this.upperBoundGraph, arrayNode, new ReverseOrder());
for (final SSAArrayReferenceInstruction instruction : this.boundsCheckUnnecessary.keySet()) {
if (instruction.getArrayRef() == array) {
Weight weight = this.upperBoundGraph.getVariableWeight(instruction.getIndex());
if (weight.getType() == Weight.Type.NUMBER && weight.getNumber() <= -1) {
this.addUnnecessaryCheck(instruction, UnnecessaryCheck.UPPER);
}
}
}
}
}
/**
* @return for each array reference instruction (load or store), if both,
* lower bound, upper bound or no check is unnecessary.
*/
public Map<SSAArrayReferenceInstruction, UnnecessaryCheck> getBoundsCheckNecessary() {
return this.boundsCheckUnnecessary;
}
}

View File

@ -0,0 +1,87 @@
package com.ibm.wala.analysis.arraybounds;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.IOperator;
import com.ibm.wala.shrikeBT.IBinaryOpInstruction.Operator;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
/**
* Normalizes a binary operation with a constant by providing direct access to
* assigned = other op constant.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class BinaryOpWithConstant {
/**
*
* @param instruction
* @param ir
* @return normalized BinaryOpWithConstant or null, if normalization was not
* successful.
*/
public static BinaryOpWithConstant create(
SSABinaryOpInstruction instruction, IR ir) {
BinaryOpWithConstant result = null;
if (instruction.mayBeIntegerOp()) {
assert instruction.getNumberOfUses() == 2;
Integer other = null;
Integer value = null;
int constantPos = -1;
for (int i = 0; i < instruction.getNumberOfUses(); i++) {
final int constant = instruction.getUse(i);
if (ir.getSymbolTable().isIntegerConstant(constant)) {
other = instruction.getUse((i + 1) % 2);
value = ir.getSymbolTable().getIntValue(constant);
constantPos = i;
}
}
final IOperator op = instruction.getOperator();
if (constantPos != -1) {
if (op == Operator.ADD || op == Operator.SUB
&& constantPos == 1) {
result = new BinaryOpWithConstant(op, other, value,
instruction.getDef());
}
}
}
return result;
}
private final IOperator op;
private final Integer other;
private final Integer value;
private final Integer assigned;
private BinaryOpWithConstant(IOperator op, Integer other, Integer value,
Integer assigned) {
super();
this.op = op;
this.other = other;
this.value = value;
this.assigned = assigned;
}
public Integer getAssignedVar() {
return this.assigned;
}
public Integer getConstantValue() {
return this.value;
}
public IOperator getOp() {
return this.op;
}
public Integer getOtherVar() {
return this.other;
}
}

View File

@ -0,0 +1,115 @@
package com.ibm.wala.analysis.arraybounds;
import com.ibm.wala.shrikeBT.IConditionalBranchInstruction.Operator;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
/**
* ConditionNormalizer normalizes a branch condition. See Constructor for more
* information.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ConditionNormalizer {
private final int lhs;
private int rhs;
private Operator op;
/**
* Creates a normalization of cnd such that lhs op rhs is true.
*
* Normalization means, that the given variable lhs, will be on the left
* hand side of the comparison, also if the branch is not taken, the
* operation needs to be negated.
*
* p.a. the condition is !(rhs >= lhs), it will be normalized to lhs > rhs
*
* @param cnd
* condition to normalize
* @param lhs
* variable, that should be on the left hand side
* @param branchIsTaken
* if the condition is for the branching case or not
*/
public ConditionNormalizer(SSAConditionalBranchInstruction cnd, int lhs,
boolean branchIsTaken) {
this.lhs = lhs;
if (cnd.getNumberOfUses() != 2) {
throw new IllegalArgumentException(
"Condition uses not exactly two variables.");
}
this.op = (Operator) cnd.getOperator();
for (int i = 0; i < cnd.getNumberOfUses(); i++) {
final int var = cnd.getUse(i);
if ((var != lhs)) {
if (cnd.getUse((i + 1) % 2) != lhs) {
throw new IllegalArgumentException(
"Lhs not contained in condition.");
}
if (i == 0) {
// make sure the other is lhs
this.op = this.swapOperator(this.op);
}
if (!branchIsTaken) {
this.op = this.negateOperator(this.op);
}
this.rhs = var;
}
}
}
public int getLhs() {
return this.lhs;
}
public Operator getOp() {
return this.op;
}
public int getRhs() {
return this.rhs;
}
private Operator negateOperator(Operator op) {
switch (op) {
case EQ:
return Operator.NE;
case NE:
return Operator.EQ;
case LT:
return Operator.GE;
case GE:
return Operator.LT;
case GT:
return Operator.LE;
case LE:
return Operator.GT;
default:
throw new RuntimeException(
"Programming Error: Got unknown operator.");
}
}
private Operator swapOperator(Operator op) {
switch (op) {
case EQ:
return op;
case NE:
return op;
case LT:
return Operator.GT;
case GE:
return Operator.LE;
case GT:
return Operator.LT;
case LE:
return Operator.GE;
default:
throw new RuntimeException(
"Programming Error: Got unknown operator.");
}
}
}

View File

@ -0,0 +1,46 @@
package com.ibm.wala.analysis.arraybounds.hypergraph;
import java.util.HashSet;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
/**
* A DirectedHyperEdge is an edge of a {@link DirectedHyperGraph}.
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <T>
* Type used in HyperNodes (HyperNode<T>)
*/
public class DirectedHyperEdge<T> {
/** Contains all destinations of this HyperEdge */
private final Set<HyperNode<T>> tail;
/** Contains multiple sources of this HyperEdge */
private final Set<HyperNode<T>> head;
private EdgeWeight weight;
public DirectedHyperEdge() {
this.tail = new HashSet<HyperNode<T>>();
this.head = new HashSet<HyperNode<T>>();
}
public Set<HyperNode<T>> getDestination() {
return this.head;
}
public Set<HyperNode<T>> getSource() {
return this.tail;
}
public EdgeWeight getWeight() {
return this.weight;
}
public void setWeight(EdgeWeight weight) {
this.weight = weight;
}
}

View File

@ -0,0 +1,80 @@
package com.ibm.wala.analysis.arraybounds.hypergraph;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
/**
* Implementation of a directed hyper graph. In a hyper graph an edge can have
* more than one head and more than one tail.
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <T>
*/
public class DirectedHyperGraph<T> {
private final Map<T, HyperNode<T>> nodes;
private final Set<DirectedHyperEdge<T>> edges;
public DirectedHyperGraph() {
this.nodes = new HashMap<>();
this.edges = new HashSet<>();
}
public Set<DirectedHyperEdge<T>> getEdges() {
return this.edges;
}
public Map<T, HyperNode<T>> getNodes() {
return this.nodes;
}
/**
* Resets the weight of all nodes.
*/
public void reset() {
for (final HyperNode<T> node : this.getNodes().values()) {
node.setWeight(Weight.NOT_SET);
node.setNewWeight(Weight.NOT_SET);
}
}
@Override
public String toString() {
final StringBuffer buffer = new StringBuffer();
for (final DirectedHyperEdge<T> edge : this.getEdges()) {
buffer.append(edge.getSource());
buffer.append(" -- ");
buffer.append(edge.getWeight());
buffer.append(" --> ");
buffer.append(edge.getDestination());
buffer.append("\n");
}
return buffer.toString();
}
/**
* The outdEdges of a node may not have been set on construction. Use this
* method to set them based on the edges of this HyperGraph.
*/
public void updateNodeEdges() {
for (final HyperNode<T> node : this.getNodes().values()) {
node.setOutEdges(new HashSet<DirectedHyperEdge<T>>());
node.setInEdges(new HashSet<DirectedHyperEdge<T>>());
}
for (final DirectedHyperEdge<T> edge : this.edges) {
for (final HyperNode<T> node : edge.getSource()) {
node.getOutEdges().add(edge);
}
for (final HyperNode<T> node : edge.getDestination()) {
node.getInEdges().add(edge);
}
}
}
}

View File

@ -0,0 +1,89 @@
package com.ibm.wala.analysis.arraybounds.hypergraph;
import java.util.HashSet;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
/**
* A HyperNode is a node of a {@link DirectedHyperGraph}.
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <T>
*/
public class HyperNode<T> {
private Weight weight;
private Weight newWeight;
/**
* Set of edges, which have this node as source, can be set automatically
* with {@link DirectedHyperGraph#updateNodeEdges()}
*/
private Set<DirectedHyperEdge<T>> outEdges;
/**
* Set of edges, which have this node as source, can be set automatically
* with {@link DirectedHyperGraph#updateNodeEdges()}
*/
private Set<DirectedHyperEdge<T>> inEdges;
private T value;
public HyperNode(T value) {
this.value = value;
this.outEdges = new HashSet<DirectedHyperEdge<T>>();
this.weight = Weight.NOT_SET;
this.newWeight = Weight.NOT_SET;
}
public Set<DirectedHyperEdge<T>> getInEdges() {
return this.inEdges;
}
public Weight getNewWeight() {
return this.newWeight;
}
public Set<DirectedHyperEdge<T>> getOutEdges() {
return this.outEdges;
}
public T getValue() {
return this.value;
}
public Weight getWeight() {
return this.weight;
}
public void setInEdges(Set<DirectedHyperEdge<T>> inEdges) {
this.inEdges = inEdges;
}
public void setNewWeight(Weight newWeight) {
this.newWeight = newWeight;
}
public void setOutEdges(Set<DirectedHyperEdge<T>> outEdges) {
this.outEdges = outEdges;
}
public void setValue(T value) {
this.value = value;
}
public void setWeight(Weight weight) {
this.weight = weight;
}
@Override
public String toString() {
if (this.weight == Weight.NOT_SET) {
return this.value.toString();
} else {
return this.value.toString() + ": " + this.weight;
}
}
}

View File

@ -0,0 +1,27 @@
/*******************************************************************************
* 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.arraybounds.hypergraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
public class SoftFinalHyperNode<T> extends HyperNode<T> {
public SoftFinalHyperNode(T value) {
super(value);
}
@Override
public void setWeight(Weight weight) {
if (weight.equals(Weight.NOT_SET) || this.getWeight().equals(Weight.NOT_SET)) {
super.setWeight(weight);
}
};
}

View File

@ -0,0 +1,227 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.algorithms;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
/**
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <T>
* NodeValueType
* @see ShortestPath#compute(DirectedHyperGraph, HyperNode, Comparator)
*/
public class ShortestPath<T> {
/**
* Computes all shortest paths from source. The result is stored in
* {@link HyperNode#getWeight()}.
*
* This is using a variation of Bellman-Ford for hyper graphs.
*
* @param graph
* @param source
* @param comparator
* defines order on weights.
*/
public static <NodeValueType> void compute(DirectedHyperGraph<NodeValueType> graph, HyperNode<NodeValueType> source,
Comparator<Weight> comparator) {
graph.reset();
source.setWeight(Weight.ZERO);
new ShortestPath<>(graph, comparator);
}
private Set<DirectedHyperEdge<T>> updatedEdges;
private final Comparator<Weight> comparator;
private final DirectedHyperGraph<T> graph;
private boolean setUnlimitedOnChange = false;
private boolean hasNegativeCycle = false;
/**
* @param graph
* Source nodes for shortest path computation should be set to 0,
* other nodes should be set to {@link Weight#NOT_SET}.
* @param comparator
* defines order on weights.
*/
private ShortestPath(DirectedHyperGraph<T> graph, Comparator<Weight> comparator) {
this.comparator = comparator;
this.graph = graph;
this.computeShortestPaths();
this.hasNegativeCycle = (this.updatedEdges.size() > 0);
if (this.hasNegativeCycle) {
// trigger, that changing values are set to infty in writeChanges:
this.setUnlimitedOnChange = true;
// we need to propagate negative cycle to all connected nodes
this.computeShortestPaths();
}
}
private void computeShortestPaths() {
this.updatedEdges = this.graph.getEdges();
final int nodeCount = this.graph.getNodes().size();
for (int i = 0; i < nodeCount - 1; i++) {
this.updateAllEdges();
if (this.updatedEdges.size() == 0) {
break;
}
}
}
/**
* @param weight
* @param otherWeight
* @return weight > otherWeight
*/
private boolean greaterThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET || this.comparator.compare(weight, otherWeight) > 0;
}
/**
* @param weight
* @param otherWeight
* @return weight < otherWeight
*/
private boolean lessThen(Weight weight, Weight otherWeight) {
return otherWeight.getType() == Type.NOT_SET || this.comparator.compare(weight, otherWeight) < 0;
}
/**
* Maximum of source weights, modified by the value of the edge. Note that
* every weight is larger than {@link Weight#NOT_SET} for max computation.
* This allows distances to propagate, even if not all nodes are connected to
* the source of the shortest path computation. Otherwise (source,
* other)->(sink) would not have a path from source to sink.
*
* @param edge
* @return max{edgeValue.newValue(sourceWeight) | sourceWeight \in
* edge.getSources()}
*/
private Weight maxOfSources(final DirectedHyperEdge<T> edge) {
final EdgeWeight edgeValue = edge.getWeight();
Weight newWeight = Weight.NOT_SET;
for (final HyperNode<T> node : edge.getSource()) {
final Weight nodeWeight = node.getWeight();
if (nodeWeight.getType() != Type.NOT_SET) {
final Weight temp = edgeValue.newValue(nodeWeight);
if (this.greaterThen(temp, newWeight)) {
newWeight = temp;
}
} else {
newWeight = Weight.NOT_SET;
break;
}
}
return newWeight;
}
/**
* We do not need to iterate all edges, but edges of which the source weight
* was changed, other edges will not lead to a change of the destination
* weight. For correct updating of the destination weight, we need to consider
* all incoming edges. (The minimum of in edges is computed per round, not
* global - see
* {@link ShortestPath#updateDestinationsWithMin(HashSet, DirectedHyperEdge, Weight)}
* )
*
* @return A set of edges, that may lead to changes of weights.
*/
private HashSet<DirectedHyperEdge<T>> selectEdgesToIterate() {
final HashSet<DirectedHyperEdge<T>> edgesToIterate = new HashSet<>();
for (final DirectedHyperEdge<T> edge : this.updatedEdges) {
for (final HyperNode<T> node : edge.getDestination()) {
edgesToIterate.addAll(node.getInEdges());
}
}
return edgesToIterate;
}
private void updateAllEdges() {
for (final DirectedHyperEdge<T> edge : this.selectEdgesToIterate()) {
final Weight maxOfSources = this.maxOfSources(edge);
if (maxOfSources.getType() != Type.NOT_SET) {
this.updateDestinationsWithMin(edge, maxOfSources);
}
}
this.writeChanges();
}
/**
* Updates Nodes with the minimum of all incoming edges. The minimum is
* computed over the minimum of all edges that were processed in this round (
* {@link ShortestPath#selectEdgesToIterate()}).
*
* This is necessary for the feature described in
* {@link ShortestPath#maxOfSources(DirectedHyperEdge)} to work properly: The
* result of different rounds is not always monotonous, p.a.:
*
* <pre>
* (n1, n2)->(n3)
* Round 1: n1 = unset, n2 = -3 -> n3 = max(unset,-3) = -3
* Round 2: n1 = 1, n2 = -3 -> n3 = max(1,-3) = 1
* </pre>
*
* Would we compute the minimum of n3 over all rounds, it would be -3, but 1
* is correct.
*
* Note: that every weight is smaller than {@link Weight#NOT_SET} for min
* computation. This allows distances to propagate, even if not all nodes are
* connected to the source of the shortest path computation. Otherwise
* (source)->(sink), (other)->(sink), would not have a path from source to
* sink.
*
* @param edge
* @param newWeight
*/
private void updateDestinationsWithMin(final DirectedHyperEdge<T> edge, Weight newWeight) {
if (!newWeight.equals(Weight.NOT_SET)) {
for (final HyperNode<T> node : edge.getDestination()) {
if (this.lessThen(newWeight, node.getNewWeight())) {
node.setNewWeight(newWeight);
}
}
}
}
/**
* This method is necessary, as the min is updated per round. (See
* {@link ShortestPath#updateDestinationsWithMin(DirectedHyperEdge, Weight)} )
*/
private void writeChanges() {
final HashSet<DirectedHyperEdge<T>> newUpdatedEdges = new HashSet<>();
for (final HyperNode<T> node : this.graph.getNodes().values()) {
final Weight oldWeight = node.getWeight();
final Weight newWeight = node.getNewWeight();
if (!newWeight.equals(Weight.NOT_SET) && !oldWeight.equals(newWeight)) {
// node weight has changed, so out edges have to be updated next
// round:
newUpdatedEdges.addAll(node.getOutEdges());
if (this.setUnlimitedOnChange) {
node.setWeight(Weight.UNLIMITED);
} else {
node.setWeight(node.getNewWeight());
}
}
node.setNewWeight(Weight.NOT_SET);
}
this.updatedEdges = newUpdatedEdges;
}
}

View File

@ -0,0 +1,6 @@
package com.ibm.wala.analysis.arraybounds.hypergraph;
/**
* This package contains a generic implementation of directed hypergraphs.
* The implementation is optimized for shortest path search, with {@link de.gobro.wala.test.analyse.hypergraph.algorithms.ShortestPath#compute() ShortestPath}
*/

View File

@ -0,0 +1,46 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.weight;
import java.util.Comparator;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
/**
* Defines a normal Order on Weight: unlimited < ... < -1 < 0 < 1 < ... not_set
* is not comparable
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class NormalOrder implements Comparator<Weight> {
@Override
public int compare(Weight o1, Weight o2) {
int result = 0;
if (o1.getType() == Type.NOT_SET || o2.getType() == Type.NOT_SET) {
throw new IllegalArgumentException(
"Tried to compare weights, which are not set yet.");
}
if (o1.getType() == o2.getType()) {
if (o1.getType() == Type.NUMBER) {
result = o1.getNumber() - o2.getNumber();
} else {
result = 0;
}
} else {
if (o1.getType() == Type.UNLIMITED) {
result = -1;
} else if (o2.getType() == Type.UNLIMITED) {
result = 1;
} else {
throw new IllegalArgumentException(
"Programming error, expected no cases to be left.");
}
}
return result;
}
}

View File

@ -0,0 +1,38 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.weight;
import java.util.Comparator;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.NormalOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
/**
* Defines a reverse Order on Weight: ... > 1 > 0 > -1 > ... > unlimited not_set
* is not comparable
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ReverseOrder implements Comparator<Weight> {
private final NormalOrder normalOrder;
public ReverseOrder() {
this.normalOrder = new NormalOrder();
}
@Override
public int compare(Weight o1, Weight o2) {
int result;
if (o1.getType() == Type.UNLIMITED) {
result = -1;
} else if (o2.getType() == Type.UNLIMITED) {
result = 1;
} else {
result = -this.normalOrder.compare(o1, o2);
}
return result;
}
}

View File

@ -0,0 +1,114 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.NormalOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.ReverseOrder;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
/**
* A weight may be not set, a number or unlimited, note that the meaning of
* unlimited is given by the chosen order (see {@link NormalOrder} and
* {@link ReverseOrder}).
*
* @author Stephan Gocht <stephan@gobro.de>
*/
public class Weight {
public enum Type {
NUMBER, NOT_SET, UNLIMITED
}
public static final Weight UNLIMITED = new Weight(Type.UNLIMITED, 0);
public static final Weight NOT_SET = new Weight(Type.NOT_SET, 0);
public static final Weight ZERO = new Weight(Type.NUMBER, 0);
private final Type type;
private final int number;
public Weight(int number) {
this.type = Type.NUMBER;
this.number = number;
}
public Weight(Type type, int number) {
super();
this.type = type;
this.number = number;
}
/**
* Returns this + other. If this is not Number this will be returned, if
* other is not number other will be returned
*
* @param other
* @return this + other
*/
public Weight add(Weight other) {
Weight result = null;
if (this.getType() == Type.NUMBER) {
if (other.getType() == Type.NUMBER) {
result = new Weight(Type.NUMBER, this.getNumber()
+ other.getNumber());
} else {
result = other;
}
} else {
result = this;
}
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final Weight other = (Weight) obj;
if (this.number != other.number) {
return false;
}
if (this.type != other.type) {
return false;
}
return true;
}
public int getNumber() {
return this.number;
}
public Type getType() {
return this.type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.number;
result = prime * result
+ ((this.type == null) ? 0 : this.type.hashCode());
return result;
}
@Override
public String toString() {
if (this.type == Type.NUMBER) {
return Integer.toString(this.number);
} else {
if (this.type == Type.NOT_SET) {
return "NOT_SET";
} else if (this.type == Type.UNLIMITED) {
return "UNLIMITED";
} else {
return "Type: " + this.type;
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.AdditiveEdgeWeight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
/**
* EdgeWeight that adds a specific value.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class AdditiveEdgeWeight implements EdgeWeight {
private final Weight value;
public AdditiveEdgeWeight(Weight value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final AdditiveEdgeWeight other = (AdditiveEdgeWeight) obj;
if (this.value == null) {
if (other.value != null) {
return false;
}
} else if (!this.value.equals(other.value)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((this.value == null) ? 0 : this.value.hashCode());
return result;
}
@Override
public Weight newValue(Weight weight) {
return weight.add(this.value);
}
@Override
public String toString() {
return this.value.toString();
}
}

View File

@ -0,0 +1,13 @@
package com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
/**
* The weight of an edge can produce a new value for the tail nodes given the
* head nodes.
*
* @author Stephan Gocht <stephan@gobro.de>
*/
public interface EdgeWeight {
public Weight newValue(Weight weight);
}

View File

@ -0,0 +1,81 @@
/*******************************************************************************
* 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.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
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.interprocedural.InterproceduralExceptionFilter;
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;
private CallGraph callGraph;
public CGIntraproceduralExceptionAnalysis(CallGraph cg, PointerAnalysis<InstanceKey> pointerAnalysis, ClassHierarchy cha,
InterproceduralExceptionFilter<SSAInstruction> filter) {
this.callGraph = cg;
this.exceptions = new LinkedHashSet<>();
this.analysis = new LinkedHashMap<>();
for (CGNode node : cg) {
if (node.getIR() == null || node.getIR().isEmptyIR()) {
analysis.put(node, IntraproceduralExceptionAnalysis.newDummy());
} else {
IntraproceduralExceptionAnalysis intraEA;
intraEA = new IntraproceduralExceptionAnalysis(node, filter.getFilter(node), cha, pointerAnalysis);
analysis.put(node, intraEA);
exceptions.addAll(intraEA.getExceptions());
exceptions.addAll(intraEA.getPossiblyCaughtExceptions());
}
}
}
/**
* @param node
* @return IntraproceduralExceptionAnalysis for given node.
*/
public IntraproceduralExceptionAnalysis getAnalysis(CGNode node) {
if (!callGraph.containsNode(node)) {
throw new IllegalArgumentException("The given CG node has to be part " + "of the call graph given during construction.");
}
IntraproceduralExceptionAnalysis result = analysis.get(node);
if (result == null) {
throw new RuntimeException("Internal Error: No result for the given node.");
}
return result;
}
/**
* Return a set of all Exceptions, which might occur within the given call
* graph.
*
* @return all exceptions, which might occur.
*/
public Set<TypeReference> getExceptions() {
return exceptions;
}
}

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* 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.HashSet;
import java.util.Set;
import com.ibm.wala.fixpoint.BitVectorVariable;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.ObjectArrayMapping;
import com.ibm.wala.util.intset.BitVector;
import com.ibm.wala.util.intset.OrdinalSetMapping;
public class Exception2BitvectorTransformer {
private OrdinalSetMapping<TypeReference> values;
public OrdinalSetMapping<TypeReference> getValues() {
return values;
}
public Exception2BitvectorTransformer(Set<TypeReference> exceptions) {
createValues(exceptions);
for (TypeReference exception : exceptions) {
BitVector bv = new BitVector(values.getSize());
bv.set(values.getMappedIndex(exception));
}
}
private void createValues(Set<TypeReference> exceptions) {
TypeReference[] exceptionsArray = new TypeReference[exceptions.size()];
exceptions.toArray(exceptionsArray);
values = new ObjectArrayMapping<TypeReference>(exceptionsArray);
}
public BitVector computeBitVector(Set<TypeReference> exceptions) {
BitVector result = new BitVector(values.getSize());
for (TypeReference exception : exceptions) {
int pos = values.getMappedIndex(exception);
if (pos != -1) {
result.set(pos);
} else {
throw new IllegalArgumentException("Got exception I don't know about,"
+ "make sure only to use exceptions given to the constructor ");
}
}
return result;
}
public Set<TypeReference> computeExceptions(BitVector bitVector) {
assert bitVector.length() == values.getSize();
Set<TypeReference> result = new HashSet<>();
for (int i = 0; i < bitVector.length(); i++) {
if (bitVector.get(i)) {
result.add(values.getMappedObject(i));
}
}
return result;
}
public Set<TypeReference> computeExceptions(BitVectorVariable bitVector) {
Set<TypeReference> result = new HashSet<>();
for (int i = 0; i < values.getSize(); i++) {
if (bitVector.get(i)) {
result.add(values.getMappedObject(i));
}
}
return result;
}
}

View File

@ -0,0 +1,245 @@
/*******************************************************************************
* 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 <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;
}
}

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

@ -0,0 +1,94 @@
/*******************************************************************************
* 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.LinkedHashSet;
import java.util.Set;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.dataflow.graph.AbstractMeetOperator;
import com.ibm.wala.dataflow.graph.BitVectorMinusVector;
import com.ibm.wala.dataflow.graph.BitVectorUnion;
import com.ibm.wala.dataflow.graph.BitVectorUnionVector;
import com.ibm.wala.dataflow.graph.ITransferFunctionProvider;
import com.ibm.wala.fixpoint.BitVectorVariable;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.intset.BitVector;
public class ExceptionTransferFunctionProvider implements ITransferFunctionProvider<CGNode, BitVectorVariable> {
private Exception2BitvectorTransformer transformer;
private CallGraph cg;
private CGIntraproceduralExceptionAnalysis intraResult;
public ExceptionTransferFunctionProvider(CGIntraproceduralExceptionAnalysis intraResult, CallGraph cg,
Exception2BitvectorTransformer transformer) {
this.cg = cg;
this.transformer = transformer;
this.intraResult = intraResult;
}
@Override
public boolean hasNodeTransferFunctions() {
return true;
}
@Override
public boolean hasEdgeTransferFunctions() {
return true;
}
@Override
public AbstractMeetOperator<BitVectorVariable> getMeetOperator() {
return BitVectorUnion.instance();
}
@Override
public UnaryOperator<BitVectorVariable> getNodeTransferFunction(CGNode node) {
Set<TypeReference> exceptions = intraResult.getAnalysis(node).getExceptions();
BitVector bitVector = transformer.computeBitVector(exceptions);
return new BitVectorUnionVector(bitVector);
}
@Override
public UnaryOperator<BitVectorVariable> getEdgeTransferFunction(CGNode dst, CGNode src) {
/*
* Note, that dst and src are swapped. For the data-flow-analysis we use
* called -> caller, but for the call graph we need caller -> called.
*/
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));
while (callsites.hasNext()) {
callsite = callsites.next();
caught.retainAll(intraResult.getAnalysis(src).getCaughtExceptions(callsite));
}
filtered = transformer.computeBitVector(caught);
return new BitVectorMinusVector(filtered);
} else {
// This case should not happen, as we should only get src, dst pairs,
// which represent an edge in the call graph. For each edge in the call
// graph should be at least one call site.
throw new RuntimeException("Internal Error: Got call graph edge without call site.");
}
}
}

View File

@ -0,0 +1,44 @@
/*******************************************************************************
* 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.dataflow.graph.BitVectorSolver;
import com.ibm.wala.dataflow.graph.IKilldallFramework;
import com.ibm.wala.fixpoint.BitVectorVariable;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.util.intset.BitVector;
public class InitializedBitVectorSolver extends BitVectorSolver<CGNode> {
public InitializedBitVectorSolver(IKilldallFramework<CGNode, BitVectorVariable> problem) {
super(problem);
}
@Override
protected BitVectorVariable makeNodeVariable(CGNode n, boolean IN) {
return newBV();
}
@Override
protected BitVectorVariable makeEdgeVariable(CGNode src, CGNode dst) {
return newBV();
}
private BitVectorVariable newBV() {
/*
* If we do not initialize BitVectorVariable, with a BitVector, it contains
* null, which may crash in combination with {@link BitVectorMinusVector}
* used in {@link ExceptionTransferFunction}
*/
BitVectorVariable result = new BitVectorVariable();
result.addAll(new BitVector());
return result;
}
}

View File

@ -0,0 +1,339 @@
/*******************************************************************************
* 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.Collection;
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;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
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;
private Set<TypeReference> possiblyCaughtExceptions;
private PointerAnalysis<InstanceKey> pointerAnalysis;
private CGNode node;
private ClassHierarchy classHierachy;
private ExceptionFilter<SSAInstruction> filter;
private IR ir;
private boolean dummy = false;
private Map<SSAInstruction, Boolean> allExceptionsCaught;
public static IntraproceduralExceptionAnalysis newDummy() {
return new IntraproceduralExceptionAnalysis();
}
/**
* Create a dummy analysis.
*/
private IntraproceduralExceptionAnalysis() {
this.dummy = true;
this.exceptions = Collections.emptySet();
}
/**
* You can use this method, if you don't have a call graph, but want some
* exception analysis. But as no pointer analysis is given, we can not
* consider throw instructions.
*
* @param ir
* @param filter
* @param cha
*/
@Deprecated
public IntraproceduralExceptionAnalysis(IR ir, ExceptionFilter<SSAInstruction> filter, ClassHierarchy cha) {
this(ir, filter, cha, null, null);
}
/**
* Create and compute intraprocedural exception analysis. (IR from
* node.getIR() will be used.)
*
* @param node
* @param filter
* @param cha
* @param pointerAnalysis
*/
public IntraproceduralExceptionAnalysis(CGNode node, ExceptionFilter<SSAInstruction> filter, ClassHierarchy cha,
PointerAnalysis<InstanceKey> pointerAnalysis) {
this(node.getIR(), filter, cha, pointerAnalysis, node);
}
/**
* Create and compute intraprocedural exception analysis.
*
* @param ir
* @param filter
* @param cha
* @param pointerAnalysis
* @param node
*/
public IntraproceduralExceptionAnalysis(IR ir, ExceptionFilter<SSAInstruction> filter, ClassHierarchy cha,
PointerAnalysis<InstanceKey> pointerAnalysis, CGNode node) {
this.pointerAnalysis = pointerAnalysis;
this.classHierachy = cha;
this.filter = filter;
this.ir = ir;
this.node = node;
this.exceptions = new LinkedHashSet<>();
this.possiblyCaughtExceptions = new LinkedHashSet<>();
this.allExceptionsCaught = new InstructionByIIndexMap<>();
compute();
}
/**
* Computes thrown exceptions for each basic block of all call graph nodes.
* Everything, but invoke instructions, will be considered. This includes
* filtered and caught exceptions.
*/
private void compute() {
if (ir != null) {
for (ISSABasicBlock block : ir.getControlFlowGraph()) {
SSAInstruction throwingInstruction = getThrowingInstruction(block);
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()) {
possiblyCaughtExceptions.add(it.next());
}
}
}
}
Set<TypeReference> subClasses = new LinkedHashSet<>();
for (TypeReference caught : possiblyCaughtExceptions) {
for (IClass iclass : this.classHierachy.computeSubClasses(caught)) {
subClasses.add(iclass.getReference());
}
}
possiblyCaughtExceptions.addAll(subClasses);
}
/**
* Return all exceptions that could be returned from getCaughtExceptions
*
* @return all exceptions that could be returned from getCaughtExceptions
*/
public Set<TypeReference> getPossiblyCaughtExceptions() {
return possiblyCaughtExceptions;
}
/**
* Returns the set of exceptions, which are to be filtered for
* throwingInstruction.
*
* @param node
* @param throwingInstruction
* @return exceptions, which are to be filtered
*/
private Set<TypeReference> collectFilteredExceptions(SSAInstruction throwingInstruction) {
if (filter != null) {
Set<TypeReference> filtered = new LinkedHashSet<>();
Collection<FilteredException> filters = filter.filteredExceptions(throwingInstruction);
for (FilteredException filter : filters) {
if (filter.isSubclassFiltered()) {
for (IClass iclass : this.classHierachy.computeSubClasses(filter.getException())) {
filtered.add(iclass.getReference());
}
} else {
filtered.add(filter.getException());
}
}
return filtered;
} else {
return Collections.emptySet();
}
}
/**
* Returns a set of exceptions, which might be thrown from this instruction
* within this method.
*
* This does include exceptions dispatched by throw instructions, but not
* exceptions from method calls.
*
* @param throwingInstruction
* @return a set of exceptions, which might be thrown from this instruction
* within this method
*/
public Set<TypeReference> collectThrownExceptions(SSAInstruction throwingInstruction) {
final LinkedHashSet<TypeReference> result = new LinkedHashSet<>();
result.addAll(throwingInstruction.getExceptionTypes());
throwingInstruction.visit(new Visitor() {
@Override
public void visitThrow(SSAThrowInstruction instruction) {
addThrown(result, instruction);
}
});
return result;
}
/**
* Collects all exceptions, which could be dispatched by the throw
* instruction, using the pointer analysis. Adds the collected exceptions to
* addTo.
*
* @param addTo
* set to add the result
* @param instruction
* the throw instruction
*/
private void addThrown(LinkedHashSet<TypeReference> addTo, SSAThrowInstruction instruction) {
int exceptionVariable = instruction.getException();
if (pointerAnalysis != null) {
PointerKey pointerKey = pointerAnalysis.getHeapModel().getPointerKeyForLocal(node, exceptionVariable);
Iterator it = pointerAnalysis.getHeapGraph().getSuccNodes(pointerKey);
while (it.hasNext()) {
Object next = it.next();
if (next instanceof InstanceKey) {
InstanceKey instanceKey = (InstanceKey) next;
IClass iclass = instanceKey.getConcreteType();
addTo.add(iclass.getReference());
} else {
throw new IllegalStateException("Internal error: Expected InstanceKey, got " + next.getClass().getName());
}
}
}
}
/**
*
* @param block
* @return an instruction which may throw exceptions, or null if this block
* can't throw exceptions
*/
public static SSAInstruction getThrowingInstruction(ISSABasicBlock block) {
SSAInstruction result = null;
if (block.getLastInstructionIndex() >= 0) {
SSAInstruction lastInstruction = block.getLastInstruction();
if (lastInstruction != null && lastInstruction.isPEI()) {
result = lastInstruction;
}
}
return result;
}
/**
* @param block
* @return a set of all exceptions which will be caught, if thrown by the
* given block.
*/
private Set<TypeReference> collectCaughtExceptions(ISSABasicBlock block) {
LinkedHashSet<TypeReference> result = new LinkedHashSet<TypeReference>();
List<ISSABasicBlock> exceptionalSuccessors = ir.getControlFlowGraph().getExceptionalSuccessors(block);
for (ISSABasicBlock succ : exceptionalSuccessors) {
if (succ.isCatchBlock()) {
Iterator<TypeReference> it = succ.getCaughtExceptionTypes();
while (it.hasNext()) {
result.add(it.next());
}
}
}
Set<TypeReference> subClasses = new LinkedHashSet<>();
for (TypeReference caught : result) {
for (IClass iclass : this.classHierachy.computeSubClasses(caught)) {
subClasses.add(iclass.getReference());
}
}
result.addAll(subClasses);
return result;
}
/**
* Returns all exceptions for the given call site in the given call graph
* node, which will be caught.
*
* @param callsite
* @return caught exceptions
*/
public Set<TypeReference> getCaughtExceptions(CallSiteReference callsite) {
Set<TypeReference> result = null;
if (dummy) {
result = Collections.emptySet();
} else {
IntSet iindices = ir.getCallInstructionIndices(callsite);
IntIterator it = iindices.intIterator();
while (it.hasNext()) {
int iindex = it.next();
SSAInstruction instruction = ir.getInstructions()[iindex];
if (!((instruction instanceof SSAInvokeInstruction))) {
throw new IllegalArgumentException("The given callsite dose not correspond to an invoke instruction." + instruction);
}
ISSABasicBlock block = ir.getBasicBlockForInstruction(instruction);
if (result == null) {
result = new LinkedHashSet<>();
result.addAll(collectCaughtExceptions(block));
} else {
result.retainAll(collectCaughtExceptions(block));
}
}
}
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.)
*
* If constructed without points-to-analysis, it does not contain exceptions
* thrown by throw statements.
*
* @return all exceptions created and thrown intraprocedural
*/
public Set<TypeReference> getExceptions() {
return exceptions;
}
}

View File

@ -0,0 +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

@ -0,0 +1,63 @@
package com.ibm.wala.analysis.nullpointer;
import com.ibm.wala.cfg.exc.intra.IntraprocNullPointerAnalysis;
import com.ibm.wala.cfg.exc.intra.NullPointerFrameWork;
import com.ibm.wala.cfg.exc.intra.NullPointerSolver;
import com.ibm.wala.cfg.exc.intra.NullPointerState;
import com.ibm.wala.cfg.exc.intra.NullPointerState.State;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.MonitorUtil.IProgressMonitor;
/**
* Intraprocedural dataflow analysis to detect impossible NullPointerExceptions.
*
* This class is basically a copy of {@link IntraprocNullPointerAnalysis}, but
* does only provide the result of the analysis instead of a pruned CFG.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class IntraproceduralNullPointerAnalysis {
static private final IProgressMonitor NO_PROGRESS_MONITOR = null;
private final NullPointerSolver<ISSABasicBlock> solver;
private final IR ir;
public IntraproceduralNullPointerAnalysis(IR ir) {
if (ir == null || ir.isEmptyIR()) {
throw new IllegalArgumentException("IR may not be null or empty.");
}
this.ir = ir;
final int maxVarNum = ir.getSymbolTable().getMaxValueNumber();
final int[] paramValNum = ir.getParameterValueNumbers();
final NullPointerFrameWork<ISSABasicBlock> problem = new NullPointerFrameWork<ISSABasicBlock>(
ir.getControlFlowGraph(), ir);
this.solver = new NullPointerSolver<ISSABasicBlock>(problem, maxVarNum,
paramValNum, ir);
try {
this.solver.solve(NO_PROGRESS_MONITOR);
} catch (final CancelException e) {
// can't happen as we have no monitor
}
}
public State nullPointerExceptionThrowState(SSAInstruction instruction) {
assert instruction != null;
if (instruction.isPEI()
&& instruction.getExceptionTypes().contains(
TypeReference.JavaLangNullPointerException)) {
final NullPointerState blockState = this.solver.getIn(this.ir
.getBasicBlockForInstruction(instruction));
final RelevantVariableFinder finder = new RelevantVariableFinder(
instruction);
assert finder.getVarNum() >= 0;
return blockState.getState(finder.getVarNum());
}
return State.NOT_NULL;
}
}

View File

@ -0,0 +1,312 @@
package com.ibm.wala.analysis.nullpointer;
import com.ibm.wala.ssa.SSAArrayLengthInstruction;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSABinaryOpInstruction;
import com.ibm.wala.ssa.SSACheckCastInstruction;
import com.ibm.wala.ssa.SSAComparisonInstruction;
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
import com.ibm.wala.ssa.SSAConversionInstruction;
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAGotoInstruction;
import com.ibm.wala.ssa.SSAInstanceofInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInstruction.IVisitor;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSANewInstruction;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.ssa.SSASwitchInstruction;
import com.ibm.wala.ssa.SSAThrowInstruction;
import com.ibm.wala.ssa.SSAUnaryOpInstruction;
/**
* Helper class to find the variable that may be null.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class RelevantVariableFinder implements IVisitor {
private int varNumNew;
private final int varNum;
public RelevantVariableFinder(SSAInstruction instrcution) {
this.varNumNew = -1;
instrcution.visit(this);
this.varNum = this.varNumNew;
}
public int getVarNum() {
return this.varNum;
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitArrayLength(com.ibm.wala
* .ssa.SSAArrayLengthInstruction)
*/
@Override
public void visitArrayLength(SSAArrayLengthInstruction instruction) {
this.varNumNew = instruction.getArrayRef();
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitArrayLoad(com.ibm.wala.
* ssa.SSAArrayLoadInstruction)
*/
@Override
public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
this.varNumNew = instruction.getArrayRef();
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitArrayStore(com.ibm.wala
* .ssa.SSAArrayStoreInstruction)
*/
@Override
public void visitArrayStore(SSAArrayStoreInstruction instruction) {
this.varNumNew = instruction.getArrayRef();
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitBinaryOp(com.ibm.wala.ssa
* .SSABinaryOpInstruction)
*/
@Override
public void visitBinaryOp(SSABinaryOpInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitCheckCast(com.ibm.wala.
* ssa.SSACheckCastInstruction)
*/
@Override
public void visitCheckCast(SSACheckCastInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitComparison(com.ibm.wala
* .ssa.SSAComparisonInstruction)
*/
@Override
public void visitComparison(SSAComparisonInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitConditionalBranch(com.ibm
* .wala.ssa.SSAConditionalBranchInstruction)
*/
@Override
public void visitConditionalBranch(
SSAConditionalBranchInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitConversion(com.ibm.wala
* .ssa.SSAConversionInstruction)
*/
@Override
public void visitConversion(SSAConversionInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitGet(com.ibm.wala.ssa.
* SSAGetInstruction)
*/
@Override
public void visitGet(SSAGetInstruction instruction) {
if (!instruction.isStatic()) {
this.varNumNew = instruction.getRef();
}
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitGetCaughtException(com.
* ibm.wala.ssa.SSAGetCaughtExceptionInstruction)
*/
@Override
public void visitGetCaughtException(
SSAGetCaughtExceptionInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitGoto(com.ibm.wala.ssa.
* SSAGotoInstruction)
*/
@Override
public void visitGoto(SSAGotoInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitInstanceof(com.ibm.wala
* .ssa.SSAInstanceofInstruction)
*/
@Override
public void visitInstanceof(SSAInstanceofInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitInvoke(com.ibm.wala.ssa
* .SSAInvokeInstruction)
*/
@Override
public void visitInvoke(SSAInvokeInstruction instruction) {
if (!instruction.isStatic()) {
this.varNumNew = instruction.getReceiver();
}
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitLoadMetadata(com.ibm.wala
* .ssa.SSALoadMetadataInstruction)
*/
@Override
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitMonitor(com.ibm.wala.ssa
* .SSAMonitorInstruction)
*/
@Override
public void visitMonitor(SSAMonitorInstruction instruction) {
this.varNumNew = instruction.getRef();
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitNew(com.ibm.wala.ssa.
* SSANewInstruction)
*/
@Override
public void visitNew(SSANewInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitPhi(com.ibm.wala.ssa.
* SSAPhiInstruction)
*/
@Override
public void visitPhi(SSAPhiInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitPi(com.ibm.wala.ssa.
* SSAPiInstruction)
*/
@Override
public void visitPi(SSAPiInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.ssa.SSAInstruction.IVisitor#visitPut(com.ibm.wala.ssa.
* SSAPutInstruction)
*/
@Override
public void visitPut(SSAPutInstruction instruction) {
if (!instruction.isStatic()) {
this.varNumNew = instruction.getRef();
}
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitReturn(com.ibm.wala.ssa
* .SSAReturnInstruction)
*/
@Override
public void visitReturn(SSAReturnInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitSwitch(com.ibm.wala.ssa
* .SSASwitchInstruction)
*/
@Override
public void visitSwitch(SSASwitchInstruction instruction) {
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitThrow(com.ibm.wala.ssa.
* SSAThrowInstruction)
*/
@Override
public void visitThrow(SSAThrowInstruction instruction) {
this.varNumNew = instruction.getException();
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.wala.ssa.SSAInstruction.IVisitor#visitUnaryOp(com.ibm.wala.ssa
* .SSAUnaryOpInstruction)
*/
@Override
public void visitUnaryOp(SSAUnaryOpInstruction instruction) {
}
}

View File

@ -138,7 +138,7 @@ public class IntraprocNullPointerAnalysis<T extends ISSABasicBlock> {
final NullPointerFrameWork<T> problem = new NullPointerFrameWork<T>(cfg, ir);
final int[] paramValNum = ir.getParameterValueNumbers();
solver = new NullPointerSolver<T>(problem, maxVarNum, paramValNum, initialState);
solver = new NullPointerSolver<T>(problem, maxVarNum, paramValNum, initialState, ir);
solver.solve(progress);
@ -199,44 +199,6 @@ public class IntraprocNullPointerAnalysis<T extends ISSABasicBlock> {
}
}
private class NullPointerSolver<B extends ISSABasicBlock> extends DataflowSolver<B, NullPointerState> {
private final int maxVarNum;
private final ParameterState parameterState;
private NullPointerSolver(NullPointerFrameWork<B> problem, int maxVarNum, int[] paramVarNum) {
this(problem, maxVarNum, paramVarNum, null);
}
private NullPointerSolver(NullPointerFrameWork<B> problem, int maxVarNum, int[] paramVarNum, ParameterState initialState) {
super(problem);
this.maxVarNum = maxVarNum;
this.parameterState = initialState;
}
/* (non-Javadoc)
* @see com.ibm.wala.dataflow.graph.DataflowSolver#makeEdgeVariable(java.lang.Object, java.lang.Object)
*/
@Override
protected NullPointerState makeEdgeVariable(B src, B dst) {
return new NullPointerState(maxVarNum, ir.getSymbolTable(), parameterState);
}
/* (non-Javadoc)
* @see com.ibm.wala.dataflow.graph.DataflowSolver#makeNodeVariable(java.lang.Object, boolean)
*/
@Override
protected NullPointerState makeNodeVariable(B n, boolean IN) {
return new NullPointerState(maxVarNum, ir.getSymbolTable(), parameterState);
}
@Override
protected NullPointerState[] makeStmtRHS(int size) {
return new NullPointerState[size];
}
}
private class NegativeCFGBuilderVisitor implements IVisitor {
private final Graph<T> deleted;

View File

@ -29,12 +29,12 @@ import com.ibm.wala.util.intset.IntPair;
* @author Juergen Graf <graf@kit.edu>
*
*/
class NullPointerFrameWork<T extends ISSABasicBlock> implements IKilldallFramework<T, NullPointerState> {
public class NullPointerFrameWork<T extends ISSABasicBlock> implements IKilldallFramework<T, NullPointerState> {
private final Graph<T> flow;
private final NullPointerTransferFunctionProvider<T> transferFunct;
NullPointerFrameWork(ControlFlowGraph<SSAInstruction, T> cfg, IR ir) {
public NullPointerFrameWork(ControlFlowGraph<SSAInstruction, T> cfg, IR ir) {
final IBinaryNaturalRelation backEdges = Acyclic.computeBackEdges(cfg, cfg.entry());
boolean hasBackEdge = backEdges.iterator().hasNext();
if (hasBackEdge) {

View File

@ -0,0 +1,61 @@
/*******************************************************************************
* 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.cfg.exc.intra;
import com.ibm.wala.dataflow.graph.DataflowSolver;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.ISSABasicBlock;
/**
* Intraprocedural dataflow analysis to detect impossible NullPointerExceptions.
*
* @author Juergen Graf <graf@kit.edu>
*
*/
public class NullPointerSolver<B extends ISSABasicBlock> extends DataflowSolver<B, NullPointerState> {
private final int maxVarNum;
private final ParameterState parameterState;
private final IR ir;
public NullPointerSolver(NullPointerFrameWork<B> problem, int maxVarNum, int[] paramVarNum, IR ir) {
this(problem, maxVarNum, paramVarNum, ParameterState.createDefault(ir.getMethod()), ir);
}
public NullPointerSolver(NullPointerFrameWork<B> problem, int maxVarNum, int[] paramVarNum, ParameterState initialState, IR ir) {
super(problem);
this.maxVarNum = maxVarNum;
this.parameterState = initialState;
this.ir = ir;
}
/* (non-Javadoc)
* @see com.ibm.wala.dataflow.graph.DataflowSolver#makeEdgeVariable(java.lang.Object, java.lang.Object)
*/
@Override
protected NullPointerState makeEdgeVariable(B src, B dst) {
return new NullPointerState(maxVarNum, ir.getSymbolTable(), parameterState);
}
/* (non-Javadoc)
* @see com.ibm.wala.dataflow.graph.DataflowSolver#makeNodeVariable(java.lang.Object, boolean)
*/
@Override
protected NullPointerState makeNodeVariable(B n, boolean IN) {
return new NullPointerState(maxVarNum, ir.getSymbolTable(), parameterState);
}
@Override
protected NullPointerState[] makeStmtRHS(int size) {
return new NullPointerState[size];
}
}

View File

@ -0,0 +1,30 @@
package com.ibm.wala.ipa.cfg.exceptionpruning;
import java.util.Collection;
/**
* To filter exceptions you can implement this interface and use it in
* combination with {@link ExceptionFilter2EdgeFilter}. For more Details see
* package-info.
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <Instruction>
*/
public interface ExceptionFilter<Instruction> {
/**
*
* @param instruction
* @return if the instruction does always throw an exception
*/
public boolean alwaysThrowsException(Instruction instruction);
/**
*
* @param instruction
* @return a list of exceptions, which have to be filtered for the given
* instruction
*/
public Collection<FilteredException> filteredExceptions(
Instruction instruction);
}

View File

@ -0,0 +1,85 @@
package com.ibm.wala.ipa.cfg.exceptionpruning;
import java.util.Collection;
import com.ibm.wala.cfg.ControlFlowGraph;
import com.ibm.wala.ipa.cfg.EdgeFilter;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAAbstractThrowInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
/**
* This class converts an exception filter to an edge filter.
*
* @author Stephan Gocht <stephan@gobro.de>
*
* @param <Block>
*/
public class ExceptionFilter2EdgeFilter<Block extends ISSABasicBlock>
implements EdgeFilter<Block> {
private final ExceptionFilter<SSAInstruction> filter;
private final ClassHierarchy cha;
private final ControlFlowGraph<SSAInstruction, Block> cfg;
public ExceptionFilter2EdgeFilter(ExceptionFilter<SSAInstruction> filter,
ClassHierarchy cha, ControlFlowGraph<SSAInstruction, Block> cfg) {
this.cfg = cfg;
this.filter = filter;
this.cha = cha;
}
@Override
public boolean hasExceptionalEdge(Block src, Block dst) {
boolean hasExceptionalEdge = this.cfg.getExceptionalSuccessors(src)
.contains(dst);
final SSAInstruction relevantInstruction = src.getLastInstruction();
if (hasExceptionalEdge && relevantInstruction != null) {
if (this.weKnowAllExceptions(relevantInstruction)) {
final Collection<TypeReference> thrownExceptions = relevantInstruction
.getExceptionTypes();
final Collection<FilteredException> filteredExceptions = this.filter
.filteredExceptions(relevantInstruction);
final boolean isFiltered = ExceptionMatcher.isFiltered(
thrownExceptions, filteredExceptions, this.cha);
hasExceptionalEdge = !isFiltered;
}
}
return hasExceptionalEdge;
}
@Override
public boolean hasNormalEdge(Block src, Block dst) {
boolean result = true;
if (src.getLastInstructionIndex() >= 0) {
final SSAInstruction relevantInstruction = src.getLastInstruction();
if (relevantInstruction != null
&& this.filter.alwaysThrowsException(relevantInstruction)) {
result = false;
}
}
return result && this.cfg.getNormalSuccessors(src).contains(dst);
}
/**
* SSAInstruction::getExceptionTypes() does not return exceptions thrown by
* throw or invoke instructions, so we may not remove edges from those
* instructions, even if all exceptions returned by
* instruction.getExceptionTypes() are to be filtered.
*
* @param instruction
* @return if we know all exceptions, that can occur at this address from
* getExceptionTypes()
*/
private boolean weKnowAllExceptions(SSAInstruction instruction) {
return !((instruction instanceof SSAAbstractInvokeInstruction) || (instruction instanceof SSAAbstractThrowInstruction));
}
}

View File

@ -0,0 +1,127 @@
package com.ibm.wala.ipa.cfg.exceptionpruning;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.types.TypeReference;
/**
* Helper class to check if an exception is part of a set of filtered
* exceptions.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ExceptionMatcher {
/**
* @param thrownExceptions
* @param filteredExceptions
* @param cha
* @return true, iff thrownExceptions is part of filteredExceptions
*/
public static boolean isFiltered(
Collection<TypeReference> thrownExceptions,
Collection<FilteredException> filteredExceptions, ClassHierarchy cha) {
final ExceptionMatcher matcher = new ExceptionMatcher(thrownExceptions,
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 LinkedHashSet<>();
this.ignoreSubclass = new LinkedHashSet<>();
this.cha = cha;
this.retainedExceptions = new LinkedHashSet<>();
this.fillIgnore(filteredExceptions);
this.computeRetainedExceptions(thrownExceptions);
this.areAllExceptionsIgnored = this.retainedExceptions.isEmpty();
this.free();
}
private void computeRetainedExceptions(Collection<TypeReference> thrownExceptions){
for (final TypeReference exception : thrownExceptions) {
if (!this.isFiltered(exception)) {
this.retainedExceptions.add(exception);
}
}
}
private boolean areAllExceptionsIgnored() {
return this.areAllExceptionsIgnored;
}
private void fillIgnore(Collection<FilteredException> filteredExceptions) {
for (final FilteredException filteredException : filteredExceptions) {
final TypeReference exception = filteredException.getException();
this.ignoreExact.add(exception);
if (filteredException.isSubclassFiltered()) {
this.ignoreSubclass.add(exception);
}
}
}
private void free() {
this.ignoreExact = null;
this.ignoreSubclass = null;
this.cha = null;
}
/**
* Check if the exception itself is filtered or if it is derived from a
* filtered exception.
*
* @param exception
* @return if the exception is filtered
*/
private boolean isFiltered(TypeReference exception) {
boolean isFiltered = false;
if (this.ignoreExact.contains(exception)) {
isFiltered = true;
} else {
for (final TypeReference ignoreException : this.ignoreSubclass) {
final IClass exceptionClass = this.cha.lookupClass(exception);
final IClass ignoreClass = this.cha
.lookupClass(ignoreException);
if (this.cha.isAssignableFrom(ignoreClass, exceptionClass)) {
isFiltered = true;
break;
}
}
}
return isFiltered;
}
public Set<TypeReference> getRetainedExceptions() {
return retainedExceptions;
}
}

View File

@ -0,0 +1,35 @@
package com.ibm.wala.ipa.cfg.exceptionpruning;
import com.ibm.wala.types.TypeReference;
/**
* FilteredException represents either a single exception or an exception and
* all its subclasses.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class FilteredException {
public static final boolean FILTER_SUBCLASSES = true;
private final TypeReference exception;
private final boolean isSubclassFiltered;
public FilteredException(TypeReference exception) {
this(exception, false);
}
public FilteredException(TypeReference exception, boolean isSubclassFiltered) {
super();
this.exception = exception;
this.isSubclassFiltered = isSubclassFiltered;
}
public TypeReference getException() {
return this.exception;
}
public boolean isSubclassFiltered() {
return this.isSubclassFiltered;
}
}

View File

@ -0,0 +1,50 @@
package com.ibm.wala.ipa.cfg.exceptionpruning.filter;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis.UnnecessaryCheck;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
/**
* Adapter for using {@link ArrayOutOfBoundsAnalysis}. This filter is filtering
* ArrayOutOfBoundException, which can not occur.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class ArrayOutOfBoundFilter implements ExceptionFilter<SSAInstruction> {
private final ArrayOutOfBoundsAnalysis analysis;
public ArrayOutOfBoundFilter(ArrayOutOfBoundsAnalysis analysis) {
this.analysis = analysis;
}
@Override
public boolean alwaysThrowsException(SSAInstruction instruction) {
return false;
}
@Override
public Collection<FilteredException> filteredExceptions(
SSAInstruction instruction) {
final UnnecessaryCheck unnecessary = this.analysis
.getBoundsCheckNecessary().get(instruction);
if (unnecessary == UnnecessaryCheck.BOTH) {
final LinkedList<FilteredException> result = new LinkedList<FilteredException>();
result.add(new FilteredException(
TypeReference.JavaLangArrayIndexOutOfBoundsException));
return result;
} else {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,55 @@
package com.ibm.wala.ipa.cfg.exceptionpruning.filter;
import java.util.Collection;
import java.util.LinkedList;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
/**
* Use this class to combine multiple {@link ExceptionFilter}
*
* @author Stephan Gocht <stephan@gobro.de>
*/
public class CombinedExceptionFilter<Instruction> implements ExceptionFilter<Instruction> {
private final Collection<ExceptionFilter<Instruction>> exceptionFilter;
public CombinedExceptionFilter() {
this.exceptionFilter = new LinkedList<>();
}
public CombinedExceptionFilter(
Collection<ExceptionFilter<Instruction>> exceptionFilter) {
this.exceptionFilter = exceptionFilter;
}
public boolean add(ExceptionFilter<Instruction> e) {
return this.exceptionFilter.add(e);
}
public boolean addAll(
Collection<? extends ExceptionFilter<Instruction>> c) {
return this.exceptionFilter.addAll(c);
}
@Override
public boolean alwaysThrowsException(Instruction instruction) {
boolean result = false;
for (final ExceptionFilter<Instruction> filter : this.exceptionFilter) {
result |= filter.alwaysThrowsException(instruction);
}
return result;
}
@Override
public Collection<FilteredException> filteredExceptions(
Instruction instruction) {
final LinkedList<FilteredException> result = new LinkedList<>();
for (final ExceptionFilter<Instruction> filter : this.exceptionFilter) {
result.addAll(filter.filteredExceptions(instruction));
}
return result;
}
}

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();
}
}

View File

@ -0,0 +1,60 @@
package com.ibm.wala.ipa.cfg.exceptionpruning.filter;
import java.util.Collection;
import java.util.LinkedList;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
/**
* For filtering specific exceptions.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class IgnoreExceptionsFilter implements ExceptionFilter<SSAInstruction> {
private final Collection<FilteredException> toBeIgnored;
/**
* All given exceptions and subclasses will be ignored.
*
* @param toBeIgnored
*/
public IgnoreExceptionsFilter(Collection<TypeReference> toBeIgnored) {
this.toBeIgnored = new LinkedList<>();
this.addAll(toBeIgnored);
}
/**
* The given exception and subclasses will be ignored.
*/
public IgnoreExceptionsFilter(TypeReference toBeIgnored) {
this.toBeIgnored = new LinkedList<>();
final LinkedList<TypeReference> list = new LinkedList<>();
list.add(toBeIgnored);
this.addAll(list);
}
private void addAll(Collection<TypeReference> toBeIgnored) {
for (final TypeReference ignored : toBeIgnored) {
this.toBeIgnored.add(new FilteredException(ignored,
FilteredException.FILTER_SUBCLASSES));
}
}
@Override
public boolean alwaysThrowsException(SSAInstruction instruction) {
return false;
}
@Override
public Collection<FilteredException> filteredExceptions(
SSAInstruction instruction) {
return this.toBeIgnored;
}
}

View File

@ -0,0 +1,47 @@
package com.ibm.wala.ipa.cfg.exceptionpruning.filter;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import com.ibm.wala.analysis.nullpointer.IntraproceduralNullPointerAnalysis;
import com.ibm.wala.cfg.exc.intra.NullPointerState.State;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.FilteredException;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.types.TypeReference;
/**
* Adapter for {@link IntraproceduralNullPointerAnalysis}. This filter is filtering
* NullPointerException, which can not occur.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
public class NullPointerExceptionFilter implements
ExceptionFilter<SSAInstruction> {
private final IntraproceduralNullPointerAnalysis analysis;
public NullPointerExceptionFilter(IntraproceduralNullPointerAnalysis analysis) {
this.analysis = analysis;
}
@Override
public boolean alwaysThrowsException(SSAInstruction instruction) {
return this.analysis.nullPointerExceptionThrowState(instruction) == State.NULL;
}
@Override
public Collection<FilteredException> filteredExceptions(
SSAInstruction instruction) {
if (this.analysis.nullPointerExceptionThrowState(instruction) == State.NOT_NULL) {
final LinkedList<FilteredException> result = new LinkedList<FilteredException>();
result.add(new FilteredException(
TypeReference.JavaLangNullPointerException));
return result;
} else {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,7 @@
/**
* All available filters should be contained in this package.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
package com.ibm.wala.ipa.cfg.exceptionpruning.filter;

View File

@ -0,0 +1,27 @@
/*******************************************************************************
* 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.interprocedural;
import com.ibm.wala.analysis.arraybounds.ArrayOutOfBoundsAnalysis;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.ArrayOutOfBoundFilter;
import com.ibm.wala.ssa.SSAInstruction;
public class ArrayOutOfBoundInterFilter extends StoringExceptionFilter<SSAInstruction>{
@Override
protected ExceptionFilter<SSAInstruction> computeFilter(CGNode node) {
ArrayOutOfBoundsAnalysis analysis = new ArrayOutOfBoundsAnalysis(node.getIR());
return new ArrayOutOfBoundFilter(analysis);
}
}

View File

@ -0,0 +1,51 @@
/*******************************************************************************
* 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.interprocedural;
import java.util.Collection;
import java.util.LinkedList;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.CombinedExceptionFilter;
public class CombinedInterproceduralExceptionFilter<Instruction> implements InterproceduralExceptionFilter<Instruction> {
private final Collection<InterproceduralExceptionFilter<Instruction>> filter;
public CombinedInterproceduralExceptionFilter() {
this.filter = new LinkedList<>();
}
public CombinedInterproceduralExceptionFilter(
Collection<InterproceduralExceptionFilter<Instruction>> filter) {
this.filter = filter;
}
public boolean add(InterproceduralExceptionFilter<Instruction> e) {
return this.filter.add(e);
}
public boolean addAll(
Collection<? extends InterproceduralExceptionFilter<Instruction>> c) {
return this.filter.addAll(c);
}
@Override
public ExceptionFilter<Instruction> getFilter(CGNode node) {
CombinedExceptionFilter<Instruction> result = new CombinedExceptionFilter<Instruction>();
for (InterproceduralExceptionFilter<Instruction> exceptionFilter:filter) {
result.add(exceptionFilter.getFilter(node));
}
return result;
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.interprocedural;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
public class IgnoreExceptionsInterFilter<Instruction> implements InterproceduralExceptionFilter<Instruction>{
private ExceptionFilter<Instruction> filter;
public IgnoreExceptionsInterFilter(ExceptionFilter<Instruction> filter){
this.filter = filter;
}
@Override
public ExceptionFilter<Instruction> getFilter(CGNode node) {
return filter;
}
}

View File

@ -0,0 +1,18 @@
/*******************************************************************************
* 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.interprocedural;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
public interface InterproceduralExceptionFilter<Instruction> {
public ExceptionFilter<Instruction> getFilter(CGNode node);
}

View File

@ -0,0 +1,27 @@
/*******************************************************************************
* 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.interprocedural;
import com.ibm.wala.analysis.nullpointer.IntraproceduralNullPointerAnalysis;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
import com.ibm.wala.ipa.cfg.exceptionpruning.filter.NullPointerExceptionFilter;
import com.ibm.wala.ssa.SSAInstruction;
public class NullPointerExceptionInterFilter extends StoringExceptionFilter<SSAInstruction>{
@Override
protected ExceptionFilter<SSAInstruction> computeFilter(CGNode node) {
IntraproceduralNullPointerAnalysis analysis = new IntraproceduralNullPointerAnalysis(node.getIR());
return new NullPointerExceptionFilter(analysis);
}
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.interprocedural;
import java.util.LinkedHashMap;
import java.util.Map;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.cfg.exceptionpruning.ExceptionFilter;
public abstract class StoringExceptionFilter<Instruction> implements InterproceduralExceptionFilter<Instruction>{
private Map<CGNode, ExceptionFilter<Instruction>> store;
public StoringExceptionFilter(){
this.store = new LinkedHashMap<>();
}
abstract protected ExceptionFilter<Instruction> computeFilter(CGNode node);
@Override
public ExceptionFilter<Instruction> getFilter(CGNode node) {
if (store.containsKey(node)) {
return store.get(node);
} else {
ExceptionFilter<Instruction> filter = computeFilter(node);
store.put(node, filter);
return filter;
}
}
}

View File

@ -0,0 +1,8 @@
/**
*
*/
/**
* @author stephan
*
*/
package com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural;

View File

@ -0,0 +1,15 @@
/**
* If we want to filter edges of a control flow graph we already have
* {@link com.ibm.wala.ipa.cfg.EdgeFilter}, but if we want to remove
* exceptions in particular we may want to combine different analysis.
* Therefore we need a possibility to collect a set of removed exceptions,
* so that an exceptional edge may be removed as soon as all exceptions,
* which can occur along these edge are filtered.
*
* This package contains classes for this job and also adapter for some
* analysis.
*
* @author Stephan Gocht <stephan@gobro.de>
*
*/
package com.ibm.wala.ipa.cfg.exceptionpruning;

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* 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.ssa;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import com.ibm.wala.util.collections.Pair;
/**
* A policy, that adds pi nodes for all variables, that are used in a branch
* instruction.
*
* @author Stephan Gocht <stephan@gobro.de>
*/
public class AllIntegerDueToBranchePiPolicy implements SSAPiNodePolicy {
@Override
public Pair<Integer, SSAInstruction> getPi(SSAAbstractInvokeInstruction call, SymbolTable symbolTable) {
return null;
}
@Override
public Pair<Integer, SSAInstruction> getPi(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable) {
final List<Pair<Integer, SSAInstruction>> pis = this.getPis(cond, def1, def2, symbolTable);
if (pis.size() == 0) {
return null;
} else if (pis.size() == 1) {
return pis.get(0);
} else {
throw new IllegalArgumentException(
"getPi was called but pi nodes should be inserted for more than one variable. Use getPis instead.");
}
}
@Override
public List<Pair<Integer, SSAInstruction>> getPis(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable) {
if (cond.isIntegerComparison()) {
final LinkedList<Pair<Integer, SSAInstruction>> result = new LinkedList<>();
for (int i = 0; i < cond.getNumberOfUses(); i++) {
result.add(Pair.make(cond.getUse(i), (SSAInstruction) cond));
}
return result;
} else {
return Collections.emptyList();
}
}
}

View File

@ -10,6 +10,9 @@
*******************************************************************************/
package com.ibm.wala.ssa;
import java.util.LinkedList;
import java.util.List;
import com.ibm.wala.util.collections.Pair;
/**
@ -101,5 +104,13 @@ public class CompoundPiPolicy implements SSAPiNodePolicy {
return true;
}
@Override
public List<Pair<Integer, SSAInstruction>> getPis(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable) {
LinkedList<Pair<Integer, SSAInstruction>> result = new LinkedList<>();
result.addAll(p1.getPis(cond, def1, def2, symbolTable));
result.addAll(p2.getPis(cond, def1, def2, symbolTable));
return result;
}
}

View File

@ -10,6 +10,9 @@
*******************************************************************************/
package com.ibm.wala.ssa;
import java.util.LinkedList;
import java.util.List;
import com.ibm.wala.util.collections.Pair;
/**
@ -75,4 +78,12 @@ public class InstanceOfPiPolicy implements SSAPiNodePolicy {
return null;
}
@Override
public List<Pair<Integer, SSAInstruction>> getPis(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable) {
LinkedList<Pair<Integer, SSAInstruction>> result = new LinkedList<>();
result.add(getPi(cond, def1, def2, symbolTable));
return result;
}
}

View File

@ -10,6 +10,9 @@
*******************************************************************************/
package com.ibm.wala.ssa;
import java.util.LinkedList;
import java.util.List;
import com.ibm.wala.util.collections.Pair;
/**
@ -58,4 +61,12 @@ public class NullTestPiPolicy implements SSAPiNodePolicy {
return null;
}
@Override
public List<Pair<Integer, SSAInstruction>> getPis(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable) {
LinkedList<Pair<Integer, SSAInstruction>> result = new LinkedList<>();
result.add(getPi(cond, def1, def2, symbolTable));
return result;
}
}

View File

@ -863,9 +863,11 @@ public class SSABuilder extends AbstractIntStackMachine {
private void maybeInsertPi(SSAConditionalBranchInstruction cond) {
if (piNodePolicy != null) {
Pair<Integer, SSAInstruction> pi = piNodePolicy.getPi(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)), symbolTable);
if (pi != null) {
reuseOrCreatePi(pi.snd, pi.fst);
for (Pair<Integer, SSAInstruction> pi : piNodePolicy.getPis(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)),
symbolTable)) {
if (pi != null) {
reuseOrCreatePi(pi.snd, pi.fst);
}
}
}
}

View File

@ -10,6 +10,8 @@
*******************************************************************************/
package com.ibm.wala.ssa;
import java.util.List;
import com.ibm.wala.util.collections.Pair;
/**
@ -43,5 +45,8 @@ public interface SSAPiNodePolicy {
*/
Pair<Integer, SSAInstruction> getPi(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable);
List<Pair<Integer, SSAInstruction>> getPis(SSAConditionalBranchInstruction cond, SSAInstruction def1, SSAInstruction def2,
SymbolTable symbolTable);
}

View File

@ -0,0 +1,122 @@
/*******************************************************************************
* 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.util.ssa;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.ssa.SSAInstruction;
public class InstructionByIIndexMap<Instruction extends SSAInstruction, T> implements Map<Instruction, T> {
private Map<InstructionByIIndexWrapper<Instruction>, T> map;
public InstructionByIIndexMap(Map<InstructionByIIndexWrapper<Instruction>, T> map) {
this.map = map;
}
public InstructionByIIndexMap() {
this.map = new LinkedHashMap<InstructionByIIndexWrapper<Instruction>, T>();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
if (key instanceof SSAInstruction) {
SSAInstruction instruction = (SSAInstruction) key;
if (instruction.iindex >= 0) {
return map.containsKey(new InstructionByIIndexWrapper<SSAInstruction>(instruction));
}
}
return false;
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public T get(Object key) {
if (key instanceof SSAInstruction) {
SSAInstruction instruction = (SSAInstruction) key;
if (instruction.iindex >= 0) {
return map.get(new InstructionByIIndexWrapper<SSAInstruction>(instruction));
}
}
return null;
}
@Override
public T put(Instruction key, T value) {
return map.put(new InstructionByIIndexWrapper<Instruction>(key), value);
}
@Override
public T remove(Object key) {
if (key instanceof SSAInstruction) {
SSAInstruction instruction = (SSAInstruction) key;
if (instruction.iindex >= 0) {
return map.remove(new InstructionByIIndexWrapper<SSAInstruction>(instruction));
}
}
return null;
}
@Override
public void putAll(Map<? extends Instruction, ? extends T> m) {
for (java.util.Map.Entry<? extends Instruction, ? extends T> entry:m.entrySet()) {
this.put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
map.clear();
}
@Override
public Set<Instruction> keySet() {
Set<Instruction> result = new LinkedHashSet<>();
for (InstructionByIIndexWrapper<Instruction> wrapper : map.keySet()) {
result.add(wrapper.getInstruction());
}
return result;
}
@Override
public Collection<T> values() {
return map.values();
}
@Override
public Set<java.util.Map.Entry<Instruction, T>> entrySet() {
Set<java.util.Map.Entry<Instruction, T>> result = new LinkedHashSet<>();
for (java.util.Map.Entry<InstructionByIIndexWrapper<Instruction>, T> entry:map.entrySet()) {
result.add(new AbstractMap.SimpleImmutableEntry<Instruction, T>(entry.getKey().getInstruction(), entry.getValue()));
}
return result;
}
}

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* 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.util.ssa;
import com.ibm.wala.ssa.SSAInstruction;
public class InstructionByIIndexWrapper<T extends SSAInstruction> {
private T instruction;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getInstruction().iindex;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InstructionByIIndexWrapper other = (InstructionByIIndexWrapper) obj;
if (getInstruction().iindex != other.getInstruction().iindex)
return false;
return true;
}
public T getInstruction() {
return instruction;
}
public InstructionByIIndexWrapper(T instruction) {
if (instruction.iindex < 0) {
throw new IllegalArgumentException("The given instruction, can not be identified by iindex.");
}
this.instruction = instruction;
}
}

View File

@ -0,0 +1,120 @@
package com.ibm.wala.util.graph.impl;
import java.util.Iterator;
import com.ibm.wala.util.graph.EdgeManager;
public class SelfLoopAddedEdgeManager<T> implements EdgeManager<T> {
private class PrependItterator implements Iterator<T>{
private boolean usedFirst = false;
private Iterator<T> original;
private T first;
public PrependItterator(Iterator<T> original, T first) {
super();
this.original = original;
this.first = first;
}
@Override
public boolean hasNext() {
if (!usedFirst) {
return true;
} else {
return original.hasNext();
}
}
@Override
public T next() {
if (!usedFirst) {
T tmp = first;
first = null;
usedFirst = true;
return tmp;
} else {
return original.next();
}
}
}
private final EdgeManager<T> original;
public SelfLoopAddedEdgeManager(EdgeManager<T> original) {
if (original == null) {
throw new IllegalArgumentException("original is null");
}
this.original = original;
}
@Override
public Iterator<T> getPredNodes(T n) {
if (original.hasEdge(n, n)) {
return original.getPredNodes(n);
} else {
return new PrependItterator(original.getPredNodes(n), n);
}
}
@Override
public int getPredNodeCount(T n) {
if (original.hasEdge(n, n)) {
return original.getPredNodeCount(n);
} else {
return original.getPredNodeCount(n) + 1;
}
}
@Override
public Iterator<T> getSuccNodes(T n) {
if (original.hasEdge(n, n)) {
return original.getSuccNodes(n);
} else {
return new PrependItterator(original.getSuccNodes(n), n);
}
}
@Override
public int getSuccNodeCount(T n) {
if (original.hasEdge(n, n)) {
return original.getSuccNodeCount(n);
} else {
return original.getSuccNodeCount(n) + 1;
}
}
@Override
public void addEdge(T src, T dst) {
original.addEdge(src, dst);
}
@Override
public void removeEdge(T src, T dst) throws UnsupportedOperationException {
original.removeEdge(src, dst);
}
@Override
public void removeAllIncidentEdges(T node) throws UnsupportedOperationException {
original.removeAllIncidentEdges(node);
}
@Override
public void removeIncomingEdges(T node) throws UnsupportedOperationException {
original.removeIncomingEdges(node);
}
@Override
public void removeOutgoingEdges(T node) throws UnsupportedOperationException {
original.removeOutgoingEdges(node);
}
@Override
public boolean hasEdge(T src, T dst) {
if (src.equals(dst)) {
return true;
} else {
return original.hasEdge(src, dst);
}
}
}

View File

@ -0,0 +1,30 @@
package com.ibm.wala.util.graph.impl;
import com.ibm.wala.util.graph.AbstractGraph;
import com.ibm.wala.util.graph.EdgeManager;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NodeManager;
public class SelfLoopAddedGraph <T> extends AbstractGraph<T> {
final private NodeManager<T> nodes;
@Override
protected NodeManager<T> getNodeManager() {
return nodes;
}
final private EdgeManager<T> edges;
@Override
protected EdgeManager<T> getEdgeManager() {
return edges;
}
public SelfLoopAddedGraph(Graph<T> G) {
nodes = G;
edges = new SelfLoopAddedEdgeManager<T>(G);
}
}