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    }