/**
* Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
*/
package org.evosuite.junit;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.evosuite.TestGenerationContext;
import org.evosuite.instrumentation.BytecodeInstrumentation;
import org.evosuite.junit.CompoundTestCase.MethodDef;
import org.evosuite.junit.CompoundTestCase.ReturnStatementPlaceholder;
import org.evosuite.junit.CompoundTestCase.TestScope;
import org.evosuite.junit.TestRuntimeValuesDeterminer.CursorableTrace;
import org.evosuite.testcase.ArrayIndex;
import org.evosuite.testcase.ArrayReference;
import org.evosuite.testcase.ArrayStatement;
import org.evosuite.testcase.AssignmentStatement;
import org.evosuite.testcase.BooleanPrimitiveStatement;
import org.evosuite.testcase.BytePrimitiveStatement;
import org.evosuite.testcase.CharPrimitiveStatement;
import org.evosuite.testcase.ConstructorStatement;
import org.evosuite.testcase.DoublePrimitiveStatement;
import org.evosuite.testcase.FieldReference;
import org.evosuite.testcase.FieldStatement;
import org.evosuite.testcase.FloatPrimitiveStatement;
import org.evosuite.testcase.IntPrimitiveStatement;
import org.evosuite.testcase.LongPrimitiveStatement;
import org.evosuite.testcase.MethodStatement;
import org.evosuite.testcase.NullStatement;
import org.evosuite.testcase.PrimitiveExpression;
import org.evosuite.testcase.PrimitiveExpression.Operator;
import org.evosuite.testcase.PrimitiveStatement;
import org.evosuite.testcase.ShortPrimitiveStatement;
import org.evosuite.testcase.StatementInterface;
import org.evosuite.testcase.StringPrimitiveStatement;
import org.evosuite.testcase.TestCase;
import org.evosuite.testcase.VariableReference;
import org.evosuite.testcase.VariableReferenceImpl;
import org.evosuite.utils.GenericClass;
import org.evosuite.utils.GenericConstructor;
import org.evosuite.utils.GenericField;
import org.evosuite.utils.GenericMethod;
/**
* This class implements the Eclipse JDT Visitor to turn an existing test case
* (in source code form) into an EvoSuite {@link TestCase}.
*
* @author roessler
*/
public class TestExtractingVisitor extends ASTVisitor {
public static interface TestReader {
int getLineNumber(int sourcePos);
CompoundTestCase readTestCase(String clazz, CompoundTestCase reference);
}
private static class BoundVariableReferenceImpl extends VariableReferenceImpl {
private static final long serialVersionUID = 1L;
protected String name;
public BoundVariableReferenceImpl(CompoundTestCase testCase,
java.lang.reflect.Type type, String name) {
super(testCase.getReference(), type);
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getStPosition() {
if (((DelegatingTestCase) testCase).isFinished()) {
return super.getStPosition();
}
return -1;
}
}
private static class ValidArrayReference extends ArrayReference {
private static final long serialVersionUID = 1L;
public ValidArrayReference(DelegatingTestCase testCase, GenericClass clazz,
int[] lengths) {
super(testCase, clazz, lengths);
}
public ValidArrayReference(TestCase tc, Class<?> clazz) {
super(tc, clazz);
}
@Override
public int getStPosition() {
if (((DelegatingTestCase) testCase).isFinished()) {
return super.getStPosition();
}
return -1;
}
}
private static class ValidConstructorStatement extends ConstructorStatement {
private static final long serialVersionUID = 1L;
public ValidConstructorStatement(TestCase tc, Constructor<?> constructor,
java.lang.reflect.Type type, List<VariableReference> parameters) {
super(tc, new GenericConstructor(constructor, type), parameters);
}
public ValidConstructorStatement(TestCase tc, Constructor<?> constructor,
VariableReference retVal, List<VariableReference> parameters) {
super(tc, new GenericConstructor(constructor, retVal.getType()), retVal,
parameters, false);
}
@Override
public boolean isValid() {
return true;
}
}
private static class ValidMethodStatement extends MethodStatement {
private static final long serialVersionUID = 1L;
public ValidMethodStatement(TestCase tc, Method method, VariableReference callee,
java.lang.reflect.Type type, List<VariableReference> parameters) {
super(tc, new GenericMethod(method, callee.getType()), callee, parameters);
}
public ValidMethodStatement(TestCase tc, Method method, VariableReference callee,
VariableReference retVal, List<VariableReference> parameters) {
super(tc, new GenericMethod(method, callee.getType()), callee, retVal,
parameters);
}
@Override
public StatementInterface copy(TestCase newTestCase, int offset) {
// Code was partly copied from MethodStatement
ArrayList<VariableReference> new_params = new ArrayList<VariableReference>();
for (VariableReference r : parameters) {
new_params.add(r.copy(newTestCase, offset));
}
if (method.isStatic()) {
// TODO: If callee is an array index, this will return an
// invalid copy of the cloned variable!
return new ValidMethodStatement(newTestCase, method.getMethod(), null,
retval.getType(), new_params);
}
VariableReference newCallee = callee.copy(newTestCase, offset);
return new MethodStatement(newTestCase, method, newCallee, new_params);
}
@Override
public boolean isValid() {
return true;
}
}
private static class ValidVariableReference extends VariableReferenceImpl {
private static final long serialVersionUID = -59873293582106016L;
// private static int counter = 0;
public ValidVariableReference(DelegatingTestCase testCase,
java.lang.reflect.Type type) {
super(testCase, type);
// System.out.println("Created ValidVariableReference #" + counter++
// + " of type " + type);
}
@Override
public int getStPosition() {
if (((DelegatingTestCase) testCase).isFinished()) {
return super.getStPosition();
}
return -1;
}
}
private static class WrongMethodBindingException extends Exception {
private static final long serialVersionUID = 1L;
public WrongMethodBindingException() {
// TODO Auto-generated constructor stub
}
}
private static Class<?> toClass(java.lang.reflect.Type type) {
if (type instanceof java.lang.reflect.ParameterizedType) {
return (Class<?>) ((java.lang.reflect.ParameterizedType) type).getRawType();
}
return (Class<?>) type;
}
private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TestExtractingVisitor.class);
private final CompoundTestCase testCase;
private final TestReader testReader;
private final String unqualifiedTest;
private final String unqualifiedTestMethod;
private final Stack<VariableReference> nestedCallResults = new Stack<VariableReference>();
private final Map<String, String> imports = new HashMap<String, String>();
private final TestRuntimeValuesDeterminer testValuesDeterminer;
private final Map<String, VariableReference> calleeResultMap = new HashMap<String, VariableReference>();
private boolean exceptionReadingMethod = false;
private CursorableTrace cursorableTrace;
// TODO Remove the following two:
private final Stack<Integer> iterations = new Stack<Integer>();
private final Stack<Integer> loopExecCnts = new Stack<Integer>();
// TODO This is bad practice: here we rely on a global variable
// for something that should be parameters to the methods!
private Integer lineNumber = null;
private static final HashSet<Class<?>> PRIMITIVE_CLASSES = new HashSet<Class<?>>();
static {
PRIMITIVE_CLASSES.add(Long.class);
PRIMITIVE_CLASSES.add(Integer.class);
PRIMITIVE_CLASSES.add(Short.class);
PRIMITIVE_CLASSES.add(Byte.class);
PRIMITIVE_CLASSES.add(Boolean.class);
PRIMITIVE_CLASSES.add(Character.class);
PRIMITIVE_CLASSES.add(Double.class);
PRIMITIVE_CLASSES.add(Float.class);
PRIMITIVE_CLASSES.add(String.class);
}
private final static HashMap<Character, Class<?>> PRIMITIVE_SIGNATURE_MAPPING = new HashMap<Character, Class<?>>();
static {
PRIMITIVE_SIGNATURE_MAPPING.put('B', Byte.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('C', Character.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('D', Double.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('F', Float.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('I', Integer.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('J', Long.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('S', Short.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('V', Void.TYPE);
PRIMITIVE_SIGNATURE_MAPPING.put('Z', Boolean.TYPE);
}
private final static HashMap<String, Class<?>> PRIMITIVE_TYPECODE_MAPPING = new HashMap<String, Class<?>>();
static {
PRIMITIVE_TYPECODE_MAPPING.put("byte", Byte.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("char", Character.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("double", Double.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("float", Float.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("int", Integer.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("long", Long.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("short", Short.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("void", Void.TYPE);
PRIMITIVE_TYPECODE_MAPPING.put("boolean", Boolean.TYPE);
}
/**
* <p>
* Constructor for TestExtractingVisitor.
* </p>
*
* @param testCase
* a {@link org.evosuite.junit.CompoundTestCase} object.
* @param testClass
* a {@link java.lang.String} object.
* @param testMethod
* a {@link java.lang.String} object.
* @param testReader
* a {@link org.evosuite.junit.TestExtractingVisitor.TestReader}
* object.
*/
public TestExtractingVisitor(CompoundTestCase testCase, String testClass,
String testMethod, TestReader testReader) {
super();
this.testCase = testCase;
this.testReader = testReader;
this.unqualifiedTest = testClass.substring(testClass.lastIndexOf(".") + 1,
testClass.length());
this.unqualifiedTestMethod = testMethod;
this.testValuesDeterminer = TestRuntimeValuesDeterminer.getInstance(testClass);
testValuesDeterminer.determineRuntimeValues();
}
/** {@inheritDoc} */
@Override
public void endVisit(Assignment assignment) {
if ((assignment.getRightHandSide() instanceof MethodInvocation)
|| (assignment.getRightHandSide() instanceof ClassInstanceCreation)) {
// treated in respective endVisit methods
return;
}
VariableReference varRef = retrieveVariableReference(assignment.getLeftHandSide(),
null);
varRef.setOriginalCode(assignment.getLeftHandSide().toString());
VariableReference newAssignment = retrieveVariableReference(assignment.getRightHandSide(),
null);
newAssignment.setOriginalCode(assignment.getRightHandSide().toString());
if (varRef instanceof ArrayIndex) {
AssignmentStatement assignmentStatement = new AssignmentStatement(
testCase.getReference(), varRef, newAssignment);
testCase.addStatement(assignmentStatement);
return;
}
testCase.variableAssignment(varRef, newAssignment);
}
/** {@inheritDoc} */
@Override
public void endVisit(Block node) {
if (testCase.getCurrentScope() == TestScope.STATIC) {
testCase.setCurrentScope(TestScope.FIELDS);
}
}
/** {@inheritDoc} */
@Override
public void endVisit(ClassInstanceCreation instanceCreation) {
if (instanceCreation.getParent() instanceof ThrowStatement) {
logger.warn("Ignoring throw statements!");
return;
}
List<?> paramTypes = Arrays.asList(instanceCreation.resolveConstructorBinding().getParameterTypes());
List<?> paramValues = instanceCreation.arguments();
Constructor<?> constructor = retrieveConstructor(instanceCreation.getType(),
paramTypes, paramValues);
List<VariableReference> params = convertParams(instanceCreation.arguments(),
paramTypes);
VariableReference retVal = retrieveVariableReference(instanceCreation, null);
retVal.setOriginalCode(instanceCreation.toString());
ConstructorStatement statement = new ValidConstructorStatement(
testCase.getReference(), constructor, retVal, params);
testCase.addStatement(statement);
}
/** {@inheritDoc} */
@Override
public void endVisit(ConditionalExpression node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public void endVisit(EnhancedForStatement node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public void endVisit(IfStatement node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public void endVisit(MethodDeclaration node) {
logger.debug("Finished extracting method {}. It caused {} exception.",
testCase.getCurrentMethod(), exceptionReadingMethod ? "an" : "no");
if (exceptionReadingMethod) {
testCase.discardMethod();
return;
}
testCase.finalizeMethod();
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
public void endVisit(MethodInvocation methodInvocation) {
// TODO If in constructor, treat calls to this() and super().
String methodName = methodInvocation.getName().toString();
if (methodName.equals("fail") || methodName.startsWith("assert")) {
logger.warn("We are ignoring fail and assert statements for now.");
for (Expression expression : (List<Expression>) methodInvocation.arguments()) {
if ((expression instanceof MethodInvocation)
|| (expression instanceof ClassInstanceCreation)) {
assert !nestedCallResults.isEmpty();
nestedCallResults.pop();
}
}
return;
}
IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
if (methodBinding == null) {
throw new RuntimeException("JDT was unable to resolve method for "
+ methodInvocation);
}
List<?> paramTypes = Arrays.asList(methodBinding.getParameterTypes());
List<VariableReference> params = convertParams(methodInvocation.arguments(),
paramTypes);
Method method = retrieveMethod(methodInvocation, methodBinding, params);
/*
Class<?> declaringClass = method.getDeclaringClass();
if (false) {
// TODO Methods can be declared in an order such that the called
// method is not yet read
if (testCase.getClassName().equals(declaringClass.getName())
|| testCase.isDescendantOf(declaringClass)) {
MethodDef methodDef = testCase.getMethod(method.getName());
VariableReference retVal = retrieveResultReference(methodInvocation);
retVal.setOriginalCode(methodInvocation.toString());
testCase.convertMethod(methodDef, params, retVal);
return;
}
}
*/
VariableReference callee = null;
if (!Modifier.isStatic(method.getModifiers())) {
callee = retrieveCalleeReference(methodInvocation);
}
MethodStatement methodStatement = null;
ASTNode parent = methodInvocation.getParent();
if (parent instanceof ExpressionStatement) {
VariableReference retVal = new ValidVariableReference(
testCase.getReference(), method.getReturnType());
retVal.setOriginalCode(methodInvocation.toString());
methodStatement = new ValidMethodStatement(testCase.getReference(), method,
callee, retVal, params);
testCase.addStatement(methodStatement);
return;
}
VariableReference retVal = retrieveResultReference(methodInvocation);
retVal.setOriginalCode(methodInvocation.toString());
methodStatement = new ValidMethodStatement(testCase.getReference(), method,
callee, retVal, params);
if (!(parent instanceof Block)) {
nestedCallResults.push(retVal);
}
testCase.addStatement(methodStatement);
}
/** {@inheritDoc} */
@Override
public void endVisit(ReturnStatement returnStatement) {
VariableReference returnValue = null;
if (returnStatement.getExpression() == null) {
logger.warn("Since we do not represent control structures, we ignore explicit empty return statements ('return;').");
return;
}
if (returnStatement.getExpression() instanceof MethodInvocation) {
returnValue = testCase.getLastStatement().getReturnValue();
} else {
returnValue = retrieveVariableReference(returnStatement.getExpression(), null);
}
returnValue.setOriginalCode(returnStatement.toString());
ReturnStatementPlaceholder returnStmt = new ReturnStatementPlaceholder(
testCase.getReference(), returnValue);
testCase.addStatement(returnStmt);
}
/** {@inheritDoc} */
@Override
public void endVisit(SuperMethodInvocation superMethodInvocation) {
List<?> paramTypes = Arrays.asList(superMethodInvocation.resolveMethodBinding().getParameterTypes());
List<VariableReference> params = convertParams(superMethodInvocation.arguments(),
paramTypes);
String name = superMethodInvocation.getName().getIdentifier();
MethodDef methodDef = testCase.getParent().getMethod(name);
VariableReference retVal = retrieveResultReference(superMethodInvocation);
retVal.setOriginalCode(superMethodInvocation.toString());
testCase.convertMethod(methodDef, params, retVal);
}
/** {@inheritDoc} */
@Override
public void endVisit(SwitchCase node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public void endVisit(SwitchStatement node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public void endVisit(WhileStatement node) {
// TODO-JRO Implement method endVisit
logger.warn("Method endVisit not implemented!");
super.endVisit(node);
}
/** {@inheritDoc} */
@Override
public boolean visit(ArrayCreation arrayCreation) {
return true;
}
/** {@inheritDoc} */
@Override
public boolean visit(Block node) {
if (testCase.getCurrentScope() == TestScope.FIELDS) {
testCase.setCurrentScope(TestScope.STATIC);
}
return true;
}
/** {@inheritDoc} */
@Override
public boolean visit(CatchClause node) {
logger.warn("We are ignoring catch-clauses for now!");
return false;
}
/** {@inheritDoc} */
@Override
public boolean visit(FieldDeclaration fieldDeclaration) {
if (Modifier.isStatic(fieldDeclaration.getModifiers())) {
testCase.setCurrentScope(TestScope.STATICFIELDS);
}
VariableDeclarationFragment varDeclFrgmnt = (VariableDeclarationFragment) fieldDeclaration.fragments().get(0);
Expression expression = varDeclFrgmnt.getInitializer();
VariableReference varRef;
if (expression == null) {
varRef = retrieveDefaultValueAssignment(retrieveTypeClass(varDeclFrgmnt));
} else {
varRef = retrieveVariableReference(expression, null);
}
varRef.setOriginalCode(fieldDeclaration.toString());
// TODO Use the name here as well?
// String name = varDeclFrgmt.getName().getIdentifier();
// new BoundVariableReferenceImpl(testCase, varType, name);
testCase.addVariable(varDeclFrgmnt.resolveBinding(), varRef);
testCase.setCurrentScope(TestScope.FIELDS);
return true;
}
/** {@inheritDoc} */
@Override
public boolean visit(ForStatement forStatement) {
int lineNumber = testReader.getLineNumber(forStatement.getBody().getStartPosition());
// TODO This works here, but not if inner loop is a while statement
// that iterates a different number of times...!
// Solution: follow the trace
// loop head is executed 1 times more than the body...
int loopExecCnt = testValuesDeterminer.getExecutionCount(lineNumber) - 1;
if (loopExecCnts.size() > 0) {
for (Integer outerLoopExecCnt : loopExecCnts) {
// loop head is executed 1 times more than the body...
loopExecCnt = (loopExecCnt - 1) / outerLoopExecCnt;
}
}
loopExecCnts.push(loopExecCnt);
int idx = 0;
for (; idx < loopExecCnt; idx++) {
iterations.push(idx);
if ((idx > 0) && (cursorableTrace != null)) {
cursorableTrace.advanceLoop();
}
acceptChildren(forStatement, forStatement.initializers());
acceptChild(forStatement, forStatement.getExpression());
acceptChildren(forStatement, forStatement.updaters());
acceptChild(forStatement, forStatement.getBody());
iterations.pop();
}
iterations.push(idx);
acceptChildren(forStatement, forStatement.initializers());
acceptChild(forStatement, forStatement.getExpression());
acceptChildren(forStatement, forStatement.updaters());
iterations.pop();
loopExecCnts.pop();
return false;
}
/** {@inheritDoc} */
@Override
public boolean visit(ImportDeclaration importDeclaration) {
String[] importParts = importDeclaration.toString().split(" ");
String fullImport = importParts[importParts.length - 1].replace(";", "").trim();
if (fullImport.contains("$")) {
imports.put(fullImport.split("$")[1], fullImport);
} else {
String[] identifiers = fullImport.split("\\.");
imports.put(identifiers[identifiers.length - 1], fullImport);
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean visit(MarkerAnnotation markerAnnotation) {
String annotation = markerAnnotation.toString();
if (annotation.equals("@BeforeClass")) {
testCase.setCurrentScope(TestScope.BEFORE_CLASS);
}
if (annotation.equals("@Before")) {
testCase.setCurrentScope(TestScope.BEFORE);
}
if (annotation.equals("@AfterClass")) {
testCase.setCurrentScope(TestScope.AFTER_CLASS);
}
if (annotation.equals("@After")) {
testCase.setCurrentScope(TestScope.AFTER);
}
return true;
}
/** {@inheritDoc} */
@Override
public boolean visit(MethodDeclaration methodDeclaration) {
String currentMethodName = methodDeclaration.getName().getIdentifier();
logger.debug("Extracting method {}.", currentMethodName);
testCase.newMethod(currentMethodName);
if ((unqualifiedTestMethod == null)
|| !unqualifiedTestMethod.equals(currentMethodName)) {
if (methodDeclaration.getName().getIdentifier().equals(unqualifiedTest)) {
testCase.setCurrentScope(TestScope.CONSTRUCTOR);
}
if (methodDeclaration.getName().getIdentifier().equals("setUp")) {
testCase.setCurrentScope(TestScope.BEFORE);
}
if (methodDeclaration.getName().getIdentifier().equals("tearDown")) {
testCase.setCurrentScope(TestScope.AFTER);
}
}
cursorableTrace = testValuesDeterminer.getMethodTrace(currentMethodName);
calleeResultMap.clear();
return saveMethodCodeExtraction(methodDeclaration);
}
/** {@inheritDoc} */
@Override
public boolean visit(SingleVariableDeclaration variableDeclaration) {
VariableReference varRef = retrieveVariableReference(variableDeclaration);
varRef.setOriginalCode(variableDeclaration.toString());
testCase.addParameter(varRef);
return true;
}
/** {@inheritDoc} */
@Override
public boolean visit(TypeDeclaration typeDeclaration) {
if (!unqualifiedTest.equals(typeDeclaration.getName().getIdentifier())) {
logger.warn("Type declarations are ignored for other types than the actual test!");
return false;
}
Type supertype = typeDeclaration.getSuperclassType();
if (supertype == null) {
return true;
}
Class<?> supertypeClass = retrieveTypeClass(supertype);
if (supertypeClass == null) {
return true;
}
String superclass = retrieveTypeClass(supertype).getName();
if (superclass == null) {
return true;
}
if (superclass.startsWith("org.junit.") || //
superclass.startsWith("junit.") || //
superclass.startsWith("java.")) {
return true;
}
testReader.readTestCase(superclass, testCase);
return true;
}
/** {@inheritDoc} */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public boolean visit(VariableDeclarationStatement variableDeclStmt) {
Class<?> varType = retrieveTypeClass(variableDeclStmt.getType());
if (varType.isPrimitive() || PRIMITIVE_CLASSES.contains(varType)) {
// Can only happen to primitive types and String,
// otherwise it is a constructor call which is handled elsewhere
logger.debug("Variable has not been treated elsewhere...");
VariableDeclarationFragment varDeclFrgmnt = (VariableDeclarationFragment) variableDeclStmt.fragments().get(0);
Expression expression = varDeclFrgmnt.getInitializer();
VariableReference varRef = retrieveVariableReference(expression, null);
varRef.setOriginalCode(variableDeclStmt.toString());
// TODO Use the name here as well?
// String name = varDeclFrgmt.getName().getIdentifier();
// new BoundVariableReferenceImpl(testCase, varType, name);
testCase.addVariable(varDeclFrgmnt.resolveBinding(), varRef);
return true;
}
if (varType.isArray()) {
// if (varType.getComponentType().isPrimitive() ||
// varType.getComponentType().equals(String.class)) {
// ... or to primitive and string arrays
VariableDeclarationFragment varDeclFrgmnt = (VariableDeclarationFragment) variableDeclStmt.fragments().get(0);
Expression expression = varDeclFrgmnt.getInitializer();
if (expression instanceof ArrayInitializer) {
ArrayReference arrayReference = new ValidArrayReference(
testCase.getReference(), varType);
ArrayStatement arrayStatement = new ArrayStatement(
testCase.getReference(), arrayReference);
arrayReference.setOriginalCode(variableDeclStmt.toString());
testCase.addStatement(arrayStatement);
testCase.addVariable(varDeclFrgmnt.resolveBinding(), arrayReference);
ArrayInitializer arrayInitializer = (ArrayInitializer) expression;
for (int idx = 0; idx < arrayInitializer.expressions().size(); idx++) {
Expression expr = (Expression) arrayInitializer.expressions().get(idx);
VariableReference valueRef;
if (expr instanceof NumberLiteral) {
valueRef = retrieveVariableReference((NumberLiteral) expr,
varType.getComponentType());
} else if (expr instanceof PrefixExpression) {
valueRef = retrieveVariableReference((PrefixExpression) expr,
varType.getComponentType());
} else {
valueRef = retrieveVariableReference(expr, null);
}
valueRef.setOriginalCode(expr.toString());
VariableReference arrayElementRef = new ArrayIndex(
testCase.getReference(), arrayReference, idx);
arrayElementRef.setOriginalCode(expr.toString());
arrayStatement.getVariableReferences().add(arrayElementRef);
AssignmentStatement arrayAssignment = new AssignmentStatement(
testCase.getReference(), arrayElementRef, valueRef);
testCase.addStatement(arrayAssignment);
}
// }
return true;
}
if (expression instanceof ArrayCreation) {
ArrayCreation arrayCreation = ((ArrayCreation) expression);
List paramTypes = new ArrayList();
for (int idx = 0; idx < arrayCreation.dimensions().size(); idx++) {
paramTypes.add(int.class);
}
List<VariableReference> lengthsVarRefs = convertParams(arrayCreation.dimensions(),
paramTypes);
ArrayReference arrayReference = new ValidArrayReference(
testCase.getReference(), varType);
arrayReference.setOriginalCode(variableDeclStmt.toString());
ArrayStatement arrayStatement = new ArrayStatement(
testCase.getReference(), arrayReference);
arrayStatement.setLengths(getLengths(variableDeclStmt, lengthsVarRefs));
testCase.addVariable(varDeclFrgmnt.resolveBinding(),
arrayStatement.getReturnValue());
testCase.addStatement(arrayStatement);
}
}
return true;
}
/**
* <p>
* extractArgumentClasses
* </p>
*
* @param arguments
* a {@link java.util.List} object.
* @return an array of {@link java.lang.Class} objects.
*/
protected Class<?>[] extractArgumentClasses(List<?> arguments) {
Class<?>[] argClasses = new Class<?>[arguments.size()];
for (int idx = 0; idx < arguments.size(); idx++) {
Object arg = arguments.get(idx);
argClasses[idx] = retrieveTypeClass(arg);
}
return argClasses;
}
/**
* <p>
* retrieveConstructor
* </p>
*
* @param type
* a {@link org.eclipse.jdt.core.dom.Type} object.
* @param argumentTypes
* a {@link java.util.List} object.
* @param arguments
* a {@link java.util.List} object.
* @return a {@link java.lang.reflect.Constructor} object.
*/
protected Constructor<?> retrieveConstructor(Type type, List<?> argumentTypes,
List<?> arguments) {
Class<?>[] argClasses = extractArgumentClasses(argumentTypes);
Constructor<?> constructor = null;
Class<?> clazz = retrieveTypeClass(type);
try {
constructor = clazz.getConstructor(argClasses);
} catch (Exception exc) {
throw new RuntimeException(exc);
}
return constructor;
}
/**
* <p>
* retrieveTypeClass
* </p>
*
* @param argument
* a {@link java.lang.Object} object.
* @return a {@link java.lang.Class} object.
*/
protected Class<?> retrieveTypeClass(Object argument) {
assert argument != null;
if (argument instanceof SimpleType) {
SimpleType simpleType = (SimpleType) argument;
return retrieveTypeClass(simpleType);
}
if (argument instanceof ITypeBinding) {
ITypeBinding binding = (ITypeBinding) argument;
return retrieveTypeClass(binding);
}
if (argument instanceof IVariableBinding) {
IVariableBinding variableBinding = (IVariableBinding) argument;
return retrieveTypeClass(variableBinding.getType());
}
if (argument instanceof SimpleName) {
SimpleName simpleName = (SimpleName) argument;
return retrieveTypeClass(simpleName.resolveBinding());
}
if (argument instanceof StringLiteral) {
return String.class;
}
if (argument instanceof NumberLiteral) {
return retrieveTypeClass((NumberLiteral) argument);
}
if (argument instanceof PrimitiveType) {
PrimitiveType primitiveType = (PrimitiveType) argument;
String typeCode = primitiveType.getPrimitiveTypeCode().toString();
Class<?> result = PRIMITIVE_TYPECODE_MAPPING.get(typeCode);
assert result != null : "Could not resolve typecode " + typeCode + ".";
return result;
}
if (argument instanceof ArrayType) {
ArrayType arrayType = (ArrayType) argument;
return retrieveTypeClass(arrayType);
}
if (argument instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) argument;
return retrieveTypeClass(parameterizedType.getType());
}
if (argument instanceof VariableDeclarationFragment) {
VariableDeclarationFragment varDeclFrgmnt = (VariableDeclarationFragment) argument;
return retrieveTypeClass(varDeclFrgmnt.resolveBinding());
}
if (argument instanceof InfixExpression) {
InfixExpression infixExpr = (InfixExpression) argument;
ITypeBinding refTypeBinding = infixExpr.resolveTypeBinding();
if (refTypeBinding != null) {
return retrieveTypeClass(refTypeBinding);
} else {
throw new RuntimeException(
"Could not determine type class of infix expression '"
+ infixExpr + "'.");
}
}
if (argument instanceof MethodInvocation) {
MethodInvocation methodInvocation = (MethodInvocation) argument;
ITypeBinding typeBinding = methodInvocation.resolveTypeBinding();
if (typeBinding != null) {
return retrieveTypeClass(typeBinding);
}
Expression typeExpression = methodInvocation.getExpression();
if (typeExpression instanceof MethodInvocation) {
MethodInvocation parentMethodInvocation = (MethodInvocation) typeExpression;
IMethodBinding parentMethodBinding = parentMethodInvocation.resolveMethodBinding();
return retrieveTypeClass(parentMethodBinding.getDeclaringClass());
} else {
return retrieveTypeClass(typeExpression);
}
}
if (argument instanceof ArrayAccess) {
ArrayAccess arrayAccess = (ArrayAccess) argument;
return retrieveTypeClass(arrayAccess.getArray());
}
if (argument instanceof Class<?>) {
return (Class<?>) argument;
}
if (argument instanceof ClassInstanceCreation) {
return retrieveTypeClass(((ClassInstanceCreation) argument).resolveTypeBinding());
}
if (argument instanceof BooleanLiteral) {
return Boolean.TYPE;
}
throw new UnsupportedOperationException("Retrieval of type "
+ argument.getClass() + " not implemented yet!");
}
/**
* <p>
* retrieveVariableReference
* </p>
*
* @param argument
* a {@link java.lang.Object} object.
* @param varType
* a {@link java.lang.Class} object.
* @return a {@link org.evosuite.testcase.VariableReference} object.
*/
protected VariableReference retrieveVariableReference(Object argument,
Class<?> varType) {
if (argument instanceof ClassInstanceCreation) {
return retrieveVariableReference((ClassInstanceCreation) argument, varType);
}
if (argument instanceof VariableDeclarationFragment) {
return retrieveVariableReference((VariableDeclarationFragment) argument);
}
if (argument instanceof SimpleName) {
SimpleName simpleName = (SimpleName) argument;
lineNumber = testReader.getLineNumber(simpleName.getStartPosition());
return retrieveVariableReference(simpleName.resolveBinding(), varType);
}
if (argument instanceof IVariableBinding) {
return retrieveVariableReference((IVariableBinding) argument, varType);
}
if (argument instanceof PrefixExpression) {
return retrieveVariableReference((PrefixExpression) argument);
}
if (argument instanceof InfixExpression) {
return retrieveVariableReference((InfixExpression) argument, varType);
}
if (argument instanceof ExpressionStatement) {
ExpressionStatement exprStmt = (ExpressionStatement) argument;
Expression expression = exprStmt.getExpression();
return retrieveVariableReference(expression, varType);
}
if (argument instanceof NullLiteral) {
return retrieveVariableReference((NullLiteral) argument, varType);
}
if (argument instanceof StringLiteral) {
return retrieveVariableReference((StringLiteral) argument);
}
if (argument instanceof NumberLiteral) {
return retrieveVariableReference((NumberLiteral) argument);
}
if (argument instanceof CharacterLiteral) {
return retrieveVariableReference((CharacterLiteral) argument);
}
if (argument instanceof BooleanLiteral) {
return retrieveVariableReference((BooleanLiteral) argument);
}
if (argument instanceof ITypeBinding) {
if (varType != null) {
return new ValidVariableReference(testCase.getReference(), varType);
}
return new ValidVariableReference(testCase.getReference(),
retrieveTypeClass(argument));
}
if (argument instanceof QualifiedName) {
return retrieveVariableReference((QualifiedName) argument);
}
if (argument instanceof MethodInvocation) {
MethodInvocation methodInvocation = (MethodInvocation) argument;
VariableReference result = retrieveResultReference(methodInvocation);
nestedCallResults.push(result);
return result;
}
if (argument instanceof ArrayCreation) {
return retrieveVariableReference((ArrayCreation) argument);
}
if (argument instanceof VariableDeclaration) {
return retrieveVariableReference((VariableDeclaration) argument);
}
if (argument instanceof ArrayAccess) {
// return retrieveVariableReference(((ArrayAccess)
// argument).getArray(), null);
return retrieveVariableReference((ArrayAccess) argument);
}
if (argument instanceof Assignment) {
return retrieveVariableReference(((Assignment) argument).getLeftHandSide(),
null);
}
if (argument instanceof CastExpression) {
CastExpression castExpression = (CastExpression) argument;
VariableReference result = retrieveVariableReference(castExpression.getExpression(),
null);
Class<?> castClass = retrieveTypeClass(castExpression.resolveTypeBinding());
assert castClass.isAssignableFrom(toClass(result.getType()));
result.setType(castClass);
return result;
}
throw new UnsupportedOperationException("Argument type " + argument.getClass()
+ " not implemented!");
}
private void acceptChild(ASTNode parent, ASTNode child) {
try {
Method acceptChild = ASTNode.class.getDeclaredMethod("acceptChild",
ASTVisitor.class,
ASTNode.class);
acceptChild.setAccessible(true);
acceptChild.invoke(parent, this, child);
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}
private void acceptChildren(ASTNode parent, List<?> childNodes) {
try {
Method[] allMethods = ASTNode.class.getDeclaredMethods();
Method acceptChildren = null;
for (Method method : allMethods) {
if (method.getName().equals("acceptChildren")) {
if (acceptChildren != null) {
throw new RuntimeException(
"Found method 'acceptChildren' more than once!");
}
acceptChildren = method;
}
}
if (acceptChildren == null) {
throw new RuntimeException(
"Method acceptChildren not found for type ASTNode!");
}
acceptChildren.setAccessible(true);
acceptChildren.invoke(parent, this, childNodes);
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}
private List<VariableReference> convertParams(List<?> params, List<?> argumentTypes) {
List<VariableReference> result = new ArrayList<VariableReference>();
if ((params.size() == 0) && (argumentTypes.size() == 0)) {
return result;
}
if ((argumentTypes.size() > params.size())
&& (argumentTypes.size() - 1 != params.size())) {
throw new RuntimeException(
"Number of declared and actual params do not match!");
}
int limit = argumentTypes.size();
Class<?> lastDeclaredParamType = retrieveTypeClass(argumentTypes.get(argumentTypes.size() - 1));
if (lastDeclaredParamType.isArray()) {
limit = argumentTypes.size() - 1;
}
for (int idx = 0; idx < limit; idx++) {
if (idx >= params.size()) {
break;
}
Object argument = params.get(idx);
if ((argument instanceof MethodInvocation)
|| (argument instanceof ClassInstanceCreation)) {
assert !nestedCallResults.isEmpty();
result.add(nestedCallResults.pop());
continue;
}
Class<?> argClass = retrieveTypeClass(argumentTypes.get(idx));
VariableReference argRef = retrieveVariableReference(argument, argClass);
argRef.setOriginalCode(argument.toString());
result.add(argRef);
}
if (limit == argumentTypes.size()) {
return result;
}
assert lastDeclaredParamType.isArray();
if (argumentTypes.size() == params.size()) {
Object lastParam = params.get(params.size() - 1);
Class<?> lastActualParamType = retrieveTypeClass(lastParam);
if (lastParam instanceof MethodInvocation) {
assert !nestedCallResults.isEmpty();
lastActualParamType = nestedCallResults.peek().getVariableClass();
}
if (lastActualParamType.isArray()) {
if ((lastParam instanceof MethodInvocation)
|| (lastParam instanceof ClassInstanceCreation)) {
assert !nestedCallResults.isEmpty();
result.add(nestedCallResults.pop());
} else {
result.add(retrieveVariableReference(lastParam, null));
}
return result;
}
}
ArrayReference arrayReference = new ValidArrayReference(testCase.getReference(),
lastDeclaredParamType);
arrayReference.setOriginalCode(params.toString());
ArrayStatement arrayStatement = new ArrayStatement(testCase.getReference(),
arrayReference);
testCase.addStatement(arrayStatement);
result.add(arrayStatement.getReturnValue());
arrayStatement.setSize(params.size() - (argumentTypes.size() - 1));
int arrayIdx = 0;
for (int idx = argumentTypes.size() - 1; idx < params.size(); idx++) {
ArrayIndex arrayIndex = new ArrayIndex(testCase.getReference(),
arrayReference, arrayIdx);
Object param = params.get(idx);
VariableReference paramRef = null;
if ((param instanceof MethodInvocation)
|| (param instanceof ClassInstanceCreation)) {
assert !nestedCallResults.isEmpty();
paramRef = nestedCallResults.pop();
} else {
paramRef = retrieveVariableReference(param,
lastDeclaredParamType.getComponentType());
}
paramRef.setOriginalCode(param.toString());
AssignmentStatement assignment = new AssignmentStatement(
testCase.getReference(), arrayIndex, paramRef);
testCase.addStatement(assignment);
arrayIdx++;
}
return result;
}
private PrimitiveStatement<?> createPrimitiveStatement(Class<?> primitiveClass,
Object value) {
if (String.class.equals(primitiveClass)) {
return new StringPrimitiveStatement(testCase.getReference(), (String) value);
}
if (primitiveClass.equals(Boolean.class) || primitiveClass.equals(boolean.class)) {
return new BooleanPrimitiveStatement(testCase.getReference(), (Boolean) value);
}
if (primitiveClass.equals(Integer.class) || primitiveClass.equals(int.class)) {
return new IntPrimitiveStatement(testCase.getReference(),
((Number) value).intValue());
}
if (primitiveClass.equals(Long.class) || primitiveClass.equals(long.class)) {
return new LongPrimitiveStatement(testCase.getReference(),
((Number) value).longValue());
}
if (primitiveClass.equals(Byte.class) || primitiveClass.equals(byte.class)) {
return new BytePrimitiveStatement(testCase.getReference(),
((Number) value).byteValue());
}
if (primitiveClass.equals(Float.class) || primitiveClass.equals(float.class)) {
return new FloatPrimitiveStatement(testCase.getReference(),
((Number) value).floatValue());
}
if (primitiveClass.equals(Short.class) || primitiveClass.equals(short.class)) {
return new ShortPrimitiveStatement(testCase.getReference(),
((Number) value).shortValue());
}
if (primitiveClass.equals(Double.class) || primitiveClass.equals(double.class)) {
return new DoublePrimitiveStatement(testCase.getReference(),
((Number) value).doubleValue());
}
throw new UnsupportedOperationException(
"Not all primitives have been implemented!");
}
private Class<?> doBoxing(Class<?> primitiveClass) {
if (primitiveClass.equals(long.class)) {
return Long.class;
}
if (primitiveClass.equals(int.class)) {
return Integer.class;
}
if (primitiveClass.equals(short.class)) {
return Short.class;
}
if (primitiveClass.equals(byte.class)) {
return Byte.class;
}
if (primitiveClass.equals(boolean.class)) {
return Boolean.class;
}
if (primitiveClass.equals(char.class)) {
return Character.class;
}
if (primitiveClass.equals(double.class)) {
return Double.class;
}
if (primitiveClass.equals(float.class)) {
return Float.class;
}
throw new UnsupportedOperationException("Cannot doBoxing for class "
+ primitiveClass);
}
private Integer getArrayIndex(Expression index) {
if (index instanceof SimpleName) {
String variable = retrieveVariableName(index);
Integer lineNumber = testReader.getLineNumber(index.getStartPosition());
if (cursorableTrace != null) {
Object value = cursorableTrace.getVariableValueAfter(lineNumber, variable);
return (Integer) value;
}
throw new RuntimeException("Cannot determine array index without trace for "
+ index + "!");
}
if (index instanceof NumberLiteral) {
Object value = ((NumberLiteral) index).resolveConstantExpressionValue();
return (Integer) value;
}
throw new RuntimeException(
"Method getArrayIndex not implemented for index expression type " + index
+ "!");
}
private int[] getLengths(VariableDeclarationStatement variableDeclStmt,
List<VariableReference> lengthsVarRefs) {
int[] lengths = new int[lengthsVarRefs.size()];
String variable = retrieveVariableName(variableDeclStmt);
int lineNumber = testReader.getLineNumber(variableDeclStmt.getStartPosition());
Object value = null;
if (cursorableTrace != null) {
value = cursorableTrace.getVariableValueAfter(lineNumber, variable);
}
int idx = 0;
while ((value != null) && value.getClass().isArray()) {
lengths[idx] = Array.getLength(value);
if (lengths[idx] > 0) {
value = Array.get(value, 0);
} else {
value = null;
}
idx++;
}
if (idx == lengths.length) {
return lengths;
}
idx = 0;
for (VariableReference lengthVarRef : lengthsVarRefs) {
// TODO This is a hack. We should use the variablereferences
// instead!
lengths[idx] = Integer.valueOf(lengthVarRef.toString());
idx++;
}
return lengths;
}
private VariableReference getLoopVariable(IVariableBinding varBinding,
Class<?> varClass) {
if (lineNumber == null) {
throw new RuntimeException("Don't know in which line we are...");
}
Object value = cursorableTrace.getVariableValueAfter(lineNumber,
varBinding.getName());
if (varClass.isPrimitive()) {
PrimitiveStatement<?> numberAssignment = createPrimitiveStatement(varClass,
value);
testCase.addStatement(numberAssignment);
return numberAssignment.getReturnValue();
}
throw new RuntimeException("Not implemented!");
}
private Class<?> loadClass(String className) {
// TODO Implement loading classes from Properties.CLASSPATH that are not
// on BugEx's classpath
String simpleName = className;
if (className.startsWith("[")) {
Pattern pattern = Pattern.compile("\\[L([\\.\\w]+);");
Matcher matcher = pattern.matcher(className);
if (matcher.find()) {
simpleName = matcher.group(1);
}
}
try {
if (BytecodeInstrumentation.isSharedClass(simpleName)) {
return Class.forName(className);
}
if (!BytecodeInstrumentation.isTargetProject(simpleName)) {
return Class.forName(className);
}
return Class.forName(className, true, TestGenerationContext.getClassLoader());
} catch (ClassNotFoundException exc) {
throw new RuntimeException(exc);
}
}
private Number negate(Number value) {
if (value instanceof Integer) {
return (Integer) value * -1;
}
if (value instanceof Long) {
return (Long) value * -1;
}
if (value instanceof Float) {
return (Float) value * -1;
}
if (value instanceof Double) {
return (Double) value * -1;
}
throw new UnsupportedOperationException("Number type " + value.getClass()
+ " not implemented!");
}
private VariableReference retrieveCalleeReference(MethodInvocation methodInvocation) {
Expression expression = methodInvocation.getExpression();
if (expression != null) {
return retrieveVariableReference(expression, null);
}
throw new RuntimeException("Callee was null for expression: " + expression);
}
private VariableReference retrieveDefaultValueAssignment(Class<?> typeClass) {
if (typeClass.isPrimitive()) {
if (typeClass.equals(boolean.class)) {
PrimitiveStatement<Boolean> charAssignment = new BooleanPrimitiveStatement(
testCase.getReference(), false);
testCase.addStatement(charAssignment);
return charAssignment.getReturnValue();
}
PrimitiveStatement<?> numberAssignment = createPrimitiveStatement(typeClass,
0);
testCase.addStatement(numberAssignment);
return numberAssignment.getReturnValue();
}
PrimitiveStatement<?> nullAssignment = new NullStatement(testCase.getReference(),
typeClass);
testCase.addStatement(nullAssignment);
return nullAssignment.getReturnValue();
}
private Method retrieveMethod(MethodInvocation methodInvocation,
IMethodBinding methodBinding, List<VariableReference> params) {
assert methodBinding != null : "Sources missing!";
String methodName = methodInvocation.getName().getIdentifier();
Class<?> clazz = retrieveTypeClass(methodBinding.getDeclaringClass());
Set<Method> methods = retrieveMethods(clazz, methodName, params);
if (methods.size() == 1) {
return methods.iterator().next();
}
// TODO Implement varargs methods
try {
Class<?>[] paramClasses = new Class<?>[methodInvocation.arguments().size()];
assert methods.size() > 0;
for (int idx = 0; idx < methodInvocation.arguments().size(); idx++) {
ITypeBinding paramTypeBinding = methodBinding.getParameterTypes()[idx];
paramClasses[idx] = retrieveTypeClass(paramTypeBinding);
VariableReference param = params.get(idx);
if (!param.isAssignableFrom(paramClasses[idx])) {
if (methods.size() == 0) {
throw new IllegalStateException(
"Param class and argument do not match!");
}
throw new WrongMethodBindingException();
}
}
try {
return clazz.getMethod(methodName, paramClasses);
} catch (Exception exc) {
try {
return clazz.getDeclaredMethod(methodName, paramClasses);
} catch (Exception exc2) {
throw new RuntimeException(exc);
}
}
} catch (WrongMethodBindingException exc) {
logger.debug("The resolved method binding is wrong. Will manually correct it...");
if (methods.size() > 1) {
logger.warn("Cannot unambiguously determine the method '{}'.", methodName);
}
return methods.iterator().next();
}
}
private Set<Method> retrieveMethods(Class<?> clazz, String methodName,
List<VariableReference> params) {
Set<Method> result = new HashSet<Method>();
Set<Method> possibleMethods = new HashSet<Method>();
possibleMethods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
possibleMethods.addAll(Arrays.asList(clazz.getMethods()));
outer: for (Method method : possibleMethods) {
if (method.getName().equals(methodName)
&& (method.getParameterTypes().length == params.size())) {
Class<?>[] declaredParamClasses = method.getParameterTypes();
for (int idx = 0; idx < params.size(); idx++) {
VariableReference param = params.get(idx);
Class<?> actualParamClass = param.getVariableClass();
Class<?> declaredParamClass = declaredParamClasses[idx];
if (!declaredParamClass.isAssignableFrom(actualParamClass)) {
if (actualParamClass.isPrimitive()) {
actualParamClass = doBoxing(actualParamClass);
if (!declaredParamClasses[idx].isAssignableFrom(actualParamClass)) {
continue outer;
}
} else {
continue outer;
}
}
}
result.add(method);
}
}
assert result.size() > 0 : "Param classes and arguments do not match!";
return result;
}
private Class<?> retrievePrimitiveClass(String className) {
assert className.length() == 1;
Class<?> result = PRIMITIVE_SIGNATURE_MAPPING.get(className.charAt(0));
if (result != null) {
return result;
}
throw new RuntimeException("Primitive type of class '" + className
+ "' is unknown.");
}
private VariableReference retrieveResultReference(MethodInvocation methodInvocation) {
VariableReference result = calleeResultMap.get(methodInvocation.toString());
if (result != null) {
return result;
}
ASTNode parent = methodInvocation.getParent();
if (parent instanceof VariableDeclarationFragment) {
return retrieveVariableReference(parent, null);
}
if (parent instanceof Assignment) {
Assignment assignment = (Assignment) parent;
return retrieveVariableReference(assignment.getLeftHandSide(), null);
}
IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
ITypeBinding returnType = methodBinding.getReturnType();
Class<?> resultClass = null;
try {
resultClass = retrieveTypeClass(returnType);
} catch (Exception exc) {
String localClass = methodBinding.getDeclaringClass().getQualifiedName()
+ "." + returnType.getName();
resultClass = loadClass(localClass);
}
result = retrieveVariableReference(returnType, resultClass);
calleeResultMap.put(methodInvocation.toString(), result);
return result;
}
private VariableReference retrieveResultReference(
SuperMethodInvocation superMethodInvocation) {
// TODO Duplicate code from retrieveResultReference(MethodInvocation)
// too bad they don't have a common matching interface
VariableReference result = calleeResultMap.get(superMethodInvocation.toString());
if (result != null) {
return result;
}
ASTNode parent = superMethodInvocation.getParent();
if (parent instanceof VariableDeclarationFragment) {
return retrieveVariableReference(parent, null);
}
if (parent instanceof Assignment) {
Assignment assignment = (Assignment) parent;
return retrieveVariableReference(assignment.getLeftHandSide(), null);
}
IMethodBinding methodBinding = superMethodInvocation.resolveMethodBinding();
result = retrieveVariableReference(methodBinding.getReturnType(), null);
calleeResultMap.put(superMethodInvocation.toString(), result);
return result;
}
private Class<?> retrieveTypeClass(ArrayType arrayType) {
try {
Class<?> componentType = retrieveTypeClass(arrayType.getComponentType());
if (componentType.isPrimitive()) {
String clazz = "[" + componentType.getName().toUpperCase().charAt(0);
return Class.forName(clazz);
}
if (componentType.isArray()) {
String clazz = "[" + componentType.getName();
return Class.forName(clazz);
}
return Class.forName("[L" + componentType.getName() + ";");
} catch (ClassNotFoundException exc) {
throw new RuntimeException(exc);
}
}
private Class<?> retrieveTypeClass(ITypeBinding binding) {
String className = binding.getBinaryName();
if (className == null) {
return null;
}
if (binding.isArray()) {
return loadClass(className);
}
if (binding.isPrimitive()) {
return retrievePrimitiveClass(binding.getBinaryName());
}
return loadClass(className);
}
private Class<?> retrieveTypeClass(NumberLiteral numberLiteral) {
Object value = numberLiteral.resolveConstantExpressionValue();
if (numberLiteral.resolveBoxing()) {
return value.getClass();
}
if (value instanceof Integer) {
return Integer.TYPE;
}
if (value instanceof Long) {
return Long.TYPE;
}
if (value instanceof Double) {
return Double.TYPE;
}
if (value instanceof Float) {
return Float.TYPE;
}
if (value instanceof Short) {
return Short.TYPE;
}
if (value instanceof Byte) {
return Byte.TYPE;
}
throw new UnsupportedOperationException("Retrieval of type "
+ numberLiteral.getClass() + " not implemented yet!");
}
private Class<?> retrieveTypeClass(SimpleType simpleType) {
ITypeBinding binding = simpleType.resolveBinding();
if (binding == null) {
String result = imports.get(simpleType.toString());
if (result != null) {
try {
return Class.forName(result);
} catch (ClassNotFoundException exc) {
throw new RuntimeException("Classpath incomplete?", exc);
}
}
}
assert binding != null : "Could not resolve binding for " + simpleType
+ ". Missing sources folder?";
return retrieveTypeClass(binding);
}
private String retrieveVariableName(Object argument) {
if (argument instanceof SimpleName) {
return ((SimpleName) argument).getIdentifier();
}
if (argument instanceof VariableDeclarationStatement) {
VariableDeclarationStatement variableDeclStmt = (VariableDeclarationStatement) argument;
return retrieveVariableName(variableDeclStmt.fragments().get(0));
}
if (argument instanceof VariableDeclarationFragment) {
return retrieveVariableName(((VariableDeclarationFragment) argument).getName());
}
throw new RuntimeException("Not implemented for type " + argument.getClass());
}
private VariableReference retrieveVariableReference(ArrayAccess arrayAccess) {
Expression expr = arrayAccess.getArray();
List<Integer> indices = new ArrayList<Integer>();
// TODO This is a shortcut
// we need a variable reference for the index value
indices.add(getArrayIndex(arrayAccess.getIndex()));
while (expr instanceof ArrayAccess) {
ArrayAccess current = (ArrayAccess) expr;
expr = current.getArray();
indices.add(getArrayIndex(current.getIndex()));
}
Collections.reverse(indices);
VariableReference varRef = retrieveVariableReference(expr, null);
ArrayReference arrayReference = (ArrayReference) varRef;
assert indices.size() == arrayReference.getArrayDimensions();
ArrayIndex arrayIndex = new ArrayIndex(testCase.getReference(), arrayReference,
indices);
return arrayIndex;
}
private VariableReference retrieveVariableReference(ArrayCreation arrayCreation) {
Class<?> arrayType = retrieveTypeClass(arrayCreation.getType());
List<?> dimensions = ((AbstractList<?>) arrayCreation.getStructuralProperty(ArrayCreation.DIMENSIONS_PROPERTY));
if (dimensions.size() > 1) {
throw new RuntimeException("Multidimensional arrays not implemented!");
}
Integer length = (Integer) ((NumberLiteral) dimensions.get(0)).resolveConstantExpressionValue();
// ArrayReference arrayReference = new
// ValidArrayReference(testCase.getReference(), arrayType, length);
ArrayStatement arrayAssignment = new ArrayStatement(testCase.getReference(),
arrayType, length);
testCase.addStatement(arrayAssignment);
return arrayAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(BooleanLiteral boolLiteral) {
boolean bool = boolLiteral.booleanValue();
PrimitiveStatement<Boolean> charAssignment = new BooleanPrimitiveStatement(
testCase.getReference(), bool);
testCase.addStatement(charAssignment);
return charAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(CharacterLiteral characterLiteral) {
char character = characterLiteral.charValue();
PrimitiveStatement<Character> charAssignment = new CharPrimitiveStatement(
testCase.getReference(), character);
testCase.addStatement(charAssignment);
return charAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(
ClassInstanceCreation instanceCreation, Class<?> varType) {
if ((instanceCreation.getParent() instanceof MethodInvocation)
|| (instanceCreation.getParent() instanceof ClassInstanceCreation)) {
VariableReference result = new ValidVariableReference(
testCase.getReference(),
retrieveTypeClass(instanceCreation.getType()));
nestedCallResults.push(result);
return result;
}
if ((instanceCreation.getParent() instanceof ExpressionStatement)
&& (instanceCreation.getParent().getParent() instanceof Block)) {
if (varType == null) {
varType = retrieveTypeClass(instanceCreation);
}
VariableReference varRef = new ValidVariableReference(
testCase.getReference(), varType);
return varRef;
}
return retrieveVariableReference(instanceCreation.getParent(), varType);
}
private VariableReference retrieveVariableReference(InfixExpression infixExpr,
Class<?> exprType) {
if (exprType == null) {
exprType = retrieveTypeClass(infixExpr);
}
VariableReference ref = new VariableReferenceImpl(testCase.getReference(),
exprType);
VariableReference leftOperand = retrieveVariableReference(infixExpr.getLeftOperand(),
null);
leftOperand.setOriginalCode(infixExpr.getLeftOperand().toString());
Operator operator = Operator.toOperator(infixExpr.getOperator().toString());
VariableReference rightOperand = retrieveVariableReference(infixExpr.getRightOperand(),
null);
rightOperand.setOriginalCode(infixExpr.getRightOperand().toString());
PrimitiveExpression expr = new PrimitiveExpression(testCase.getReference(), ref,
leftOperand, operator, rightOperand);
testCase.addStatement(expr);
return ref;
}
private VariableReference retrieveVariableReference(IVariableBinding varBinding,
Class<?> varClass) {
if (varClass == null) {
varClass = retrieveTypeClass(varBinding.getType());
}
VariableReference localVar = testCase.getVariableReference(varBinding);
if (localVar != null) {
return localVar;
}
if (!iterations.isEmpty()) {
return getLoopVariable(varBinding, varClass);
}
logger.warn("No variable reference found for variable binding {}, creating new one.",
varBinding);
return new BoundVariableReferenceImpl(testCase, varClass, varBinding.getName());
}
private VariableReference retrieveVariableReference(NullLiteral nullLiteral,
Class<?> varType) {
if (varType == null) {
varType = retrieveTypeClass(nullLiteral.getParent());
}
PrimitiveStatement<?> nullAssignment = new NullStatement(testCase.getReference(),
varType);
testCase.addStatement(nullAssignment);
return nullAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(NumberLiteral numberLiteral) {
Class<?> numberClass = retrieveTypeClass(numberLiteral);
return retrieveVariableReference(numberLiteral, numberClass);
}
private VariableReference retrieveVariableReference(NumberLiteral numberLiteral,
Class<?> numberClass) {
Object value = numberLiteral.resolveConstantExpressionValue();
PrimitiveStatement<?> numberAssignment = createPrimitiveStatement(numberClass,
value);
testCase.addStatement(numberAssignment);
return numberAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(PrefixExpression prefixExpr) {
if (prefixExpr.getOperator() == org.eclipse.jdt.core.dom.PrefixExpression.Operator.MINUS) {
Class<?> numberClass = retrieveTypeClass(prefixExpr.getOperand());
return retrieveVariableReference(prefixExpr, numberClass);
}
throw new UnsupportedOperationException("Prefix " + prefixExpr
+ " not implemented!");
}
private VariableReference retrieveVariableReference(PrefixExpression prefixExpr,
Class<?> numberClass) {
if (prefixExpr.getOperator() == org.eclipse.jdt.core.dom.PrefixExpression.Operator.MINUS) {
NumberLiteral numberLiteral = (NumberLiteral) prefixExpr.getOperand();
Number value = (Number) numberLiteral.resolveConstantExpressionValue();
value = negate(value);
PrimitiveStatement<?> numberAssignment = createPrimitiveStatement(numberClass,
value);
testCase.addStatement(numberAssignment);
return numberAssignment.getReturnValue();
}
throw new UnsupportedOperationException("Prefix " + prefixExpr
+ " not implemented!");
}
private VariableReference retrieveVariableReference(QualifiedName qualifiedName) {
try {
Class<?> referencedClass = retrieveTypeClass(qualifiedName.getQualifier().resolveTypeBinding());
Field field = referencedClass.getField(qualifiedName.getName().getIdentifier());
FieldReference fieldReference = new FieldReference(testCase.getReference(),
new GenericField(field, referencedClass));
Class<?> resultClass = retrieveTypeClass(qualifiedName.resolveTypeBinding());
FieldStatement fieldStatement = new FieldStatement(testCase.getReference(),
new GenericField(field, resultClass), fieldReference);
testCase.addStatement(fieldStatement);
return fieldStatement.getReturnValue();
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}
private VariableReference retrieveVariableReference(StringLiteral stringLiteral) {
String string = stringLiteral.getLiteralValue();
PrimitiveStatement<String> stringAssignment = new StringPrimitiveStatement(
testCase.getReference(), string);
testCase.addStatement(stringAssignment);
return stringAssignment.getReturnValue();
}
private VariableReference retrieveVariableReference(VariableDeclaration varDecl) {
IVariableBinding variableBinding = varDecl.resolveBinding();
Class<?> clazz = retrieveTypeClass(variableBinding.getType());
VariableReference result = new BoundVariableReferenceImpl(testCase, clazz,
variableBinding.getName());
testCase.addVariable(variableBinding, result);
return result;
}
private VariableReference retrieveVariableReference(
VariableDeclarationFragment varDeclFrgmnt) {
IVariableBinding variableBinding = varDeclFrgmnt.resolveBinding();
Class<?> clazz = retrieveTypeClass(variableBinding.getType());
if (clazz.isArray()) {
ArrayReference arrayReference = new ValidArrayReference(
testCase.getReference(), clazz);
arrayReference.setOriginalCode(varDeclFrgmnt.toString());
testCase.addVariable(varDeclFrgmnt.resolveBinding(), arrayReference);
return arrayReference;
}
VariableReference result = new BoundVariableReferenceImpl(testCase, clazz,
variableBinding.getName());
testCase.addVariable(variableBinding, result);
return result;
}
private boolean saveMethodCodeExtraction(MethodDeclaration methodDeclaration) {
exceptionReadingMethod = false;
logger.warn("Omitting acceptChild(visitor, getJavadoc());");
logger.warn("Omitting acceptChildren(visitor, this.modifiers);");
logger.warn("Omitting acceptChildren(visitor, this.typeParameters);");
logger.warn("Omitting acceptChild(visitor, getReturnType2());");
logger.warn("Omitting acceptChild(visitor, getName());");
logger.warn("Omitting acceptChildren(visitor, this.parameters);");
logger.warn("Omitting acceptChildren(visitor, this.thrownExceptions);");
logger.warn("Method not accessible, would be: methodDeclaration.acceptChild(this, methodDeclaration.getBody());");
try {
Method acceptChild = ASTNode.class.getDeclaredMethod("acceptChild",
ASTVisitor.class,
ASTNode.class);
acceptChild.setAccessible(true);
acceptChild.invoke(methodDeclaration, this, methodDeclaration.getBody());
} catch (SecurityException exc) {
throw new RuntimeException(exc);
} catch (NoSuchMethodException exc) {
throw new RuntimeException(exc);
} catch (IllegalArgumentException exc) {
throw new RuntimeException(exc);
} catch (IllegalAccessException exc) {
throw new RuntimeException(exc);
} catch (InvocationTargetException exc) {
logger.error("Exception reading code of method '{}', skipping it!",
methodDeclaration.getName(), exc.getCause());
exceptionReadingMethod = true;
}
return false;
}
}