当前位置: 首页 > 面试题库 >

Java ASM GeneratorAdapter变量命名

廖君昊
2023-03-14
问题内容

我正在生成一个简单的类,并且无法注入适当的变量名。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);
    }

}

我正在使用AGeneratorAdapter来简化代码生成。由于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
}

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

根据我的观察,发生这种情况的原因如下。LocalVariablesSorter维护从旧变量索引到新变量索引的映射。它还会覆盖方法,visitLocalVariable并在将访问链下放一个呼叫之前,它会根据newIndex映射计算a 。将newIndex经由另一私人方法计算remap()。此方法检查给定变量的映射是否已经存在,如果不存在,则创建一个新的映射。我看到的问题是该newLocal()方法未向映射添加任何内容。

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

因此,我的问题是如何使用GeneratorAdapter这样的变量,以便在发出的字节码中正确命名变量,或者如何在链中进行组合GeneratorAdapterLocalVariablesSorter使它们正常工作?


问题答案:

由于GeneratorAdapterextendsLocalVariablesSorter的目的是适应所有访问者调用,因此与引入的专用方法不同,所有访问者API的方法都将得到适应GeneratorAdapter。这种设计允许将新代码插入到现有方法中,在旧方法中,旧代码通过访问者API报告。

因此visitLocalVariable,作为访问者API一部分的方法必须MethodVisitor绕过目标在目标上调用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常数变量有任何命名约定吗? 通常,我们使用名称包含大写字母和下划线()的变量。 例如: 问题答案: 是。这就对了。它也经常用于。 唯一常见的例外是记录您可能会看到的位置 但是我更喜欢 我通常将其写为UPPER_CASE,但我也为类编写TitleCase,为变量和方法编写camelCase。

  • 问题内容: 这个问题比什么都更能促进我的知识… Java是否具有类似于PHP生成变量名的功能?我有一个SCJA证书,我正在为SCJP学习,但从未见过,但很好奇。 PHP示例 Java是否有类似的东西?我在这里一直在阅读,一般的答案是使用我不感兴趣的HashMap,因为这不是解决实际问题的方法。我对这可能的解决方案更感兴趣?如果不是这样,那就只是尝试扩大我的知识! 谢谢贾里德 问题答案: 不,变量(

  • 为了使Playbook更灵活、通用性更强,允许用户在执行的时候传入变量的值,这个时候就需要用到“额外变量”。 定义命令行变量 在release.yml文件里,hosts和user都定义为变量,需要从命令行传递变量值。 --- - hosts: '{{ hosts }}' remote_user: '{{ user }}' tasks: - ... 使用命令行变量 在命令行里面传值

  • 问题内容: 在PHP中,我们(至少是优秀的程序员)总是以小写字母开头的通用变量名称,而以大写字母开头的类变量/对象则区分它们。以相同的方式,我们以小写字母开头的通用文件名,但是包含具有大写字母的类的文件。 例如: Java中的约定是否相同,即对象以大写字母开头,其余对象以小写字母开头?还是像我在其他地方读过的所有内容都以小写字母开头? 问题答案: 通常,所有变量都以小写字母开头: 有些人喜欢在所有

  • 本文向大家介绍JavaScript 销毁时重命名变量,包括了JavaScript 销毁时重命名变量的使用技巧和注意事项,需要的朋友参考一下 示例 解构允许我们引用一个对象中的一个键,但是将其声明为具有不同名称的变量。该语法看起来像普通JavaScript对象的键值语法。            

  • 我的应用程序在另一个门户应用程序下运行。两者都是在Spring中实现的,并且都使用csrf安全性。 我需要的基本上是改变csrf令牌在会话中的命名方式,这样两个令牌都可以在没有冲突的情况下工作。到目前为止,我尝试的是创建另一个令牌存储库并尝试更改安全配置类中的参数名称和会话属性名称。 当我发出请求时,Spring不太喜欢这个新设置,日志会生成以下行: 我还应该做些什么?我错过什么了吗?