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

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

对类的植入锁定进行判断

几个可以对覆盖率跟踪的Java类定义进行instrument的API

public byte[] instrument(final ClassReader reader) {
    final ClassWriter writer = new ClassWriter(reader, 0) {
      @Override
      protected String getCommonSuperClass(final String type1,
          final String type2) {
        throw new IllegalStateException();
      }
    };
    final IProbeArrayStrategy strategy = ProbeArrayStrategyFactory
        .createFor(reader, accessorGenerator);
    final ClassVisitor visitor = new ClassProbesAdapter(
        new ClassInstrumenter(strategy, writer), true);
    reader.accept(visitor, ClassReader.EXPAND_FRAMES);
    return writer.toByteArray();
  }
  public byte[] instrument(final byte[] buffer, final String name)
      throws IOException {
    try {
      return instrument(new ClassReader(buffer));
    } catch (final RuntimeException e) {
      throw instrumentError(name, e);
    }
  }
  public byte[] instrument(final InputStream input, final String name)
      throws IOException {
    final byte[] bytes;
    try {
      bytes = InputStreams.readFully(input);
    } catch (final IOException e) {
      throw instrumentError(name, e);
    }
    return instrument(bytes, name);
  }
  public void instrument(final InputStream input, final OutputStream output,
      final String name) throws IOException {
    output.write(instrument(input, name));
  }
  private IOException instrumentError(final String name,
      final Exception cause) {
    final IOException ex = new IOException(
        String.format("Error while instrumenting %s.", name));
    ex.initCause(cause);
    return ex;
  }

执行流程图:

15.png

所以最终的出口在于最下面的instrument(input,output,string),下面是剩余部分代码:

public int instrumentAll(final InputStream input, final OutputStream output,
      final String name) throws IOException {
    final ContentTypeDetector detector;
    try {
      detector = new ContentTypeDetector(input);
    } catch (final IOException e) {
      throw instrumentError(name, e);
    }
    switch (detector.getType()) {
    case ContentTypeDetector.CLASSFILE:
      instrument(detector.getInputStream(), output, name);
      return 1;
    case ContentTypeDetector.ZIPFILE:
      return instrumentZip(detector.getInputStream(), output, name);
    case ContentTypeDetector.GZFILE:
      return instrumentGzip(detector.getInputStream(), output, name);
    case ContentTypeDetector.PACK200FILE:
      return instrumentPack200(detector.getInputStream(), output, name);
    default:
      copy(detector.getInputStream(), output, name);
      return 0;
    }
  }
  private int instrumentZip(final InputStream input,
      final OutputStream output, final String name) throws IOException {
    final ZipInputStream zipin = new ZipInputStream(input);
    final ZipOutputStream zipout = new ZipOutputStream(output);
    ZipEntry entry;
    int count = 0;
    while ((entry = nextEntry(zipin, name)) != null) {
      final String entryName = entry.getName();
      if (signatureRemover.removeEntry(entryName)) {
        continue;
      }
      zipout.putNextEntry(new ZipEntry(entryName));
      if (!signatureRemover.filterEntry(entryName, zipin, zipout)) {
        count += instrumentAll(zipin, zipout, name + "@" + entryName);
      }
      zipout.closeEntry();
    }
    zipout.finish();
    return count;
  }
  private ZipEntry nextEntry(final ZipInputStream input,
      final String location) throws IOException {
    try {
      return input.getNextEntry();
    } catch (final IOException e) {
      throw instrumentError(location, e);
    }
  }
  private int instrumentGzip(final InputStream input,
      final OutputStream output, final String name) throws IOException {
    final GZIPInputStream gzipInputStream;
    try {
      gzipInputStream = new GZIPInputStream(input);
    } catch (final IOException e) {
      throw instrumentError(name, e);
    }
    final GZIPOutputStream gzout = new GZIPOutputStream(output);
    final int count = instrumentAll(gzipInputStream, gzout, name);
    gzout.finish();
    return count;
  }
  private int instrumentPack200(final InputStream input,
      final OutputStream output, final String name) throws IOException {
    final InputStream unpackedInput;
    try {
      unpackedInput = Pack200Streams.unpack(input);
    } catch (final IOException e) {
      throw instrumentError(name, e);
    }
    final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    final int count = instrumentAll(unpackedInput, buffer, name);
    Pack200Streams.pack(buffer.toByteArray(), output);
    return count;
  }
  private void copy(final InputStream input, final OutputStream output,
      final String name) throws IOException {
    final byte[] buffer = new byte[1024];
    int len;
    while ((len = read(input, buffer, name)) != -1) {
      output.write(buffer, 0, len);
    }
  }
  private int read(final InputStream input, final byte[] buffer,
      final String name) throws IOException {
    try {
      return input.read(buffer);
    } catch (final IOException e) {
      throw instrumentError(name, e);
    }
  }

核心关键是instrumentAll这个方法,根据文件包是class还是zip,或者gz等,不同的加载方式。

ClassInstrumenter 类

适配器为了类覆盖率跟踪。

import org.jacoco.core.internal.flow.ClassProbesVisitor;
import org.jacoco.core.internal.flow.MethodProbesVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
/**
 * Adapter that instruments a class for coverage tracing.
 */
public class ClassInstrumenter extends ClassProbesVisitor {
  private final IProbeArrayStrategy probeArrayStrategy;
  private String className;
  /**
   * Emits a instrumented version of this class to the given class visitor.
   * 向给定的class 访问者发出此类的instrumented版本
   *
   * @param probeArrayStrategy
   *            该策略将用于访问探针数组
   * @param cv
   *            访问链中的下一位 delegate 将获得 instrumente 类
   */
  public ClassInstrumenter(final IProbeArrayStrategy probeArrayStrategy,
      final ClassVisitor cv) {
    super(cv);
    this.probeArrayStrategy = probeArrayStrategy;
  }
  @Override
  public void visit(final int version, final int access, final String name,
      final String signature, final String superName,
      final String[] interfaces) {
    this.className = name;
    super.visit(version, access, name, signature, superName, interfaces);
  }
  @Override
  public FieldVisitor visitField(final int access, final String name,
      final String desc, final String signature, final Object value) {
    InstrSupport.assertNotInstrumented(name, className);
    return super.visitField(access, name, desc, signature, value);
  }
  @Override
  public MethodProbesVisitor visitMethod(final int access, final String name,
      final String desc, final String signature,
      final String[] exceptions) {
    InstrSupport.assertNotInstrumented(name, className);
    final MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
        exceptions);
    if (mv == null) {
      return null;
    }
    final MethodVisitor frameEliminator = new DuplicateFrameEliminator(mv);
    final ProbeInserter probeVariableInserter = new ProbeInserter(access,
        name, desc, frameEliminator, probeArrayStrategy);
    return new MethodInstrumenter(probeVariableInserter,
        probeVariableInserter);
  }
  @Override
  public void visitTotalProbeCount(final int count) {
    probeArrayStrategy.addMembers(cv, count);
  }
}
目录
相关文章
|
2月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
125 4
|
2月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
214 5
|
2月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
200 1
|
2月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
256 1
|
2月前
|
存储 Java 程序员
【Java】(6)全方面带你了解Java里的日期与时间内容,介绍 Calendar、GregorianCalendar、Date类
java.util 包提供了 Date 类来封装当前的日期和时间。Date 类提供两个构造函数来实例化 Date 对象。第一个构造函数使用当前日期和时间来初始化对象。Date( )第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
205 0
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
384 2
|
9月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
939 29
|
9月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
403 4
|
9月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
9月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS