Я создаю простой класс и не могу ввести правильное имя переменной. Версия 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
}
Я использую тот же индекс переменной, что и вызов newLocal()
в visitLocalVariable()
. Однако в сопоставленном байт-коде индексе 3
вместо 1
. Если переменная имеет «более короткий» тип, такой как int
, тогда индекс будет 2
, а не 1
, как следует.
По моим наблюдениям это происходит из-за следующего. LocalVariablesSorter
поддерживает сопоставление старых индексов переменных с новыми. Он также переопределяет метод visitLocalVariable
и перед делегированием вызова по цепочке посетителей вычисляет newIndex
из сопоставления. newIndex
вычисляется с помощью другого закрытого метода remap()
. Этот метод проверяет, существует ли сопоставление для данной переменной, и если нет, то создается новое сопоставление. Проблема, как я вижу, заключается в том, что метод newLocal()
ничего не добавляет к отображению.
Также я вижу из источников ASM, что storeInsn()
в GeneratorAdapter
делегатах visitVarInsn()
вызывает цепочку вместо вызова реализации LocalVariablesSorter
. Поскольку в реализации LocalVariablesSorter
вызывается метод remap()
для индекса переменной, и сопоставление обновляется.
Поэтому мой вопрос заключается в том, как использовать GeneratorAdapter
, чтобы переменные правильно назывались в испускаемом байт-коде, или как объединить GeneratorAdapter
с LocalVariablesSorter
в цепочке, чтобы они правильно работали вместе?
LocalVariablesSorter
состоит в том, чтобы изменить поведение последующих вызововvisit…
. Очевидно, что все операции, которые не должны изменяться, должны обходитьLocalVariablesSorter
, как это делают методы, представленныеGeneratorAdapter
. - person Holger   schedule 02.08.2017GeneratorAdapter
наследуется отLocalVariablesSorter
, если он не может обеспечить функциональность родителя? - person alllex   schedule 04.08.2017ClassWriter
. Если вы можете объяснить, как построить действующую цепочку посетителей метода, пожалуйста, сделайте это. - person alllex   schedule 07.08.2017