そこjacocoクラスレベル、メソッドレベル、論理レベル、およびブランチラインレベルは、特殊なパッケージング処理を行います。具体的にカプセル化されたクラスinternal.analysis.flow
IFrameのインタフェース
import org.objectweb.asm.MethodVisitor;
/**
* 当前的 stackmap frame 内容的表示
*/
public interface IFrame {
/**
* 向给定的访问者发出具有当前内容的 frame 事件
*
* @param mv 向该方法发出 frame 事件的method visitor
*/
void accept(MethodVisitor mv);
}
クラスはClassprobesAdapter.java(クラスレベル)、Instruction.java(命令レベル)、LabelFlowAnalysis.java(分岐論理レベル)とMethodProbesAdapter.java(メソッドレベル)に関与しています。
ClassprobesAdapter
publicfinal MethodVisitor visitMethod(intaccess, String name, String desc, String signature, String[] exceptions)
{
MethodProbesVisitor mv =this.cv.visitMethod(access, name, desc, signature, exceptions);
MethodProbesVisitor methodProbes;
final MethodProbesVisitor methodProbes;
if (mv == null) {
methodProbes =EMPTY_METHOD_PROBES_VISITOR;
} else {
methodProbes = mv;
}
new MethodSanitizer(null, access, name,desc, signature, exceptions)
{
public void visitEnd()
{
super.visitEnd();
LabelFlowAnalyzer.markLabels(this);
MethodProbesAdapter probesAdapter = newMethodProbesAdapter(methodProbes, ClassProbesAdapter.this);
if(ClassProbesAdapter.this.trackFrames)
{
AnalyzerAdapter analyzer = new AnalyzerAdapter(ClassProbesAdapter.this.name,this.access, this.name, this.desc, probesAdapter); probesAdapter.setAnalyzer(analyzer);
accept(analyzer);
}
else
{
accept(probesAdapter);
}
}
};
}
ビジュアルクラスのカバレッジは、実際にコールクラスカバレッジラインを記録する方法である限り、行う組み込みロジックの一人ひとりの枝は、自然のクラスは、統計にカバーされるバイトコードのクラスメソッドを埋め込まれています。
MethodProbesAdapter
@Override
public void visitLabel(final Label label) {
if (LabelInfo.needsProbe(label)) {
if(tryCatchProbeLabels.containsKey(label)) {
probesVisitor.visitLabel(tryCatchProbeLabels.get(label));
}
probesVisitor.visitProbe(idGenerator.nextId());
}
probesVisitor.visitLabel(label);
}
@Override
public void visitInsn(final int opcode) {
switch (opcode) {
case Opcodes.IRETURN:
case Opcodes.LRETURN:
case Opcodes.FRETURN:
case Opcodes.DRETURN:
case Opcodes.ARETURN:
case Opcodes.RETURN:
case Opcodes.ATHROW:
probesVisitor.visitInsnWithProbe(opcode,idGenerator.nextId());
break;
default:
probesVisitor.visitInsn(opcode);
break;
}
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
if (LabelInfo.isMultiTarget(label)) {
probesVisitor.visitJumpInsnWithProbe(opcode,label,
idGenerator.nextId(), frame(jumpPopCount(opcode)));
} else {
probesVisitor.visitJumpInsn(opcode,label);
}
}
private int jumpPopCount(final int opcode) {
switch (opcode) {
case Opcodes.GOTO:
return 0;
case Opcodes.IFEQ:
case Opcodes.IFNE:
case Opcodes.IFLT:
case Opcodes.IFGE:
case Opcodes.IFGT:
case Opcodes.IFLE:
case Opcodes.IFNULL:
case Opcodes.IFNONNULL:
return 1;
default: // IF_CMPxx and IF_ACMPxx
return 2;
}
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[]keys,
final Label[] labels) {
if (markLabels(dflt, labels)) {
probesVisitor.visitLookupSwitchInsnWithProbes(dflt,keys, labels,
frame(1));
} else {
probesVisitor.visitLookupSwitchInsn(dflt,keys, labels);
}
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label...labels) {
if (markLabels(dflt, labels)) {
probesVisitor.visitTableSwitchInsnWithProbes(min,max, dflt,
labels, frame(1));
} else {
probesVisitor.visitTableSwitchInsn(min,max, dflt, labels);
}
}
在MethodProbesAdapter中明显看到字节码指令信息,对于一个方法的进入,jvm中是一个方法栈的创建,入口指令是入栈指令,退出是return:
privateint jumpPopCount(finalint opcode) {
switch (opcode) {
case Opcodes.GOTO:
return0;
caseOpcodes.IFEQ:
caseOpcodes.IFNE:
caseOpcodes.IFLT:
caseOpcodes.IFGE:
caseOpcodes.IFGT:
caseOpcodes.IFLE:
caseOpcodes.IFNULL:
caseOpcodes.IFNONNULL:
return1;
default:// IF_CMPxx and IF_ACMPxx
return2;
}
}
終了方法は、命令を返すことです。
publicvoid visitInsn(finalint opcode) {
switch (opcode) {
case Opcodes.IRETURN:
caseOpcodes.LRETURN:
caseOpcodes.FRETURN:
caseOpcodes.DRETURN:
caseOpcodes.ARETURN:
caseOpcodes.RETURN:
caseOpcodes.ATHROW:
probesVisitor.visitInsnWithProbe(opcode,idGenerator.nextId());
break;
default:
probesVisitor.visitInsn(opcode);
break;
}
}
逻辑跳转的有switch,if
publicvoid visitTableSwitchInsn(finalint min, final int max,
final Label dflt, final Label...labels) {
if (markLabels(dflt, labels)) {
probesVisitor.visitTableSwitchInsnWithProbes(min,max, dflt,
labels, frame(1));
} else {
probesVisitor.visitTableSwitchInsn(min,max, dflt, labels);
}
}
支店の場合:
case Opcodes.GOTO:
return0;
caseOpcodes.IFEQ:
caseOpcodes.IFNE:
caseOpcodes.IFLT:
caseOpcodes.IFGE:
caseOpcodes.IFGT:
caseOpcodes.IFLE:
caseOpcodes.IFNULL:
caseOpcodes.IFNONNULL:
return1;
default:// IF_CMPxx and IF_ACMPxx
return2;
}
LabelFlowAnalysis主要实现代码:
@Override
public void visitJumpInsn(final int opcode, final Label label) {
LabelInfo.setTarget(label);
if (opcode == Opcodes.JSR) {
thrownew AssertionError("Subroutines not supported.");
}
successor = opcode != Opcodes.GOTO;
first = false;
}
@Override
public void visitLabel(final Label label) {
if (first) {
LabelInfo.setTarget(label);
}
if (successor) {
LabelInfo.setSuccessor(label);
}
}
@Override
public void visitLineNumber(final int line, final Label start) {
lineStart = start;
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label...labels) {
visitSwitchInsn(dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[]keys,
final Label[] labels) {
visitSwitchInsn(dflt, labels);
}
@Override
public void visitInsn(final int opcode) {
switch (opcode) {
case Opcodes.RET:
throw new AssertionError("Subroutinesnot supported.");
case Opcodes.IRETURN:
case Opcodes.LRETURN:
case Opcodes.FRETURN:
case Opcodes.DRETURN:
case Opcodes.ARETURN:
case Opcodes.RETURN:
case Opcodes.ATHROW:
successor = false;
break;
default:
successor = true;
break;
}
first = false;
}
首先要知道对于一串指令比如:
iLoad A;
iLoad B;
Add A,B;
iStore;
……
プローブ命令が実行される限り、次のコマンドがない限り実行されるように、私たちはただ、始めの良い時にプローブを追加するので、あなたは、実行するために、コマンド値、命令GOTOラベルまたはジャンプをジャンプしない場合ジャンプロジックを持ちます。私達はちょうど追加するプローブのすべての始めと終わりにジャンプするので、あなたは完全にカバレッジ統計を達成するために、コードのブロックすることができますが、プローブを注入されるそれぞれの行は必要ありません。