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);
建立将探針數組執行個體存儲在給定變量中的代碼。