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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 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);

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

目录
相关文章
|
29天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
63 7
|
5天前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
52 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
12天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
12天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
10天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
22天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
106 13
|
8月前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
330 1
|
Java BI API
在Java代码中打日志需要注意什么?
日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手,是撕逼和甩锅的利器!
716 0
|
缓存 Java 网络架构
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
172 0
|
Java BI Apache
在Java代码中打日志需要注意什么?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 为什么要打日志? 日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手! 做一件事情之前,先思考为什么。
在Java代码中打日志需要注意什么?

热门文章

最新文章