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

org.evosuite.coverage.branch.BranchPool Maven / Gradle / Ivy

/**
 * 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.coverage.branch;

import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.evosuite.setup.DependencyAnalysis;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

// TODO: root branches should not be special cases
// every root branch should be a branch just
// like every other branch with it's own branchId and all

/**
 * This class is supposed to hold all the available information concerning
 * Branches.
 * 
 * The addBranch()-Method gets called during class analysis. Whenever the
 * BytecodeInstructionPool detects a BytecodeInstruction that corresponds to a
 * Branch in the class under test as defined in
 * BytecodeInstruction.isActualBranch() it calls the registerAsBranch() method
 * of this class which in turn properly registers the instruction within this
 * pool.
 * 
 * There are two kinds of Branch objects: normal branches and switch case
 * branches. For more details about the difference between these two look at the
 * Branch class.
 * 
 * @author Andre Mis
 */
public class BranchPool {

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

	// maps className -> method inside that class -> list of branches inside
	// that method
	private Map>> branchMap = new HashMap>>();

	// set of all known methods without a Branch
	private Map> branchlessMethods = new HashMap>();

	// maps the branchIDs assigned by this pool to their respective Branches
	private Map branchIdMap = new HashMap();

	// maps all known branch instructions to their branchId
	private Map registeredNormalBranches = new HashMap();

	// maps all known switch instructions to a list containing all of their
	// associated Branch objects
	private Map> registeredSwitches = new HashMap>();

	private Map registeredDefaultCases = new HashMap();

	private Map> switchLabels = new HashMap>();

	// number of known Branches - used for actualBranchIds
	private int branchCounter = 0;

	private static Map instanceMap = new HashMap();

	public static BranchPool getInstance(ClassLoader classLoader) {
		if (!instanceMap.containsKey(classLoader)) {
			instanceMap.put(classLoader, new BranchPool());
		}

		return instanceMap.get(classLoader);
	}
	// fill the pool

	/**
	 * Gets called by the CFGMethodAdapter whenever it detects a method without
	 * any branches.
	 * 
	 * @param methodName
	 *            Unique methodName - consisting of . -
	 *            of a method without Branches
	 * @param className
	 *            a {@link java.lang.String} object.
	 */
	public void addBranchlessMethod(String className, String methodName,
	        int lineNumber) {
		if (!branchlessMethods.containsKey(className))
			branchlessMethods.put(className, new HashMap());
		branchlessMethods.get(className).put(methodName, lineNumber);
	}

	/**
	 * Called by the BytecodeInstructionPool whenever it detects an instruction
	 * that corresponds to a Branch in the class under test as defined by
	 * BytecodeInstruction.isActualBranch().
	 * 
	 * @param instruction
	 *            a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
	 */
	public void registerAsBranch(BytecodeInstruction instruction) {
		if (!(instruction.isActualBranch()))
			throw new IllegalArgumentException("CFGVertex of a branch expected");
		if (isKnownAsBranch(instruction))
			return;
		if (!DependencyAnalysis.shouldInstrument(instruction.getClassName(),
		                                         instruction.getMethodName())) {
			return;
		} 
		
		registerInstruction(instruction);

	}

	private void registerInstruction(BytecodeInstruction v) {
		if (isKnownAsBranch(v))
			throw new IllegalStateException(
			        "expect registerInstruction() to be called at most once for each instruction");

		if (v.isBranch())
			registerNormalBranchInstruction(v);
		else if (v.isSwitch())
			registerSwitchInstruction(v);
		else
			throw new IllegalArgumentException(
			        "expect given instruction to be an actual branch");
	}

	private void registerNormalBranchInstruction(BytecodeInstruction v) {
		if (!v.isBranch())
			throw new IllegalArgumentException("normal branch instruction expceted");

		if (registeredNormalBranches.containsKey(v))
			throw new IllegalArgumentException(
			        "instruction already registered as a normal branch");

		branchCounter++;
		registeredNormalBranches.put(v, branchCounter);

		Branch b = new Branch(v, branchCounter);
		addBranchToMap(b);
		branchIdMap.put(branchCounter, b);

		logger.info("Branch " + branchCounter + " at line " + v.getLineNumber());
	}

	private void registerSwitchInstruction(BytecodeInstruction v) {
		if (!v.isSwitch())
			throw new IllegalArgumentException("expect a switch instruction");

		LabelNode defaultLabel = null;

		switch (v.getASMNode().getOpcode()) {
		case Opcodes.TABLESWITCH:
			TableSwitchInsnNode tableSwitchNode = (TableSwitchInsnNode) v.getASMNode();
			registerTableSwitchCases(v, tableSwitchNode);
			defaultLabel = tableSwitchNode.dflt;

			break;
		case Opcodes.LOOKUPSWITCH:
			LookupSwitchInsnNode lookupSwitchNode = (LookupSwitchInsnNode) v.getASMNode();
			registerLookupSwitchCases(v, lookupSwitchNode);
			defaultLabel = lookupSwitchNode.dflt;
			break;
		default:
			throw new IllegalStateException(
			        "expect ASMNode of a switch to either be a LOOKUP- or TABLESWITCH");
		}

		registerDefaultCase(v, defaultLabel);
	}

	private void registerDefaultCase(BytecodeInstruction v, LabelNode defaultLabel) {

		if (defaultLabel == null)
			throw new IllegalStateException("expect variable to bet set");

		Branch defaultBranch = createSwitchCaseBranch(v, null, defaultLabel);
		if (!defaultBranch.isSwitchCaseBranch() || !defaultBranch.isDefaultCase())
			throw new IllegalStateException(
			        "expect created branch to be a default case branch of a switch");
	}

	private void registerTableSwitchCases(BytecodeInstruction v,
	        TableSwitchInsnNode tableSwitchNode) {

		int num = 0;

		for (int i = tableSwitchNode.min; i <= tableSwitchNode.max; i++) {
			LabelNode targetLabel = (LabelNode) tableSwitchNode.labels.get(num);
			Branch switchBranch = createSwitchCaseBranch(v, i, targetLabel);
			if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase())
				throw new IllegalStateException(
				        "expect created branch to be an actual case branch of a switch");
			num++;
		}
	}

	private void registerLookupSwitchCases(BytecodeInstruction v,
	        LookupSwitchInsnNode lookupSwitchNode) {

		for (int i = 0; i < lookupSwitchNode.keys.size(); i++) {
			LabelNode targetLabel = (LabelNode) lookupSwitchNode.labels.get(i);
			Branch switchBranch = createSwitchCaseBranch(v,
			                                             (Integer) lookupSwitchNode.keys.get(i),
			                                             targetLabel);
			if (!switchBranch.isSwitchCaseBranch() || !switchBranch.isActualCase())
				throw new IllegalStateException(
				        "expect created branch to be an actual case branch of a switch");
		}
	}

	private Branch createSwitchCaseBranch(BytecodeInstruction v,
	        Integer caseValue, LabelNode targetLabel) {

		branchCounter++;

		Branch switchBranch = new Branch(v, caseValue, targetLabel, branchCounter);
		registerSwitchBranch(v, switchBranch);
		addBranchToMap(switchBranch);
		branchIdMap.put(branchCounter, switchBranch);

		registerSwitchLabel(switchBranch, targetLabel);

		// default case
		if (caseValue == null) {
			if (registeredDefaultCases.containsKey(v))
				throw new IllegalStateException(
				        "instruction already registered as a branch");
			registeredDefaultCases.put(v, switchBranch);
		}

		if (!switchBranch.isSwitchCaseBranch())
			throw new IllegalStateException("expect created Branch to be a switch branch");

		return switchBranch;
	}

	private void registerSwitchLabel(Branch b, LabelNode targetLabel) {

		if (switchLabels.get(targetLabel) == null)
			switchLabels.put(targetLabel, new ArrayList());

		List oldList = switchLabels.get(targetLabel);

		if (oldList.contains(b))
			throw new IllegalStateException(
			        "branch already registered for this switch label");

		oldList.add(b);

		// TODO several Branches can map to one Label, so switchLabels should
		// either map from branches to labels, not the other way around. or it
		// should map labels to a list of branches
		// this stems from the fact that empty case: blocks do not have their
		// own label

		// TODO STOPPED HERE

		switchLabels.put(targetLabel, oldList);
	}

	private void registerSwitchBranch(BytecodeInstruction v, Branch switchBranch) {
		if (!v.isSwitch())
			throw new IllegalArgumentException("switch instruction expected");

		if (registeredSwitches.get(v) == null)
			registeredSwitches.put(v, new ArrayList());

		List oldList = registeredSwitches.get(v);

		if (oldList.contains(switchBranch))
			throw new IllegalArgumentException("switch branch already registered  "
			        + switchBranch.toString());

		oldList.add(switchBranch);

		registeredSwitches.put(v, oldList);
	}

	private void addBranchToMap(Branch b) {

		logger.info("Adding to map the branch {}", b);

		String className = b.getClassName();
		String methodName = b.getMethodName();

		if (!branchMap.containsKey(className))
			branchMap.put(className, new HashMap>());
		if (!branchMap.get(className).containsKey(methodName))
			branchMap.get(className).put(methodName, new ArrayList());
		branchMap.get(className).get(methodName).add(b);
	}

	// retrieve information from the pool

	/**
	 * Checks whether the given instruction has Branch objects associated with
	 * it.
	 * 
	 * Returns true if the given BytecodeInstruction previously passed a call to
	 * registerAsBranch(instruction), false otherwise
	 * 
	 * @param instruction
	 *            a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
	 * @return a boolean.
	 */
	public boolean isKnownAsBranch(BytecodeInstruction instruction) {
		return isKnownAsNormalBranchInstruction(instruction)
		        || isKnownAsSwitchBranchInstruction(instruction);
	}

	/**
	 * 

* isKnownAsNormalBranchInstruction *

* * @param ins * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean isKnownAsNormalBranchInstruction(BytecodeInstruction ins) { return registeredNormalBranches.containsKey(ins); } /** *

* isKnownAsSwitchBranchInstruction *

* * @param instruction * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a boolean. */ public boolean isKnownAsSwitchBranchInstruction(BytecodeInstruction instruction) { return registeredSwitches.containsKey(instruction); } /** *

* getActualBranchIdForNormalBranchInstruction *

* * @param ins * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a int. */ public int getActualBranchIdForNormalBranchInstruction(BytecodeInstruction ins) { if (!isKnownAsNormalBranchInstruction(ins)) throw new IllegalArgumentException( "instruction not registered as a normal branch"); if (registeredNormalBranches.containsKey(ins)) return registeredNormalBranches.get(ins); throw new IllegalStateException( "expect registeredNormalBranches to contain a key for each known normal branch instruction"); } /** *

* getCaseBranchesForSwitch *

* * @param instruction * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a {@link java.util.List} object. */ public List getCaseBranchesForSwitch(BytecodeInstruction instruction) { if (instruction == null) throw new IllegalArgumentException("null given"); if (!instruction.isSwitch()) throw new IllegalArgumentException("switch instruction expected"); if (!isKnownAsSwitchBranchInstruction(instruction)) throw new IllegalArgumentException("not registered as a switch instruction"); return registeredSwitches.get(instruction); } /** *

* getBranchForInstruction *

* * @param instruction * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a {@link org.evosuite.coverage.branch.Branch} object. */ public Branch getBranchForInstruction(BytecodeInstruction instruction) { if (instruction == null) throw new IllegalArgumentException("null given"); if (!isKnownAsNormalBranchInstruction(instruction)) throw new IllegalArgumentException( "expect given instruction to be known as a normal branch"); return getBranch(registeredNormalBranches.get(instruction)); } /** *

* getBranchForLabel *

* * @param label * a {@link org.objectweb.asm.tree.LabelNode} object. * @return a {@link java.util.List} object. */ public List getBranchForLabel(LabelNode label) { // TODO see registerSwitchLabel()! return switchLabels.get(label); } /** * Returns the number of known Branches for a given methodName in a given * class. * * @return The number of currently known Branches inside the given method * @param className * a {@link java.lang.String} object. * @param methodName * a {@link java.lang.String} object. */ public int getBranchCountForMethod(String className, String methodName) { if (branchMap.get(className) == null) return 0; if (branchMap.get(className).get(methodName) == null) return 0; return branchMap.get(className).get(methodName).size(); } public int getNonArtificialBranchCountForMethod(String className, String methodName) { if (branchMap.get(className) == null) return 0; if (branchMap.get(className).get(methodName) == null) return 0; int num = 0; for (Branch b : branchMap.get(className).get(methodName)) { if (!b.isInstrumented()) num++; } return num; } /** * Returns the number of known Branches for a given class * * @return The number of currently known Branches inside the given class * @param className * a {@link java.lang.String} object. */ public int getBranchCountForClass(String className) { if (branchMap.get(className) == null) return 0; int total = 0; for (String method : branchMap.get(className).keySet()) { total += branchMap.get(className).get(method).size(); } return total; } /** * Returns the number of known Branches for a given class * * @return The number of currently known Branches inside the given class * @param prefix * a {@link java.lang.String} object. */ public int getBranchCountForPrefix(String prefix) { int num = 0; for (String className : branchMap.keySet()) { if (className.startsWith(prefix)) { logger.info("Found matching class for branch count: " + className + "/" + prefix); for (String method : branchMap.get(className).keySet()) { num += branchMap.get(className).get(method).size(); } } } return num; } /** * Returns the number of known Branches for a given class * * @return The number of currently known Branches inside the given class * @param prefix * a {@link java.lang.String} object. */ public Set getBranchIdsForPrefix(String prefix) { Set ids = new HashSet<>(); Set sutBranches = new HashSet<>(); for (String className : branchMap.keySet()) { if (className.startsWith(prefix)) { logger.info("Found matching class for branch ids: " + className + "/" + prefix); for (String method : branchMap.get(className).keySet()) { sutBranches.addAll(branchMap.get(className).get(method)); } } } for (Integer id : branchIdMap.keySet()) { if(sutBranches.contains(branchIdMap.get(id))){ ids.add(id); } } return ids; } /** * Returns the number of known Branches for a given class * * @return The number of currently known Branches inside the given class * @param prefix * a {@link java.lang.String} object. */ public int getBranchCountForMemberClasses(String prefix) { int num = 0; for (String className : branchMap.keySet()) { if (className.equals(prefix) || className.startsWith(prefix + "$")) { logger.info("Found matching class for branch count: " + className + "/" + prefix); for (String method : branchMap.get(className).keySet()) { num += branchMap.get(className).get(method).size(); } } } return num; } /** * Returns the number of currently known Branches * * @return The number of currently known Branches */ public int getBranchCounter() { return branchCounter; } public int getNumArtificialBranches() { int num = 0; for (Branch b : branchIdMap.values()) { if (b.isInstrumented()) num++; } return num; } /** * Returns the Branch object associated with the given branchID * * @param branchId * The ID of a branch * @return The branch, or null if it does not exist */ public Branch getBranch(int branchId) { return branchIdMap.get(branchId); } public Collection getAllBranches() { return branchIdMap.values(); } /** * Returns a set with all unique methodNames of methods without Branches. * * @return A set with all unique methodNames of methods without Branches. * @param className * a {@link java.lang.String} object. */ public Set getBranchlessMethods(String className) { if (!branchlessMethods.containsKey(className)) return new HashSet(); return branchlessMethods.get(className).keySet(); } /** * Returns a set with all unique methodNames of methods without Branches. * * @return A set with all unique methodNames of methods without Branches. * @param className * a {@link java.lang.String} object. */ public Set getBranchlessMethodsPrefix(String className) { Set methods = new HashSet(); for (String name : branchlessMethods.keySet()) { if (name.equals(className) || name.startsWith(className + "$")) { methods.addAll(branchlessMethods.get(name).keySet()); } } return methods; } /** * Returns a set with all unique methodNames of methods without Branches. * * @return A set with all unique methodNames of methods without Branches. * @param className * a {@link java.lang.String} object. */ public Set getBranchlessMethodsMemberClasses(String className) { Set methods = new HashSet(); for (String name : branchlessMethods.keySet()) { if (name.equals(className) || name.startsWith(className + "$")) { methods.addAll(branchlessMethods.get(name).keySet()); } } return methods; } public int getBranchlessMethodLineNumber(String className, String methodName) { // check if the given method is branchless if (branchlessMethods.get(className) != null && branchlessMethods.get(className).get(className + "." + methodName) != null) { return branchlessMethods.get(className).get(className + "." + methodName); } // otherwise consult the branchMap and return the lineNumber of the earliest Branch return branchlessMethods.get(className).get(className + "." + methodName); } /** * Returns a set with all unique methodNames of methods without Branches. * * @return A set with all unique methodNames of methods without Branches. */ public Set getBranchlessMethods() { Set methods = new HashSet(); for (String name : branchlessMethods.keySet()) { methods.addAll(branchlessMethods.get(name).keySet()); } return methods; } public boolean isBranchlessMethod(String className, String methodName) { Map methodMap = branchlessMethods.get(className); if(methodMap != null) { return methodMap.containsKey(methodName); } return false; } /** * Returns the number of methods without Branches for class className * * @return The number of methods without Branches. * @param className * a {@link java.lang.String} object. */ public int getNumBranchlessMethods(String className) { if (!branchlessMethods.containsKey(className)) return 0; return branchlessMethods.get(className).size(); } /** * Returns the number of methods without Branches for class className * * @return The number of methods without Branches. * @param className * a {@link java.lang.String} object. */ public int getNumBranchlessMethodsPrefix(String className) { int num = 0; for (String name : branchlessMethods.keySet()) { if (name.startsWith(className)) num += branchlessMethods.get(name).size(); } return num; } /** * Returns the number of methods without Branches for class className * * @return The number of methods without Branches. * @param className * a {@link java.lang.String} object. */ public int getNumBranchlessMethodsMemberClasses(String className) { int num = 0; for (String name : branchlessMethods.keySet()) { if (name.equals(className) || name.startsWith(className + "$")) num += branchlessMethods.get(name).size(); } return num; } /** * Returns the total number of methods without branches in the instrumented * classes * * @return */ public int getNumBranchlessMethods() { int num = 0; for (String name : branchlessMethods.keySet()) { num += branchlessMethods.get(name).size(); } return num; } /** * Returns a Set containing all classes for which this pool knows Branches * for as Strings * * @return a {@link java.util.Set} object. */ public Set knownClasses() { Set r = new HashSet(); r.addAll(branchMap.keySet()); r.addAll(branchlessMethods.keySet()); if (logger.isDebugEnabled()) { logger.debug("Known classes: " + r); } return r; } /** * Returns a Set containing all methods in the class represented by the * given String for which this pool knows Branches for as Strings * * @param className * a {@link java.lang.String} object. * @return a {@link java.util.Set} object. */ public Set knownMethods(String className) { Set r = new HashSet(); Map> methods = branchMap.get(className); if (methods != null) r.addAll(methods.keySet()); return r; } /** * Returns a List containing all Branches in the given class and method * * Should no such Branch exist an empty List is returned * * @param className * a {@link java.lang.String} object. * @param methodName * a {@link java.lang.String} object. * @return a {@link java.util.List} object. */ public List retrieveBranchesInMethod(String className, String methodName) { List r = new ArrayList(); if (branchMap.get(className) == null) return r; List branches = branchMap.get(className).get(methodName); if (branches != null) r.addAll(branches); return r; } /** *

* getDefaultBranchForSwitch *

* * @param v * a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object. * @return a {@link org.evosuite.coverage.branch.Branch} object. */ public Branch getDefaultBranchForSwitch(BytecodeInstruction v) { if (!v.isSwitch()) throw new IllegalArgumentException("switch instruction expected"); if (!isKnownAsSwitchBranchInstruction(v)) throw new IllegalArgumentException( "instruction not known to be a switch instruction"); if (!registeredDefaultCases.containsKey(v)) throw new IllegalArgumentException( "there is no registered default case for this instruction"); return registeredDefaultCases.get(v); } /** * Reset all the data structures used to keep track of the branch * information */ public void reset() { branchCounter = 0; branchMap.clear(); branchlessMethods.clear(); branchIdMap.clear(); registeredNormalBranches.clear(); registeredSwitches.clear(); registeredDefaultCases.clear(); switchLabels.clear(); } /** *

* clear *

* * TODO: One of these two methods should go */ public void clear() { branchCounter = 0; branchMap.clear(); branchIdMap.clear(); branchlessMethods.clear(); switchLabels.clear(); registeredDefaultCases.clear(); registeredNormalBranches.clear(); registeredSwitches.clear(); } /** *

* clear *

* * @param className * a {@link java.lang.String} object. */ public void clear(String className) { branchMap.remove(className); branchlessMethods.remove(className); } /** *

* clear *

* * @param className * a {@link java.lang.String} object. * @param methodName * a {@link java.lang.String} object. */ public void clear(String className, String methodName) { int numBranches = 0; if (branchMap.containsKey(className)) { if (branchMap.get(className).containsKey(methodName)) numBranches = branchMap.get(className).get(methodName).size(); branchMap.get(className).remove(methodName); } if (branchlessMethods.containsKey(className)) branchlessMethods.get(className).remove(methodName); logger.info("Resetting branchCounter from " + branchCounter + " to " + (branchCounter - numBranches)); branchCounter -= numBranches; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy