All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.graphs.cfg.CFGMethodAdapter Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2010-2018 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 .
 */
package org.evosuite.graphs.cfg;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.evosuite.Properties;
import org.evosuite.Properties.Criterion;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.runtime.annotation.EvoSuiteExclude;
import org.evosuite.runtime.instrumentation.AnnotatedMethodNode;
import org.evosuite.instrumentation.coverage.BranchInstrumentation;
import org.evosuite.instrumentation.coverage.DefUseInstrumentation;
import org.evosuite.instrumentation.coverage.MethodInstrumentation;
import org.evosuite.instrumentation.coverage.MutationInstrumentation;
import org.evosuite.runtime.classhandling.ClassResetter;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.utils.ArrayUtil;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Create a minimized control flow graph for the method and store it. In
 * addition, this adapter also adds instrumentation for branch distance
 * measurement
 * 
 * defUse, concurrency and LCSAJs instrumentation is also added (if the
 * properties are set).
 * 
 * @author Gordon Fraser
 */
public class CFGMethodAdapter extends MethodVisitor {

	private static final Logger logger = LoggerFactory.getLogger(CFGMethodAdapter.class);

	/**
	 * A list of Strings representing method signatures. Methods matching those
	 * signatures are not instrumented and no CFG is generated for them. Except
	 * if some MethodInstrumentation requests it.
	 */
	public static final List EXCLUDE = Arrays.asList("()V",
																ClassResetter.STATIC_RESET+"()V",
																ClassResetter.STATIC_RESET);
	/**
	 * The set of all methods which can be used during test case generation This
	 * excludes e.g. synthetic, initializers, private and deprecated methods
	 */
	public static Map>> methods = new HashMap>>();

	/**
	 * This is the name + the description of the method. It is more like the
	 * signature and less like the name. The name of the method can be found in
	 * this.plain_name
	 */
	private final String methodName;

	private final MethodVisitor next;
	private final String plain_name;
	private final int access;
	private final String className;
	private final ClassLoader classLoader;

	private int lineNumber = 0;
	
	/** Can be set by annotation */
	private boolean excludeMethod = false;

	/**
	 * 

* Constructor for CFGMethodAdapter. *

* * @param className * a {@link java.lang.String} object. * @param access * a int. * @param name * a {@link java.lang.String} object. * @param desc * a {@link java.lang.String} object. * @param signature * a {@link java.lang.String} object. * @param exceptions * an array of {@link java.lang.String} objects. * @param mv * a {@link org.objectweb.asm.MethodVisitor} object. */ public CFGMethodAdapter(ClassLoader classLoader, String className, int access, String name, String desc, String signature, String[] exceptions, MethodVisitor mv) { // super(new MethodNode(access, name, desc, signature, exceptions), // className, // name.replace('/', '.'), null, desc); super(Opcodes.ASM5, new AnnotatedMethodNode(access, name, desc, signature, exceptions)); this.next = mv; this.className = className; // .replace('/', '.'); this.access = access; this.methodName = name + desc; this.plain_name = name; this.classLoader = classLoader; if(!methods.containsKey(classLoader)) methods.put(classLoader, new HashMap>()); } /* (non-Javadoc) * @see org.objectweb.asm.MethodVisitor#visitLineNumber(int, org.objectweb.asm.Label) */ @Override public void visitLineNumber(int line, Label start) { lineNumber = line; super.visitLineNumber(line, start); } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if(Type.getDescriptor(EvoSuiteExclude.class).equals(desc)) { logger.info("Method has EvoSuite annotation: "+desc); excludeMethod = true; } return super.visitAnnotation(desc, visible); } /** {@inheritDoc} */ @Override public void visitEnd() { logger.debug("Creating CFG of "+className+"."+methodName); boolean isExcludedMethod = excludeMethod || EXCLUDE.contains(methodName); boolean isMainMethod = plain_name.equals("main") && Modifier.isStatic(access); List instrumentations = new ArrayList(); if (DependencyAnalysis.shouldInstrument(className, methodName)) { if (ArrayUtil.contains(Properties.CRITERION, Criterion.DEFUSE) || ArrayUtil.contains(Properties.CRITERION, Criterion.ALLDEFS)) { instrumentations.add(new BranchInstrumentation()); instrumentations.add(new DefUseInstrumentation()); } else if (ArrayUtil.contains(Properties.CRITERION, Criterion.MUTATION) || ArrayUtil.contains(Properties.CRITERION, Criterion.WEAKMUTATION) || ArrayUtil.contains(Properties.CRITERION, Criterion.ONLYMUTATION) || ArrayUtil.contains(Properties.CRITERION, Criterion.STRONGMUTATION)) { instrumentations.add(new BranchInstrumentation()); instrumentations.add(new MutationInstrumentation()); } else { instrumentations.add(new BranchInstrumentation()); } } else { //instrumentations.add(new BranchInstrumentation()); } boolean executeOnMain = false; boolean executeOnExcluded = false; for (MethodInstrumentation instrumentation : instrumentations) { executeOnMain = executeOnMain || instrumentation.executeOnMainMethod(); executeOnExcluded = executeOnExcluded || instrumentation.executeOnExcludedMethods(); } // super.visitEnd(); // Generate CFG of method MethodNode mn = (AnnotatedMethodNode) mv; boolean checkForMain = false; if (Properties.CONSIDER_MAIN_METHODS) { checkForMain = true; } else { checkForMain = !isMainMethod || executeOnMain; } // Only instrument if the method is (not main and not excluded) or (the // MethodInstrumentation wants it anyway) if (checkForMain && (!isExcludedMethod || executeOnExcluded) && (access & Opcodes.ACC_ABSTRACT) == 0 && (access & Opcodes.ACC_NATIVE) == 0) { logger.info("Analyzing method " + methodName + " in class " + className); // MethodNode mn = new CFGMethodNode((MethodNode)mv); // System.out.println("Generating CFG for "+ className+"."+mn.name + // " ("+mn.desc +")"); BytecodeAnalyzer bytecodeAnalyzer = new BytecodeAnalyzer(); logger.info("Generating CFG for method " + methodName); try { bytecodeAnalyzer.analyze(classLoader, className, methodName, mn); logger.trace("Method graph for " + className + "." + methodName + " contains " + bytecodeAnalyzer.retrieveCFGGenerator().getRawGraph().vertexSet().size() + " nodes for " + bytecodeAnalyzer.getFrames().length + " instructions"); // compute Raw and ActualCFG and put both into GraphPool bytecodeAnalyzer.retrieveCFGGenerator().registerCFGs(); logger.info("Created CFG for method " + methodName); if (DependencyAnalysis.shouldInstrument(className, methodName)) { if (!methods.get(classLoader).containsKey(className)) methods.get(classLoader).put(className, new HashSet()); // add the actual instrumentation logger.info("Instrumenting method " + methodName + " in class " + className); for (MethodInstrumentation instrumentation : instrumentations) instrumentation.analyze(classLoader, mn, className, methodName, access); handleBranchlessMethods(); String id = className + "." + methodName; if (isUsable()) { methods.get(classLoader).get(className).add(id); logger.debug("Counting: " + id); } } } catch (AnalyzerException e) { logger.error("Analyzer exception while analyzing " + className + "." + methodName + ": " + e); e.printStackTrace(); } } else { logger.debug("NOT Creating CFG of "+className+"."+methodName+": "+checkForMain+", "+((!isExcludedMethod || executeOnExcluded)) +", "+((access & Opcodes.ACC_ABSTRACT) == 0)+", "+((access & Opcodes.ACC_NATIVE) == 0)); super.visitEnd(); } mn.accept(next); } /* * (non-Javadoc) * * @see org.objectweb.asm.commons.LocalVariablesSorter#visitMaxs(int, int) */ /** {@inheritDoc} */ @Override public void visitMaxs(int maxStack, int maxLocals) { int maxNum = 7; super.visitMaxs(Math.max(maxNum, maxStack), maxLocals); } private void handleBranchlessMethods() { String id = className + "." + methodName; if (BranchPool.getInstance(classLoader).getNonArtificialBranchCountForMethod(className, methodName) == 0) { if (isUsable()) { logger.debug("Method has no branches: " + id); BranchPool.getInstance(classLoader).addBranchlessMethod(className, id, lineNumber); } } } /** * See description of CFGMethodAdapter.EXCLUDE * * @return */ private boolean isUsable() { if((this.access & Opcodes.ACC_SYNTHETIC) != 0) return false; if((this.access & Opcodes.ACC_BRIDGE) != 0) return false; if((this.access & Opcodes.ACC_NATIVE) != 0) return false; if(methodName.contains("")) return false; // If we are not using reflection, covering private constructors is difficult? if(Properties.P_REFLECTION_ON_PRIVATE <= 0.0) { if(methodName.contains("") && (access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE) return false; } return true; } public Set getMethods(String className) { return getMethods(classLoader, className); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. * @param className * a {@link java.lang.String} object. */ public static Set getMethods(ClassLoader classLoader, String className) { Set targetMethods = new HashSet(); if(!methods.containsKey(classLoader)) return targetMethods; for (String currentClass : methods.get(classLoader).keySet()) { if (currentClass.equals(className) || currentClass.startsWith(className + "$")) targetMethods.addAll(methods.get(classLoader).get(currentClass)); } return targetMethods; } public Set getMethods() { return getMethods(classLoader); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. */ public static Set getMethods(ClassLoader classLoader) { Set targetMethods = new HashSet(); if(!methods.containsKey(classLoader)) return targetMethods; for (String currentClass : methods.get(classLoader).keySet()) { targetMethods.addAll(methods.get(classLoader).get(currentClass)); } return targetMethods; } public Set getMethodsPrefix(String className) { return getMethodsPrefix(classLoader, className); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. * @param className * a {@link java.lang.String} object. */ public static Set getMethodsPrefix(ClassLoader classLoader, String className) { Set matchingMethods = new HashSet(); if(!methods.containsKey(classLoader)) return matchingMethods; for (String name : methods.get(classLoader).keySet()) { if (name.startsWith(className)) { matchingMethods.addAll(methods.get(classLoader).get(name)); } } return matchingMethods; } public int getNumMethodsPrefix(String className) { return getNumMethodsPrefix(classLoader, className); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. * @param className * a {@link java.lang.String} object. */ public static int getNumMethodsPrefix(ClassLoader classLoader, String className) { int num = 0; if(!methods.containsKey(classLoader)) return num; for (String name : methods.get(classLoader).keySet()) { if (name.startsWith(className)) { num += methods.get(classLoader).get(name).size(); } } return num; } public int getNumMethods() { return getNumMethods(classLoader); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. */ public static int getNumMethods(ClassLoader classLoader) { int num = 0; if(!methods.containsKey(classLoader)) return num; for (String name : methods.get(classLoader).keySet()) { num += methods.get(classLoader).get(name).size(); } return num; } public int getNumMethodsMemberClasses(String className) { return getNumMethodsMemberClasses(classLoader, className); } /** * Returns a set with all unique methodNames of methods. * * @return A set with all unique methodNames of methods. * @param className * a {@link java.lang.String} object. */ public static int getNumMethodsMemberClasses(ClassLoader classLoader, String className) { int num = 0; if(!methods.containsKey(classLoader)) return num; for (String name : methods.get(classLoader).keySet()) { if (name.equals(className) || name.startsWith(className + "$")) { num += methods.get(classLoader).get(name).size(); } } return num; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy