2

Я использую ASM library для изменения байт-кода, созданного другими пользователями. Для произвольного метода в произвольном классе я хотел бы создать LdcInsnNode, который добавляет текущий класс в стек.Как создать ASM LdcInsnNode, который статически добавляет текущий класс в стек?

Например, допустим, что я преобразовываю класс под названием com.example.ExampleClass. Я хотел бы создать байт-код, который эквивалентен System.out.println(ExampleClass.class.getName());.

Это похоже на относительно простую задачу. Когда я использую рамочную плагин Eclipse, байт-код, он говорит, что следующий байт-код эквивалентен:

GETSTATIC java/lang/System.out : Ljava/io/PrintStream; 
LDC Lcom/example/ExampleClass;.class 
INVOKEVIRTUAL java/lang/Class.getName()Ljava/lang/String; 
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V 

Я попытался следующий код:

private InsnList printClass() { 
    InsnList result = new InsnList(); 
    result.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); 
    result.add(new LdcInsnNode("L" + name + ";.class")); 
    result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false)); 
    result.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false)); 
    return result; 
} 

Это время запуска в расширении ClassNode, так name относится к полю ClassNode.name. Возвращаемый этим методом InsnList вставляется перед существующим AbstractInsnNode с использованием InsnList.insertBefore(AbstractInsnNode, printClass()). Когда эта точка будет достигнута в байткоде, я получаю сообщение об ошибке со следующей причиной:

Type 'java/lang/String' (current frame, stack[1]) is not assignable to 'java/lang/Class' 

Это ясно, потому что команда LDC является добавлением строки "Lcom/example/ExampleClass;.class" вместо фактического класса Lcom/example/ExampleClass;.class.

Есть ли обходной путь для этого? Кажется невозможным напрямую добавить объект Class в LdcInsnNode, потому что класс еще не существует. Но есть ли способ добавить инструкцию, которая загружает объект Class?

В моем конкретном случае вызов метода Object.getClass() не является вариантом, поскольку он должен работать из статического контекста.

ответ

2

Вам не нужно ссылаться на объект Class, по сути, он даже не поддерживается (напрямую). Если вы хотите нажать Class на стек операнда через ASM, вам нужно обратиться к нему как к примеру Type.

E.g.

new LdcInsnNode(Type.getObjectType(name)) 

с использованием методы Type.getObjectType(…) завода, который ожидает name, чтобы представить внутреннюю форму имени, например, com/example/ExampleClass без L … ; вокруг. Это эквивалентно Type.getType('L'+name+';') для типов без массива. Выбор фабричного метода важен, поскольку объект Type может также представлять собой примитивные типы или типы методов. Поскольку оба, ldc и Type.getObjectType, поддерживают только ссылочные типы, это естественная комбинация.

Кажется, LdcInsnNode constructor documentation немного устарел, так как он упоминает только числовые типы и String (Class поддержки с ldc поскольку Java 5 существует, но документация содержит ссылки на 1.4.2). Вместо этого вы можете ссылаться на MethodVisitor.visitLdcInsn(…), которому объект в конечном итоге передается при создании байтового кода.

 Смежные вопросы

  • Нет связанных вопросов^_^