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

org.evosuite.instrumentation.BytecodeInstrumentation 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.instrumentation;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.evosuite.PackageInfo;
import org.evosuite.Properties;
import org.evosuite.assertion.CheapPurityAnalyzer;
import org.evosuite.classpath.ResourceList;
import org.evosuite.graphs.cfg.CFGClassAdapter;
import org.evosuite.instrumentation.error.ErrorConditionClassAdapter;
import org.evosuite.instrumentation.testability.BooleanTestabilityTransformation;
import org.evosuite.instrumentation.testability.ComparisonTransformation;
import org.evosuite.instrumentation.testability.ContainerTransformation;
import org.evosuite.instrumentation.testability.StringTransformation;
import org.evosuite.junit.writer.TestSuiteWriterUtils;
import org.evosuite.runtime.RuntimeSettings;
import org.evosuite.runtime.instrumentation.*;
import org.evosuite.seeding.PrimitiveClassAdapter;
import org.evosuite.setup.DependencyAnalysis;
import org.evosuite.setup.TestCluster;
import org.evosuite.testcarver.instrument.Instrumenter;
import org.evosuite.testcarver.instrument.TransformerUtil;
import org.evosuite.runtime.util.ComputeClassWriter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.SerialVersionUIDAdder;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.TraceClassVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The bytecode transformer - transforms bytecode depending on package and
 * whether it is the class under test
 *
 * @author Gordon Fraser
 */
public class BytecodeInstrumentation {

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

	private final Instrumenter testCarvingInstrumenter;

	/**
	 * 

* Constructor for BytecodeInstrumentation. *

*/ public BytecodeInstrumentation() { this.testCarvingInstrumenter = new Instrumenter(); } private static String[] getEvoSuitePackages() { return new String[] { PackageInfo.getEvoSuitePackage(), "org.exsyst", "de.unisb.cs.st.testcarver", "de.unisb.cs.st.evosuite", "testing.generation.evosuite", "de.unisl.cs.st.bugex" }; } /** * Check if we can instrument the given class * * @param className * a {@link java.lang.String} object. * @return a boolean. */ public static boolean checkIfCanInstrument(String className) { return RuntimeInstrumentation.checkIfCanInstrument(className); } /** * Check if we the class belongs to an EvoSuite package * * @param className * a {@link java.lang.String} object. * @return a boolean. */ public static boolean checkIfEvoSuitePackage(String className) { for (String s : BytecodeInstrumentation.getEvoSuitePackages()) { if (className.startsWith(s)) { return true; } } return false; } /** *

* shouldTransform *

* * @param className * a {@link java.lang.String} object. * @return a boolean. */ public boolean shouldTransform(String className) { if (!Properties.TT) return false; switch (Properties.TT_SCOPE) { case ALL: logger.info("Allowing transformation of " + className); return true; case TARGET: if (className.equals(Properties.TARGET_CLASS) || className.startsWith(Properties.TARGET_CLASS + "$")) return true; break; case PREFIX: if (className.startsWith(Properties.PROJECT_PREFIX)) return true; } logger.info("Preventing transformation of " + className); return false; } private boolean isTargetClassName(String className) { // TODO: Need to replace this in the long term return TestCluster.isTargetClassName(className); } /** *

* transformBytes *

* * @param className * a {@link java.lang.String} object. * @param reader * a {@link org.objectweb.asm.ClassReader} object. * @return an array of byte. */ public byte[] transformBytes(ClassLoader classLoader, String className, ClassReader reader) { int readFlags = ClassReader.SKIP_FRAMES; if (Properties.INSTRUMENTATION_SKIP_DEBUG) readFlags |= ClassReader.SKIP_DEBUG; String classNameWithDots = ResourceList.getClassNameFromResourcePath(className); if (!checkIfCanInstrument(classNameWithDots)) { throw new RuntimeException("Should not transform a shared class (" + classNameWithDots + ")! Load by parent (JVM) classloader."); } TransformationStatistics.reset(); /* * To use COMPUTE_FRAMES we need to remove JSR commands. Therefore, we * have a JSRInlinerAdapter in NonTargetClassAdapter as well as * CFGAdapter. */ int asmFlags = ClassWriter.COMPUTE_FRAMES; ClassWriter writer = new ComputeClassWriter(asmFlags); ClassVisitor cv = writer; if (logger.isDebugEnabled()) { cv = new TraceClassVisitor(cv, new PrintWriter(System.err)); } if (Properties.RESET_STATIC_FIELDS) { cv = new StaticAccessClassAdapter(cv, className); } if (Properties.PURE_INSPECTORS) { CheapPurityAnalyzer purityAnalyzer = CheapPurityAnalyzer.getInstance(); cv = new PurityAnalysisClassVisitor(cv, className, purityAnalyzer); } if (Properties.MAX_LOOP_ITERATIONS >= 0) { cv = new LoopCounterClassAdapter(cv); } // Apply transformations to class under test and its owned classes if (DependencyAnalysis.shouldAnalyze(classNameWithDots)) { logger.debug("Applying target transformation to class " + classNameWithDots); if (!Properties.TEST_CARVING && Properties.MAKE_ACCESSIBLE) { cv = new AccessibleClassAdapter(cv, className); } cv = new RemoveFinalClassAdapter(cv); cv = new ExecutionPathClassAdapter(cv, className); cv = new CFGClassAdapter(classLoader, cv, className); if (Properties.EXCEPTION_BRANCHES) { cv = new ExceptionTransformationClassAdapter(cv, className); } if (Properties.ERROR_BRANCHES) { cv = new ErrorConditionClassAdapter(cv, className); } } else { logger.debug("Not applying target transformation"); cv = new NonTargetClassAdapter(cv, className); if (Properties.MAKE_ACCESSIBLE) { cv = new AccessibleClassAdapter(cv, className); } // If we are doing testability transformation on all classes we need // to create the CFG first if (Properties.TT && classNameWithDots.startsWith(Properties.CLASS_PREFIX)) { cv = new CFGClassAdapter(classLoader, cv, className); } } // Collect constant values for the value pool cv = new PrimitiveClassAdapter(cv, className); if (Properties.RESET_STATIC_FIELDS) { cv = handleStaticReset(className, cv); } // Mock instrumentation (eg File and TCP). if (TestSuiteWriterUtils.needToUseAgent()) { cv = new MethodCallReplacementClassAdapter(cv, className); /* * If the class is serializable, then doing any change (adding hashCode, static reset, etc) * will change the serialVersionUID if it is not defined in the class. * Hence, if it is not defined, we have to define it to * avoid problems in serialising the class, as reading Master will not do instrumentation. * The serialVersionUID HAS to be the same as the un-instrumented class */ if(RuntimeSettings.applyUIDTransformation) cv = new SerialVersionUIDAdder(cv); } // Testability Transformations if (classNameWithDots.startsWith(Properties.PROJECT_PREFIX) || (!Properties.TARGET_CLASS_PREFIX.isEmpty() && classNameWithDots.startsWith(Properties.TARGET_CLASS_PREFIX)) || shouldTransform(classNameWithDots)) { ClassNode cn = new AnnotatedClassNode(); reader.accept(cn, readFlags); logger.info("Starting transformation of " + className); if (Properties.STRING_REPLACEMENT) { StringTransformation st = new StringTransformation(cn); if (isTargetClassName(classNameWithDots) || shouldTransform(classNameWithDots)) cn = st.transform(); } ComparisonTransformation cmp = new ComparisonTransformation(cn); if (isTargetClassName(classNameWithDots) || shouldTransform(classNameWithDots)) { cn = cmp.transform(); ContainerTransformation ct = new ContainerTransformation(cn); cn = ct.transform(); } if (shouldTransform(classNameWithDots)) { logger.info("Testability Transforming " + className); BooleanTestabilityTransformation tt = new BooleanTestabilityTransformation(cn, classLoader); try { cn = tt.transform(); } catch (Throwable t) { throw new Error(t); } logger.info("Testability Transformation done: " + className); } // ----- cn.accept(cv); if (Properties.TEST_CARVING && TransformerUtil.isClassConsideredForInstrumentation(className)) { return handleCarving(className, writer); } } else { reader.accept(cv, readFlags); } return writer.toByteArray(); } private byte[] handleCarving(String className, ClassWriter writer) { ClassReader cr = new ClassReader(writer.toByteArray()); ClassNode cn2 = new ClassNode(); cr.accept(cn2, ClassReader.EXPAND_FRAMES); this.testCarvingInstrumenter.transformClassNode(cn2, className); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cn2.accept(cw); if (logger.isDebugEnabled()) { final StringWriter sw = new StringWriter(); cn2.accept(new TraceClassVisitor(new PrintWriter(sw))); logger.debug("test carving instrumentation result:\n{}", sw); } return cw.toByteArray(); } /** * Adds the instrumentation to deal with re-iniatilizing classes: adding * __STATIC_RESET() methods, inserting callbacks for PUTSTATIC and GETSTATIC * instructions * * @param className * @param cv * @return */ private static ClassVisitor handleStaticReset(String className, ClassVisitor cv) { // Create a __STATIC_RESET() cloning the original method or // create one by default final CreateClassResetClassAdapter resetClassAdapter; if (Properties.RESET_STATIC_FINAL_FIELDS) { resetClassAdapter= new CreateClassResetClassAdapter(cv, className, true); } else { resetClassAdapter= new CreateClassResetClassAdapter(cv, className, false); } cv = resetClassAdapter; // Adds a callback before leaving the method EndOfClassInitializerVisitor exitClassInitAdapter = new EndOfClassInitializerVisitor(cv, className); cv = exitClassInitAdapter; return cv; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy