001/* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2011 Piotr Tabor 005 * 006 * Note: This file is dual licensed under the GPL and the Apache 007 * Source License (so that it can be used from both the main 008 * Cobertura classes and the ant tasks). 009 * 010 * Cobertura is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published 012 * by the Free Software Foundation; either version 2 of the License, 013 * or (at your option) any later version. 014 * 015 * Cobertura is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 * General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with Cobertura; if not, write to the Free Software 022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 023 * USA 024 */ 025 026package net.sourceforge.cobertura.instrument.pass3; 027 028import java.util.Map; 029 030import net.sourceforge.cobertura.instrument.TouchPointListener; 031import net.sourceforge.cobertura.instrument.tp.ClassMap; 032 033import org.apache.log4j.Logger; 034import org.objectweb.asm.Label; 035import org.objectweb.asm.MethodVisitor; 036 037/** 038 * Inject code provided by {@link #codeProvider} into the instrumented method's body. Injects code that 039 * is responsible for incrementing counters. Mapping of places into counters is provided by {@link #classMap}. 040 * 041 * @author piotr.tabor@gmail.com 042 */ 043public class InjectCodeTouchPointListener implements TouchPointListener{ 044 private final static Logger logger=Logger.getLogger(InjectCodeTouchPointListener.class); 045 /** 046 * Component that is resposible for generation of the snipets 047 */ 048 private final CodeProvider codeProvider; 049 050 /** 051 * Source of mapping from place (eventId) into counterId that is incremented if the place is touched 052 */ 053 private final ClassMap classMap; 054 055 private int lastJumpIdVariableIndex; 056 057 public InjectCodeTouchPointListener(ClassMap classMap, CodeProvider codeProvider) { 058 this.classMap=classMap; 059 this.codeProvider=codeProvider; 060 } 061 062 /** 063 * Before jump we will store into 'internal variable' the counterId of a 'true' branch of the JUMP 064 */ 065 public void beforeJump(int eventId, Label label, int currentLine,MethodVisitor nextMethodVisitor) { 066 Integer jumpTrueCounterId=classMap.getCounterIdForJumpTrue(eventId); 067 if (jumpTrueCounterId!=null){ 068 codeProvider.generateCodeThatSetsJumpCounterIdVariable(nextMethodVisitor,jumpTrueCounterId,lastJumpIdVariableIndex); 069 } 070 } 071 072 /** 073 * After jump we will increment counterId for the 'false' branch of the JUMP. 074 * Then we set internal variable to ZERO to avoid fake interpretation (another one incrementation) 075 */ 076 public void afterJump(int eventId, Label label, int currentLine, 077 MethodVisitor nextMethodVisitor) { 078 logger.debug("After jump:"+currentLine+"("+eventId+") to :"+label); 079 Integer jumpFalseCounterId=classMap.getCounterIdForJumpFalse(eventId); 080 if (jumpFalseCounterId!=null){ 081 codeProvider.generateCodeThatIncrementsCoberturaCounter(nextMethodVisitor,jumpFalseCounterId,classMap.getClassName()); 082 codeProvider.generateCodeThatZeroJumpCounterIdVariable(nextMethodVisitor,lastJumpIdVariableIndex); 083 } 084 } 085 086 /** 087 * Before switch we set the internal variable to a special counterId connected with the switch. This counterId is not 088 * connected with any branch of the switch. 089 */ 090 public void beforeSwitch(int eventId, Label def, Label[] labels,int currentLine, MethodVisitor mv, String conditionType) { 091 Integer switchCounterId = classMap.getCounterIdForSwitch(eventId); 092 if (switchCounterId != null){ 093 codeProvider.generateCodeThatSetsJumpCounterIdVariable(mv, switchCounterId, lastJumpIdVariableIndex); 094 } 095 } 096 097 /** 098 * <p>If the label is JUMP destination, we will increment the counter stored inside the 'internal variable'. This way we are 099 * incrementing the 'true' branch of the condition. </p> 100 * 101 * <p>If the label is SWITCH destination, we check all switch instructions that have targets in the label we generate 102 * code that checks if the 'internal variable' is equal to id of considered switch and if so increments counterId connected to the switch. 103 */ 104 public void afterLabel(int eventId, Label label, int currentLine, MethodVisitor mv) { 105 logger.debug("Looking for jumps going to event("+eventId+"):"+label+" "); 106 if (classMap.isJumpDestinationLabel(eventId)){ 107 codeProvider.generateCodeThatIncrementsCoberturaCounterFromInternalVariable(mv,lastJumpIdVariableIndex,classMap.getClassName()); 108 } 109 110 Map<Integer,Integer> branchTouchPoints = classMap.getBranchLabelDescriptorsForLabelEvent(eventId); 111 if(branchTouchPoints!=null){ 112 /*map of counterId of a switch into counterId of the branch of the switch*/ 113 for(Map.Entry<Integer, Integer> entry:branchTouchPoints.entrySet()){ 114 codeProvider.generateCodeThatIncrementsCoberturaCounterIfVariableEqualsAndCleanVariable(mv,entry.getKey(),entry.getValue(),lastJumpIdVariableIndex,classMap.getClassName()); 115 } 116 } 117 118 if (classMap.isJumpDestinationLabel(eventId)){ 119 codeProvider.generateCodeThatZeroJumpCounterIdVariable(mv, lastJumpIdVariableIndex); 120 } 121 } 122 123 /** 124 * After every 'linenumber' instruction we increments counter connected with the line number. 125 */ 126 public void afterLineNumber(int eventId, Label label, int currentLine,MethodVisitor nextMethodVisitor,String methodName, String methodSignature) { 127 Integer lineCounterId=classMap.getCounterIdForLineEventId(eventId); 128 if(lineCounterId!=null){ 129 codeProvider.generateCodeThatIncrementsCoberturaCounter(nextMethodVisitor,lineCounterId,classMap.getClassName()); 130 } 131 } 132 133 /** 134 * At the start of every method we initiates the 'internal variable' with zero. 135 */ 136 public void afterMethodStart(MethodVisitor nextMethodVisitor) { 137 codeProvider.generateCodeThatZeroJumpCounterIdVariable(nextMethodVisitor, lastJumpIdVariableIndex); 138 } 139 140// ------------------- ignored events ------------------------------- 141 142 public void beforeLabel(int eventId, Label label, int currentLine,MethodVisitor mv) {} 143 public void ignoreLine(int eventId, int currentLine){} 144 145// ------------------- getters and setters -------------------------- 146 147 /** 148 * Index of 'internal variable'. Should be detected by {@link ShiftVariableMethodAdapter#calculateFirstStackVariable(int, String)}. 149 */ 150 public void setLastJumpIdVariableIndex(int lastJumpIdVariableIndex) { 151 this.lastJumpIdVariableIndex = lastJumpIdVariableIndex; 152 } 153 154}