ASM中MethodVisitor如何记录访问过的常量

问题描述 投票:0回答:0

我正在尝试检查给定类中常量的使用次数。我的想法是先使用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 中。

java bytecode java-bytecode-asm
© www.soinside.com 2019 - 2024. All rights reserved.