ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java ASM GeneratorAdapter变量命名

2019-10-08 13:02:54  阅读:253  来源: 互联网

标签:java-bytecode-asm java bytecode


我正在生成一个简单的类,无法注入适当的变量名称.
ASM版本是5.2.

这是代码:

package com.test;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {

    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        String name = "com.test.Sub";
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name.replace('.', '/'), null, "java/lang/Object", null);
        Method ctor = Method.getMethod("void <init>()");
        GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, ctor, null, null, cw);
        mg.visitCode();
        mg.loadThis();
        mg.invokeConstructor(Type.getType(Object.class), ctor);
        int var = mg.newLocal(Type.INT_TYPE);
        mg.push(42.42);
        mg.storeLocal(var);
        Label varLabel = mg.mark();
        mg.returnValue();
        Label endLabel = mg.mark();
        mg.visitLocalVariable("x", "D", null, varLabel, endLabel, var);
        mg.endMethod();
        cw.visitEnd();
        byte[] bytes = cw.toByteArray();
        Files.write(Paths.get(name + ".class"), bytes);
    }

}

我正在使用GeneratorAdapter来简化代码生成.由于GeneratorAdapter继承自LocalVariablesSorter,我假设允许使用newLocal(Type)方法.

除了变量的名称之外,发出的字节码没有任何问题.调用visitLocalVariable()方法时,不是为变量赋值,而是在字节码中创建一个新名称.

发射字节码:

// class version 52.0 (52)
// access flags 0x1
public class com/test/Sub {
  // access flags 0x1
  public <init>()V
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    LDC 42.42
    DSTORE 1
   L0
    RETURN
   L1
    LOCALVARIABLE x D L0 L1 3
    MAXSTACK = 2
    MAXLOCALS = 5
}

我在visitLocalVariable()中使用newLocal()调用提供的相同变量索引.但是在字节码中,映射索引是3而不是1.如果变量具有“较短”类型(如int),则索引将为2,仍然不应该为1.

根据我的观察,这是因为以下原因. LocalVariablesSorter维护从旧变量索引到新变量索引的映射.它还会覆盖方法visitLocalVariable,并且在向访问者链委托调用之前,它会根据映射计算newIndex. newIndex是通过另一个私有方法remap()计算的.此方法检查给定变量的映射是否已存在,如果不存在,则创建新映射.我认为问题是newLocal()方法不会向映射添加任何内容.

我还可以从ASM源看到,GeneratorAdapter中的storeInsn()委托visitVarInsn()调用链而不是调用LocalVariablesSorter的实现.因为它在LocalVariablesSorter实现中,所以为变量索引调用remap()方法并更新映射.

因此,我的问题是如何使用GeneratorAdapter,以便在发出的字节码中正确命名变量,或者如何将ChainAdapter与LocalVariablesSorter组合在一起,以便它们一起正常工作?

解决方法:

由于GeneratorAdapter扩展了LocalVariablesSorter,其目的是调整所有访问者调用,所以作为访问者API的一部分的所有方法都得到了调整,这与GeneratorAdapter引入的专用方法不同.此设计允许将新代码插入到现有方法中,其中旧代码通过访问者API进行报告.

因此必须在目标MethodVisitor上调用visitLocalVariable方法(它是访问者API的一部分),绕过LocalVariablesSorter:

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
String name = "com.test.Sub";
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,
         name.replace('.', '/'), null, "java/lang/Object", null);
Method ctor = Method.getMethod("void <init>()");
MethodVisitor direct = cw.visitMethod(
         Opcodes.ACC_PUBLIC, ctor.getName(), ctor.getDescriptor(), null, null);
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, ctor, direct);
mg.visitCode();
mg.loadThis();
mg.invokeConstructor(Type.getType(Object.class), ctor);
int var = mg.newLocal(Type.DOUBLE_TYPE);
mg.push(42.42);
mg.storeLocal(var);
Label varLabel = mg.mark();
mg.returnValue();
Label endLabel = mg.mark();
direct.visitLocalVariable("x", "D", null, varLabel, endLabel, var);
mg.endMethod();
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Files.write(Paths.get(name + ".class"), bytes);

由于这可能会造成混淆,因此这里可以直接使用目标MethodVisitor,而不需要像GeneratorAdapter这样的任何便利包装器.它并不复杂,虽然它需要更多的知识,然而,在处理Java字节码和类文件时,开发人员应该知道它…

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
String name = "com.test.Sub";
String superClName = "java/lang/Object", ctorName = "<init>", ctorDesc = "()V";
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name.replace('.','/'), null, superClName, null);
MethodVisitor direct = cw.visitMethod(Opcodes.ACC_PUBLIC, ctorName, ctorDesc, null, null);
direct.visitCode();
// "this" is alway 0 (zero) and for parameterless methods the next var location is 1 (one)
int thisVar = 0, var = 1;
direct.visitVarInsn(Opcodes.ALOAD, thisVar);
direct.visitMethodInsn(Opcodes.INVOKESPECIAL, superClName, ctorName, ctorDesc, false);
direct.visitLdcInsn(42.42);
Label varLabel = new Label(), endLabel = new Label();
direct.visitVarInsn(Opcodes.DSTORE, var);
direct.visitLabel(varLabel);
direct.visitInsn(Opcodes.RETURN);
direct.visitLabel(endLabel);
direct.visitLocalVariable("x", "D", null, varLabel, endLabel, var);
direct.visitMaxs(-1, -1);// no actual values, using COMPUTE_FRAMES
direct.visitEnd();
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Files.write(Paths.get(name + ".class"), bytes);

如果您对使用()V直接使用无参数void方法感到不舒服,您仍然可以像以前一样使用Method对象或Type.getMethodDescriptor(Type.VOID_TYPE)

标签:java-bytecode-asm,java,bytecode
来源: https://codeday.me/bug/20191008/1872000.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有