DuplicateFrameEliminator
消除了导致ASM创建无效类文件的连续 stackmap frames 定义。 当原始类文件在意外偏移处包含其他 stackmap frames 时,就会发生这种情况,某些使用ECJ编译的类文件就是这种情况。
ProbeInserter - 探针植入类
内部实用程序,用于将探针添加到方法的控制流中。
探针的代码只是将布尔数组的某个插槽设置为true。
另外,必须在方法开始时检索探针数组并将其存储在局部变量中。
构造方法
- 创建一个新的ProbeInserter
/** * * @param access * access flags of the adapted method * @param name * the method's name * @param desc * the method's descriptor * @param mv * the method visitor to which this adapter delegates calls * @param arrayStrategy * callback to create the code that retrieves the reference to * the probe array */
visitmax
探针代码的最大堆栈大小为3,这可以增加到原始堆栈大小,具体取决于探针位置。 访问者堆栈大小是绝对最大值,因为当堆栈大小为空时,访问者代码会在每种方法的开头插入。
@Override public void visitMaxs(final int maxStack, final int maxLocals) { final int increasedStack = Math.max(maxStack + 3, accessorStackSize); mv.visitMaxs(increasedStack, maxLocals + 1); }
insertProbe - 插入具有给定id的探针
visitIincInsn - 访问 IINC 指令
visitLocalVariable - 访问局部变量声明
Visits a local variable declaration.
private void visitInsn() { final Instruction insn = newInstruction(currentNode, currentLine); nodeToInstruction.put(currentNode,insn); instructions.add(insn); if (lastInsn != null) { insn.setPredecessor(lastInsn, 0); } final int labelCount =currentLabel.size(); if (labelCount > 0) { for (int i = labelCount; --i >=0;) { LabelInfo.setInstruction(currentLabel.get(i),insn); } currentLabel.clear(); } lastInsn = insn; }
大致就是,在对应字节码的执行入口和跳转入口处,置放 probe,是一个数值(该数值和probe id有关),入栈后加1,则记录一次执行
- 所有放入的探针对应一个boolean[]
- 探针入栈之后,那么boolean[] 对应的位置变成true,记录执行了。
InstrSupport 类原理
Constants and utilities for byte code instrumentation
字节码检测的常量和实用程序。
属性
public static final int ASM_API_VERSION = Opcodes.ASM7;
接口初始化方法的名称。
static final String CLINIT_NAME = "<clinit>";
存储类的boolean[]数组的coverage信息的字段的数据类型
public static final String DATAFIELD_DESC = "[Z";
初始化方法的名称。
public static final String INITMETHOD_NAME = "$jacocoInit";
初始化方法的描述符。
public static final String INITMETHOD_DESC = "()[Z";
/** * Access modifiers of the initialization method. */ public static final int INITMETHOD_ACC = Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
needsFrames
/** * 确定给定的 class 文件版本是否需要 stackmap frames. * * @param version * class file version * @return <code>true</code> if frames are required */ public static boolean needsFrames(final int version) { // consider major version only (due to 1.1 anomaly) return (version & 0xFFFF) >= Opcodes.V1_6; }
classReaderFor
/** * Creates a {@link ClassReader} instance for given bytes of class even if * its version not yet supported by ASM. * * @param b * bytes of class * @return {@link ClassReader} */ public static ClassReader classReaderFor(final byte[] b) { final int originalVersion = getMajorVersion(b); if (originalVersion == Opcodes.V14 + 1) { // temporarily downgrade version to bypass check in ASM setMajorVersion(Opcodes.V14, b); } final ClassReader classReader = new ClassReader(b); setMajorVersion(originalVersion, b); return classReader; }
assertNotInstrumented
Ensures that the given member does not correspond to a internal member created by the instrumentation process. This would mean that the class is already instrumented.
确保给定成员与 instrumentation 过程创建的内部成员不对应。 这意味着该类已经被检测。
push
Generates the instruction to push the given int value on the stack.
Implementation taken from org.objectweb.asm.commons.GeneratorAdapter#push(int)
生成指令以将给定的int值压入堆栈。
取自org.objectweb.asm.commons.GeneratorAdapter#push(int)的实现
Push是用来对于不同的变量值入栈的不同方式,当int取值
- -1 ~ 5,JVM采用iconst指令将常量压入栈中
- -128 ~ 127,bipush
- -32768 ~ 32767,sipush
- -2147483648~2147483647,ldc
主要作为单例的使用,ClassInstrumenter, ClassAnalyzer调用InstrSupport
ClassAnalyzer 类调用如下:
public static final String DATAFIELD_DESC = "[Z"; // === Init Method === /** * Name of the initialization method. */ public static final String INITMETHOD_NAME = "$jacocoInit"; /** * Descriptor of the initialization method. */ public static final String INITMETHOD_DESC = "()[Z"; public static void assertNotInstrumented(final String member, final String owner) throws IllegalStateException { if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) { throw new IllegalStateException(format( "Class %s is already instrumented.", owner)); } }
IProbeArrayStrategy
检索类型内每个方法的探针数组实例的策略。 这种抽象是必需的,因为我们需要根据所检测的类型是类还是接口来遵循不同的策略。
storeInstance
/** * Creates code that stores the probe array instance in the given variable. * * @param mv * visitor to create code * @param clinit * true in case of {@code <clinit>} method * @param variable * variable index to store probe array to * @return maximum stack size required by the generated code */ int storeInstance(MethodVisitor mv, boolean clinit, int variable);
创建将探针数组实例存储在给定变量中的代码。