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}