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
023 package net.sourceforge.cobertura.instrument;
024
025 import java.util.Collection;
026 import java.util.HashMap;
027 import java.util.Map;
028
029 import net.sourceforge.cobertura.coveragedata.ClassData;
030 import net.sourceforge.cobertura.util.RegexUtil;
031
032 import org.objectweb.asm.Label;
033 import org.objectweb.asm.MethodAdapter;
034 import org.objectweb.asm.MethodVisitor;
035 import org.objectweb.asm.Opcodes;
036 import org.objectweb.asm.tree.MethodNode;
037
038 public 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 }