net.sourceforge.cobertura.instrument.SecondPassMethodInstrumenter Maven / Gradle / Ivy
/*
* Cobertura - http://cobertura.sourceforge.net/
*
* Copyright (C) 2005 Mark Doliner
* Copyright (C) 2006 Jiri Mares
*
* Cobertura is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Cobertura is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cobertura; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package net.sourceforge.cobertura.instrument;
import net.sourceforge.cobertura.util.RegexUtil;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
/*
* TODO: If class is abstract then do not count the "public abstract class bleh" line as a SLOC.
*/
public class SecondPassMethodInstrumenter extends NewLocalVariableMethodAdapter implements Opcodes
{
private int currentLine;
private int currentJump;
private boolean methodStarted;
private int myVariableIndex;
private Label startLabel;
private Label endLabel;
private JumpHolder lastJump;
private FirstPassMethodInstrumenter firstPass;
private static final int BOOLEAN_TRUE = ICONST_0;
private static final int BOOLEAN_FALSE = ICONST_1;
public SecondPassMethodInstrumenter(FirstPassMethodInstrumenter firstPass)
{
super(firstPass.getWriterMethodVisitor(), firstPass.getMyAccess(), firstPass.getMyDescriptor(), 2);
this.firstPass = firstPass;
this.currentLine = 0;
}
public void visitJumpInsn(int opcode, Label label)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
// Ignore any jump instructions in the "class init" method.
// When initializing static variables, the JVM first checks
// that the variable is null before attempting to set it.
// This check contains an IFNONNULL jump instruction which
// would confuse people if it showed up in the reports.
if ((opcode != GOTO) && (opcode != JSR) && (currentLine != 0)
&& (!this.firstPass.getMyName().equals("")))
{
lastJump = new JumpHolder(currentLine, currentJump++);
mv.visitIntInsn(SIPUSH, currentLine);
mv.visitVarInsn(ISTORE, myVariableIndex);
mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber());
mv.visitVarInsn(ISTORE, myVariableIndex + 1);
}
super.visitJumpInsn(opcode, label);
}
public void visitLineNumber(int line, Label start)
{
// Record initial information about this line of code
currentLine = line;
currentJump = 0;
instrumentGetClassData();
// Mark the current line number as covered:
// classData.touch(line)
mv.visitIntInsn(SIPUSH, line);
mv.visitMethodInsn(INVOKEVIRTUAL,
"net/sourceforge/cobertura/coveragedata/ClassData", "touch",
"(I)V");
super.visitLineNumber(line, start);
}
public void visitMethodInsn(int opcode, String owner, String name,
String desc)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitMethodInsn(opcode, owner, name, desc);
// If any of the ignore patterns match this line
// then remove it from our data
if (RegexUtil.matches(firstPass.getIgnoreRegexs(), owner))
{
firstPass.removeLine(currentLine);
}
}
public void visitFieldInsn(int opcode, String owner, String name, String desc)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitFieldInsn(opcode, owner, name, desc);
}
public void visitIincInsn(int var, int increment)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitIincInsn(var, increment);
}
public void visitInsn(int opcode)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitInsn(opcode);
}
public void visitIntInsn(int opcode, int operand)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitIntInsn(opcode, operand);
}
public void visitLabel(Label label)
{
//When this is the first method's label ... create the 2 new local variables (lineNumber and branchNumber)
if (methodStarted)
{
methodStarted = false;
myVariableIndex = getFirstStackVariable();
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, myVariableIndex);
mv.visitIntInsn(SIPUSH, -1);
mv.visitVarInsn(ISTORE, myVariableIndex + 1);
startLabel = label;
}
//to have the last label for visitLocalVariable
endLabel = label;
super.visitLabel(label);
//instrument the branch coverage collection
if (firstPass.getJumpTargetLabels().keySet().contains(label))
{ //this label is the true branch label
if (lastJump != null)
{ //this is also label after jump - we have to check the branch number whether this is the true or false branch
Label newLabelX = instrumentIsLastJump();
instrumentGetClassData();
instrumentPutLineAndBranchNumbers();
mv.visitInsn(BOOLEAN_FALSE);
instrumentInvokeTouchJump();
Label newLabelY = new Label();
mv.visitJumpInsn(GOTO, newLabelY);
mv.visitLabel(newLabelX);
mv.visitVarInsn(ILOAD, myVariableIndex + 1);
mv.visitJumpInsn(IFLT, newLabelY);
instrumentGetClassData();
instrumentPutLineAndBranchNumbers();
mv.visitInsn(BOOLEAN_TRUE);
instrumentInvokeTouchJump();
mv.visitLabel(newLabelY);
}
else
{ //just hit te true branch
//just check whether the jump has been invoked or the label has been touched other way
mv.visitVarInsn(ILOAD, myVariableIndex + 1);
Label newLabelX = new Label();
mv.visitJumpInsn(IFLT, newLabelX);
instrumentJumpHit(true);
mv.visitLabel(newLabelX);
}
}
else if (lastJump != null)
{ //this is "only" after jump label, hit the false branch only if the lastJump is same as stored stack lineNumber and jumpNumber
Label newLabelX = instrumentIsLastJump();
instrumentJumpHit(false);
mv.visitLabel(newLabelX);
}
lastJump = null;
SwitchHolder sh = (SwitchHolder) firstPass.getSwitchTargetLabels().get(label);
if (sh != null)
{
instrumentSwitchHit(sh.getLineNumber(), sh.getSwitchNumber(), sh.getBranch());
}
//we have to manually invoke the visitLineNumber because of not correct MedthodNode's handling
Integer line = (Integer) firstPass.getLineLabels().get(label);
if (line != null) {
visitLineNumber(line.intValue(), label);
}
}
public void visitLdcInsn(Object cst)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitLdcInsn(cst);
}
public void visitMultiANewArrayInsn(String desc, int dims)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitMultiANewArrayInsn(desc, dims);
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitLookupSwitchInsn(dflt, keys, labels);
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitTableSwitchInsn(min, max, dflt, labels);
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitTryCatchBlock(start, end, handler, type);
}
public void visitTypeInsn(int opcode, String desc)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
super.visitTypeInsn(opcode, desc);
}
public void visitVarInsn(int opcode, int var)
{
//to touch the previous branch (when there is such)
touchBranchFalse();
//this is to change the variable instructions to conform to 2 new variables
super.visitVarInsn(opcode, var);
}
public void visitCode()
{
methodStarted = true;
super.visitCode();
}
private void touchBranchFalse() {
if (lastJump != null) {
lastJump = null;
instrumentJumpHit(false);
}
}
private void instrumentGetClassData()
{
// Get an instance of ProjectData:
// ProjectData.getGlobalProjectData()
mv.visitMethodInsn(INVOKESTATIC,
"net/sourceforge/cobertura/coveragedata/ProjectData",
"getGlobalProjectData",
"()Lnet/sourceforge/cobertura/coveragedata/ProjectData;");
// Get the ClassData object for this class:
// projectData.getClassData("name.of.this.class")
mv.visitLdcInsn(firstPass.getOwnerClass());
mv
.visitMethodInsn(INVOKEVIRTUAL,
"net/sourceforge/cobertura/coveragedata/ProjectData",
"getOrCreateClassData",
"(Ljava/lang/String;)Lnet/sourceforge/cobertura/coveragedata/ClassData;");
}
private void instrumentSwitchHit(int lineNumber, int switchNumber, int branch)
{
instrumentGetClassData();
//Invoke the touchSwitch(lineNumber, switchNumber, branch)
mv.visitIntInsn(SIPUSH, lineNumber);
mv.visitIntInsn(SIPUSH, switchNumber);
mv.visitIntInsn(SIPUSH, branch);
instrumentInvokeTouchSwitch();
}
private void instrumentJumpHit(boolean branch)
{
instrumentGetClassData();
//Invoke the touchJump(lineNumber, branchNumber, branch)
instrumentPutLineAndBranchNumbers();
mv.visitInsn(branch ? BOOLEAN_TRUE : BOOLEAN_FALSE);
instrumentInvokeTouchJump();
}
private void instrumentInvokeTouchJump()
{
mv.visitMethodInsn(INVOKEVIRTUAL, "net/sourceforge/cobertura/coveragedata/ClassData", "touchJump", "(IIZ)V");
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
mv.visitVarInsn(ISTORE, myVariableIndex + 1);
}
private void instrumentInvokeTouchSwitch()
{
mv.visitMethodInsn(INVOKEVIRTUAL, "net/sourceforge/cobertura/coveragedata/ClassData", "touchSwitch", "(III)V");
}
private void instrumentPutLineAndBranchNumbers()
{
mv.visitVarInsn(ILOAD, myVariableIndex);
mv.visitVarInsn(ILOAD, myVariableIndex + 1);
}
private Label instrumentIsLastJump() {
mv.visitVarInsn(ILOAD, myVariableIndex);
mv.visitIntInsn(SIPUSH, lastJump.getLineNumber());
Label newLabelX = new Label();
mv.visitJumpInsn(IF_ICMPNE, newLabelX);
mv.visitVarInsn(ILOAD, myVariableIndex + 1);
mv.visitIntInsn(SIPUSH, lastJump.getJumpNumber());
mv.visitJumpInsn(IF_ICMPNE, newLabelX);
return newLabelX;
}
public void visitMaxs(int maxStack, int maxLocals)
{
mv.visitLocalVariable("__cobertura__line__number__", "I", null, startLabel, endLabel, myVariableIndex);
mv.visitLocalVariable("__cobertura__branch__number__", "I", null, startLabel, endLabel, myVariableIndex + 1);
super.visitMaxs(maxStack, maxLocals);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy