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}