天天看点

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(上)对类的植入锁定进行判断ClassInstrumenter 类

对类的植入锁定进行判断

几个可以对覆盖率跟踪的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;
    }      

执行流程图:

Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析(上)对类的植入锁定进行判断ClassInstrumenter 类

所以最终的出口在于最下面的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);
    }

}