gpt4 book ai didi

Java ASM GeneratorAdapter变量命名

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:49:52 25 4
gpt4 key购买 nike

我正在生成一个简单的类,但无法注入(inject)正确的变量名。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,并且在将调用委托(delegate)给访问者链之前,它会根据映射计算一个 newIndexnewIndex 是通过另一个私有(private)方法 remap() 计算的。此方法检查给定变量的映射是否已存在,如果不存在,则创建一个新映射。我看到的问题是 newLocal() 方法没有向映射添加任何内容。

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

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

最佳答案

由于 GeneratorAdapter 扩展了 LocalVariablesSorter,其目的是适应所有访问者调用,访问者 API 中的所有方法都得到适应,这与引入的专用方法不同生成器适配器。这种设计允许将新代码插入现有方法,旧代码通过访问者 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 ASM GeneratorAdapter变量命名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45441933/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com