This commit is contained in:
commit
22ce3d1144
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package exceptionpruning;
|
||||
|
||||
public class OwnException extends RuntimeException {
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
*/
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author stephan
|
||||
*
|
||||
*/
|
||||
package com.ibm.wala.ipa.cfg.exceptionpruning.interprocedural;
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue