/** * Copyright (C) 2010-2017 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 <http://www.gnu.org/licenses/>. */ package org.evosuite.instrumentation.testability; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.evosuite.PackageInfo; import org.evosuite.Properties; import org.evosuite.TestGenerationContext; import org.evosuite.classpath.ResourceList; import org.evosuite.graphs.cfg.CFGClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <em>Note:</em> Do not inadvertently use multiple instances of this class in * the application! This may lead to hard to detect and debug errors. Yet this * class cannot be an singleton as it might be necessary to do so... * * @author roessler * @author Gordon Fraser */ public class TestabilityTransformationClassLoader extends ClassLoader { private final static Logger logger = LoggerFactory.getLogger(TestabilityTransformationClassLoader.class); private final ClassLoader classLoader; private final Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); /** * <p> * Constructor for InstrumentingClassLoader. * </p> */ public TestabilityTransformationClassLoader() { super(TestabilityTransformationClassLoader.class.getClassLoader()); setClassAssertionStatus(Properties.TARGET_CLASS, true); classLoader = TestabilityTransformationClassLoader.class.getClassLoader(); } /** * 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) { for (String s : getPackagesShouldNotBeInstrumented()) { if (className.startsWith(s)) { return false; } } return true; } /** * <p> * getPackagesShouldNotBeInstrumented * </p> * * @return the names of class packages EvoSuite is not going to instrument */ public static String[] getPackagesShouldNotBeInstrumented() { //explicitly blocking client projects such as specmate is only a //temporary solution, TODO allow the user to specify //packages that should not be instrumented return new String[] { "java.", "javax.", "sun.", PackageInfo.getEvoSuitePackage(), "org.exsyst", "de.unisb.cs.st.testcarver", "de.unisb.cs.st.evosuite", "org.uispec4j", "de.unisb.cs.st.specmate", "org.xml", "org.w3c", "testing.generation.evosuite", "com.yourkit", "com.vladium.emma.", "daikon.", // Need to have these in here to avoid trouble with UnsatisfiedLinkErrors on Mac OS X and Java/Swing apps "apple.", "com.apple.", "com.sun", "org.junit", "junit.framework", "org.apache.xerces.dom3", "de.unisl.cs.st.bugex", "corina.cross.Single" // I really don't know what is wrong with this class, but we need to exclude it }; } /** {@inheritDoc} */ @Override public Class<?> loadClass(String name) throws ClassNotFoundException { //if (instrumentation.isTargetProject(name)) { // if (TestCluster.isTargetClassName(name)) { if (!checkIfCanInstrument(name) //|| (Properties.VIRTUAL_FS && (name.startsWith("org.apache.commons.vfs") || name.startsWith("org.apache.commons.logging"))) ) { Class<?> result = findLoadedClass(name); if (result != null) { return result; } result = classLoader.loadClass(name); return result; } else { Class<?> result = findLoadedClass(name); if (result != null) { return result; } else { result = classes.get(name); if (result != null) { return result; } else { logger.info("Seeing class for first time: " + name); Class<?> instrumentedClass = null; //LoggingUtils.muteCurrentOutAndErrStream(); try { instrumentedClass = instrumentClass(name); } finally { //LoggingUtils.restorePreviousOutAndErrStream(); } return instrumentedClass; } } } //} else { // logger.trace("Not instrumenting: " + name); //} /* Class<?> result = findLoadedClass(name); if (result != null) { return result; } result = classLoader.loadClass(name); return result; */ } private Class<?> instrumentClass(String fullyQualifiedTargetClass) throws ClassNotFoundException { logger.info("Instrumenting class '" + fullyQualifiedTargetClass + "'."); try { String className = fullyQualifiedTargetClass.replace('.', '/'); InputStream is = ResourceList.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getClassAsStream(className); if(is == null){ throw new ClassNotFoundException("Class '" + className + ".class" + "' should be in target project, but could not be found!"); } ClassReader reader = new ClassReader(is); ClassNode classNode = new ClassNode(); reader.accept(classNode, ClassReader.SKIP_FRAMES); ClassVisitor cv = new CFGClassAdapter(classLoader, null, className); classNode.accept(cv); BooleanTestabilityTransformation tt = new BooleanTestabilityTransformation( classNode, this); // cv = new TraceClassVisitor(writer, new // PrintWriter(System.out)); //cv = new TraceClassVisitor(cv, new PrintWriter(System.out)); //cv = new CheckClassAdapter(cv); try { //tt.transform().accept(cv); //if (isTargetClassName(classNameWithDots)) classNode = tt.transform(); } catch (Throwable t) { throw new Error(t); } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES); classNode.accept(writer); byte[] byteBuffer = writer.toByteArray(); Class<?> result = defineClass(fullyQualifiedTargetClass, byteBuffer, 0, byteBuffer.length); classes.put(fullyQualifiedTargetClass, result); logger.info("Keeping class: " + fullyQualifiedTargetClass); return result; } catch (Throwable t) { throw new ClassNotFoundException(t.getMessage(), t); } } }