Java ASM GeneratorAdapter变量命名 [英] Java ASM GeneratorAdapter variable naming

查看:165
本文介绍了Java ASM GeneratorAdapter变量命名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在生成一个简单的类,无法注入正确的变量名。
ASM版本 5.2

I am generating a simple class and unable to inject a proper variable name. ASM version is 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 )来自它的方法。

I am using a GeneratorAdapter to simplify code generation. Since GeneratorAdapter is inherited from LocalVariablesSorter, I assume that it is allowed to use newLocal(Type) method from it.

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

There is nothing wrong with the emitted bytecode except the name of the variable. When visitLocalVariable() method is called, instead of assigning a name to the variable it creates a new one in the bytecode.

发射字节码:

// 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
}

我使用的是由<$ c提供的相同变量索引$ c> newLocal()在中调用visitLocalVariable()。但是在字节码映射索引是 3 而不是 1 。如果变量具有较短类型,例如 int ,那么索引将是 2 并且仍然不是 1 应该如此。

I am using the same variable index provided by newLocal() call in visitLocalVariable(). However in the bytecode mapped index is 3 instead of 1. If the variable has a "shorter" type such as int then the index would be 2 and still not 1 as it should.

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

From my observations this happens because of the following. LocalVariablesSorter maintains a mapping from old variable indices to new ones. It also overrides method visitLocalVariable and before delegating a call down a visitor chain it computes a newIndex from the mapping. The newIndex is calculated via another private method remap(). This method checks whether the mapping already exists for a given variable and if not then a new mapping is created. The problem as I see it is that newLocal() method does not add anything to the mapping.

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

Also I can see from ASM sources that storeInsn() in GeneratorAdapter delegates visitVarInsn() call down the chain instead of calling the implementation of LocalVariablesSorter. Because it is in LocalVariablesSorter implementation the remap() method is called for the variable index and mapping is updated.

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

Therefore my question is how to use GeneratorAdapter so variables are named properly in emitted bytecode or how to combine GeneratorAdapter with LocalVariablesSorter in a chain so they work properly together?

推荐答案

由于 GeneratorAdapter 扩展 LocalVariablesSorter ,其目的是调整所有访客呼叫,与 GeneratorAdapter 引入的专用方法不同,所有属于访问者API的方法都可以进行调整。此设计允许将新代码插入到现有方法中,其中旧代码通过访问者API报告。

Since the GeneratorAdapter extends LocalVariablesSorter, whose purpose is to adapt all visitor calls, all methods which are part of the visitor API get adapted, unlike the dedicated methods introduced by GeneratorAdapter. This design allows to insert new code into an existing method, where the old code gets reported through the visitor API.

因此方法 visitLocalVariable MethodVisitor 上调用code>,它是访问者API的一部分,绕过 LocalVariablesSorter

So the method visitLocalVariable, which is part of the visitor API, must be called on the target MethodVisitor, bypassing the 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字节码和类文件时,开发人员应该知道...

Since this can be confusing, here the alternative directly working on the target MethodVisitor entirely without any convenience wrapper like GeneratorAdapter. It isn’t more complex, though it requires slightly more knowledge, however, it’s knowledge that developers should have anyway, when dealing with Java bytecode and class files…

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 方法,您仍然可以像以前一样使用方法对象或 Type.getMethodDescriptor(Type.VOID_TYPE)

If you don’t feel comfortable with using ()V for a parameterless void method directly, you could still use the Method object like before or Type.getMethodDescriptor(Type.VOID_TYPE)

这篇关于Java ASM GeneratorAdapter变量命名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆