Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

DuplicateFrameEliminator

消除了导致ASM创建无效类文件的连续 stackmap frames 定义。 当原始类文件在意外偏移处包含其他 stackmap frames 时,就会发生这种情况,某些使用ECJ编译的类文件就是这种情况。


ProbeInserter - 探针植入类

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

内部实用程序,用于将探针添加到方法的控制流中。

探针的代码只是将布尔数组的某个插槽设置为true。

另外,必须在方法开始时检索探针数组并将其存储在局部变量中。

构造方法

  • 创建一个新的ProbeInserter
    /**
     *
     * @param access
     *            access flags of the adapted method
     * @param name
     *            the method's name
     * @param desc
     *            the method's descriptor
     * @param mv
     *            the method visitor to which this adapter delegates calls
     * @param arrayStrategy
     *            callback to create the code that retrieves the reference to
     *            the probe array
     */

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

visitmax

探针代码的最大堆栈大小为3,这可以增加到原始堆栈大小,具体取决于探针位置。 访问者堆栈大小是绝对最大值,因为当堆栈大小为空时,访问者代码会在每种方法的开头插入。

    @Override
    public void visitMaxs(final int maxStack, final int maxLocals) {
        final int increasedStack = Math.max(maxStack + 3, accessorStackSize);
        mv.visitMaxs(increasedStack, maxLocals + 1);
    }

insertProbe - 插入具有给定id的探针

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

visitIincInsn - 访问 IINC 指令

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

visitLocalVariable - 访问局部变量声明

Visits a local variable declaration.

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

    private void visitInsn() {
        final Instruction insn = newInstruction(currentNode, currentLine);
        nodeToInstruction.put(currentNode,insn);
        instructions.add(insn);
        if (lastInsn != null) {
            insn.setPredecessor(lastInsn, 0);
        }
 
        final int labelCount =currentLabel.size();
        if (labelCount > 0) {
            for (int i = labelCount; --i >=0;) {
                LabelInfo.setInstruction(currentLabel.get(i),insn);
            }
            currentLabel.clear();
        }
        lastInsn = insn;
    }

大致就是,在对应字节码的执行入口和跳转入口处,置放 probe,是一个数值(该数值和probe id有关),入栈后加1,则记录一次执行

  • 所有放入的探针对应一个boolean[]
  • 探针入栈之后,那么boolean[] 对应的位置变成true,记录执行了。

InstrSupport 类原理

Constants and utilities for byte code instrumentation

字节码检测的常量和实用程序。

属性

public static final int ASM_API_VERSION = Opcodes.ASM7;

接口初始化方法的名称。

static final String CLINIT_NAME = "<clinit>";

存储类的boolean[]数组的coverage信息的字段的数据类型

public static final String DATAFIELD_DESC = "[Z";

初始化方法的名称。

public static final String INITMETHOD_NAME = "$jacocoInit";

初始化方法的描述符。

public static final String INITMETHOD_DESC = "()[Z";
    /**
     * Access modifiers of the initialization method.
     */
    public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC
            | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;

needsFrames

    /**
     * 确定给定的 class 文件版本是否需要 stackmap frames.
     *
     * @param version
     *            class file version
     * @return <code>true</code> if frames are required
     */
    public static boolean needsFrames(final int version) {
        // consider major version only (due to 1.1 anomaly)
        return (version & 0xFFFF) >= Opcodes.V1_6;
    }

classReaderFor

    /**
     * Creates a {@link ClassReader} instance for given bytes of class even if
     * its version not yet supported by ASM.
     *
     * @param b
     *            bytes of class
     * @return {@link ClassReader}
     */
    public static ClassReader classReaderFor(final byte[] b) {
        final int originalVersion = getMajorVersion(b);
        if (originalVersion == Opcodes.V14 + 1) {
            // temporarily downgrade version to bypass check in ASM
            setMajorVersion(Opcodes.V14, b);
        }
        final ClassReader classReader = new ClassReader(b);
        setMajorVersion(originalVersion, b);
        return classReader;
    }

assertNotInstrumented

Ensures that the given member does not correspond to a internal member created by the instrumentation process. This would mean that the class is already instrumented.

确保给定成员与 instrumentation 过程创建的内部成员不对应。 这意味着该类已经被检测。

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

push

Generates the instruction to push the given int value on the stack.


Implementation taken from org.objectweb.asm.commons.GeneratorAdapter#push(int)

生成指令以将给定的int值压入堆栈。

取自org.objectweb.asm.commons.GeneratorAdapter#push(int)的实现

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(下)

Push是用来对于不同的变量值入栈的不同方式,当int取值

  • -1 ~ 5,JVM采用iconst指令将常量压入栈中
  • -128 ~ 127,bipush
  • -32768 ~ 32767,sipush
  • -2147483648~2147483647,ldc

主要作为单例的使用,ClassInstrumenter, ClassAnalyzer调用InstrSupport

ClassAnalyzer 类调用如下:

public static final String DATAFIELD_DESC = "[Z";
 
    // === Init Method ===
 
    /**
     * Name of the initialization method.
     */
    public static final String INITMETHOD_NAME = "$jacocoInit";
 
    /**
     * Descriptor of the initialization method.
     */
    public static final String INITMETHOD_DESC = "()[Z";
        public static void assertNotInstrumented(final String member,
            final String owner) throws IllegalStateException {
        if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) {
            throw new IllegalStateException(format(
                    "Class %s is already instrumented.", owner));
        }
    }

IProbeArrayStrategy

检索类型内每个方法的探针数组实例的策略。 这种抽象是必需的,因为我们需要根据所检测的类型是类还是接口来遵循不同的策略。

storeInstance

    /**
     * Creates code that stores the probe array instance in the given variable.
     *
     * @param mv
     *            visitor to create code
     * @param clinit
     *            true in case of {@code <clinit>} method
     * @param variable
     *            variable index to store probe array to
     * @return maximum stack size required by the generated code
     */
    int storeInstance(MethodVisitor mv, boolean clinit, int variable);

创建将探针数组实例存储在给定变量中的代码。

上一篇:在sublime中使用vim+ctags阅读源码


下一篇:Android铃声偏好设置-获取数据