/*
* 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 java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.pitest.classinfo.ClassName;
import org.pitest.functional.F;
import org.pitest.mutationtest.engine.Location;
import org.pitest.mutationtest.engine.MethodName;
import org.pitest.mutationtest.engine.gregor.analysis.InstructionTrackingMethodVisitor;
import org.pitest.mutationtest.engine.gregor.blocks.BlockTrackingMethodDecorator;
class MutatingClassVisitor extends ClassVisitor {
private final F<MethodInfo, Boolean> filter;
private final ClassContext context;
private final Set<MethodMutatorFactory> methodMutators = new HashSet<MethodMutatorFactory>();
private final PremutationClassInfo classInfo;
MutatingClassVisitor(final ClassVisitor delegateClassVisitor,
final ClassContext context, final F<MethodInfo, Boolean> filter,
final PremutationClassInfo classInfo,
final Collection<MethodMutatorFactory> mutators) {
super(Opcodes.ASM5, delegateClassVisitor);
this.context = context;
this.filter = filter;
this.methodMutators.addAll(mutators);
this.classInfo = classInfo;
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName, final String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
this.context.registerClass(new ClassInfo(version, access, name, signature,
superName, interfaces));
}
@Override
public void visitSource(final String source, final String debug) {
super.visitSource(source, debug);
this.context.registerSourceFile(source);
}
@Override
public MethodVisitor visitMethod(final int access, final String methodName,
final String methodDescriptor, final String signature,
final String[] exceptions) {
MethodMutationContext methodContext = new MethodMutationContext(
this.context, Location.location(
ClassName.fromString(this.context.getClassInfo().getName()),
MethodName.fromString(methodName), methodDescriptor));
final MethodVisitor methodVisitor = this.cv.visitMethod(access, methodName,
methodDescriptor, signature, exceptions);
final MethodInfo info = new MethodInfo()
.withOwner(this.context.getClassInfo()).withAccess(access)
.withMethodName(methodName).withMethodDescriptor(methodDescriptor);
if (this.filter.apply(info)) {
return this.visitMethodForMutation(methodContext, info, methodVisitor);
} else {
return methodVisitor;
}
}
private MethodVisitor visitMethodForMutation(
MethodMutationContext methodContext, final MethodInfo methodInfo,
final MethodVisitor methodVisitor) {
MethodVisitor next = methodVisitor;
for (final MethodMutatorFactory each : this.methodMutators) {
next = each.create(methodContext, methodInfo, next);
}
return new InstructionTrackingMethodVisitor(wrapWithDecorators(
methodContext, wrapWithFilters(methodContext, next)), methodContext);
}
private static MethodVisitor wrapWithDecorators(MethodMutationContext methodContext,
final MethodVisitor mv) {
return wrapWithAnnotationFilter(methodContext, wrapWithBlockTracker(methodContext,
wrapWithLineTracker(methodContext, mv)));
}
private static MethodVisitor wrapWithBlockTracker(
MethodMutationContext methodContext, final MethodVisitor mv) {
return new BlockTrackingMethodDecorator(methodContext, mv);
}
private static MethodVisitor wrapWithLineTracker(
MethodMutationContext methodContext, final MethodVisitor mv) {
return new LineTrackingMethodVisitor(methodContext, mv);
}
private MethodVisitor wrapWithFilters(MethodMutationContext methodContext,
final MethodVisitor wrappedMethodVisitor) {
return wrapWithLineFilter(methodContext,
wrapWithAssertFilter(methodContext, wrappedMethodVisitor));
}
private static MethodVisitor wrapWithAssertFilter(
MethodMutationContext methodContext,
final MethodVisitor wrappedMethodVisitor) {
return new AvoidAssertsMethodAdapter(methodContext, wrappedMethodVisitor);
}
private MethodVisitor wrapWithLineFilter(MethodMutationContext methodContext,
final MethodVisitor wrappedMethodVisitor) {
return new LineFilterMethodAdapter(methodContext, this.classInfo,
wrappedMethodVisitor);
}
private static MethodVisitor wrapWithAnnotationFilter(
MethodMutationContext methodContext,
final MethodVisitor wrappedMethodVisitor) {
return new AvoidAnnotatedMethodsFilter(methodContext, wrappedMethodVisitor);
}
}