package ca.uwaterloo.ece.qhanam.jrsrepair.context; import java.util.HashMap; import java.util.HashSet; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.FileASTRequestor; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import ca.uwaterloo.ece.qhanam.jrsrepair.DocumentASTRewrite; import ca.uwaterloo.ece.qhanam.jrsrepair.LineCoverage; import ca.uwaterloo.ece.qhanam.jrsrepair.MutationASTRequestor; import ca.uwaterloo.ece.qhanam.jrsrepair.Statements; /** * ParserContext stores the context for and provides parsing operations. * * @author qhanam */ public class ParserContext { private ASTParser parser; private HashMap<String, HashSet<String>> scope; private HashMap<String, DocumentASTRewrite> sourceFileContents; private String[] classpaths; private String[] sourcepaths; private String[] sourceFilesArray; private LineCoverage faultyLineCoverage; private LineCoverage seedLineCoverage; private Statements faultyStatements; private Statements seedStatements; public ParserContext(HashMap<String, HashSet<String>> scope, HashMap<String, DocumentASTRewrite> sourceFileContents, String[] classpaths, String[] sourcepaths, String[] sourceFilesArray, LineCoverage faultyLineCoverage, LineCoverage seedLineCoverage, Statements faultyStatements, Statements seedStatements){ this.parser = ASTParser.newParser(AST.JLS8); this.scope = scope; this.sourceFileContents = sourceFileContents; this.classpaths = classpaths; this.sourcepaths = sourcepaths; this.sourceFilesArray = sourceFilesArray; this.faultyLineCoverage = faultyLineCoverage; this.seedLineCoverage = seedLineCoverage; this.faultyStatements = faultyStatements; this.seedStatements = seedStatements; } /** * Create the ASTParser with the source files to generate ASTs for, and set up the * environment using ASTParser.setEnvironment. * * NOTE: This MUST be called before anything else in JRSRepair. */ public void buildASTs() throws Exception{ /* setEnvironment( * String[] classpathEntries, * String[] sourcepathEntries, * String[] encodings, * boolean includeRunningVMBootclasspath) */ parser.setEnvironment(this.classpaths, this.sourcepaths, null, true); parser.setResolveBindings(true); /* Set up the AST handler. We need to create LineCoverage and Statements classes to store * and filter the statements from the ASTs. */ FileASTRequestor fileASTRequestor = new MutationASTRequestor(sourceFileContents, scope, faultyLineCoverage, seedLineCoverage, faultyStatements, seedStatements); /* createASTs( * String[] sourceFilePaths, * String[] encodings, - the source file encodings (e.g., "ASCII", "UTF8", "UTF-16"). Can be set to null if platform encoding is sufficient. * String[] bindingKeys, * FileASTRequestor requestor, * IProgressMonitor monitor) */ parser.createASTs(sourceFilesArray, null, new String[] {}, fileASTRequestor, null); } /** * Checks that the AST produced by the mutation has all variables in-scope. * * To do this, we create a new AST by parsing the mutated source and compute * bindings. We then look to see if there are any bindings missing. If bindings * are missing, there is something not in-scope and the program will not compile. * * @param rewriter The AST rewriter that contains the mutated document. */ public boolean checkScope(DocumentASTRewrite rewriter) { String source = rewriter.modifiedDocument.get(); this.parser.setSource(source.toCharArray()); this.parser.setEnvironment(this.classpaths, this.sourcepaths, null, true); this.parser.setResolveBindings(true); this.parser.setUnitName(rewriter.backingFile.getName()); ASTNode node = this.parser.createAST(null); ScopeASTVisitor scopeASTVisitor = new ScopeASTVisitor(); node.accept(scopeASTVisitor); if(!scopeASTVisitor.inScope){ System.out.println(" - Some variables are out of scope."); return false; } return true; } /** * Checks that each variable, field, method and type has a binding. If * one of these does not have a binding, we assume it is out of scope. * This is used for checking to make sure mutated ASTs will be compilable. * @author qhanam */ private class ScopeASTVisitor extends ASTVisitor{ public boolean inScope; public ScopeASTVisitor(){ this.inScope = true; } /** * Check that each QualifiedName has a binding. This * will ensure that the fully qualified name gets * checked instead of it's parts. */ public boolean visit(QualifiedName qn){ if(!this.inScope) return false; // No point in checking if we're already out of scope if(qn.resolveBinding() == null) { this.inScope = false; } return false; } /** * Check that each SimpleName ASTNode has a binding. These * will be variables, fields, methods and types. */ public boolean visit(SimpleName s) { if(!this.inScope) return false; // No point in checking if we're already out of scope if(s.resolveBinding() == null) { this.inScope = false; } return false; } } }