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);
  }
}
目录
相关文章
|
10月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
455 4
|
10月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
430 5
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
488 1
|
10月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
586 1
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
583 1
|
Java BI API
在Java代码中打日志需要注意什么?
日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手,是撕逼和甩锅的利器!
988 0
|
缓存 Java 网络架构
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
312 0
|
缓存 架构师 搜索推荐
别在 Java 代码里乱打日志了,这才是正确的日志打印姿势!
使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
|
Java BI Apache
在Java代码中打日志需要注意什么?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 为什么要打日志? 日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手! 做一件事情之前,先思考为什么。
在Java代码中打日志需要注意什么?
|
Java Android开发 C语言
02_JNI中Java代码调用C代码,Android中使用log库打印日志,javah命令的使用,Android.mk文件的编写,交叉编译
 1  编写以下案例(下面的三个按钮都调用了底层的C语言): 项目案例的代码结构如下: 2 编写DataProvider的代码: package com.example.ndkpassdata;   public class DataProvider {         /**      * 计算x和y的加法  apktools      *
1580 0

热门文章

最新文章

推荐镜像

更多
  • DNS