org.evosuite.coverage.dataflow.DefUsePool 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.dataflow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is supposed to hold all the available information concerning
* Definitions and Uses.
*
* The addDefinition()- and addUse()-Method get called by the
* DefUseInstrumentation whenever it detects a BytecodeInstruction that
* corresponds to a Definition or Use in the class under test.
*
* BytecodeInstructions that are not known to this pool can not be instantiated
* as Definition or Use by the DefUseFactory
*
* @author Andre Mis
*/
public class DefUsePool {
private static Logger logger = LoggerFactory.getLogger(DefUsePool.class);
// trees of all known definitions and uses
// className -> methodName -> DUVarName -> List of Definitions in that
// method for the variable
private static Map>>> defMap = new HashMap>>>();
// className -> methodName -> DUVarName -> List of Uses in that method for
// the variable
private static Map>>> useMap = new HashMap>>>();
// maps IDs to objects
private static Map defuseIdsToDefUses = new HashMap();
private static Map defuseIdsToDefs = new HashMap();
private static Map defuseIdsToUses = new HashMap();
// maps objects to IDs
// register of all known DefUse-, Definition- and Use-IDs
private static Map registeredDUs = new HashMap();
private static Map registeredDefs = new HashMap();
private static Map registeredUses = new HashMap();
// an extra one to keep track of parameterUses
private static List knownParameterUses = new ArrayList();
// and an extra one to keep track of field method calls
private static List knownFieldMethodCalls = new ArrayList();
// keep track of known DUs and assign IDs accordingly
private static int defCounter = 0;
private static int useCounter = 0;
private static int duCounter = 0;
/**
* Gets called by DefUseInstrumentation whenever it detects a definition as
* defined by ASMWrapper.isDefinition()
*
* Registers the given instruction as a definition, assigns a fresh defId
* for it and if the given instruction does not represent an IINC also
* assigns a fresh defUseId to the given instruction.
*
* Warning: - Should the instruction be an IINC it is expected to have
* passed addAsUse() first! - if registering of the given instruction fails
* (like it does for IINCs due to the fact above) for any reason this method
* throws an IllegalStateException!
*
* Return false if the given instruction does not represent an instruction
* which is a definition as defined in ASMWrapper.isDefinition().
*
* Should isKnownAsDefinition() return true for the instruction before
* calling this method, it also returns false. After the call
* isKnownAsDefinition() is expected to return true for the instruction at
* hand however.
*
* @param d
* CFGVertex corresponding to a Definition in the CUT
* @return a boolean.
*/
public static boolean addAsDefinition(BytecodeInstruction d) {
if (!(d.isDefinition() || d.isMethodCallOfField())) {
logger.error("expect instruction of a definition");
return false;
}
if (isKnownAsDefinition(d)) {
logger.error("each definition can be added at most once");
return false;
}
if (!d.canBeInstrumented())
return false;
// if (d.isLocalArrayDefinition())
// LoggingUtils.getEvoLogger().info("registering LOCAL ARRAY VAR DEF "
// + d.toString());
// register instruction
// IINCs and field method calls already have duID set so this can fail
boolean registeredAsDU = registerAsDefUse(d);
// sanity check for IINCs
if (!registeredAsDU && !(d.isIINC() || d.isMethodCallOfField()))
throw new IllegalStateException(
"expect registering to fail only on IINCs and field method calls");
registerAsDefinition(d);
// if (d.isMethodCallOfField())
// LoggingUtils.getEvoLogger().info("Registered field method call as Definition "
// + d.toString());
return true;
}
/**
* Gets called by DefUseInstrumentation whenever it detects a use as defined
* by ASMWrapper.isUse()
*
* Registers the given instruction as a use, assigns a fresh useId a fresh
* defUseId to the given instruction.
*
* Return false if the given instruction does not represent an instruction
* which is a use as defined in ASMWrapper.isUse().
*
* Should isKnown() return true for the instruction before calling this
* method, it also returns false.
*
* After the call isKnown() and isKnownAsUse() are expected to return true
* for the instruction at hand however.
*
* @param u
* CFGVertex corresponding to a Use in the CUT
* @return a boolean.
*/
public static boolean addAsUse(BytecodeInstruction u) {
if (!(u.isUse() || u.isMethodCallOfField()))
return false;
if (isKnownAsUse(u))
return false;
if (!u.canBeInstrumented())
return false;
// register instruction
// field method calls already have duID set so this can fail
boolean registeredAsDU = registerAsDefUse(u);
// sanity check for IINCs
if (!registeredAsDU && !u.isMethodCallOfField())
throw new IllegalStateException(
"expect registering to fail only on field method calls");
registerAsUse(u);
// if (u.isMethodCallOfField())
// LoggingUtils.getEvoLogger().info("Registered field method call as Use "
// + u.toString());
return true;
}
/**
* Gets called by DefUseInstrumentation whenever it detects a field method
* call as defined by ASMWrapper.isMethodCallField()
*
* It is not clear whether a field method call represents a Definition or
* Use when it is first detected. Later on the DefUseCoverageFactory (TODO
* or some other part of evosuite) will decide this using the complete CCFGs
* and their purity analysis, which are not available when
* DefUseInstrumentation has its turn.
*
* The instrumentation will call a special method of the ExecutionTracer
* which will redirect the instrumentation call to either passedDefinition()
* or passedUse() depending on how the given instruction will be categorized
* later on.
*
* Registers the given instruction as a field method call and assigns a
* fresh defUseId to the given instruction.
*
* Return false if the given instruction does not represent an instruction
* which is a use as defined in ASMWrapper.isUse().
*
* Should isKnown() return true for the instruction before calling this
* method, it also returns false.
*
* After the call isKnown() and isKnownAsUse() are expected to return true
* for the instruction at hand however.
*
* @param u
* CFGVertex corresponding to a Use in the CUT
* @return a boolean.
*/
public static boolean addAsFieldMethodCall(BytecodeInstruction f) {
if (!f.isMethodCallOfField())
return false;
if (!f.canBeInstrumented())
return false;
registerAsDefUse(f);
registerAsFieldMethodCall(f);
return true;
}
// registering
private static boolean registerAsDefUse(BytecodeInstruction d) {
if (registeredDUs.containsKey(d))
return false;
// assign fresh defUseId
duCounter++;
registeredDUs.put(d, duCounter);
return true;
}
private static boolean registerAsDefinition(BytecodeInstruction d) {
if (!registeredDUs.containsKey(d))
throw new IllegalStateException(
"expect registerAsDefUse() to be called before registerAsDefinition()/Use()");
if (registeredDefs.containsKey(d))
return false;
// assign fresh defId
defCounter++;
registeredDefs.put(d, defCounter);
// now the first Definition instance for this instruction can be created
Definition def = DefUseFactory.makeDefinition(d);
// if (d.isLocalArrayDefinition())
// LoggingUtils.getEvoLogger().info("succesfully registered LOCAL ARRAY VAR DEF "
// + def.toString());
// finally add the Definition to all corresponding maps
fillDefinitionMaps(def);
return true;
}
private static boolean registerAsUse(BytecodeInstruction d) {
if (!registeredDUs.containsKey(d))
throw new IllegalStateException(
"expect registerAsDefUse() to be called before registerAsDefinition()/Use()");
if (registeredUses.containsKey(d))
return false;
// assign fresh useId
useCounter++;
registeredUses.put(d, useCounter);
// check if this particular use is a parameterUse
if (d.isLocalVariableUse() && !knowsDefinitionForVariableOf(d))
registerParameterUse(d);
// now the first Use instance for this instruction can be created
Use use = DefUseFactory.makeUse(d);
// finally add the use to all corresponding maps
fillUseMaps(use);
return true;
}
private static void registerParameterUse(BytecodeInstruction d) {
if (!knownParameterUses.contains(d))
knownParameterUses.add(d);
}
private static void registerAsFieldMethodCall(BytecodeInstruction f) {
if (!knownFieldMethodCalls.contains(f))
knownFieldMethodCalls.add(f);
}
private static void fillDefinitionMaps(Definition def) {
addToDefMap(def);
defuseIdsToDefUses.put(def.getDefUseId(), def);
defuseIdsToDefs.put(def.getDefUseId(), def);
logger.debug("Added to DefUsePool as def: " + def.toString());
}
private static void fillUseMaps(Use use) {
addToUseMap(use);
defuseIdsToDefUses.put(use.getDefUseId(), use);
defuseIdsToUses.put(use.getDefUseId(), use);
logger.debug("Added to DefUsePool as use: " + use.toString());
}
// filling the maps
private static boolean addToDefMap(Definition d) {
String className = d.getClassName();
String methodName = d.getMethodName();
String varName = d.getVariableName();
initMap(defMap, className, methodName, varName);
return defMap.get(className).get(methodName).get(varName).add(d);
}
private static boolean addToUseMap(Use u) {
String className = u.getClassName();
String methodName = u.getMethodName();
String varName = u.getVariableName();
initMap(useMap, className, methodName, varName);
return useMap.get(className).get(methodName).get(varName).add(u);
}
private static void initMap(Map>>> map,
String className, String methodName, String varName) {
if (!map.containsKey(className))
map.put(className, new HashMap>>());
if (!map.get(className).containsKey(methodName))
map.get(className).put(methodName, new HashMap>());
if (!map.get(className).get(methodName).containsKey(varName))
map.get(className).get(methodName).put(varName, new ArrayList());
}
// functionality to retrieve information from the pool
/**
*
* knowsDefinitionForVariableOf
*
*
* @param du
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean knowsDefinitionForVariableOf(BytecodeInstruction du) {
if (!du.isDefUse())
throw new IllegalArgumentException("defuse expected");
String className = du.getClassName();
String methodName = du.getMethodName();
String varName = du.getVariableName();
try {
return defMap.get(className).get(methodName).get(varName).size() > 0;
} catch (NullPointerException nex) {
// expected
return false;
}
}
/**
*
* isKnown
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean isKnown(BytecodeInstruction instruction) {
return isKnownAsDefinition(instruction) || isKnownAsUse(instruction);
}
/**
*
* isKnownAsDefinition
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean isKnownAsDefinition(BytecodeInstruction instruction) {
return registeredDefs.containsKey(instruction);
}
public static boolean isKnownAsDefinition(int defuseId) {
return defuseIdsToDefs.containsKey(defuseId);
}
public static boolean isKnownAsUse(int defuseId) {
return defuseIdsToUses.containsKey(defuseId);
}
/**
*
* isKnownAsUse
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean isKnownAsUse(BytecodeInstruction instruction) {
return registeredUses.containsKey(instruction);
}
/**
*
* isKnownAsFieldMethodCall
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean isKnownAsFieldMethodCall(BytecodeInstruction instruction) {
return knownFieldMethodCalls.contains(instruction);
}
/**
*
* isKnownAsParameterUse
*
*
* @param instruction
* a {@link org.evosuite.graphs.cfg.BytecodeInstruction} object.
* @return a boolean.
*/
public static boolean isKnownAsParameterUse(BytecodeInstruction instruction) {
return knownParameterUses.contains(instruction);
}
/**
*
* retrieveRegisteredDefinitions
*
*
* @return a {@link java.util.Set} object.
*/
public static Set retrieveRegisteredDefinitions() {
Set r = new HashSet();
for (Integer defId : registeredDefs.values()) {
r.add(getDefinitionByDefId(defId));
}
return r;
}
/**
*
* retrieveRegisteredUses
*
*
* @return a {@link java.util.Set} object.
*/
public static Set