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

org.evosuite.coverage.dataflow.analysis.AllUsesAnalysis 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.coverage.dataflow.analysis;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.evosuite.coverage.dataflow.DefUseCoverageFactory;
import org.evosuite.coverage.dataflow.DefUseCoverageTestFitness;
import org.evosuite.coverage.dataflow.DefUseCoverageTestFitness.DefUsePairType;
import org.evosuite.graphs.ccfg.CCFGCodeNode;
import org.evosuite.graphs.ccfg.CCFGEdge;
import org.evosuite.graphs.ccfg.CCFGFieldClassCallNode;
import org.evosuite.graphs.ccfg.CCFGFrameNode;
import org.evosuite.graphs.ccfg.CCFGMethodCallNode;
import org.evosuite.graphs.ccfg.CCFGMethodEntryNode;
import org.evosuite.graphs.ccfg.CCFGMethodExitNode;
import org.evosuite.graphs.ccfg.CCFGMethodReturnNode;
import org.evosuite.graphs.ccfg.CCFGNode;
import org.evosuite.graphs.ccfg.ClassControlFlowGraph;
import org.evosuite.graphs.ccfg.ClassControlFlowGraph.FrameNodeType;
import org.evosuite.graphs.ccg.ClassCallNode;
import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.evosuite.utils.LoggingUtils;

/**
 * This class computes all uses testing requirements of the relative CCFG.
 * @author Andre Mis, Alessandra Gorla
 */
public class AllUsesAnalysis {

	private final int UPPER_PAIR_SEARCH_INVOCATION_BOUND = 2000000;
	private boolean warnedAboutAbortion = false;
	private ClassControlFlowGraph ccfg;

	// map methods to Sets of definitions that can be active at method
	// return. map according to defined variables name
	private Map>> determinedActiveDefs = new HashMap>>();
	// map methods to Sets of Uses that have a definition-free path
	// from their methods entry
	private Map> determinedFreeUses = new HashMap>();
	
	private Set analyzedMethods = new HashSet();

	// debug profiling
	private long timeSpentMingling = 0l;



	/**
	 * Given the ClassCallGraph of a class this constructor will build up the
	 * corresponding CCFG using the RCFGs from the GraphPool.
	 * 
	 * @param ccg
	 *            a {@link org.evosuite.graphs.ccg.ClassCallGraph} object.
	 */
	public AllUsesAnalysis(ClassControlFlowGraph ccfg) {
		this.ccfg = ccfg;
	}

	// Definition-Use Pair computation

	/**
	 * Makes a run of determineInterMethodPairs() for each public method. If you
	 * reach a use for which you have no def yet, remember that also remember
	 * activeDefs after each run then create intra-class pairs from these uses
	 * and defs and during each single run we detect intra and inter method
	 * pairs
	 * 
	 * @return a {@link java.util.Set} object.
	 */
	public Set determineDefUsePairs() {

		// TODO clinit? id say uses dont count, defs do

		Set r = preAnalyzeMethods();
		// create inter-method-pairs
		for (CCFGMethodEntryNode publicMethodEntry : ccfg.publicMethods) {
			if (analyzedMethods.contains(publicMethodEntry)) {
				continue;
			}
			if (publicMethodEntry.getEntryInstruction() == null)
				throw new IllegalStateException(
						"expect each CCFGMethodEntryNode to have its entryInstruction set");

			r.addAll(determineIntraInterMethodPairs(publicMethodEntry));
		}
		// create intra-method pairs
		r.addAll(createIntraClassPairs());

		freeMemory();
		return r;
	}

	/**
	 * Checks if there are methods in the CCG that dont call any other methods
	 * except for maybe itself. For these we can predetermine free uses and
	 * activeDefs prior to looking for inter_method_pairs. After that we can
	 * even repeat this process for methods we now have determined free uses and
	 * activeDefs! that way you can save a lot of computation. Map activeDefs
	 * and freeUses according to the variable so you can easily determine which
	 * defs will be active and which uses are free once you encounter a
	 * methodCall to that method without looking at its part of the CCFG
	 */
	private Set preAnalyzeMethods() {

		// TODO after preanalyze, order the remaining methods as follows:
		// first order each method by the number of instructions within them in
		// ascending order. after that for each method check if there is a
		// method that calls this one and also has to be analyzed still. if you
		// find such a pair move the calling method in front of the called one

		Set r = new HashSet();

		LinkedList toAnalyze = new LinkedList();
		toAnalyze.addAll(getInitialPreAnalyzeableMethods());

		while (!toAnalyze.isEmpty()) {
			ClassCallNode currentMethod = toAnalyze.poll();
			CCFGMethodEntryNode analyzeableEntry = ccfg.getMethodEntryNodeForClassCallNode(currentMethod);
			if (analyzedMethods.contains(analyzeableEntry))
				continue;

			r.addAll(determineIntraInterMethodPairs(analyzeableEntry));

			// check if we can pre-analyze further methods now
			Set parents = ccfg.getCcg().getParents(currentMethod);
			for (ClassCallNode parent : parents) {
				if (toAnalyze.contains(parent))
					continue; // will be analyzed anyway
				if (analyzedMethods
						.contains(ccfg.getMethodEntryNodeForClassCallNode(parent)))
					continue; // was already analyzed

				Set parentsChildren = ccfg.getCcg().getChildren(parent);
				boolean canAnalyzeNow = true;
				for (ClassCallNode parentsChild : parentsChildren) {
					if(parentsChild == null)
						continue;
					if (!parentsChild.equals(parent)
							&& !(toAnalyze.contains(parentsChild) || analyzedMethods
									.contains(ccfg.getMethodEntryNodeForClassCallNode(parentsChild)))) {
						// found child of parent that will not be pre-analyzed
						canAnalyzeNow = false;
						break;
					}
				}
				if (canAnalyzeNow) {
					toAnalyze.offer(parent);
				}
			}
		}

		return r;
	}

	/**
	 * Every CCGNode that has no children except for maybe itself can be
	 * initially pre-analyzed
	 */
	private Set getInitialPreAnalyzeableMethods() {
		Set preAnalyzeable = new HashSet();
		for (ClassCallNode ccgNode : ccfg.getCcg().vertexSet()) {
			boolean add = true;
			for (ClassCallNode child : ccfg.getCcg().getChildren(ccgNode)){
				if(child == null)
					continue;
				if (!child.equals(ccgNode))
					add = false;
			}

			if (add)
				preAnalyzeable.add(ccgNode);
		}

		return preAnalyzeable;
	}

	// intra-class pairs

	private Set createIntraClassPairs() {
		Set r = new HashSet();

		for (String method : determinedFreeUses.keySet()) {
			if (!ccfg.isPublicMethod(method)) {
				continue;
			}
			for (BytecodeInstruction freeUse : determinedFreeUses.get(method))
				r.addAll(createIntraClassPairsForFreeUse(freeUse));
		}

		return r;
	}

	private Set createIntraClassPairsForFreeUse(
			BytecodeInstruction freeUse) {
		checkFreeUseSanity(freeUse);

		Set r = new HashSet();
		for (String method : determinedActiveDefs.keySet()) {
			if (!ccfg.isPublicMethod(method)) {
				continue;
			}
			Set> activeDefss = determinedActiveDefs
					.get(method);
			for (Map activeDefs : activeDefss) {
				// checkActiveDefsSanity(activeDefs);
				// if (activeDefs.get(freeUse.getDUVariableName()) == null)
				// continue;

				BytecodeInstruction activeDef = activeDefs.get(freeUse
						.getVariableName());
				if (activeDef == null)
					continue;
				addNewGoalToFoundPairs(null, activeDef, freeUse,
						DefUsePairType.INTRA_CLASS, r);
			}
		}
		return r;
	}

	// intra- and inter-method pair search

	private Set determineIntraInterMethodPairs(
			CCFGMethodEntryNode methodEntry) {

		long start = System.currentTimeMillis();
		long mingled = timeSpentMingling;

		// TODO get a logger and replace System.outs with logger.debug

		LoggingUtils.getEvoLogger().debug("* Searching for pairs in " + methodEntry.getMethod()
				+ " ... ");

		warnedAboutAbortion = false;

		// initialize variables
		Set foundPairs = new HashSet();
		Set> activeDefs = createInitialActiveDefs();
		Set freeUses = new HashSet();
		Stack callStack = createInitialCallStack(methodEntry);

		// search
		Integer calls = determineIntraInterMethodPairs(methodEntry,
				methodEntry.getEntryInstruction(), new HashSet(),
				new HashSet(), activeDefs, freeUses, foundPairs,
				callStack, 0, true);

		long spentTime = System.currentTimeMillis() - start;

		// check if search was aborted
		Integer rerunCalls = 0;
		// if (calls >= UPPER_PAIR_SEARCH_INVOCATION_BOUND) {
		// System.out.println();
		// System.out.println("* ABORTED pairSearch for method"
		// + methodEntry.getMethod());
		//
		// System.out.print("* Re-Searching for pairs without concidering loops in "
		// + methodEntry.getMethod()
		// + " ... ");
		// // if we previously tried to analyze this method but had to abort
		// // try to rerun without handling loops
		// activeDefs = createInitialActiveDefs();
		// Set freeUses2 = new
		// HashSet();
		// callStack = createInitialCallStack(methodEntry);
		// rerunCalls = determineInterMethodPairs(methodEntry,
		// methodEntry.getEntryInstruction(), new HashSet(),
		// new HashSet(), activeDefs, freeUses2, foundPairs,
		// callStack, 0, false);
		// freeUses.addAll(freeUses2);
		//
		// spentTime = System.currentTimeMillis() - start;
		// }

		mingled = timeSpentMingling - mingled;

		System.out.println("  invocations: " + (calls + rerunCalls) + " took "
				+ spentTime + "ms (" + mingled + ") found " + foundPairs.size()
				+ " pairs");

		analyzedMethods.add(methodEntry);

		return foundPairs;
	}

	private int determineIntraInterMethodPairs(
			CCFGMethodEntryNode investigatedMethod, CCFGNode node,
			Set handled, Set handledBackEdges,
			Set> activeDefs,
			Set freeUses,
			Set foundPairs,
			Stack callStack, int invocationCount,
			boolean handleLoops) {

//		LoggingUtils.getEvoLogger().debug("  processing " + node.toString());

		handleHandledNodesSet(node, handled);

		invocationCount++;
		if (checkInvocationBound(invocationCount, callStack))
			return invocationCount;

		if (node instanceof CCFGFieldClassCallNode)
			handleFieldCallNode(investigatedMethod, node, callStack,
					activeDefs, freeUses, foundPairs);
		else if (node instanceof CCFGCodeNode)
			handleCodeNode(investigatedMethod, node, callStack, activeDefs,
					freeUses, foundPairs);
		else if (node instanceof CCFGMethodCallNode)
			handled = handleMethodCallNode(node, callStack, handled);
		else if (node instanceof CCFGMethodReturnNode)
			handleMethodReturnNode(node, callStack);
		else if (node instanceof CCFGFrameNode)
			handleFrameNode();
		else if (node instanceof CCFGMethodExitNode)
			handleMethodExitNode(node, investigatedMethod, activeDefs, freeUses);

		node = determineNextRelevantNode(node, handled);

		Set children = ccfg.getChildren(node);

		boolean skipChildren = shouldSkipChildren(node, handledBackEdges,
				children, handleLoops);

		if (!skipChildren)
			for (CCFGNode child : children) {
				if (!shouldProcessChild(node, child, handled, handledBackEdges,
						handleLoops))
					continue;

				// System.out.println("  nextChild of "+node.toString()+" is "+child.toString());
				// for(MethodCall mc : callStack)
				// System.out.println("    "+mc.toString());

				// we don't want to take every child into account all the time
				// for example if we previously found a methodCallNode and then
				// later visit a MethodExitNode we do want to follow the edge
				// from that node to the MethodReturnNode of our previous
				// methodCallNode. However we do not want to follow the edge
				// back to Frame.RETURN. on the other hand if we did not visit a
				// methodCallNode and find a MethodExitNode we do not want to
				// follow the edges from there to methodReturnNodes

				Stack nextCallStack = callStack;

				if (child instanceof CCFGMethodReturnNode) {
					if (handleReturnNodeChild(child, callStack))
						continue;
				} else if (child instanceof CCFGFrameNode) {
					handleFrameNodeChild(child);
					continue;
				} else if (child instanceof CCFGMethodCallNode) {
					CCFGMethodCallNode callNode = (CCFGMethodCallNode) child;
					if (alreadyAnalzedMethod(callNode.getCalledMethod())) {

						nextCallStack = copyCallStack(callStack);

						// use previously stored information
						activeDefs = handleMethodCallNodeChild(callNode,
								activeDefs, freeUses, foundPairs,
								nextCallStack, investigatedMethod);
						// now we can continue our search with the
						// CCFGMethodReturnNode of our call
						child = callNode.getReturnNode();
					}
				}

				// if (children.size() > 1)
				// System.out.println("  found branching point: "
				// + node.toString());

				// only have to copy stuff if current node has more than one
				// child
				if (children.size() > 1)
					invocationCount = determineIntraInterMethodPairs(
							investigatedMethod, child, new HashSet(
									handled), new HashSet(
									handledBackEdges),
							copyActiveDefs(activeDefs),
							new HashSet(freeUses),
							foundPairs, copyCallStack(nextCallStack),
							invocationCount, handleLoops);
				else
					invocationCount = determineIntraInterMethodPairs(
							investigatedMethod, child, handled,
							handledBackEdges, activeDefs, freeUses, foundPairs,
							nextCallStack, invocationCount, handleLoops);

				if (invocationCount >= UPPER_PAIR_SEARCH_INVOCATION_BOUND)
					continue;
			}
		return invocationCount;
	}

	/**
	 * If the child is a CCFGMethodCallNode check if we previously determined
	 * reachable DUs in there and if so handle that call separately in order to
	 * minimize computation. make sure to update activeDefs and freeUses
	 * accordingly. after that just proceed with the child of that
	 * CCFGMethodCallNode
	 * 
	 * @param investigatedMethod
	 * 
	 */
	private Set> handleMethodCallNodeChild(
			CCFGMethodCallNode callNode,
			Set> activeDefs,
			Set freeUses,
			Set foundPairs,
			Stack callStack, CCFGMethodEntryNode investigatedMethod) {
		// create MethodCall object to avoid weird special cases
		MethodCall call = MethodCall.constructForCallNode(callNode);
		// since we already analyzed the called method we will
		// use the previously stored information in determinedActiveDefs
		// and determinedFreeUses
		activeDefs = useStoredInformationForMethodCall(investigatedMethod,
				callNode, activeDefs, freeUses, foundPairs, call);
		// and push a MethodCall onto the Stack in order to avoid special
		// cases in handleMethodReturnNode()
		updateCallStackForCall(callStack, call);

		return activeDefs;
	}

	/**
	 * If the given Set of handled nodes already contains the given node, an
	 * IllegalStateException is thrown, because that should not happen.
	 * Otherwise the given node is added to the given Set.
	 * 
	 * Well ... funny method name i know
	 */
	private void handleHandledNodesSet(CCFGNode node, Set handled) {
		if (handled.contains(node)){
			//TODO recursion is probably not handled correctly. Fix it properly
			// https://caloriecount.svn.sourceforge.net/svnroot/caloriecount/trunk/src/com/lts/xml/simple/SimpleElementConverter.java
			// project 78 is a good case study
			LoggingUtils.getEvoLogger().info("We are in a recursive call. Skipping the node");
			//Actually, this can happen in case of recursion 
			//throw new IllegalStateException(
			//		"visiting already handled node "+node+", should not happen")
			};
		handled.add(node);
	}

	/**
	 * Pushes a MethodCall according to the given MethodCallNode onto the
	 * callStack and filters Set of handled nodes to no longer contain nodes of
	 * the called method except the method call itself.
	 * 
	 * Filtering the handled Set is due to the fact, that we will have to visit
	 * some nodes more than once in case of a recursive method call for example
	 */
	private Set handleMethodCallNode(CCFGNode node,
			Stack callStack, Set handled) {

		CCFGMethodCallNode callNode = (CCFGMethodCallNode) node;
		updateCallStackForCallNode(callStack, callNode);

		return filterHandledMapForMethodCallNode(callNode, handled);
	}

	/**
	 * If we encounter a CCFGMethodReturnNode during pair search but we no
	 * longer had MethodCalls on our callStack except the initial MethodCall, we
	 * throw an IllegalStateException. We also do this if the top of the
	 * callStack is from a method different to the one from our
	 * CCFGMethodReturnNode.
	 * 
	 * Otherwise we pop the top of our callStack.
	 */
	private void handleMethodReturnNode(CCFGNode node,
			Stack callStack) {
		if (callStack.peek().isInitialMethodCall())
			throw new IllegalStateException(
					"found method return but had no more method calls on stack");

		CCFGMethodReturnNode retrn = (CCFGMethodReturnNode) node;
		if (!callStack.peek().isMethodCallFor(retrn.getCallInstruction()))
			throw new IllegalStateException(
					"visiting MethodReturnNode even though lastly visited MethodCallNode was from a different method");

		callStack.pop();
	}

	private void handleFrameNode() {
		throw new IllegalStateException(
				"visiting CCFGFrameNode during pair search, which should not happen");
	}

	/**
	 * If this is the methodExit of our investigated public method we remember
	 * our current activeDefs for intra-class pairs
	 * 
	 * @param freeUses
	 */
	private void handleMethodExitNode(CCFGNode node,
			CCFGMethodEntryNode investigatedMethod,
			Set> activeDefs,
			Set freeUses) {

		CCFGMethodExitNode exitNode = (CCFGMethodExitNode) node;
		if (exitNode.isExitOfMethodEntry(investigatedMethod)) {
			rememberActiveDefs(exitNode.getMethod(), activeDefs);
			rememberFreeUses(exitNode.getMethod(), freeUses);
		}
	}

	private void handleFieldCallNode(CCFGMethodEntryNode investigatedMethod,
			CCFGNode node, Stack callStack,
			Set> activeDefs,
			Set freeUses,
			Set foundPairs) {

		BytecodeInstruction code = ((CCFGCodeNode) node).getCodeInstruction();

//		LoggingUtils.getEvoLogger().debug(
//				"Processing field call: " + node.toString());

		handleDefUse(investigatedMethod, code, callStack, activeDefs, freeUses,
				foundPairs);
	}

	private void handleCodeNode(CCFGMethodEntryNode investigatedMethod,
			CCFGNode node, Stack callStack,
			Set> activeDefs,
			Set freeUses,
			Set foundPairs) {

		BytecodeInstruction code = ((CCFGCodeNode) node).getCodeInstruction();

		handleDefUse(investigatedMethod, code, callStack, activeDefs, freeUses,
				foundPairs);
	}

	private void handleDefUse(CCFGMethodEntryNode investigatedMethod,
			BytecodeInstruction code, Stack callStack,
			Set> activeDefs,
			Set freeUses,
			Set foundPairs) {

		checkCallStackSanity(callStack, code);

		if (code.isUse())
			handleUseInstruction(investigatedMethod, code, callStack,
					activeDefs, freeUses, foundPairs);

		if (code.isDefinition())
			handleDefInstruction(code, callStack, activeDefs);
	}

	private void handleDefInstruction(BytecodeInstruction code,
			Stack callStack,
			Set> activeDefMaps) {

		VariableDefinition def = new VariableDefinition(code, callStack.peek());
		for (Map activeDefMap : activeDefMaps) {
			activeDefMap.put(code.getVariableName(), def);
			// System.out.println("  setting activeDef:" + def.toString());
		}
	}

	private void handleUseInstruction(CCFGMethodEntryNode investigatedMethod,
			BytecodeInstruction code, Stack callStack,
			Set> activeDefMaps,
			Set freeUses,
			Set foundPairs) {

		String varName = code.getVariableName();
//		LoggingUtils.getEvoLogger().info("Processing Use for "+varName);

		for (Map activeDefs : activeDefMaps) {

			VariableDefinition activeDef = activeDefs.get(varName);
			if (activeDef != null) {

				// we have an intraMethodPair iff use and definition are in
				// the
				// same method and executed during a single invocation of
				// that method
				boolean isIntraPair = activeDef.getMethodCall().equals(
						callStack.peek());
				DefUseCoverageTestFitness.DefUsePairType type;
				if (isIntraPair)
					type = DefUseCoverageTestFitness.DefUsePairType.INTRA_METHOD;
				else {
					type = DefUseCoverageTestFitness.DefUsePairType.INTER_METHOD;
				}

				if (!activeDef.getDefinition().isLocalDU()
						|| type.equals(DefUsePairType.INTRA_METHOD))
					addNewGoalToFoundPairs(investigatedMethod, activeDef, code,
							type, foundPairs);
			} else {
				// if we encounter a use here but have no activeDef yet we know
				// the
				// use has a definition-free path from method start
				if (code.isFieldUse()) {
					freeUses.add(code);
					// System.out.println("  adding free use: " +
					// code.toString());
					;
				}
			}
		}
	}

	/**
	 * When we go back to previously visited nodes we do not have to visit nodes
	 * after our current node again. If we follow backEdges we do that so we
	 * find all intra-method pairs within loops, so we go through loops twice so
	 * to speak. however the possible activeDefs are already determined after
	 * the first walk through the loop. so after we make our two runs through
	 * the loop we don't have to walk through everything after the loop again
	 */
	private boolean shouldSkipChildren(CCFGNode node,
			Set handledBackEdges, Set children,
			boolean handleLoops) {
		if(node == null || children == null)
			return true;
		boolean skipChildren = false;
		if (handleLoops) {
			for (CCFGNode child : children) {
				CCFGEdge currentEdge = ccfg.getEdge(node, child);
				if (handledBackEdges.contains(currentEdge)) {
					skipChildren = true;
					// System.out.println("Skipping nodes. Found already handled backEdge between "+node.toString()+" and "+child.toString());
					break;
				}
			}
		}
		return skipChildren;
	}

	private boolean shouldProcessChild(CCFGNode node, CCFGNode child,
			Set handled, Set handledBackEdges,
			boolean handleLoops) {

		if (handleLoops) {
			CCFGEdge currentEdge = ccfg.getEdge(node, child);
			if (handledBackEdges.contains(currentEdge))
				throw new IllegalStateException(
						"should have been detected earlier");
			if (handled.contains(child)) {
				// whenever we encounter an edge back to a previously handled
				// node we need to clear handled set once, otherwise we are
				// missing
				// intra-method pairs in loops
				handledBackEdges.add(currentEdge);
				handled.clear();
			}
		} else {
			if (handled.contains(child))
				return false;
		}

		return true;
	}

	/**
	 * While a node has exactly 1 further child which is a CCFGCodeNode and not
	 * a DefUse-instruction that child does not need to be processed explicitly
	 * and can be skipped
	 */
	private CCFGNode determineNextRelevantNode(CCFGNode node,
			Set handled) {
		CCFGNode nextNode;
		while (ccfg.outDegreeOf(node) == 1
				&& (nextNode = ccfg.getSingleChild(node)) instanceof CCFGCodeNode
				&& !((CCFGCodeNode) nextNode).getCodeInstruction().isDefUse()
				&& !handled.contains(nextNode)) {
			node = nextNode;
		}
		return node;
	}

	
	private Set> copyActiveDefs(
			Set> activeDefs) {

		HashSet> r = new HashSet>();
		for (Map activeDef : activeDefs)
			r.add(new HashMap(activeDef));
		return r;
	}

	private Set> useStoredInformationForMethodCall(
			CCFGMethodEntryNode investigatedMethod,
			CCFGMethodCallNode callNode,
			Set> activeDefMapsInCaller,
			Set freeUses,
			Set foundPairs, MethodCall call) {

		//
		Set freeUsesInCalledMethod = determinedFreeUses
				.get(callNode.getCalledMethod());

		for (BytecodeInstruction freeUseInCalledMethod : freeUsesInCalledMethod) {
			for (Map activeDefMap : activeDefMapsInCaller) {
				VariableDefinition activeDef = activeDefMap
						.get(freeUseInCalledMethod.getVariableName());

				if (activeDef == null) {
					// there was a path to the calledMethod that did not define
					// the variable of our freeUse, so it is still free
					freeUses.add(freeUseInCalledMethod);
				} else {
					// checkActiveDefsSetSanity(activeDefs);

					// otherwise, we have an active definition for a variable
					// that is free in the called method so we have a new
					// inter-method pair
					if (freeUseInCalledMethod.isFieldUse()) {
						addNewGoalToFoundPairs(investigatedMethod, activeDef,
								freeUseInCalledMethod,
								DefUsePairType.INTER_METHOD, foundPairs);
					}
				}
			}
		}

		Set> activeDefMapsInCallee = determinedActiveDefs
				.get(callNode.getCalledMethod());

		Set> activeDefMapsAfterCurrentCall = new HashSet>();

		long start = System.currentTimeMillis();

		// since every defMap in my previously determined activeDefMaps
		// represents one possible configuration of activeDefs i will have to
		// mingle each of these maps with each of the currently active maps
		for (Map activeDefMapInCallee : activeDefMapsInCallee) {
			for (Map activeDefMapInCaller : activeDefMapsInCaller) {
				Set relevantVariables = new HashSet(
						activeDefMapInCallee.keySet());
				relevantVariables.addAll(activeDefMapInCaller.keySet());

				// mingle both activeDefMaps from prior to the call and when
				// returning from call to a new one that will be true after the
				// call
				Map activeDefMapAfterCurrentCall = new HashMap();
				for (String variable : relevantVariables) {
					BytecodeInstruction activeDefAfterCall = activeDefMapInCallee
							.get(variable);
					VariableDefinition activeDefPriorToCall = activeDefMapInCaller
							.get(variable);

					if (activeDefAfterCall == null) {
						if (activeDefPriorToCall == null)
							throw new IllegalStateException(
									"expect activeDefMaps not to map to null values");

						// variable was not overwritten in called
						// method, so the activeDef prior to the call stays
						// active
						activeDefMapAfterCurrentCall.put(variable,
								activeDefPriorToCall);
					} else {
						// variable was overwritten in call, so we will make a
						// new VariableDefinition and keep that active in the
						// newly created map
						VariableDefinition overwritingDefinition = new VariableDefinition(
								activeDefAfterCall, call);
						activeDefMapAfterCurrentCall.put(variable,
								overwritingDefinition);
					}
				}

				// System.out.println("mingled map:");
				// printVDDefMap(activeDefMapAfterCurrentCall);

				activeDefMapsAfterCurrentCall.add(activeDefMapAfterCurrentCall);
			}
		}

		// System.out.println("Finished mingling. #Resulting-Maps: "+activeDefMapsAfterCurrentCall.size());

		timeSpentMingling += System.currentTimeMillis() - start;

		return activeDefMapsAfterCurrentCall;
	}

	private boolean alreadyAnalzedMethod(String method) {

		if (determinedFreeUses.get(method) != null) {
			if (determinedActiveDefs.get(method) == null)
				throw new IllegalStateException(
						"found already determined freeUse but no activeDefs for method "
								+ method);
			return true;
		}
		return false;
	}

	/**
	 * Creates a new MethodCall object for the given MethodCallNode and pushes
	 * it onto the given callStack.
	 */
	private void updateCallStackForCallNode(Stack callStack,
			CCFGMethodCallNode callNode) {

		MethodCall call = MethodCall.constructForCallNode(callNode);
		updateCallStackForCall(callStack, call);
	}

	/**
	 * Pushes the given MethodCall object onto the given callStack
	 */
	private void updateCallStackForCall(Stack callStack,
			MethodCall call) {

		callStack.push(call);
	}

	private void addNewGoalToFoundPairs(CCFGMethodEntryNode investigatedMethod,
			VariableDefinition activeDef, BytecodeInstruction code,
			DefUsePairType type, Set foundPairs) {

		addNewGoalToFoundPairs(investigatedMethod, activeDef.getDefinition(),
				code, type, foundPairs);
	}

	private void addNewGoalToFoundPairs(CCFGMethodEntryNode investigatedMethod,
			BytecodeInstruction activeDef, BytecodeInstruction freeUse,
			DefUsePairType type, Set foundPairs) {

		checkDefinitionSanity(activeDef);
		checkUseSanity(freeUse);

		if (type.equals(DefUsePairType.INTER_METHOD)
				&& !ccfg.isPublicMethod(investigatedMethod))
			return;

		DefUseCoverageTestFitness goal = DefUseCoverageFactory.createGoal(
				activeDef, freeUse, type);
		if (goal != null) {
			foundPairs.add(goal);
//			System.out.println();
//			System.out.println("  created goal: " + goal.toString());
		}
	}

	private boolean handleReturnNodeChild(CCFGNode child,
			Stack callStack) {

		if (callStack.peek().isInitialMethodCall())
			return true;
		CCFGMethodReturnNode retrn = (CCFGMethodReturnNode) child;
		if (!callStack.peek().isMethodCallFor(retrn.getCallInstruction()))
			return true;

		return false;
	}

	private void handleFrameNodeChild(CCFGNode child) {
		CCFGFrameNode frameNode = (CCFGFrameNode) child;
		if (!frameNode.getType().equals(FrameNodeType.RETURN))
			throw new IllegalStateException(
					"found CCFGFrameNode that was not of type RETURN. should not be possible "
							+ frameNode.toString());
	}

	private void rememberActiveDefs(String method,
			Set> activeDefMaps) {

		if (determinedActiveDefs.get(method) == null)
			determinedActiveDefs.put(method,
					new HashSet>());

		Set> defMaps = toRememberableBytecodeInstructionMap(activeDefMaps);

		for (Map defMap : defMaps) {
			determinedActiveDefs.get(method).add(defMap);
		}
	}

	private void rememberFreeUses(String method,
			Set freeUses) {
		if (determinedFreeUses.get(method) == null)
			determinedFreeUses.put(method, new HashSet());
		determinedFreeUses.get(method).addAll(freeUses);
	}

	private Stack copyCallStack(Stack callStack) {
		Stack r = new Stack();
		r.setSize(callStack.size());
		Collections.copy(r, callStack);
		return r;
	}

	private Set filterHandledMapForMethodCallNode(
			CCFGMethodCallNode callNode, Set handled) {
		Set r = new HashSet();
		for (CCFGNode node : handled)
			if (!nodeBelongsToMethod(node, callNode.getCalledMethod())
					|| (node instanceof CCFGMethodCallNode))
				r.add(node);

		r.add(callNode);
		return r;
	}

	private boolean nodeBelongsToMethod(CCFGNode node, String method) {
		if (node instanceof CCFGCodeNode)
			return ((CCFGCodeNode) node).getMethod().equals(method);
		else if (node instanceof CCFGMethodCallNode)
			return ((CCFGMethodCallNode) node).getMethod().equals(method);
		else if (node instanceof CCFGMethodReturnNode)
			return ((CCFGMethodReturnNode) node).getMethod().equals(method);
		else if (node instanceof CCFGMethodEntryNode)
			return ((CCFGMethodEntryNode) node).getMethod().equals(method);
		else if (node instanceof CCFGMethodExitNode)
			return ((CCFGMethodExitNode) node).getMethod().equals(method);
		// frame nodes belong to no method
		return false;
	}

	private void freeMemory() {
		determinedActiveDefs = null;
		determinedFreeUses = null;
		analyzedMethods = null;
	}



	// sanity functions

	/**
	 * Returns true iff the given invocationCount has exceeded the upper limit
	 * defined by UPPER_PAIR_SEARCH_INVOCATION_BOUND
	 */
	private boolean checkInvocationBound(int invocationCount,
			Stack callStack) {

		if (invocationCount % (UPPER_PAIR_SEARCH_INVOCATION_BOUND / 10) == 0) {
			int percent = invocationCount
					/ (UPPER_PAIR_SEARCH_INVOCATION_BOUND / 10);
			System.out.print(percent + "0% .. ");
		}

		if (invocationCount >= UPPER_PAIR_SEARCH_INVOCATION_BOUND) {
			if (!warnedAboutAbortion) {
				System.out.println();
				System.out.println("* ABORTED inter method pair search in "
						+ callStack.peek()
						+ "! Reached maximum invocation limit: "
						+ UPPER_PAIR_SEARCH_INVOCATION_BOUND);
				warnedAboutAbortion = true;
			}
			return true;
		}
		return false;
	}

	/**
	 * If the method on top of the callStack differs from the method of the
	 * given BytecodeInstruction this methods throws an IllegalStateException
	 */
	private void checkCallStackSanity(Stack callStack,
			BytecodeInstruction code) {

		if (!callStack.peek().getCalledMethodName()
				.equals(code.getMethodName())) {

			for (MethodCall mc : callStack) {
				System.out.println("  " + mc.toString());
			}

			throw new IllegalStateException(
					"insane callStack: peek is in method "
							+ callStack.peek().getCalledMethodName()
							+ " and i encountered code of method "
							+ code.getMethodName());
		}
	}

	private void checkFreeUseSanity(BytecodeInstruction freeUse) {
		checkUseSanity(freeUse);

		if (!freeUse.isFieldUse())
			throw new IllegalStateException(
					"expect all freeUses to be Use instructions for field variable");
	}

	private void checkUseSanity(BytecodeInstruction freeUse) {
		if (freeUse == null)
			throw new IllegalStateException(
					"null values not allowed in freeUses map");
		else if (!freeUse.isUse())
			throw new IllegalStateException(
					"expect all freeUses to be Use instructions");
	}

	private void checkDefinitionSanity(BytecodeInstruction activeDef) {
		if (activeDef == null)
			throw new IllegalStateException(
					"null values not allowed in activeDef map");
		else if (!activeDef.isDefinition())
			throw new IllegalStateException(
					"expect all activeDefs to be Definition instructions");
	}

	/**
	 * Copies the given Maps to VariableDefinitions to correpsonding Maps to
	 * BytecodeInstructions and filters out local variables.
	 */
	private Set> toRememberableBytecodeInstructionMap(
			Set> activeDefMaps) {

		Set> r = new HashSet>();

		for (Map activeDefMap : activeDefMaps) {
			Map instructionMap = new HashMap();
			for (String var : activeDefMap.keySet()) {
				VariableDefinition activeDef = activeDefMap.get(var);
				if (activeDef.getDefinition().isLocalDU())
					continue;
				instructionMap.put(var, activeDef.getDefinition());
			}
			r.add(instructionMap);
		}
		return r;
	}


	private Set> createInitialActiveDefs() {
		Set> activeDefs = new HashSet>();
		// add initial activeDefMap
		activeDefs.add(new HashMap());
		return activeDefs;
	}

	private Stack createInitialCallStack(
			CCFGMethodEntryNode publicMethodEntry) {
		Stack callStack = new Stack();
		// null will represent the public method call itself
		callStack.add(new MethodCall(null, publicMethodEntry.getMethod()));

		return callStack;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy