Coverage Report - net.sourceforge.cobertura.instrument.SecondPassMethodInstrumenter
 
Classes in this File Line Coverage Branch Coverage Complexity
SecondPassMethodInstrumenter
98%
144/147
88%
23/26
1.346
 
 1  
 /*
 2  
  * Cobertura - http://cobertura.sourceforge.net/
 3  
  *
 4  
  * Copyright (C) 2005 Mark Doliner
 5  
  * Copyright (C) 2006 Jiri Mares
 6  
  *
 7  
  * Cobertura is free software; you can redistribute it and/or modify
 8  
  * it under the terms of the GNU General Public License as published
 9  
  * by the Free Software Foundation; either version 2 of the License,
 10  
  * or (at your option) any later version.
 11  
  *
 12  
  * Cobertura is distributed in the hope that it will be useful, but
 13  
  * WITHOUT ANY WARRANTY; without even the implied warranty of
 14  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15  
  * General Public License for more details.
 16  
  *
 17  
  * You should have received a copy of the GNU General Public License
 18  
  * along with Cobertura; if not, write to the Free Software
 19  
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 20  
  * USA
 21  
  */
 22  
 
 23  
 package net.sourceforge.cobertura.instrument;
 24  
 
 25  
 import net.sourceforge.cobertura.util.RegexUtil;
 26  
 
 27  
 import org.objectweb.asm.Label;
 28  
 import org.objectweb.asm.Opcodes;
 29  
 
 30  
 /*
 31  
  * TODO: If class is abstract then do not count the "public abstract class bleh" line as a SLOC.
 32  
  */
 33  
 public class SecondPassMethodInstrumenter extends NewLocalVariableMethodAdapter implements Opcodes
 34  
 {
 35  
         private int currentLine;
 36  
    
 37  
         private int currentJump;
 38  
         
 39  
         private boolean methodStarted;
 40  
         
 41  
         private int myVariableIndex;
 42  
 
 43  
         private Label startLabel;
 44  
         
 45  
         private Label endLabel;
 46  
         
 47  
         private JumpHolder lastJump;
 48  
    
 49  
         private FirstPassMethodInstrumenter firstPass;
 50  
         
 51  
         private static final int BOOLEAN_TRUE = ICONST_0;
 52  
         private static final int BOOLEAN_FALSE = ICONST_1;
 53  
 
 54  
         public SecondPassMethodInstrumenter(FirstPassMethodInstrumenter firstPass)
 55  
         {
 56  35
                 super(firstPass.getWriterMethodVisitor(), firstPass.getMyAccess(), firstPass.getMyDescriptor(), 2);
 57  35
                 this.firstPass = firstPass;
 58  35
                 this.currentLine = 0;
 59  35
         }
 60  
 
 61  
         public void visitJumpInsn(int opcode, Label label)
 62  
         {
 63  
                 //to touch the previous branch (when there is such)
 64  11
                 touchBranchFalse();
 65  
                 
 66  
                 // Ignore any jump instructions in the "class init" method.
 67  
                 // When initializing static variables, the JVM first checks
 68  
                 // that the variable is null before attempting to set it.
 69  
                 // This check contains an IFNONNULL jump instruction which
 70  
                 // would confuse people if it showed up in the reports.
 71  11
                 if ((opcode != GOTO) && (opcode != JSR) && (currentLine != 0)
 72  
                                 && (!this.firstPass.getMyName().equals("<clinit>")))
 73  
                 {
 74  7
                         lastJump = new JumpHolder(currentLine, currentJump++); 
 75  7
                         mv.visitIntInsn(SIPUSH, currentLine);
 76  7
                         mv.visitVarInsn(ISTORE, myVariableIndex);
 77  7
                         mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber());
 78  7
                         mv.visitVarInsn(ISTORE, myVariableIndex + 1);
 79  
                 }
 80  
                 
 81  11
                 super.visitJumpInsn(opcode, label);
 82  11
         }
 83  
 
 84  
         public void visitLineNumber(int line, Label start)
 85  
         {
 86  
                 // Record initial information about this line of code
 87  108
                 currentLine = line;
 88  108
                 currentJump = 0;
 89  
 
 90  108
                 instrumentGetClassData();
 91  
 
 92  
                 // Mark the current line number as covered:
 93  
                 // classData.touch(line)
 94  108
                 mv.visitIntInsn(SIPUSH, line);
 95  108
                 mv.visitMethodInsn(INVOKEVIRTUAL,
 96  
                                 "net/sourceforge/cobertura/coveragedata/ClassData", "touch",
 97  
                                 "(I)V");
 98  
 
 99  108
                 super.visitLineNumber(line, start);
 100  108
         }
 101  
 
 102  
         public void visitMethodInsn(int opcode, String owner, String name,
 103  
                         String desc)
 104  
         {
 105  
                 //to touch the previous branch (when there is such)
 106  38
                 touchBranchFalse();
 107  
                 
 108  38
                 super.visitMethodInsn(opcode, owner, name, desc);
 109  
 
 110  
                 // If any of the ignore patterns match this line
 111  
                 // then remove it from our data
 112  38
                 if (RegexUtil.matches(firstPass.getIgnoreRegexs(), owner)) 
 113  
                 {
 114  2
                         firstPass.removeLine(currentLine);
 115  
                 }
 116  38
         }
 117  
 
 118  
         public void visitFieldInsn(int opcode, String owner, String name, String desc)
 119  
         {
 120  
                 //to touch the previous branch (when there is such)
 121  12
                 touchBranchFalse();
 122  
                 
 123  12
                 super.visitFieldInsn(opcode, owner, name, desc);
 124  12
         }
 125  
 
 126  
         public void visitIincInsn(int var, int increment)
 127  
         {
 128  
                 //to touch the previous branch (when there is such)
 129  1
                 touchBranchFalse();
 130  
                 
 131  1
                 super.visitIincInsn(var, increment);
 132  1
         }
 133  
 
 134  
         public void visitInsn(int opcode)
 135  
         {
 136  
                 //to touch the previous branch (when there is such)
 137  70
                 touchBranchFalse();
 138  
                 
 139  70
                 super.visitInsn(opcode);
 140  70
         }
 141  
 
 142  
         public void visitIntInsn(int opcode, int operand)
 143  
         {
 144  
                 //to touch the previous branch (when there is such)
 145  0
                 touchBranchFalse();
 146  
                 
 147  0
                 super.visitIntInsn(opcode, operand);
 148  0
         }
 149  
 
 150  
         public void visitLabel(Label label)
 151  
         {
 152  
                 //When this is the first method's label ... create the 2 new local variables (lineNumber and branchNumber)
 153  146
                 if (methodStarted) 
 154  
                 {
 155  35
                         methodStarted = false;
 156  35
                         myVariableIndex = getFirstStackVariable();
 157  35
                         mv.visitInsn(ICONST_0);
 158  35
                         mv.visitVarInsn(ISTORE, myVariableIndex);
 159  35
                         mv.visitIntInsn(SIPUSH, -1); 
 160  35
                         mv.visitVarInsn(ISTORE, myVariableIndex + 1);
 161  35
                         startLabel = label;
 162  
                 }
 163  
                 //to have the last label for visitLocalVariable
 164  146
                 endLabel = label;
 165  
                 
 166  146
                 super.visitLabel(label);
 167  
                 
 168  
                 //instrument the branch coverage collection
 169  146
                 if (firstPass.getJumpTargetLabels().keySet().contains(label)) 
 170  
                 { //this label is the true branch label
 171  7
                         if (lastJump != null) 
 172  
                         { //this is also label after jump - we have to check the branch number whether this is the true or false branch
 173  4
                                 Label newLabelX = instrumentIsLastJump();
 174  4
                                 instrumentGetClassData();
 175  4
                                 instrumentPutLineAndBranchNumbers();
 176  4
                                 mv.visitInsn(BOOLEAN_FALSE);
 177  4
                                 instrumentInvokeTouchJump();
 178  4
                                 Label newLabelY = new Label();
 179  4
                                 mv.visitJumpInsn(GOTO, newLabelY);
 180  4
                                 mv.visitLabel(newLabelX);
 181  4
                                 mv.visitVarInsn(ILOAD, myVariableIndex + 1);
 182  4
                                 mv.visitJumpInsn(IFLT, newLabelY);
 183  4
                                 instrumentGetClassData();
 184  4
                                 instrumentPutLineAndBranchNumbers();
 185  4
                                 mv.visitInsn(BOOLEAN_TRUE);
 186  4
                                 instrumentInvokeTouchJump();
 187  4
                                 mv.visitLabel(newLabelY);
 188  4
                         }
 189  
                         else
 190  
                         { //just hit te true branch
 191  
                                 //just check whether the jump has been invoked or the label has been touched other way 
 192  3
                                 mv.visitVarInsn(ILOAD, myVariableIndex + 1);
 193  3
                                 Label newLabelX = new Label();
 194  3
                                 mv.visitJumpInsn(IFLT, newLabelX);
 195  3
                                 instrumentJumpHit(true);
 196  3
                                 mv.visitLabel(newLabelX);
 197  
                         }
 198  3
                 } 
 199  139
                 else if (lastJump != null) 
 200  
                 { //this is "only" after jump label, hit the false branch only if the lastJump is same as stored stack lineNumber and jumpNumber
 201  1
                         Label newLabelX = instrumentIsLastJump();
 202  1
                         instrumentJumpHit(false); 
 203  1
                         mv.visitLabel(newLabelX);
 204  
                 }
 205  146
                 lastJump = null;
 206  
                 
 207  146
                 SwitchHolder sh = (SwitchHolder) firstPass.getSwitchTargetLabels().get(label);
 208  146
                 if (sh != null)
 209  
                 {
 210  13
                         instrumentSwitchHit(sh.getLineNumber(), sh.getSwitchNumber(), sh.getBranch());
 211  
                 }
 212  
                 
 213  
                 //we have to manually invoke the visitLineNumber because of not correct MedthodNode's handling
 214  146
                 Integer line = (Integer) firstPass.getLineLabels().get(label);
 215  146
                 if (line != null) {
 216  108
                         visitLineNumber(line.intValue(), label);
 217  
                 }
 218  146
         }
 219  
 
 220  
         public void visitLdcInsn(Object cst)
 221  
         {
 222  
                 //to touch the previous branch (when there is such)
 223  21
                 touchBranchFalse();
 224  
                 
 225  21
                 super.visitLdcInsn(cst);
 226  21
         }
 227  
 
 228  
         public void visitMultiANewArrayInsn(String desc, int dims)
 229  
         {
 230  
                 //to touch the previous branch (when there is such)
 231  1
                 touchBranchFalse();
 232  
                 
 233  1
                 super.visitMultiANewArrayInsn(desc, dims);
 234  1
         }
 235  
 
 236  
         public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)
 237  
         {
 238  
                 //to touch the previous branch (when there is such)
 239  1
                 touchBranchFalse();
 240  
                 
 241  1
                 super.visitLookupSwitchInsn(dflt, keys, labels);
 242  1
         }
 243  
 
 244  
         public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels)
 245  
         {
 246  
                 //to touch the previous branch (when there is such)
 247  1
                 touchBranchFalse();
 248  
                 
 249  1
                 super.visitTableSwitchInsn(min, max, dflt, labels);
 250  1
         }
 251  
 
 252  
         public void visitTryCatchBlock(Label start, Label end, Label handler, String type)
 253  
         {
 254  
                 //to touch the previous branch (when there is such)
 255  3
                 touchBranchFalse();
 256  
                 
 257  3
                 super.visitTryCatchBlock(start, end, handler, type);
 258  3
         }
 259  
 
 260  
         public void visitTypeInsn(int opcode, String desc)
 261  
         {
 262  
                 //to touch the previous branch (when there is such)
 263  2
                 touchBranchFalse();
 264  
                 
 265  2
                 super.visitTypeInsn(opcode, desc);
 266  2
         }
 267  
 
 268  
         public void visitVarInsn(int opcode, int var)
 269  
         {
 270  
                 //to touch the previous branch (when there is such)
 271  66
                 touchBranchFalse();
 272  
                 
 273  
                 //this is to change the variable instructions to conform to 2 new variables
 274  66
                 super.visitVarInsn(opcode, var);
 275  66
         }
 276  
 
 277  
         public void visitCode()
 278  
         {
 279  35
                 methodStarted = true;
 280  35
                 super.visitCode();
 281  35
         }
 282  
         
 283  
         private void touchBranchFalse() {
 284  227
                 if (lastJump != null) {
 285  2
                         lastJump = null;
 286  2
                         instrumentJumpHit(false);
 287  
                 }
 288  227
         }
 289  
 
 290  
         private void instrumentGetClassData()
 291  
         {
 292  
                 // Get an instance of ProjectData:
 293  
                 // ProjectData.getGlobalProjectData()
 294  135
                 mv.visitMethodInsn(INVOKESTATIC,
 295  
                                 "net/sourceforge/cobertura/coveragedata/ProjectData",
 296  
                                 "getGlobalProjectData",
 297  
                                 "()Lnet/sourceforge/cobertura/coveragedata/ProjectData;");
 298  
 
 299  
                 // Get the ClassData object for this class:
 300  
                 // projectData.getClassData("name.of.this.class")
 301  135
                 mv.visitLdcInsn(firstPass.getOwnerClass());
 302  135
                 mv
 303  
                         .visitMethodInsn(INVOKEVIRTUAL,
 304  
                                         "net/sourceforge/cobertura/coveragedata/ProjectData",
 305  
                                         "getOrCreateClassData",
 306  
                                         "(Ljava/lang/String;)Lnet/sourceforge/cobertura/coveragedata/ClassData;");
 307  135
         }
 308  
         
 309  
         private void instrumentSwitchHit(int lineNumber, int switchNumber, int branch)
 310  
         {
 311  13
                 instrumentGetClassData();
 312  
                 
 313  
                 //Invoke the touchSwitch(lineNumber, switchNumber, branch)
 314  13
                 mv.visitIntInsn(SIPUSH, lineNumber);
 315  13
                 mv.visitIntInsn(SIPUSH, switchNumber);
 316  13
                 mv.visitIntInsn(SIPUSH, branch);
 317  13
                 instrumentInvokeTouchSwitch();
 318  13
         }
 319  
         
 320  
         private void instrumentJumpHit(boolean branch)
 321  
         {
 322  6
                 instrumentGetClassData();
 323  
                 
 324  
                 //Invoke the touchJump(lineNumber, branchNumber, branch)
 325  6
                 instrumentPutLineAndBranchNumbers();
 326  6
                 mv.visitInsn(branch ? BOOLEAN_TRUE : BOOLEAN_FALSE);
 327  6
                 instrumentInvokeTouchJump();
 328  6
         }
 329  
 
 330  
         private void instrumentInvokeTouchJump()
 331  
         {
 332  14
                 mv.visitMethodInsn(INVOKEVIRTUAL, "net/sourceforge/cobertura/coveragedata/ClassData", "touchJump", "(IIZ)V");
 333  14
                 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
 334  14
                 mv.visitVarInsn(ISTORE, myVariableIndex + 1);
 335  14
         }
 336  
 
 337  
         private void instrumentInvokeTouchSwitch()
 338  
         {
 339  13
                 mv.visitMethodInsn(INVOKEVIRTUAL, "net/sourceforge/cobertura/coveragedata/ClassData", "touchSwitch", "(III)V");
 340  13
         }
 341  
 
 342  
         private void instrumentPutLineAndBranchNumbers()
 343  
         {
 344  14
                 mv.visitVarInsn(ILOAD, myVariableIndex);
 345  14
                 mv.visitVarInsn(ILOAD, myVariableIndex + 1);
 346  14
         }
 347  
 
 348  
         private Label instrumentIsLastJump() {
 349  5
                 mv.visitVarInsn(ILOAD, myVariableIndex);
 350  5
                 mv.visitIntInsn(SIPUSH, lastJump.getLineNumber());
 351  5
                 Label newLabelX = new Label();
 352  5
                 mv.visitJumpInsn(IF_ICMPNE, newLabelX);
 353  5
                 mv.visitVarInsn(ILOAD, myVariableIndex + 1);
 354  5
                 mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber());
 355  5
                 mv.visitJumpInsn(IF_ICMPNE, newLabelX);
 356  5
                 return newLabelX;
 357  
         }
 358  
 
 359  
         public void visitMaxs(int maxStack, int maxLocals)
 360  
         {
 361  35
                 mv.visitLocalVariable("__cobertura__line__number__", "I", null, startLabel, endLabel, myVariableIndex);
 362  35
                 mv.visitLocalVariable("__cobertura__branch__number__", "I", null, startLabel, endLabel, myVariableIndex + 1);
 363  35
                 super.visitMaxs(maxStack, maxLocals);
 364  35
         }
 365  
 
 366  
 }