org.evosuite.setup.CallTreeGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.setup.CallTreeGenerator.java

Source

/**
 * Copyright (C) 2011,2012 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 Public License as published by the Free Software Foundation,
 * either version 3 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 Public License for more details.
 * 
 * You should have received a copy of the GNU Public License along with
 * EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * 
 */
package org.evosuite.setup;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.evosuite.Properties;
import org.evosuite.instrumentation.BytecodeInstrumentation;
import org.evosuite.utils.LoggingUtils;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Gordon Fraser
 * 
 */
public class CallTreeGenerator {

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

    public static CallTree analyze(String className) {
        ClassNode targetClass = DependencyAnalysis.getClassNode(className);

        CallTree callTree = new CallTree(className);
        if (targetClass != null)
            handle(callTree, targetClass, 0);
        if (Properties.INSTRUMENT_PARENT) {
            handleSuperClasses(callTree, targetClass);
        }
        return callTree;
    }

    private static boolean isOverridden(String methodName) {
        return true;
    }

    /**
     * If we want to have the calltree also for the superclasses, we need to
     * determine which methods are callable
     * 
     * @param callTree
     * @param targetClass
     */
    @SuppressWarnings("unchecked")
    private static void handleSuperClasses(CallTree callTree, ClassNode targetClass) {
        String superClassName = targetClass.superName;
        if (superClassName == null || superClassName.isEmpty())
            return;

        if (superClassName.equals("java/lang/Object"))
            return;

        logger.debug("Creating calltree for superclass: {}", superClassName);
        ClassNode superClass = DependencyAnalysis.getClassNode(superClassName);
        List<MethodNode> methods = superClass.methods;
        for (MethodNode mn : methods) {
            logger.debug("Method: {}", mn.name);

            // Do not check super-constructors
            if (mn.name.equals("<init>"))
                continue;
            if (mn.name.equals("<clinit>"))
                continue;

            // Skip abstract etc
            if ((mn.access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT)
                continue;

            // Do not handle classes if they are overridden by the subclass
            if ((mn.access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
                if (!isOverridden(mn.name + mn.desc)) {
                    handleMethodNode(callTree, superClass, mn, 0);
                }
            }
        }
        handleSuperClasses(callTree, superClass);

    }

    @SuppressWarnings("unchecked")
    private static void handle(CallTree callTree, ClassNode targetClass, int depth) {
        List<MethodNode> methods = targetClass.methods;
        for (MethodNode mn : methods) {
            logger.debug("Method: {}", mn.name);
            handleMethodNode(callTree, targetClass, mn, depth);
        }
    }

    @SuppressWarnings("unchecked")
    private static void handle(CallTree callTree, ClassNode targetClass, String methodName, int depth) {
        List<MethodNode> methods = targetClass.methods;
        for (MethodNode mn : methods) {
            if (methodName.equals(mn.name + mn.desc))
                handleMethodNode(callTree, targetClass, mn, depth);
        }
    }

    private static void handle(CallTree callTree, String className, String methodName, int depth) {
        ClassNode cn = DependencyAnalysis.getClassNode(className);
        if (cn == null)
            return;

        handle(callTree, cn, methodName, depth);
    }

    /**
     * Add all possible calls for a given method
     * 
     * @param callGraph
     * @param mn
     */
    @SuppressWarnings("unchecked")
    private static void handleMethodNode(CallTree callTree, ClassNode cn, MethodNode mn, int depth) {
        handlePublicMethodNode(callTree, cn, mn);

        InsnList instructions = mn.instructions;
        Iterator<AbstractInsnNode> iterator = instructions.iterator();

        // TODO: This really shouldn't be here but in its own class
        while (iterator.hasNext()) {
            AbstractInsnNode insn = iterator.next();
            if (insn instanceof MethodInsnNode) {
                handleMethodInsnNode(callTree, cn, mn, (MethodInsnNode) insn, depth + 1);
            }
        }
    }

    private static void handlePublicMethodNode(CallTree callTree, ClassNode cn, MethodNode mn) {
        if ((mn.access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
            callTree.addPublicMethod(cn.name, mn.name + mn.desc);
        }
    }

    /**
     * Descend into a call
     * 
     * @param callGraph
     * @param mn
     * @param methodCall
     */
    private static void handleMethodInsnNode(CallTree callTree, ClassNode cn, MethodNode mn,
            MethodInsnNode methodCall, int depth) {

        // Only build calltree for instrumentable classes
        if (BytecodeInstrumentation.checkIfCanInstrument(methodCall.owner.replaceAll("/", "."))) {
            logger.debug("Handling method: {}", methodCall.name);
            if (!callTree.hasCall(cn.name, mn.name + mn.desc, methodCall.owner,
                    methodCall.name + methodCall.desc)) {

                // Add call from mn to methodCall to callgraph
                callTree.addCall(cn.name, mn.name + mn.desc, methodCall.owner, methodCall.name + methodCall.desc);

                handle(callTree, methodCall.owner, methodCall.name + methodCall.desc, depth);
            }
        }
    }

    /**
     * Update connections in the call tree according to the inheritance: For
     * each connection, if the method is overridden in a subclass add a
     * connection to the method in the subclass
     * 
     * @param callTree
     * @param inheritanceTree
     */
    static void update(CallTree callTree, InheritanceTree inheritanceTree) {
        logger.info("Updating call tree ");

        Set<CallTreeEntry> subclassCalls = new LinkedHashSet<CallTreeEntry>();
        for (CallTreeEntry call : callTree) {
            String targetClass = call.getTargetClass();
            String targetMethod = call.getTargetMethod();

            // Ignore constructors
            if (targetMethod.startsWith("<init>"))
                continue;

            // Ignore calls to Array (e.g. clone())
            if (targetClass.startsWith("["))
                continue;

            if (!inheritanceTree.hasClass(targetClass)) {
                // Private classes are not in the inheritance tree
                //LoggingUtils.getEvoLogger().warn("Inheritance tree does not contain {}, please check classpath",
                //                                 targetClass);
                continue;
            }

            for (String subclass : inheritanceTree.getSubclasses(targetClass)) {
                if (inheritanceTree.isMethodDefined(subclass, targetMethod)) {
                    subclassCalls.add(new CallTreeEntry(call.getSourceClass(), call.getSourceMethod(), subclass,
                            targetMethod));
                }
            }
        }
        callTree.addCalls(subclassCalls);
    }

}