WALA/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/arraybounds/ArrayboundsAnalysisTest.java

154 lines
5.6 KiB
Java

package com.ibm.wala.core.tests.arraybounds;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
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 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.ipa.cha.ClassHierarchyFactory;
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 {@code <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 = ClassHierarchyFactory.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() {
scope = null;
cha = null;
irFactory = null;
options = null;
}
}