001    /*
002     * Cobertura - http://cobertura.sourceforge.net/
003     *
004     * Copyright (C) 2005 Mark Doliner <thekingant@users.sourceforge.net>
005     *
006     * Cobertura is free software; you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published
008     * by the Free Software Foundation; either version 2 of the License,
009     * or (at your option) any later version.
010     *
011     * Cobertura is distributed in the hope that it will be useful, but
012     * WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014     * General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License
017     * along with Cobertura; if not, write to the Free Software
018     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019     * USA
020     */
021    
022    package net.sourceforge.cobertura.coverage;
023    
024    import java.util.regex.Pattern;
025    
026    import org.objectweb.asm.Label;
027    import org.objectweb.asm.MethodAdapter;
028    import org.objectweb.asm.MethodVisitor;
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     * TODO: For branches, only count the branch as covered if both paths are accessed?
034     */
035    public class MethodInstrumenter extends MethodAdapter implements Opcodes
036    {
037            private final String ownerClass;
038            private String myName;
039            private String myDescriptor;
040            private Pattern ignoreRegexp;
041            private CoverageData coverageData;
042    
043            private int currentLine = 0;
044    
045            public MethodInstrumenter(final MethodVisitor mv,
046                            CoverageData coverageData, final String owner,
047                            final String myName, final String myDescriptor,
048                            final Pattern ignoreRegexp)
049            {
050                    super(mv);
051                    this.coverageData = coverageData;
052                    this.ownerClass = owner;
053                    this.myName = myName;
054                    this.myDescriptor = myDescriptor;
055                    this.ignoreRegexp = ignoreRegexp;
056            }
057    
058            public void visitJumpInsn(int opcode, Label label)
059            {
060                    super.visitJumpInsn(opcode, label);
061    
062                    // Ignore any jump instructions in the "class init" method.
063                    // When initializing static variables, the JVM first checks
064                    // that the variable is null before attempting to set it.
065                    // This check contains an IFNONNULL jump instruction which
066                    // would confuse people if it showed up in the reports.
067                    if ((opcode != GOTO) && (currentLine != 0)
068                                    && (!this.myName.equals("<clinit>")))
069                            coverageData.markLineAsConditional(currentLine);
070            }
071    
072            public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)
073            {
074                    super.visitLookupSwitchInsn(dflt, keys, labels);
075                    if (currentLine != 0)
076                            coverageData.markLineAsConditional(currentLine);
077            }
078    
079            public void visitLineNumber(int line, Label start)
080            {
081                    // Record initial information about this line of code
082                    currentLine = line;
083                    coverageData.addLine(currentLine, myName, myDescriptor);
084    
085                    // Get an instance of CoverageDataFactory
086                    mv.visitMethodInsn(INVOKESTATIC,
087                                    "net/sourceforge/cobertura/coverage/CoverageDataFactory",
088                                    "getInstance",
089                                    "()Lnet/sourceforge/cobertura/coverage/CoverageDataFactory;");
090    
091                    // Get the CoverageData object for this class
092                    mv.visitLdcInsn(ownerClass);
093                    mv
094                                    .visitMethodInsn(
095                                                    INVOKEVIRTUAL,
096                                                    "net/sourceforge/cobertura/coverage/CoverageDataFactory",
097                                                    "newInstrumentation",
098                                                    "(Ljava/lang/String;)Lnet/sourceforge/cobertura/coverage/CoverageData;");
099    
100                    // Call "coverageData.touch(line);"
101                    mv.visitIntInsn(SIPUSH, line);
102                    mv.visitMethodInsn(INVOKEVIRTUAL,
103                                    "net/sourceforge/cobertura/coverage/CoverageData", "touch",
104                                    "(I)V");
105    
106                    super.visitLineNumber(line, start);
107            }
108    
109            public void visitMethodInsn(int opcode, String owner, String name,
110                            String desc)
111            {
112                    super.visitMethodInsn(opcode, owner, name, desc);
113    
114                    if ((ignoreRegexp != null) && (ignoreRegexp.matcher(owner).matches()))
115                            coverageData.removeLine(currentLine);
116            }
117    
118    }