package fr.inria.diversify.diversification; import fr.inria.diversify.codeFragment.CodeFragment; import fr.inria.diversify.codeFragment.CodeFragmentList; import fr.inria.diversify.codeFragmentProcessor.*; import fr.inria.diversify.coverage.ICoverageReport; import fr.inria.diversify.diversification.accessors.Accessor; import fr.inria.diversify.diversification.accessors.SourceAccesor; import fr.inria.diversify.diversification.accessors.TypeAccesor; import fr.inria.diversify.util.Log; import fr.inria.diversify.util.StringSimilarity; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import spoon.processing.AbstractProcessor; import spoon.processing.ProcessingManager; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtReturn; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.visitor.QueryVisitor; import spoon.reflect.visitor.filter.TypeFilter; import spoon.support.QueueProcessingManager; import java.util.*; //import java.util.stream.Collectors; /** * The InputProgram class encapsulates all the known information of the program being sosiefiecated * <p/> * Created by marcel on 6/06/14. */ public class InputProgram { /** * The preferred generator version indicates the preferred version of the generator to modify this * program */ private String preferredGeneratorVersion = InputConfiguration.LATEST_GENERATOR_VERSION; /** * Code fragment processor to identify interesting fragments for the input program */ private AbstractCodeFragmentProcessor<?> codeFragmentProcessor; /** * Default tolerance value for the code fragment searching algorithm */ private double searchToleranceThreshold = 0.9999999; /** * List of all the code fragments extracted by Spoon of the input program */ protected CodeFragmentList codeFragments; /** * Coverage report for the input program */ private ICoverageReport coverageReport; /** * Path to the root directory of the input program */ private String programDir; /** * Path to the source code of the input program */ private String sourceCodeDir; private String externalSourceCodeDir = ""; /** * Path to the test source code of the input program */ private String testSourceCodeDir; /** * java version of this program */ private int javaVersion; /** * Path to the built classes */ private String classesDir; /** * Path to the coverage information */ private String coverageDir; /** * Path to previous transformations made in this input program */ private String previousTransformationsPath; /** * Number of transformations that we are going to attempt in every run of the diversificator */ private int transformationPerRun; /** * Minimum number of transformations that we are going to attempt in every run of the diversificator */ private int minTransformationsPerRun; /** * Root spoon element for an input program, mostly upper level packages */ private Set<CtElement> roots; /** * List Spoon return statements that can be found in this program */ protected List<CtReturn> returns; protected Map<Class, List<CtElement>> typeToObject = new HashMap<Class, List<CtElement>>(); /** * List of inline constants that can be found in this program */ protected List<CtLocalVariable> inlineConstant; /** * Java assists methods for byte code manipulation */ private List<CtMethod> javassistMethods; /** * Spoon factory to process all AST elements */ private Factory factory; /** * Code fragments separated by class */ protected HashMap<String, CodeFragmentList> codeFragmentsByClass = null; /** * Process all code fragments. Used to early process them. */ public void processCodeFragments() { if (codeFragments == null || codeFragments.size() == 0) { ProcessingManager pm = new QueueProcessingManager(factory); AbstractCodeFragmentProcessor<?> processor = getCodeFragmentProcessor(); pm.addProcessor(processor); pm.process(); codeFragments = processor.getCodeFragments(); codeFragmentsByClass = processor.getCodeFragmentsByClass(); } } /** * Process only the code fragments needed to handle a known set of transformations. * <p/> * This is faster than processing all. Useful for multi-sosies when we start from a known set of single-sosies * instead of searching out of the fragments. * * @param transformations An array of stored transformations. * @deprecated: Set a KnownTransfStatementProcessor as the code fragment processor instead and use processCodeFragments() without parameters */ @Deprecated public void processCodeFragments(JSONArray transformations) throws JSONException { if (codeFragments == null) { ProcessingManager pm = new QueueProcessingManager(factory); KnownTransfStatementProcessor processor = null; processor = new KnownTransfStatementProcessor(transformations); pm.addProcessor(processor); pm.process(); codeFragments = processor.getCodeFragments(); } } /** * List of all the code fragments extracted by Spoon of the input program */ public synchronized CodeFragmentList getCodeFragments() { processCodeFragments(); return codeFragments; } /** * Search for a specific code fragment given its serialized JSON object. * <p/> * Note: The JSON object is the serialized version OF THE CODE FRAGMENT by no means * THE TRANSFORMATION containing a particular code fragment. * * @param serialized Serialized object * @return The code fragment */ @Deprecated public synchronized CodeFragment getCodeFragment(JSONObject serialized) { if (serialized.has("position")) { try { String position = serialized.getString("position"); if (serialized.has("sourceCode")) { return getCodeFragment(position, serialized.getString("sourceCode")); } else if (serialized.has("sourcecode")) { // Super Hack!!! return getCodeFragment(position, serialized.getString("sourcecode").replace("\n", "\r\n")); } else if (serialized.has("type")) { return findCodeFragment(position, serialized.getString("type"), new TypeAccesor()); } else { return null; } } catch (JSONException e) { return null; } } return null; } /** * Search for a code fragment given a search predicate. The method will always search for code locations * in the proximity of the position that meet some features. * * @param position position where the source code was "last seen". * @param searchValue Value to search for * @param accesor Accesor function to the property we are looking for * @return */ public CodeFragment findCodeFragment(String position, String searchValue, Accessor<CodeFragment, String> accesor) { return findCodeFragment(position, searchValue, accesor, 5, 0.85); } public CodeFragment findCodeFragment(String position, String searchValue, Accessor<CodeFragment, String> accesor, int lineThreshold, double valueThreshold) { CodeFragment result = null; String[] s = position.split(":"); position = s[0]; int lineNumber = Integer.parseInt(s[1]); int minDiff = Integer.MAX_VALUE; //int minDiff = lineThreshold; double sDiff = 0; //double sDiff = valueThreshold; int similiarFragmentCount = 0; int similarMinDist = Integer.MAX_VALUE; CodeFragmentList fragments = getCodeFragmentsByClass().get(position); if (fragments != null) { for (CodeFragment codeFragment : fragments) { String[] cfPos = codeFragment.positionString().split(":"); //Analyze only code fragments in the file of the one we are looking for if (cfPos[0].equals(position)) { int cfLine = Integer.parseInt(cfPos[1]); String ctValue = accesor.getValue(codeFragment); if (ctValue.equals(searchValue) && cfLine == lineNumber) { //If it is of the same code and the same line: we found it!! return codeFragment; } else { //Similarity factor (provide flexibility...) double x = StringSimilarity.CompareStrings(ctValue, searchValue); //Line distance int k = Math.abs(cfLine - lineNumber); //Do not analyze this fragment if it is to different or to far if (x < valueThreshold || k > lineThreshold) continue; if (x > sDiff) { similiarFragmentCount = 0;//A better value is found, erase similar count minDiff = k;//Store line distance sDiff = x; result = codeFragment; } else if (Math.abs(x - sDiff) < 0.0000001) { similiarFragmentCount++; //equally good fragment found, augment the amount of fragments int d = Math.abs(cfLine - lineNumber); if (d < minDiff) { similarMinDist = minDiff; //else return the nearest one with same code result = codeFragment; minDiff = d; } } } } } } //Log.info("Search completed of snippet at pos " + position); if (result == null) { Log.error("Unable to find " + searchValue + " at " + position + ":" + lineNumber); } else { position = position + ":" + lineNumber; if (!result.positionString().equals(position)) { Log.warn("Unable to find fragment at " + position); Log.info("Best match at " + result.positionString()); if (sDiff < 1.0 || similiarFragmentCount != 0) { Log.info("Dice: " + sDiff + " Similars: " + similiarFragmentCount + " Similar MinDist: " + similarMinDist); Log.info("Search value: " + searchValue); if (sDiff < 0) Log.info("Value found: " + accesor.getValue(result)); } } } return result; } /** * Returns an specific code fragment given its position and source. The source is optional. * However, you should supply both, since is possible that a code fragment * is not found given only position since a difference of line numbers is usual. * * @param position Position of the code fragment * @param source Source of the code Fragment * @return */ public synchronized CodeFragment getCodeFragment(String position, String source) { return findCodeFragment(position, source, new SourceAccesor()); } /** * Root spoon element for an input program, mostly upper level packages */ public synchronized Set<CtElement> getRoots() { if (roots == null) { roots = new HashSet<>(); ProcessingManager pm = new QueueProcessingManager(factory); AbstractProcessor<CtPackage> processor = new AbstractProcessor<CtPackage>() { @Override public void process(CtPackage element) { CtElement root = element; while (root.getParent() != null && !root.getParent().toString().equals("")) { root = root.getParent(); } roots.add(root); } }; pm.addProcessor(processor); pm.process(); } return roots; } /** * Java assists methods for byte code manipulation */ public synchronized List<CtMethod> getJavassistMethods() { if (javassistMethods == null) { javassistMethods = new ArrayList<>(); ClassPool pool = ClassPool.getDefault(); //pool.insertClassPath(DiversifyProperties.getProperty("project") + "/" + DiversifyProperties.getProperty("classes")); try { pool.insertClassPath(classesDir); } catch (NotFoundException e) { throw new RuntimeException("Cannot find classesDir " + classesDir, e); } for (CtType cl : getCodeFragments().getAllClasses()) { CtClass cc; try { cc = pool.get(cl.getQualifiedName()); } catch (NotFoundException e) { throw new RuntimeException(e); } for (CtMethod method : cc.getDeclaredMethods()) if (!method.isEmpty()) { javassistMethods.add(method); } } } return javassistMethods; } /** * Get the inline constant statements on the program * * @return */ public synchronized List<CtLocalVariable> getInlineConstant() { if (inlineConstant == null) { ProcessingManager pm = new QueueProcessingManager(factory); InlineConstantProcessor processor = new InlineConstantProcessor(); pm.addProcessor(processor); pm.process(); inlineConstant = processor.getInlineConstant(); } return inlineConstant; } public synchronized List<CtElement> getAllElement(Class cl) { if (!typeToObject.containsKey(cl)) { QueryVisitor query = new QueryVisitor(new TypeFilter(cl)); List<CtElement> elements = new ArrayList<>(); for (CtElement e : getRoots()) { e.accept(query); elements.addAll(query.getResult()); } /* getRoots().stream() .flatMap(root -> { root.accept(executeQuery); return executeQuery.getResult().stream(); }) .collect(Collectors.toList()); */ typeToObject.put(cl, elements); } return typeToObject.get(cl); // return null; } /** * Get return statements of the program * * @return */ public synchronized List<CtReturn> getReturns() { if (returns == null) { ProcessingManager pm = new QueueProcessingManager(factory); ReturnProcessor processor = new ReturnProcessor(); pm.addProcessor(processor); pm.process(); returns = processor.getReturns(); } return returns; } /** * Copies properties from the configuration * * @param configuration */ public void configure(InputConfiguration configuration) { setRelativeSourceCodeDir(configuration.getRelativeSourceCodeDir()); setProgramDir(configuration.getProjectPath()); setRelativeSourceCodeDir(configuration.getRelativeSourceCodeDir()); setPreviousTransformationsPath(configuration.getPreviousTransformationPath()); setClassesDir(configuration.getClassesDir()); setCoverageDir(configuration.getCoverageDir()); } public AbstractCodeFragmentProcessor<?> getCodeFragmentProcessor() { //Convention over configuration if (codeFragmentProcessor == null) codeFragmentProcessor = new StatementProcessor(externalSourceCodeDir); return codeFragmentProcessor; } public void setCodeFragmentProcessor(AbstractCodeFragmentProcessor<?> codeFragmentProcessor) { this.codeFragmentProcessor = codeFragmentProcessor; } /** * Spoon factory to process all AST elements */ public Factory getFactory() { return factory; } public void setFactory(Factory factory) { this.factory = factory; } /** * Coverage report for the input program */ public ICoverageReport getCoverageReport() { return coverageReport; } /** * Coverage report for the input program */ public void setCoverageReport(ICoverageReport coverageReport) { this.coverageReport = coverageReport; } /** * Path to the test source code of the input program */ public String getRelativeTestSourceCodeDir() { return testSourceCodeDir; } /** * Path to the test source code of the input program */ public String getAbsoluteTestSourceCodeDir() { return programDir + "/" + testSourceCodeDir; } public void setRelativeTestSourceCodeDir(String testSourceCodeDir) { this.testSourceCodeDir = testSourceCodeDir; } /** * Path to the source of the input program */ public String getAbsoluteSourceCodeDir() { return programDir + "/" + sourceCodeDir; } /** * Path to the source of the input program */ public String getRelativeSourceCodeDir() { return sourceCodeDir; } public void setRelativeSourceCodeDir(String sourceCodeDir) { this.sourceCodeDir = sourceCodeDir; } public void setExternalSourceCodeDir(String externalSourceCodeDir) { this.externalSourceCodeDir = externalSourceCodeDir; } public String getExternalSourceCodeDir() { return externalSourceCodeDir; } /** * Path to the know sosie information stored in file */ public String getPreviousTransformationsPath() { return previousTransformationsPath; } public void setPreviousTransformationsPath(String path) { this.previousTransformationsPath = path; } /** * Number of transformations that we are going to attempt in every run of the diversificator */ public int getTransformationPerRun() { return transformationPerRun; } public void setTransformationPerRun(int transformationPerRun) { this.transformationPerRun = transformationPerRun; } /** * Path to the root directory of the input program */ public String getProgramDir() { return programDir; } public void setProgramDir(String programDir) { this.programDir = programDir; } public void setJavaVersion(int javaVersion) { this.javaVersion = javaVersion; } public int getJavaVersion() { return javaVersion; } /** * Path to the built classes */ public String getClassesDir() { return classesDir; } public void setClassesDir(String classesDir) { this.classesDir = classesDir; } /** * Path to the coverage information */ public String getCoverageDir() { return coverageDir; } public void setCoverageDir(String coverageDir) { this.coverageDir = coverageDir; } /** * Minimum number of transformations that we are going to attempt in every run of the diversificator */ public int getMinTransformationsPerRun() { return minTransformationsPerRun; } public void setMinTransformationsPerRun(int minTransformationsPerRun) { this.minTransformationsPerRun = minTransformationsPerRun; } public String getPreferredGeneratorVersion() { return preferredGeneratorVersion; } public void setPreferredGeneratorVersion(String preferredGeneratorVersion) { this.preferredGeneratorVersion = preferredGeneratorVersion; } public HashMap<String, CodeFragmentList> getCodeFragmentsByClass() { if (codeFragmentsByClass == null) processCodeFragments(); return codeFragmentsByClass; } public InputProgram clone() { InputProgram clone = new InputProgram(); clone.programDir = programDir; clone.sourceCodeDir = sourceCodeDir; clone.externalSourceCodeDir = externalSourceCodeDir; clone.testSourceCodeDir = testSourceCodeDir; clone.javaVersion = javaVersion; clone.coverageDir = coverageDir; clone.classesDir = classesDir; return clone; } }