package ca.uwaterloo.ece.qhanam.jrsrepair; import java.util.HashMap; import java.util.HashSet; import java.util.Stack; import org.eclipse.jdt.core.dom.*; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; /** * Handles new ASTs and variable bindings that are generated by parsing a * list of source code files. Inserts the statements in the ASTs into a * Statements object if they are in the set of potential seed statements * or faulty statements (determined by statement coverage of the test * cases). * @author qhanam * */ public class MutationASTRequestor extends FileASTRequestor { private HashMap<String, HashSet<String>> scope; private HashMap<String, DocumentASTRewrite> sourceFileContents; private LineCoverage faultyLineCoverage; private LineCoverage seedLineCoverage; private Statements faultyStatements; private Statements seedStatements; /** * @param faultyStatements * @param seedStatements */ public MutationASTRequestor(HashMap<String, DocumentASTRewrite> sourceFileContents, HashMap<String, HashSet<String>> scope, LineCoverage faultyLineCoverage, LineCoverage seedLineCoverage, Statements faultyStatements, Statements seedStatements){ this.sourceFileContents = sourceFileContents; this.faultyLineCoverage = faultyLineCoverage; this.seedLineCoverage = seedLineCoverage; this.faultyStatements = faultyStatements; this.seedStatements = seedStatements; this.scope = scope; } @Override public void acceptBinding(String bindingKey, IBinding binding) { } /** * Handles new ASTs that are generated from parsing a list of source * code files. */ @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { /* TODO: Turn on modification logging for the CompilationUnit. We * can use this instead of manually keeping track of * ASTRewrite objects for each CompilationUnit. */ // cu.recordModifications(); /* Create an ASTRewriter to track the this file's mutations. */ AST ast = cu.getAST(); this.sourceFileContents.get(sourceFilePath).rewriter = ASTRewrite.create(ast); /* Store the statements that are covered by test cases. */ StatementASTVisitor statementASTVisitor = new StatementASTVisitor(sourceFilePath); cu.accept(statementASTVisitor); /* Get the member variable names. */ ClassVarASTVisitor cvav = new ClassVarASTVisitor(); cu.accept(cvav); /* Build a HashSet of variable names for each method. This is the scope. */ MethodVarASTVisitor mvav = new MethodVarASTVisitor(sourceFilePath, cvav.variableNames); cu.accept(mvav); } /** * Adds statements to the statement lists if they are in the faulty/seed * statement coverage lists. * @author qhanam * */ private class StatementASTVisitor extends ASTVisitor { String sourceFilePath; public StatementASTVisitor(String sourceFilePath){ this.sourceFilePath = sourceFilePath; } /** * Checks a statement against the coverage lists and inserts valid statements. * * @param s */ private void insertStatement(Statement s){ /* Build the LCNode to check if this statement is in the line coverage. */ CompilationUnit cu = (CompilationUnit) s.getRoot(); String packageName = cu.getPackage() == null ? "" : cu.getPackage().getName().toString(); String className = ((AbstractTypeDeclaration)cu.types().get(0)).getName().toString(); int lineNumber = cu.getLineNumber(s.getStartPosition()); LCNode node = new LCNode(packageName, className, lineNumber); /* Check if this statement has been covered. If so add it to the appropriate statement * list with its weight. */ Double weight; if((weight = MutationASTRequestor.this.faultyLineCoverage.contains(node)) != null){ MutationASTRequestor.this.faultyStatements.addStatement(new SourceStatement(this.sourceFilePath, s), weight); } if((weight = MutationASTRequestor.this.seedLineCoverage.contains(node)) != null){ MutationASTRequestor.this.seedStatements.addStatement(new SourceStatement(this.sourceFilePath, s), weight); } } /** * We need to handle all subtypes of Statement. */ public boolean visit(AssertStatement node){insertStatement(node); return true;} //public boolean visit(Block node){insertStatement(node); return true;} public boolean visit(BreakStatement node){insertStatement(node); return true;} public boolean visit(ConstructorInvocation node){insertStatement(node); return true;} public boolean visit(ContinueStatement node){insertStatement(node); return true;} public boolean visit(DoStatement node){insertStatement(node); return true;} public boolean visit(EmptyStatement node){insertStatement(node); return true;} public boolean visit(ExpressionStatement node){insertStatement(node); return true;} public boolean visit(ForStatement node){insertStatement(node); return true;} public boolean visit(IfStatement node){insertStatement(node); return true;} public boolean visit(LabeledStatement node){insertStatement(node); return true;} public boolean visit(ReturnStatement node){insertStatement(node); return true;} public boolean visit(SuperConstructorInvocation node){insertStatement(node); return true;} public boolean visit(SwitchCase node){insertStatement(node); return true;} public boolean visit(SwitchStatement node){insertStatement(node); return true;} public boolean visit(SynchronizedStatement node){insertStatement(node); return true;} public boolean visit(ThrowStatement node){insertStatement(node); return true;} public boolean visit(TryStatement node){insertStatement(node); return true;} public boolean visit(TypeDeclarationStatement node){insertStatement(node); return true;} public boolean visit(VariableDeclarationStatement node){insertStatement(node); return true;} public boolean visit(WhileStatement node){insertStatement(node); return true;} } /** * Collects the names of member variables used in the method. * @author qhanam */ private class MethodVarASTVisitor extends ASTVisitor{ public HashSet<String> variableNames; public String sourceFilePath; /** * @param memberVariableNames The collection of member variables from the method's class. */ public MethodVarASTVisitor(String sourceFilePath, Stack<String> memberVariableNames){ this.variableNames = new HashSet<String>(memberVariableNames); this.sourceFilePath = sourceFilePath; } /** * Get the names of variables declared in the method. */ public boolean visit(MethodDeclaration md) { md.accept(new ASTVisitor() { public boolean visit(VariableDeclarationFragment var) { // Field declarations, local variable declarations, ForStatement initializers MethodVarASTVisitor.this.variableNames.add(var.getName().toString()); return false; } public boolean visit(SingleVariableDeclaration var) { // Formal parameters and catch statements MethodVarASTVisitor.this.variableNames.add(var.getName().toString()); return false; } }); MutationASTRequestor.this.scope.put(this.sourceFilePath + "." + md.getName().toString(), this.variableNames); return false; } /** * Maybe don't use this yet... * * TODO: Build a specific scope for every class in a compilation unit. * @param md * @return */ @SuppressWarnings("unused") private String getClassPath(MethodDeclaration md){ ASTNode node = md; while(!(node instanceof AbstractTypeDeclaration)){ node = node.getParent(); } AbstractTypeDeclaration atd = (AbstractTypeDeclaration) node; return ((CompilationUnit) atd.getRoot()).getPackage().getName().toString() + "." + atd.getName().toString(); } } /** * Collects the names of member variables used in the class. * @author qhanam */ private class ClassVarASTVisitor extends ASTVisitor{ public Stack<String> variableNames; public ClassVarASTVisitor(){ this.variableNames = new Stack<String>(); } /** * Get the names of member variables declared in this class. */ public boolean visit(VariableDeclarationFragment var) { this.variableNames.add(var.getName().toString()); return false; } /** * We are only getting member variables, so don't visit anything below methods. */ public boolean visit(MethodDeclaration md) { return false; } } }