天天看點

Java代碼覆寫率架構JaCoCo的core-instr core.internal.instr 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

DuplicateFrameEliminator

消除了導緻ASM建立無效類檔案的連續 stackmap frames 定義。 當原始類檔案在意外偏移處包含其他 stackmap frames 時,就會發生這種情況,某些使用ECJ編譯的類檔案就是這種情況。

ProbeInserter - 探針植入類

Java代碼覆寫率架構JaCoCo的core-instr core.internal.instr 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

内部實用程式,用于将探針添加到方法的控制流中。

探針的代碼隻是将布爾數組的某個插槽設定為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 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

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 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy
Java代碼覆寫率架構JaCoCo的core-instr core.internal.instr 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

visitIincInsn - 通路 IINC 指令

Java代碼覆寫率架構JaCoCo的core-instr core.internal.instr 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

visitLocalVariable - 通路局部變量聲明

Visits a local variable declaration.

Java代碼覆寫率架構JaCoCo的core-instr core.internal.instr 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy
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 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

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 包類源碼解析(下)DuplicateFrameEliminatorProbeInserter - 探針植入類InstrSupport 類原理IProbeArrayStrategy

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);      

建立将探針數組執行個體存儲在給定變量中的代碼。