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

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

DuplicateFrameEliminator

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


ProbeInserter - 探针植入类

16.png

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

探针的代码只是将布尔数组的某个插槽设置为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
   */

17.png

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的探针

18.png

19.png

visitIincInsn - 访问 IINC 指令

21.png

visitLocalVariable - 访问局部变量声明

Visits a local variable declaration.

22.png

    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 过程创建的内部成员不对应。 这意味着该类已经被检测。

23.png

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)的实现

25.png

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

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

目录
相关文章
|
2月前
|
Java 开发工具
【Azure Storage Account】Java Code访问Storage Account File Share的上传和下载代码示例
本文介绍如何使用Java通过azure-storage-file-share SDK实现Azure文件共享的上传下载。包含依赖引入、客户端创建及完整示例代码,助你快速集成Azure File Share功能。
391 5
|
3月前
|
IDE Java 关系型数据库
Java 初学者学习路线(含代码示例)
本教程为Java初学者设计,涵盖基础语法、面向对象、集合、异常处理、文件操作、多线程、JDBC、Servlet及MyBatis等内容,每阶段配核心代码示例,强调动手实践,助你循序渐进掌握Java编程。
502 3
|
3月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
405 3
|
3月前
|
机器学习/深度学习 JSON Java
Java调用Python的5种实用方案:从简单到进阶的全场景解析
在机器学习与大数据融合背景下,Java与Python协同开发成为企业常见需求。本文通过真实案例解析5种主流调用方案,涵盖脚本调用到微服务架构,助力开发者根据业务场景选择最优方案,提升开发效率与系统性能。
991 0
|
3月前
|
Java API 开发工具
【Azure Developer】Java代码实现获取Azure 资源的指标数据却报错 "invalid time interval input"
在使用 Java 调用虚拟机 API 获取指标数据时,因本地时区设置非 UTC,导致时间格式解析错误。解决方法是在代码中手动指定时区为 UTC,使用 `ZoneOffset.ofHours(0)` 并结合 `withOffsetSameInstant` 方法进行时区转换,从而避免因时区差异引发的时间格式问题。
239 3
|
2月前
|
Java 数据处理 API
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
272 115
|
3月前
|
Java
Java的CAS机制深度解析
CAS(Compare-And-Swap)是并发编程中的原子操作,用于实现多线程环境下的无锁数据同步。它通过比较内存值与预期值,决定是否更新值,从而避免锁的使用。CAS广泛应用于Java的原子类和并发包中,如AtomicInteger和ConcurrentHashMap,提升了并发性能。尽管CAS具有高性能、无死锁等优点,但也存在ABA问题、循环开销大及仅支持单变量原子操作等缺点。合理使用CAS,结合实际场景选择同步机制,能有效提升程序性能。
|
2月前
|
安全 Java 编译器
为什么你的Java代码需要泛型?类型安全的艺术
为什么你的Java代码需要泛型?类型安全的艺术
194 98
|
4月前
|
人工智能 监控 安全
智慧工地解决方案,java智慧工地程序代码
智慧工地系统融合物联网、AI、大数据等技术,实现对施工现场“人、机、料、法、环”的全面智能监控与管理,提升安全、效率与决策水平。
152 2

推荐镜像

更多
  • DNS