001 /* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2005 Mark Doliner 005 * Copyright (C) 2006 Jiri Mares 006 * Copyright (C) 2010 Piotr Tabor 007 * 008 * Cobertura is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License as published 010 * by the Free Software Foundation; either version 2 of the License, 011 * or (at your option) any later version. 012 * 013 * Cobertura is distributed in the hope that it will be useful, but 014 * WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 016 * General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with Cobertura; if not, write to the Free Software 020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 021 * USA 022 */ 023 024 package net.sourceforge.cobertura.instrument; 025 026 import net.sourceforge.cobertura.util.RegexUtil; 027 028 import org.objectweb.asm.Label; 029 import org.objectweb.asm.Opcodes; 030 031 /* 032 * TODO: If class is abstract then do not count the "public abstract class bleh" line as a SLOC. 033 */ 034 public class SecondPassMethodInstrumenter extends NewLocalVariableMethodAdapter implements Opcodes 035 { 036 private String TOUCH_COLLECTOR_CLASS="net/sourceforge/cobertura/coveragedata/TouchCollector"; 037 038 private int currentLine; 039 040 private int currentJump; 041 042 private boolean methodStarted; 043 044 private int myVariableIndex; 045 046 private Label startLabel; 047 048 private Label endLabel; 049 050 private JumpHolder lastJump; 051 052 private FirstPassMethodInstrumenter firstPass; 053 054 private static final int BOOLEAN_TRUE = ICONST_0; 055 private static final int BOOLEAN_FALSE = ICONST_1; 056 057 public SecondPassMethodInstrumenter(FirstPassMethodInstrumenter firstPass) 058 { 059 super(firstPass.getWriterMethodVisitor(), firstPass.getMyAccess(), firstPass.getMyDescriptor(), 2); 060 this.firstPass = firstPass; 061 this.currentLine = 0; 062 } 063 064 public void visitJumpInsn(int opcode, Label label) 065 { 066 //to touch the previous branch (when there is such) 067 touchBranchFalse(); 068 069 // Ignore any jump instructions in the "class init" method. 070 // When initializing static variables, the JVM first checks 071 // that the variable is null before attempting to set it. 072 // This check contains an IFNONNULL jump instruction which 073 // would confuse people if it showed up in the reports. 074 if ((opcode != GOTO) && (opcode != JSR) && (currentLine != 0) 075 && (!this.firstPass.getMyName().equals("<clinit>"))) 076 { 077 lastJump = new JumpHolder(currentLine, currentJump++); 078 mv.visitIntInsn(SIPUSH, currentLine); 079 mv.visitVarInsn(ISTORE, myVariableIndex); 080 mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber()); 081 mv.visitVarInsn(ISTORE, myVariableIndex + 1); 082 } 083 084 super.visitJumpInsn(opcode, label); 085 } 086 087 public void visitLineNumber(int line, Label start) 088 { 089 // Record initial information about this line of code 090 currentLine = line; 091 currentJump = 0; 092 093 instrumentOwnerClass(); 094 095 // Mark the current line number as covered: 096 // classData.touch(line) 097 mv.visitIntInsn(SIPUSH, line); 098 mv.visitMethodInsn(INVOKESTATIC, 099 TOUCH_COLLECTOR_CLASS, "touch", 100 "(Ljava/lang/String;I)V"); 101 102 super.visitLineNumber(line, start); 103 } 104 105 public void visitMethodInsn(int opcode, String owner, String name, 106 String desc) 107 { 108 //to touch the previous branch (when there is such) 109 touchBranchFalse(); 110 111 super.visitMethodInsn(opcode, owner, name, desc); 112 113 // If any of the ignore patterns match this line 114 // then remove it from our data 115 if (RegexUtil.matches(firstPass.getIgnoreRegexs(), owner)) 116 { 117 firstPass.removeLine(currentLine); 118 } 119 } 120 121 public void visitFieldInsn(int opcode, String owner, String name, String desc) 122 { 123 //to touch the previous branch (when there is such) 124 touchBranchFalse(); 125 126 super.visitFieldInsn(opcode, owner, name, desc); 127 } 128 129 public void visitIincInsn(int var, int increment) 130 { 131 //to touch the previous branch (when there is such) 132 touchBranchFalse(); 133 134 super.visitIincInsn(var, increment); 135 } 136 137 public void visitInsn(int opcode) 138 { 139 //to touch the previous branch (when there is such) 140 touchBranchFalse(); 141 142 super.visitInsn(opcode); 143 } 144 145 public void visitIntInsn(int opcode, int operand) 146 { 147 //to touch the previous branch (when there is such) 148 touchBranchFalse(); 149 150 super.visitIntInsn(opcode, operand); 151 } 152 153 public void visitLabel(Label label) 154 { 155 //When this is the first method's label ... create the 2 new local variables (lineNumber and branchNumber) 156 if (methodStarted) 157 { 158 methodStarted = false; 159 myVariableIndex = getFirstStackVariable(); 160 mv.visitInsn(ICONST_0); 161 mv.visitVarInsn(ISTORE, myVariableIndex); 162 mv.visitIntInsn(SIPUSH, -1); 163 mv.visitVarInsn(ISTORE, myVariableIndex + 1); 164 startLabel = label; 165 } 166 //to have the last label for visitLocalVariable 167 endLabel = label; 168 169 super.visitLabel(label); 170 171 //instrument the branch coverage collection 172 if (firstPass.getJumpTargetLabels().keySet().contains(label)) 173 { //this label is the true branch label 174 if (lastJump != null) 175 { //this is also label after jump - we have to check the branch number whether this is the true or false branch 176 Label newLabelX = instrumentIsLastJump(); 177 instrumentOwnerClass(); 178 instrumentPutLineAndBranchNumbers(); 179 mv.visitInsn(BOOLEAN_FALSE); 180 instrumentInvokeTouchJump(); 181 Label newLabelY = new Label(); 182 mv.visitJumpInsn(GOTO, newLabelY); 183 mv.visitLabel(newLabelX); 184 mv.visitVarInsn(ILOAD, myVariableIndex + 1); 185 mv.visitJumpInsn(IFLT, newLabelY); 186 instrumentOwnerClass(); 187 instrumentPutLineAndBranchNumbers(); 188 mv.visitInsn(BOOLEAN_TRUE); 189 instrumentInvokeTouchJump(); 190 mv.visitLabel(newLabelY); 191 } 192 else 193 { //just hit te true branch 194 //just check whether the jump has been invoked or the label has been touched other way 195 mv.visitVarInsn(ILOAD, myVariableIndex + 1); 196 Label newLabelX = new Label(); 197 mv.visitJumpInsn(IFLT, newLabelX); 198 instrumentJumpHit(true); 199 mv.visitLabel(newLabelX); 200 } 201 } 202 else if (lastJump != null) 203 { //this is "only" after jump label, hit the false branch only if the lastJump is same as stored stack lineNumber and jumpNumber 204 Label newLabelX = instrumentIsLastJump(); 205 instrumentJumpHit(false); 206 mv.visitLabel(newLabelX); 207 } 208 lastJump = null; 209 210 SwitchHolder sh = (SwitchHolder) firstPass.getSwitchTargetLabels().get(label); 211 if (sh != null) 212 { 213 instrumentSwitchHit(sh.getLineNumber(), sh.getSwitchNumber(), sh.getBranch()); 214 } 215 216 //we have to manually invoke the visitLineNumber because of not correct MedthodNode's handling 217 Integer line = (Integer) firstPass.getLineLabels().get(label); 218 if (line != null) { 219 visitLineNumber(line.intValue(), label); 220 } 221 } 222 223 public void visitLdcInsn(Object cst) 224 { 225 //to touch the previous branch (when there is such) 226 touchBranchFalse(); 227 228 super.visitLdcInsn(cst); 229 } 230 231 public void visitMultiANewArrayInsn(String desc, int dims) 232 { 233 //to touch the previous branch (when there is such) 234 touchBranchFalse(); 235 236 super.visitMultiANewArrayInsn(desc, dims); 237 } 238 239 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) 240 { 241 //to touch the previous branch (when there is such) 242 touchBranchFalse(); 243 244 super.visitLookupSwitchInsn(dflt, keys, labels); 245 } 246 247 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) 248 { 249 //to touch the previous branch (when there is such) 250 touchBranchFalse(); 251 252 super.visitTableSwitchInsn(min, max, dflt, labels); 253 } 254 255 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) 256 { 257 //to touch the previous branch (when there is such) 258 touchBranchFalse(); 259 260 super.visitTryCatchBlock(start, end, handler, type); 261 } 262 263 public void visitTypeInsn(int opcode, String desc) 264 { 265 //to touch the previous branch (when there is such) 266 touchBranchFalse(); 267 268 super.visitTypeInsn(opcode, desc); 269 } 270 271 public void visitVarInsn(int opcode, int var) 272 { 273 //to touch the previous branch (when there is such) 274 touchBranchFalse(); 275 276 //this is to change the variable instructions to conform to 2 new variables 277 super.visitVarInsn(opcode, var); 278 } 279 280 public void visitCode() 281 { 282 methodStarted = true; 283 super.visitCode(); 284 } 285 286 private void touchBranchFalse() { 287 if (lastJump != null) { 288 lastJump = null; 289 instrumentJumpHit(false); 290 } 291 } 292 293 private void instrumentOwnerClass() 294 { 295 // OwnerClass is the name of the class being instrumented 296 mv.visitLdcInsn(firstPass.getOwnerClass()); 297 } 298 299 private void instrumentSwitchHit(int lineNumber, int switchNumber, int branch) 300 { 301 instrumentOwnerClass(); 302 303 //Invoke the touchSwitch(lineNumber, switchNumber, branch) 304 mv.visitIntInsn(SIPUSH, lineNumber); 305 mv.visitIntInsn(SIPUSH, switchNumber); 306 mv.visitIntInsn(SIPUSH, branch); 307 instrumentInvokeTouchSwitch(); 308 } 309 310 private void instrumentJumpHit(boolean branch) 311 { 312 instrumentOwnerClass(); 313 314 //Invoke the touchJump(lineNumber, branchNumber, branch) 315 instrumentPutLineAndBranchNumbers(); 316 mv.visitInsn(branch ? BOOLEAN_TRUE : BOOLEAN_FALSE); 317 instrumentInvokeTouchJump(); 318 } 319 320 private void instrumentInvokeTouchJump() 321 { 322 mv.visitMethodInsn(INVOKESTATIC, TOUCH_COLLECTOR_CLASS, "touchJump", "(Ljava/lang/String;IIZ)V"); 323 mv.visitIntInsn(SIPUSH, -1); //is important to reset current branch, because we have to know that the branch info on stack has already been used and can't be used 324 mv.visitVarInsn(ISTORE, myVariableIndex + 1); 325 } 326 327 private void instrumentInvokeTouchSwitch() 328 { 329 mv.visitMethodInsn(INVOKESTATIC, TOUCH_COLLECTOR_CLASS, "touchSwitch", "(Ljava/lang/String;III)V"); 330 } 331 332 private void instrumentPutLineAndBranchNumbers() 333 { 334 mv.visitVarInsn(ILOAD, myVariableIndex); 335 mv.visitVarInsn(ILOAD, myVariableIndex + 1); 336 } 337 338 private Label instrumentIsLastJump() { 339 mv.visitVarInsn(ILOAD, myVariableIndex); 340 mv.visitIntInsn(SIPUSH, lastJump.getLineNumber()); 341 Label newLabelX = new Label(); 342 mv.visitJumpInsn(IF_ICMPNE, newLabelX); 343 mv.visitVarInsn(ILOAD, myVariableIndex + 1); 344 mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber()); 345 mv.visitJumpInsn(IF_ICMPNE, newLabelX); 346 return newLabelX; 347 } 348 349 public void visitMaxs(int maxStack, int maxLocals) 350 { 351 mv.visitLocalVariable("__cobertura__line__number__", "I", null, startLabel, endLabel, myVariableIndex); 352 mv.visitLocalVariable("__cobertura__branch__number__", "I", null, startLabel, endLabel, myVariableIndex + 1); 353 super.visitMaxs(maxStack, maxLocals); 354 } 355 356 }