/* * 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.TouchPointListener; import net.sourceforge.cobertura.instrument.tp.ClassMap; import org.apache.log4j.Logger; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import java.util.Map; /** * Inject code provided by {@link #codeProvider} into the instrumented method's body. Injects code that * is responsible for incrementing counters. Mapping of places into counters is provided by {@link #classMap}. * * @author piotr.tabor@gmail.com */ public class InjectCodeTouchPointListener implements TouchPointListener { private final static Logger logger = Logger .getLogger(InjectCodeTouchPointListener.class); /** * Component that is resposible for generation of the snipets */ private final CodeProvider codeProvider; /** * Source of mapping from place (eventId) into counterId that is incremented if the place is touched */ private final ClassMap classMap; private int lastJumpIdVariableIndex; public InjectCodeTouchPointListener(ClassMap classMap, CodeProvider codeProvider) { this.classMap = classMap; this.codeProvider = codeProvider; } /** * Before jump we will store into 'internal variable' the counterId of a 'true' branch of the JUMP */ public void beforeJump(int eventId, Label label, int currentLine, MethodVisitor nextMethodVisitor) { Integer jumpTrueCounterId = classMap.getCounterIdForJumpTrue(eventId); if (jumpTrueCounterId != null) { codeProvider.generateCodeThatSetsJumpCounterIdVariable( nextMethodVisitor, jumpTrueCounterId, lastJumpIdVariableIndex); } } /** * After jump we will increment counterId for the 'false' branch of the JUMP. * Then we set internal variable to ZERO to avoid fake interpretation (another one incrementation) */ public void afterJump(int eventId, Label label, int currentLine, MethodVisitor nextMethodVisitor) { logger.debug("After jump:" + currentLine + "(" + eventId + ") to :" + label); Integer jumpFalseCounterId = classMap.getCounterIdForJumpFalse(eventId); if (jumpFalseCounterId != null) { codeProvider.generateCodeThatIncrementsCoberturaCounter( nextMethodVisitor, jumpFalseCounterId, classMap .getClassName()); codeProvider.generateCodeThatZeroJumpCounterIdVariable( nextMethodVisitor, lastJumpIdVariableIndex); } } /** * Before switch we set the internal variable to a special counterId connected with the switch. This counterId is not * connected with any branch of the switch. */ public void beforeSwitch(int eventId, Label def, Label[] labels, int currentLine, MethodVisitor mv, String conditionType) { Integer switchCounterId = classMap.getCounterIdForSwitch(eventId); if (switchCounterId != null) { codeProvider.generateCodeThatSetsJumpCounterIdVariable(mv, switchCounterId, lastJumpIdVariableIndex); } } /** * <p>If the label is JUMP destination, we will increment the counter stored inside the 'internal variable'. This way we are * incrementing the 'true' branch of the condition. </p> * <p/> * <p>If the label is SWITCH destination, we check all switch instructions that have targets in the label we generate * code that checks if the 'internal variable' is equal to id of considered switch and if so increments counterId connected to the switch. */ public void afterLabel(int eventId, Label label, int currentLine, MethodVisitor mv) { logger.debug("Looking for jumps going to event(" + eventId + "):" + label + " "); if (classMap.isJumpDestinationLabel(eventId)) { codeProvider .generateCodeThatIncrementsCoberturaCounterFromInternalVariable( mv, lastJumpIdVariableIndex, classMap .getClassName()); } Map<Integer, Integer> branchTouchPoints = classMap .getBranchLabelDescriptorsForLabelEvent(eventId); if (branchTouchPoints != null) { /*map of counterId of a switch into counterId of the branch of the switch*/ for (Map.Entry<Integer, Integer> entry : branchTouchPoints .entrySet()) { codeProvider .generateCodeThatIncrementsCoberturaCounterIfVariableEqualsAndCleanVariable( mv, entry.getKey(), entry.getValue(), lastJumpIdVariableIndex, classMap .getClassName()); } } if (classMap.isJumpDestinationLabel(eventId)) { codeProvider.generateCodeThatZeroJumpCounterIdVariable(mv, lastJumpIdVariableIndex); } } /** * After every 'linenumber' instruction we increments counter connected with the line number. */ public void afterLineNumber(int eventId, Label label, int currentLine, MethodVisitor nextMethodVisitor, String methodName, String methodSignature) { Integer lineCounterId = classMap.getCounterIdForLineEventId(eventId); if (lineCounterId != null) { codeProvider.generateCodeThatIncrementsCoberturaCounter( nextMethodVisitor, lineCounterId, classMap.getClassName()); } } /** * At the start of every method we initiates the 'internal variable' with zero. */ public void afterMethodStart(MethodVisitor nextMethodVisitor) { codeProvider.generateCodeThatZeroJumpCounterIdVariable( nextMethodVisitor, lastJumpIdVariableIndex); } // ------------------- ignored events ------------------------------- public void beforeLabel(int eventId, Label label, int currentLine, MethodVisitor mv) { } public void ignoreLine(int eventId, int currentLine) { } // ------------------- getters and setters -------------------------- /** * Index of 'internal variable'. Should be detected by {@link ShiftVariableMethodAdapter#calculateFirstStackVariable(int, String)}. */ public void setLastJumpIdVariableIndex(int lastJumpIdVariableIndex) { this.lastJumpIdVariableIndex = lastJumpIdVariableIndex; } }