对类的植入锁定进行判断
几个可以对覆盖率跟踪的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; }
执行流程图:
所以最终的出口在于最下面的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); } }