首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何用ASM转换字节码来初始化静态块中的原始常数?

如何用ASM转换字节码来初始化静态块中的原始常数?
EN

Stack Overflow用户
提问于 2020-04-12 07:04:33
回答 1查看 170关注 0票数 0

我需要用ASM转换Java字节码,以初始化类中的public static final块中的static {...}字段。例如:

输入:

代码语言:javascript
运行
复制
public static final int CONSTANT = 10;

输出:

代码语言:javascript
运行
复制
public static final int CONSTANT;

static {
    CONSTANT = 10;
}

我需要这种转换,因为编译器用字节码中的实际值替换原语常量,因此它们的使用变得无法跟踪。此转换允许跟踪常量的使用情况。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-14 14:25:47

对于这种转换,您可以使用通常的ClassReaderClassVisitor(转换器)→ClassWriter链。有三个基本步骤:

value.

  • Override重写
  • visitField以跟踪所有具有常量值的字段,并调用不带常数的超级访问方法(即使用null )来保持字段声明,但删除常量
  • visitMethod以注意是否已经存在类初始化器(<clinit>方法)。如果是这样的话,返回一个特殊的MethodVisitor,它将在代码开始时注入字段初始化,并清除映射,因此第三步成为一个MethodVisitor visitEnd,如果存在常量字段,并且没有现有的类初始化器,则创建类初始化器。新创建的类初始化程序必须执行相同的字段分配,因此值得在injectFieldInit方法中使用公共代码。然后,我们只需要为已经存在的initializer.

附加不需要添加的强制性RETURN指令

这段代码使用数组作为映射键,这里没有问题,因为每个字段都是不同的,因此数组没有基于内容的equals方法这一事实无关紧要。我们本来可以使用一个List<Map.Entry<…>>或一个专用元素类型的列表来保存所有必需的值,其结果与代码不进行查找,但只在已发现的字段上迭代一次相同。

代码语言:javascript
运行
复制
public static byte[] transform(byte[] classFile) {
    ClassReader cr = new ClassReader(classFile);
    ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
    ClassVisitor trans = new ClassVisitor(Opcodes.ASM5, cw) {
        private String currClassName;
        private Map<String[],Object> 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 acc, String name, String desc,
                                                 String sig, Object value) {
            if(value != null && (acc & Opcodes.ACC_STATIC) != 0)
                constants.put(new String[]{currClassName, name, desc}, value);
            return super.visitField(acc, name, desc, sig, null);
        }
        @Override public MethodVisitor visitMethod(int acc, String name, String desc,
                                                   String sig, String[] ex) {
            MethodVisitor mv = super.visitMethod(acc, name, desc, sig, ex);
            if(name.equals("<clinit>")) {
                mv = new MethodVisitor(Opcodes.ASM5, mv) {
                    @Override public void visitCode() {
                        super.visitCode();
                        injectFieldInit(this, constants);
                        constants.clear();
                    }
                };
            }
            return mv;
        }
        @Override public void visitEnd() {
            if(!constants.isEmpty()) {
                MethodVisitor mv = super.visitMethod(
                    Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
                mv.visitCode();
                injectFieldInit(mv, constants);
                mv.visitInsn(Opcodes.RETURN);
                mv.visitMaxs(-1, -1);
                mv.visitEnd();
            }
            super.visitEnd();
        }
    };
    cr.accept(trans, 0);
    return cw.toByteArray();
}
static void injectFieldInit(MethodVisitor target, Map<String[], Object> constants) {
    for(Map.Entry<String[],Object> e: constants.entrySet()) {
        target.visitLdcInsn(e.getValue());
        String[] field = e.getKey();
        target.visitFieldInsn(Opcodes.PUTSTATIC, field[0], field[1], field[2]);
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61167940

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档