我想使用ASM提取类的方法中存在的局部变量名和值。请提供建议。
答案 0 :(得分:1)
在Java字节码中,局部变量集中在局部列表中,该列表将在方法参数的开头(包括接收方-对this
的引用)包含在内。然后,在声明局部变量时,接下来将分配它们。最后,如果不再要使用局部变量或参数,则会“释放”空间,以便可以将其再次用于另一个变量。
可以从MethodVisitor#visitLocalVariable(...)
或LocalVariableNode
中获得局部变量名称及其在局部变量列表中的位置之间的关系(仅当该类已使用调试信息进行编译时)。
仅通过使用ASM查看字节码不能获得局部变量值,您必须添加指令以在所需的点打印局部变量的值。一种选择是检测商店指令并插入指令以在其之前记录值。
示例(假设所有变量均为int
,对于其他类型,则需要调用log
的重载版本)。
// In a AdviceAdapter
private static final Type VAR_LOGGER = Type.getInternalName(VarLogger.class);
private static final Method LOG_I = Method.getMethod("void log(int, int)");
private static final Method LOG_L = Method.getMethod("void log(long, int)");
private static final Method LOG_F = Method.getMethod("void log(float, int)");
private static final Method LOG_D = Method.getMethod("void log(double, int)");
private static final Method LOG_A = Method.getMethod("void log(Object, int)");
@Override
public void visitVarInsn(int opcode, int var) {
if (isStoreOp(opcode)) {
dup();
push(var);
invokeStatic(VAR_LOGGER, getLogMethod(opcode));
}
super.visitVarInsn(opcode, var);
}
private boolean isStoreOp(int opcode) {
switch (opcode) {
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
return true;
default:
return false;
}
}
private Method getLogMethod(int opcode) {
switch (opcode) {
case ISTORE: return LOG_I;
case LSTORE: return LOG_L;
case FSTORE: return LOG_F;
case DSTORE: return LOG_D;
case ASTORE: return LOG_A;
default:
throw new RuntimeException("Invalid store code: " + opcode);
}
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 3, maxLocals);
}
注意:仅当您不使用visitMaxs
时,才有必要覆盖COMPUTE_MAXS
。