/* * Copyright 2010 Henry Coles * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package org.pitest.mutationtest.engine.gregor; import static org.pitest.functional.prelude.Prelude.and; import static org.pitest.functional.prelude.Prelude.not; import static org.pitest.util.Functions.classNameToJVMClassName; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.pitest.bytecode.FrameOptions; import org.pitest.bytecode.NullVisitor; import org.pitest.classinfo.ClassByteArraySource; import org.pitest.classinfo.ClassName; import org.pitest.classinfo.ComputeClassWriter; import org.pitest.functional.F; import org.pitest.functional.FCollection; import org.pitest.functional.FunctionalList; import org.pitest.functional.Option; import org.pitest.functional.predicate.Predicate; import org.pitest.mutationtest.engine.Mutant; import org.pitest.mutationtest.engine.Mutater; import org.pitest.mutationtest.engine.MutationDetails; import org.pitest.mutationtest.engine.MutationIdentifier; import org.pitest.mutationtest.engine.gregor.inlinedcode.InlinedCodeFilter; public class GregorMutater implements Mutater { private final Map<String, String> computeCache = new HashMap<String, String>(); private final Predicate<MethodInfo> filter; private final ClassByteArraySource byteSource; private final Set<MethodMutatorFactory> mutators = new HashSet<MethodMutatorFactory>(); private final Set<String> loggingClasses = new HashSet<String>(); private final InlinedCodeFilter inlinedCodeDetector; public GregorMutater(final ClassByteArraySource byteSource, final Predicate<MethodInfo> filter, final Collection<MethodMutatorFactory> mutators, final Collection<String> loggingClasses, final InlinedCodeFilter inlinedCodeDetector) { this.filter = filter; this.mutators.addAll(mutators); this.byteSource = byteSource; this.loggingClasses.addAll(FCollection.map(loggingClasses, classNameToJVMClassName())); this.inlinedCodeDetector = inlinedCodeDetector; } @Override public FunctionalList<MutationDetails> findMutations( final ClassName classToMutate) { final ClassContext context = new ClassContext(); context.setTargetMutation(Option.<MutationIdentifier> none()); return GregorMutater.this.byteSource.getBytes( classToMutate.asInternalName()).flatMap(findMutations(context)); } private F<byte[], Iterable<MutationDetails>> findMutations( final ClassContext context) { return new F<byte[], Iterable<MutationDetails>>() { @Override public Iterable<MutationDetails> apply(final byte[] bytes) { return findMutationsForBytes(context, bytes); } }; } private Collection<MutationDetails> findMutationsForBytes( final ClassContext context, final byte[] classToMutate) { final PremutationClassInfo classInfo = performPreScan(classToMutate); final ClassReader first = new ClassReader(classToMutate); final NullVisitor nv = new NullVisitor(); final MutatingClassVisitor mca = new MutatingClassVisitor(nv, context, filterMethods(), classInfo, this.mutators); first.accept(mca, ClassReader.EXPAND_FRAMES); return this.inlinedCodeDetector.process(context.getCollectedMutations()); } private PremutationClassInfo performPreScan(final byte[] classToMutate) { final ClassReader reader = new ClassReader(classToMutate); final PreMutationAnalyser an = new PreMutationAnalyser(this.loggingClasses); reader.accept(an, 0); return an.getClassInfo(); } @Override public Mutant getMutation(final MutationIdentifier id) { final ClassContext context = new ClassContext(); context.setTargetMutation(Option.some(id)); final Option<byte[]> bytes = this.byteSource.getBytes(id.getClassName() .asJavaName()); final PremutationClassInfo classInfo = performPreScan(bytes.value()); final ClassReader reader = new ClassReader(bytes.value()); final ClassWriter w = new ComputeClassWriter(this.byteSource, this.computeCache, FrameOptions.pickFlags(bytes.value())); final MutatingClassVisitor mca = new MutatingClassVisitor(w, context, filterMethods(), classInfo, FCollection.filter(this.mutators, isMutatorFor(id))); reader.accept(mca, ClassReader.EXPAND_FRAMES); final List<MutationDetails> details = context.getMutationDetails(context .getTargetMutation().value()); return new Mutant(details.get(0), w.toByteArray()); } private static Predicate<MethodMutatorFactory> isMutatorFor( final MutationIdentifier id) { return new Predicate<MethodMutatorFactory>() { @Override public Boolean apply(final MethodMutatorFactory a) { return id.getMutator().equals(a.getGloballyUniqueId()); } }; } @SuppressWarnings("unchecked") private Predicate<MethodInfo> filterMethods() { return and(this.filter, filterSyntheticMethods(), not(isGeneratedEnumMethod()), not(isGroovyClass())); } private static F<MethodInfo, Boolean> isGroovyClass() { return new Predicate<MethodInfo>() { @Override public Boolean apply(final MethodInfo a) { return a.isInGroovyClass(); } }; } private static Predicate<MethodInfo> filterSyntheticMethods() { return new Predicate<MethodInfo>() { @Override public Boolean apply(final MethodInfo a) { // filter out synthetic methods, // except lambda$... methods, which contain code from lambda expressions return !a.isSynthetic() || a.getName().startsWith("lambda$"); } }; } private static Predicate<MethodInfo> isGeneratedEnumMethod() { return new Predicate<MethodInfo>() { @Override public Boolean apply(final MethodInfo a) { return a.isGeneratedEnumMethod(); } }; } }