
net.sourceforge.cobertura.instrument.pass3.InjectCodeClassInstrumenter Maven / Gradle / Ivy
Show all versions of cobertura Show documentation
/*
* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2011 Piotr Tabor
*
* Note: This file is dual licensed under the GPL and the Apache
* Source License (so that it can be used from both the main
* Cobertura classes and the ant tasks).
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package net.sourceforge.cobertura.instrument.pass3;
import net.sourceforge.cobertura.instrument.AbstractFindTouchPointsClassInstrumenter;
import net.sourceforge.cobertura.instrument.FindTouchPointsMethodAdapter;
import net.sourceforge.cobertura.instrument.tp.ClassMap;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.LocalVariablesSorter;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
* This class is responsible for real instrumentation of the user's class.
*
* It uses information acquired
* by {@link BuildClassMapClassVisitor} ( {@link #classMap} ) and
* {@link DetectDuplicatedCodeClassVisitor} and injects
* code snippet provided by {@link CodeProvider} ( {@link #codeProvider} ).
*
* @author [email protected]
*/
public class InjectCodeClassInstrumenter
extends
AbstractFindTouchPointsClassInstrumenter {
/**
* This class is responsible for injecting code inside 'interesting places' of methods inside instrumented class
*/
private final InjectCodeTouchPointListener touchPointListener;
/**
* {@link ClassMap} generated in previous instrumentation pass by {@link BuildClassMapClassVisitor}
*/
private final ClassMap classMap;
/**
* {@link CodeProvider} used to generate pieces of asm code that is injected into instrumented class.
*
* We are strictly recommending here using {@link FastArrayCodeProvider} instead of {@link AtomicArrayCodeProvider} because
* of performance.
*/
private final CodeProvider codeProvider;
/**
* When we processing the class we want to now if we processed 'static initialization block' (clinit method).
*
* If there is no such a method in the instrumented class - we will need to generate it at the end
*/
private boolean wasStaticInitMethodVisited = false;
private final Set ignoredMethods;
/**
* @param cv - a listener for code-instrumentation events
* @param ignoreRegexes - list of patters of method calls that should be ignored from line-coverage-measurement
* @param classMap - map of all interesting places in the class. You should acquire it by {@link BuildClassMapClassVisitor} and remember to
* prepare it using {@link ClassMap#assignCounterIds()} before using it with {@link InjectCodeClassInstrumenter}
* @param duplicatedLinesMap - map of found duplicates in the class. You should use {@link DetectDuplicatedCodeClassVisitor} to find the duplicated lines.
*/
public InjectCodeClassInstrumenter(ClassVisitor cv,
Collection ignoreRegexes, boolean threadsafeRigorous,
ClassMap classMap,
Map> duplicatedLinesMap,
Set ignoredMethods) {
super(cv, ignoreRegexes, duplicatedLinesMap);
this.classMap = classMap;
this.ignoredMethods = ignoredMethods;
codeProvider = threadsafeRigorous
? new AtomicArrayCodeProvider()
: new FastArrayCodeProvider();
touchPointListener = new InjectCodeTouchPointListener(classMap,
codeProvider);
}
/**
* Marks the class 'already instrumented' and injects code connected to the fields that are keeping counters.
*/
@Override
public void visit(int version, int access, String name, String signature,
String supertype, String[] interfaces) {
super.visit(version, access, name, signature, supertype, interfaces);
codeProvider.generateCountersField(cv);
}
/**
* Instrumenting a code in a single method. Special conditions for processing 'static initialization block'.
*
* This method also uses {@link ShiftVariableMethodAdapter} that is used firstly to calculate the index of internal
* variable injected to store information about last 'processed' jump or switch in runtime ( {@link ShiftVariableMethodAdapter#calculateFirstStackVariable(int, String)} ),
* and then is used to inject code responsible for keeping the variable and shifting (+1) all previously seen variables.
*/
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature,
exceptions);
if (ignoredMethods.contains(name + desc)) {
return mv;
}
if ((access & Opcodes.ACC_STATIC) != 0) {
mv = new GenerateCallCoberturaInitMethodVisitor(mv, classMap
.getClassName());
if ("".equals(name)) {
wasStaticInitMethodVisited = true;
}
}
FindTouchPointsMethodAdapter instrumenter = new FindTouchPointsMethodAdapter(
mv, classMap.getClassName(), name, desc, eventIdGenerator,
duplicatedLinesMap, lineIdGenerator);
instrumenter.setTouchPointListener(touchPointListener);
instrumenter.setIgnoreRegexp(getIgnoreRegexp());
LocalVariablesSorter sorter = new LocalVariablesSorter(access, desc,
instrumenter);
int variable = sorter.newLocal(Type.INT_TYPE);
touchPointListener.setLastJumpIdVariableIndex(variable);
return sorter;
//return new ShiftVariableMethodAdapter(instrumenter, access, desc, 1);
}
/**
* Method instrumenter that injects {@link CodeProvider#generateCINITmethod(MethodVisitor, String, int)} code, and
* then forwards the whole previous content of the method.
*
* @author [email protected]
*/
private class GenerateCallCoberturaInitMethodVisitor extends MethodVisitor {
private String className;
public GenerateCallCoberturaInitMethodVisitor(MethodVisitor arg0,
String className) {
super(Opcodes.ASM4, arg0);
this.className = className;
}
@Override
public void visitCode() {
codeProvider.generateCallCoberturaInitMethod(mv, className);
super.visitCode();
}
}
/**
* If there was no 'static initialization block' in the class, the method is responsible for generating the method.
* It is also responsible for generating method that keeps mapping of counterIds into source places connected to them
*/
@Override
public void visitEnd() {
if (!wasStaticInitMethodVisited) {
//We need to generate new method
MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC,
"", "()V", null, null);
mv.visitCode();
codeProvider.generateCallCoberturaInitMethod(mv, classMap
.getClassName());
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(/*stack*/3,/*local*/0);
mv.visitEnd();
wasStaticInitMethodVisited = true;
}
codeProvider.generateCoberturaInitMethod(cv, classMap.getClassName(),
classMap.getMaxCounterId() + 1);
codeProvider.generateCoberturaClassMapMethod(cv, classMap);
codeProvider.generateCoberturaGetAndResetCountersMethod(cv, classMap
.getClassName());
super.visitEnd();
}
}