天天看點

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

}