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

org.evosuite.graphs.cfg.CFGGenerator 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.graphs.cfg;

import java.util.List;

import org.evosuite.graphs.GraphPool;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This classed is used to create the RawControlFlowGraph which can then be used
 * to create the ActualControlFlowGraph
 * 
 * When analyzing a CUT the BytecodeAnalyzer creates an instance of this class
 * for each method contained in it
 * 
 * This class's methods get called in the following order:
 * 
 * - upon constructing, the method at hand is registered via
 * registerMethodNode() which fills the BytecodeInstructionPool with all
 * instructions inside that method
 * 
 * - then registerControlFlowEdge() is called by the BytecodeAnalyzer for each
 * possible transition from one byteCode instruction to another within the
 * current method. In this step the CFGGenerator asks the
 * BytecodeInstructionPool for the previously created instructions and fills up
 * it's RawControlFlowGraph
 * 
 * After those calls the RawControlFlowGraph of the method at hand is complete
 * It should contain a Vertex for each BytecodeInstruction inside the specified
 * method and an edge for every possible transition between these instructions
 * 
 * @author Andre Mis
 */
public class CFGGenerator {

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

	private RawControlFlowGraph rawGraph;

	private boolean nodeRegistered = false;
	private MethodNode currentMethod;
	private String className;
	private String methodName;
	private final ClassLoader classLoader;

	/**
	 * Initializes this generator to generate the CFG for the method identified
	 * by the given parameters
	 * 
	 * Calls registerMethodNode() which in turn calls
	 * BytecodeInstructionPool.registerMethodNode() leading to the creation of
	 * all BytecodeInstruction instances for the method at hand
	 * 
	 * TODO might not want to give asm.MethodNode to the outside, but rather a
	 * MyMethodNode extended from BytecodeInstruction or something
	 * 
	 * @param className
	 *            a {@link java.lang.String} object.
	 * @param methodName
	 *            a {@link java.lang.String} object.
	 * @param node
	 *            a {@link org.objectweb.asm.tree.MethodNode} object.
	 */
	public CFGGenerator(ClassLoader classLoader, String className, String methodName,
	        MethodNode node) {
		this.classLoader = classLoader;
		registerMethodNode(node, className, methodName);
	}

	/**
	 * Adds the RawControlFlowGraph created by this instance to the GraphPool,
	 * computes the resulting ActualControlFlowGraph
	 */
	public void registerCFGs() {

		int removed = getRawGraph().removeIsolatedNodes();
		if (removed > 0)
			logger.info("removed isolated nodes: " + removed + " in " + methodName);

		// non-minimized cfg needed for defuse-coverage and control
		// dependence calculation
		GraphPool.getInstance(classLoader).registerRawCFG(getRawGraph());
		GraphPool.getInstance(classLoader).registerActualCFG(computeActualCFG());
	}

	// build up the graph

	private void registerMethodNode(MethodNode currentMethod, String className,
	        String methodName) {
		if (nodeRegistered)
			throw new IllegalStateException(
			        "registerMethodNode must not be called more than once for each instance of CFGGenerator");
		if (currentMethod == null || methodName == null || className == null)
			throw new IllegalArgumentException("null given");

		this.currentMethod = currentMethod;
		this.className = className;
		this.methodName = methodName;

		this.rawGraph = new RawControlFlowGraph(classLoader, className, methodName,
		        currentMethod.access);

		List instructionsInMethod = BytecodeInstructionPool.getInstance(classLoader).registerMethodNode(currentMethod,
		                                                                                                                     className,
		                                                                                                                     methodName);

		// sometimes there is a Label at the very end of a method without a
		// controlFlowEdge to it. In order to keep the graph as connected as
		// possible and since this is just a label we will simply ignore these
		int count = 0;
		for (BytecodeInstruction ins : instructionsInMethod) {
			count++;
			if (!ins.isLabel() || count < instructionsInMethod.size())
				rawGraph.addVertex(ins);
		}

		nodeRegistered = true;
	}

	/**
	 * Internal management of fields and actual building up of the rawGraph
	 * 
	 * Is called by the corresponding BytecodeAnalyzer whenever it detects a
	 * control flow edge
	 * 
	 * @param src
	 *            a int.
	 * @param dst
	 *            a int.
	 * @param frames
	 *            an array of {@link org.objectweb.asm.tree.analysis.Frame}
	 *            objects.
	 * @param isExceptionEdge
	 *            a boolean.
	 */
	public void registerControlFlowEdge(int src, int dst, Frame[] frames,
	        boolean isExceptionEdge) {
		if (!nodeRegistered)
			throw new IllegalStateException(
			        "CFGGenrator.registerControlFlowEdge() cannot be called unless registerMethodNode() was called first");
		if (frames == null)
			throw new IllegalArgumentException("null given");
		CFGFrame srcFrame = (CFGFrame) frames[src];
		Frame dstFrame = frames[dst];

		if (srcFrame == null)
			throw new IllegalArgumentException(
			        "expect given frames to know srcFrame for " + src);

		if (dstFrame == null) {
			// documentation of getFrames() tells us the following:
			// Returns:
			// the symbolic state of the execution stack frame at each bytecode
			// instruction of the method. The size of the returned array is
			// equal to the number of instructions (and labels) of the method. A
			// given frame is null if the corresponding instruction cannot be
			// reached, or if an error occured during the analysis of the
			// method.
			// so let's say we expect the analyzer to return null only if
			// dst is not reachable and if that happens we just suppress the
			// corresponding ControlFlowEdge for now
			// TODO can the CFG become disconnected like that?
			return;
		}

		srcFrame.successors.put(dst, (CFGFrame) dstFrame);

		AbstractInsnNode srcNode = currentMethod.instructions.get(src);
		AbstractInsnNode dstNode = currentMethod.instructions.get(dst);

		// those nodes should have gotten registered by registerMethodNode()
		BytecodeInstruction srcInstruction = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
		                                                                                                     methodName,
		                                                                                                     src,
		                                                                                                     srcNode);
		BytecodeInstruction dstInstruction = BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
		                                                                                                     methodName,
		                                                                                                     dst,
		                                                                                                     dstNode);

		srcInstruction.setCFGFrame(srcFrame);

		if (dstInstruction == null)
			throw new IllegalStateException(
			        "expect BytecodeInstructionPool to know the instructions in the method of this edge");

		if (null == rawGraph.addEdge(srcInstruction, dstInstruction, isExceptionEdge))
			logger.error("internal error while adding edge");
	}

	/**
	 * Computes the ActualCFG with BasicBlocks rather then BytecodeInstructions
	 * for this RawCFG.
	 * 
	 * See ActualControlFlowGraph and GraphPool for further details.
	 * 
	 * @return a {@link org.evosuite.graphs.cfg.ActualControlFlowGraph} object.
	 */
	public ActualControlFlowGraph computeActualCFG() {
		BytecodeInstructionPool.getInstance(classLoader).logInstructionsIn(className,
		                                                                   methodName);

		ActualControlFlowGraph cfg = new ActualControlFlowGraph(rawGraph);

		return cfg;
	}

	// getter

	/**
	 * 

* Getter for the field rawGraph. *

* * @return a {@link org.evosuite.graphs.cfg.RawControlFlowGraph} object. */ protected RawControlFlowGraph getRawGraph() { return rawGraph; } /** *

* Getter for the field className. *

* * @return a {@link java.lang.String} object. */ public String getClassName() { return className; } /** *

* Getter for the field methodName. *

* * @return a {@link java.lang.String} object. */ public String getMethodName() { return methodName; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy