001/*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2005 Mark Doliner
005 * Copyright (C) 2006 Jiri Mares
006 *
007 * Cobertura is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published
009 * by the Free Software Foundation; either version 2 of the License,
010 * or (at your option) any later version.
011 *
012 * Cobertura is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with Cobertura; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020 * USA
021 */
022
023package net.sourceforge.cobertura.instrument;
024
025import java.util.Collection;
026import java.util.HashMap;
027import java.util.Map;
028
029import net.sourceforge.cobertura.coveragedata.ClassData;
030import net.sourceforge.cobertura.util.RegexUtil;
031
032import org.objectweb.asm.Label;
033import org.objectweb.asm.MethodAdapter;
034import org.objectweb.asm.MethodVisitor;
035import org.objectweb.asm.Opcodes;
036import org.objectweb.asm.tree.MethodNode;
037
038public class FirstPassMethodInstrumenter extends MethodAdapter implements Opcodes
039{
040
041        private final String ownerClass;
042
043        private String myName;
044
045        private String myDescriptor;
046
047        private int myAccess;
048   
049        private Collection ignoreRegexs;
050   
051        private Collection ignoreBranchesRegexs;
052
053        private ClassData classData;
054
055        private int currentLine;
056   
057        private int currentJump;
058   
059        private int currentSwitch;
060        
061        private Map jumpTargetLabels;
062
063        private Map switchTargetLabels;
064   
065        private Map lineLabels;
066   
067        private MethodVisitor writerMethodVisitor;
068   
069        private MethodNode methodNode;
070
071        public FirstPassMethodInstrumenter(ClassData classData, final MethodVisitor mv,
072                        final String owner, final int access, final String name, final String desc, 
073                        final String signature, final String[] exceptions, final Collection ignoreRegexs,
074                        final Collection ignoreBranchesRegexs)
075        {
076                super(new MethodNode(access, name, desc, signature, exceptions));
077                writerMethodVisitor = mv;
078                this.ownerClass = owner;
079                this.methodNode = (MethodNode) this.mv;
080                this.classData = classData;
081                this.myAccess = access;
082                this.myName = name;
083                this.myDescriptor = desc;
084                this.ignoreRegexs = ignoreRegexs;
085                this.ignoreBranchesRegexs = ignoreBranchesRegexs;
086                this.jumpTargetLabels = new HashMap();
087                this.switchTargetLabels = new HashMap();
088                this.lineLabels = new HashMap();
089                this.currentLine = 0;
090        }
091
092        public void visitEnd() {
093                super.visitEnd();
094
095                methodNode.accept(lineLabels.isEmpty() ? writerMethodVisitor : new SecondPassMethodInstrumenter(this)); //when there is no line number info -> no instrumentation
096        }
097
098        public void visitJumpInsn(int opcode, Label label)
099        {
100                // Ignore any jump instructions in the "class init" method.
101                // When initializing static variables, the JVM first checks
102                // that the variable is null before attempting to set it.
103                // This check contains an IFNONNULL jump instruction which
104                // would confuse people if it showed up in the reports.
105                if ((opcode != GOTO) && (opcode != JSR) && (currentLine != 0)
106                                && (!this.myName.equals("<clinit>")))
107                {
108                        classData.addLineJump(currentLine, currentJump);
109                        jumpTargetLabels.put(label, new JumpHolder(currentLine, currentJump++));
110                }
111                
112                super.visitJumpInsn(opcode, label);
113        }
114
115        public void visitLineNumber(int line, Label start)
116        {
117                // Record initial information about this line of code
118                currentLine = line;
119                classData.addLine(currentLine, myName, myDescriptor);
120                currentJump = 0;
121                currentSwitch = 0;
122      
123                lineLabels.put(start, new Integer(line));
124
125                //removed because the MethodNode doesn't reproduce visitLineNumber where they are but at the end of the file :-(( 
126                //therefore we don't need them
127                //We can directly instrument the visit line number here, but it is better to leave all instrumentation in the second pass
128                //therefore we just collects what label is the line ...
129                //super.visitLineNumber(line, start);
130        }
131
132        public void visitMethodInsn(int opcode, String owner, String name,
133                        String desc)
134        {
135                super.visitMethodInsn(opcode, owner, name, desc);
136
137                // If any of the ignore patterns match this line
138                // then remove it from our data
139                if (RegexUtil.matches(ignoreRegexs, owner)) 
140                {
141                        classData.removeLine(currentLine);
142                }
143        }
144
145        public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)
146        {
147                super.visitLookupSwitchInsn(dflt, keys, labels);
148      
149                if (currentLine != 0)
150                {
151                        switchTargetLabels.put(dflt, new SwitchHolder(currentLine, currentSwitch, -1)); 
152                        for (int i = labels.length -1; i >=0; i--)
153                                switchTargetLabels.put(labels[i], new SwitchHolder(currentLine, currentSwitch, i));
154                        classData.addLineSwitch(currentLine, currentSwitch++, keys);
155                }
156        }
157
158        public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels)
159        {
160                super.visitTableSwitchInsn(min, max, dflt, labels);
161      
162                if (currentLine != 0)
163                {
164                        switchTargetLabels.put(dflt, new SwitchHolder(currentLine, currentSwitch, -1)); 
165                        for (int i = labels.length -1; i >=0; i--)
166                                switchTargetLabels.put(labels[i], new SwitchHolder(currentLine, currentSwitch, i));
167                        classData.addLineSwitch(currentLine, currentSwitch++, min, max);
168                }
169        }
170
171        protected void removeLine(int lineNumber) 
172        {
173                classData.removeLine(lineNumber);
174        }
175   
176        protected MethodVisitor getWriterMethodVisitor() 
177        {
178                return writerMethodVisitor;
179        }
180
181        protected Collection getIgnoreRegexs() 
182        {
183                return ignoreRegexs;
184        }
185
186        protected Map getJumpTargetLabels() 
187        {
188                return jumpTargetLabels;
189        }
190
191        protected Map getSwitchTargetLabels() 
192        {
193                return switchTargetLabels;
194        }
195
196        protected int getMyAccess() 
197        {
198                return myAccess;
199        }
200
201        protected String getMyDescriptor() 
202        {
203                return myDescriptor;
204        }
205
206        protected String getMyName() 
207        {
208                return myName;
209        }
210
211        protected String getOwnerClass() 
212        {
213                return ownerClass;
214        }
215
216        protected Map getLineLabels() 
217        {
218                return lineLabels;
219        }
220
221}