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

org.evosuite.regression.bytecode.RegressionClassDiff 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.regression.bytecode;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import java.util.Objects;
import org.evosuite.TestGenerationContext;
import org.evosuite.classpath.ResourceList;
import org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.objectweb.asm.ClassReader;
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.MethodNode;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author sina
 * 
 */
public class RegressionClassDiff {

  protected static final Logger logger = LoggerFactory.getLogger(RegressionClassDiff.class);

  /*
   * Is the underlying code of two java classes, one on ProjectCP and one on regression_cp,
   * different?
   */
  public static boolean differentAcrossClassloaders(String classFullPath) {

    InputStream originalClassIS =
        ResourceList.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
            .getClassAsStream(classFullPath);
    InputStream regressionClassIS = ResourceList
        .getInstance(TestGenerationContext.getInstance().getRegressionClassLoaderForSUT())
        .getClassAsStream(classFullPath);

    boolean different = false;

    Map> methodInstructionsA =
        RegressionClassDiff.getClassInstructions(originalClassIS);
    Map> methodInstructionsB =
        RegressionClassDiff.getClassInstructions(regressionClassIS);

    int sizeA = methodInstructionsA.size();
    int sizeB = methodInstructionsB.size();

    // If the size of the lists differs, they have different methods and are obviously different
    if (sizeA != sizeB)
      different = true;
    else
      for (Entry> e : methodInstructionsA.entrySet()) {
        List miA = e.getValue();
        List miB = methodInstructionsB.get(e.getKey());
        // using .equals as the order of instructions matters
        if (miB == null || !miA.equals(miB)) {
          different = true;
          break;
        }

      }

    // logger.warn("Were not equal? {}", different);

    if (!different) {
      logger.warn("class {} was equal on both versions", classFullPath);
    } else {
      logger.warn("class {} has been modified between the two versions provided", classFullPath);
      logger.debug("Different Classes: classA {}", methodInstructionsA);
      logger.debug("Different Classes: classB {}", methodInstructionsB);
    }



    return different;
  }

  /*
   * Get bytecode instructions for the class based on the following format: Method -> List of
   * instructions
   */
  private static Map> getClassInstructions(InputStream classAsInputStream) {
    HashMap> methodInstructionsMap = new HashMap<>();
    try {
      ClassReader reader = new ClassReader(classAsInputStream);
      ClassNode classNode = new ClassNode();
      reader.accept(classNode, 0);
      @SuppressWarnings("unchecked")
      final List methods = classNode.methods;
      Printer printer = new Textifier();
      TraceMethodVisitor mp = new TraceMethodVisitor(printer);
      for (MethodNode m : methods) {
        List instructions = new ArrayList<>();

        InsnList inList = m.instructions;

        String mathodID = m.name + ": " + m.desc;
        System.out.println(mathodID);
        int[] methodInstructions = new int[inList.size()];
        for (int i = 0; i < inList.size(); i++) {
          int op = inList.get(i).getOpcode();
          methodInstructions[i] = op;
          AbstractInsnNode insn = inList.get(i);
          insn.accept(mp);

          // Uncomment the following comment block to print the bytecode
          // instructions
          // StringWriter sw = new StringWriter();
          // printer.print(new PrintWriter(sw));
          // printer.getText().clear();
          // System.out.println(sw.toString());
          // logger.warn("{} -> {}", sw.toString(), op);
          if (op != -1)
            instructions.add(op);
        }
        methodInstructionsMap.put(mathodID, instructions);
      }
    } catch (IOException e) {
      // Will fail if ClassReader fails
      e.printStackTrace();
    }
    return methodInstructionsMap;
  }

  public static boolean sameCFG() {
    Collection branchesOriginal = BranchPool
        .getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getAllBranches();
    Collection branchesRegression =
        BranchPool.getInstance(TestGenerationContext.getInstance().getRegressionClassLoaderForSUT())
            .getAllBranches();

    if (branchesOriginal.size() != branchesRegression.size()) {
      logger.error("Different number of branches between two versions: {} vs {}", branchesOriginal.size(), branchesRegression.size());
      return false;
    }

    Iterator branchesOriginalIterator = branchesOriginal.iterator();
    Iterator branchesRegressionIterator = branchesRegression.iterator();

    boolean sameBranches = true;

    while (branchesOriginalIterator.hasNext()) {
      Branch bOrig = branchesOriginalIterator.next();
      Branch bReg = branchesRegressionIterator.next();

      int bOrigOpcode = bOrig.getInstruction().getASMNode().getOpcode();
      int bRegOpcode = bReg.getInstruction().getASMNode().getOpcode();

      // Are branches from the same family of branches?
      if (!Objects.equals(RegressionClassDiff.getBranchFamily(bOrigOpcode), RegressionClassDiff
          .getBranchFamily(bRegOpcode))) {
        logger.error("Different family found between branches: {} vs {}", bOrigOpcode, bRegOpcode);
        sameBranches = false;
        break;
      }



    }
    return sameBranches;
  }

  private static String getBranchFamily(int opcode) {
    // The default family is the opcode itself
    // Admittedly we could've use ints/enums for performance, but strings should be interned anyway
    String family = "" + opcode;
    switch (opcode) {
      // copmpare int with zero
      case Opcodes.IFEQ:
      case Opcodes.IFNE:
      case Opcodes.IFLT:
      case Opcodes.IFGE:
      case Opcodes.IFGT:
      case Opcodes.IFLE:
        family = "int_zero";
        break;
      // copmpare int with int
      case Opcodes.IF_ICMPEQ:
      case Opcodes.IF_ICMPNE:
      case Opcodes.IF_ICMPLT:
      case Opcodes.IF_ICMPGE:
      case Opcodes.IF_ICMPGT:
      case Opcodes.IF_ICMPLE:
        family = "int_int";
        break;
      // copmpare reference with reference
      case Opcodes.IF_ACMPEQ:
      case Opcodes.IF_ACMPNE:
        family = "ref_ref";
        break;
      // compare reference with null
      case Opcodes.IFNULL:
      case Opcodes.IFNONNULL:
        family = "ref_null";
        break;

    }
    return family;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy