我正在尝试检查给定类中常量的使用次数。我的想法是先使用ClassReader.accept(ClassVisitor, ...)访问类,然后使用MethodVisitor.visitMethod()为每个方法声明一个methodVisitor。然后,对于每个 MethodVisitor,我尝试使用 visitFieldInsn。但是,此方法从不访问常量。相反,访问所有其他非常量变量。
这是我的代码:
import org.objectweb.asm.*;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
public class ASMVisitor {
static class SimpleMethodVisitor extends MethodVisitor {
public String currMethodName;
public List<String[]> constants = new ArrayList<>();
private final Map<Object, Integer> constantFrequencyMap;
public SimpleMethodVisitor(int api, MethodVisitor methodVisitor, String methodName) {
super(Opcodes.ASM9, methodVisitor);
currMethodName = methodName;
constantFrequencyMap = new HashMap<>();
}
@Override
public void visitMethodInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String descriptor, boolean isInterface) {
// System.out.println(Opcodes.LDC);
// System.out.println("Visited method instruction: " + opcode + ", " + owner + ", " + name + ", " + descriptor + ", " + isInterface);
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
public Map<Object, Integer> getConstantFrequencyMap() {
return constantFrequencyMap;
}
@Override
public void visitLdcInsn(Object cst) {
// System.out.println("Method " + this.currMethodName + " contains ldc instruction: " + cst);
super.visitLdcInsn(cst);
}
@Override
public void visitInsn(int opcode) {
System.out.println("Method " + this.currMethodName + " contains instruction: " + opcode);
super.visitInsn(opcode);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
System.out.printf("FieldInsn: opcode = %s, Owner = %s, Name = %s, Descriptor = %s\n",
opcode, owner, name, descriptor);
constants.add(new String[]{owner, name, descriptor});
}
}
static class SimpleClassVisitor extends ClassVisitor {
public String currClassName;
public Map<String[],Object> constants;
public Set<MethodVisitor> mvs = new HashSet<>();
public SimpleClassVisitor() {
super(Opcodes.ASM9);
currClassName = "";
this.constants = new HashMap<>();
}
@Override
public void visit(int version, int acc, String name,
String sig, String superName, String[] ifs) {
currClassName = name;
super.visit(version, acc, name, sig, superName, ifs);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
System.out.printf("visitField: Name = %s, Desc = %s, Signature = %s, Value = %s\n",
name, desc, signature, value);
if (value != null && (access & Opcodes.ACC_STATIC) != 0) {
constants.put(new String[]{currClassName, name, desc}, value);
}
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// System.out.printf("visitMethod: Name = %s, Desc = %s, Signature = %s\n", name, desc, signature);
MethodVisitor methodvisitor = super.visitMethod(access, name, desc, signature, exceptions);
MethodVisitor mv = new SimpleMethodVisitor(Opcodes.ASM9, methodvisitor, name);
mvs.add(mv);
return mv;
}
}
public static void main(String[] args) throws IOException {
...
try {
byte[] bytes = Files.readAllBytes(file);
ClassReader classReader = new ClassReader(bytes);
SimpleClassVisitor scv = new SimpleClassVisitor();
classReader.accept(scv, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
这是我的虚拟测试
package DummyTests2;
public class DummyTest1 {
public int publicIVInteger1;
public static int notFinal = -1;
public final int notStatic = -1;
public static final int dummy0 = 0;
public static final int dummy1 = -1;
public DummyTest1() {
this.publicIVInteger1 = DummyTest2.dummy1;
getPublicIVInteger1();
}
public int getPublicIVInteger1() {
setPublicIVInteger1(1);
notFinal -= dummy1;
notFinal -= dummy0;
publicIVInteger1 -= dummy1;
publicIVInteger1 -= dummy0;
return this.publicIVInteger1;
}
public void setPublicIVInteger1(int n) {
int localInt = 0;
publicIVInteger1 += dummy1;
}
}
在这种情况下,只打印出不是常量的变量,而没有像dummy0和dummy1这样带有static和final的常量。我还尝试使用 visitInsn 并且似乎访问了常量,因为访问了相应的指令,但也从未访问过 visitLdcInsn。我想知道为什么 visitFieldInsn 无法识别常量,以及如何将常量的频率存储到 constantFrequencyMap 中。