Есть ли аналог visitLdcInsn для загрузки объектов (не константа)?

Мы написали простой интерпретатор PostScript на Java и хотим оптимизировать его, генерируя байт-код непосредственно для определенных частей исходного кода. Для этого нам нужно загрузить объект из контекста контекста байт-кода Java. Указывать такой объект в сигнатуре сгенерированного методом байткода нехорошо, так как в нашем случае их может быть большое количество.

В Java Asm у нас есть метод

public void visitLdcInsn (объект cst)

Он посещает инструкцию LDC. Параметр cst - константа для загрузки в стек.

Есть ли способ загрузить не постоянный объект?

Спасибо


person Rustam Makulov    schedule 09.09.2014    source источник
comment
Есть Unsafe.defineAnonymousClass(), но это черная магия. Последний параметр позволяет патчить пул констант произвольными объектами и загружать их через LDC   -  person Andrey Breslav    schedule 09.09.2014
comment
Что вы пытаетесь достичь? По определению, LDC можно использовать только для констант.   -  person Antimony    schedule 09.09.2014


Ответы (3)


Начиная с Java 11 можно загружать произвольные константы с помощью инструкции LDC. Это могут быть объекты произвольного типа, но имеющие постоянную семантику, поэтому желательно, чтобы они были неизменяемыми.

Чтобы это работало, указанная запись пула констант должна быть CONSTANT_Dynamic_info, который имеет структуру, аналогичную структуре CONSTANT_InvokeDynamic_info, и также описывает метод начальной загрузки.

Одно отличие состоит в том, что запись name_and_type_index динамической информационной структуры будет указывать на дескриптор поля. Кроме того, метод начальной загрузки имеет сигнатуру (MethodHandles.Lookup,String,Class[,static arguments]) с аргументом Class, представляющим ожидаемый тип константы, а не объект MethodType. Метод начальной загрузки должен напрямую возвращать постоянное значение, а не сайт вызова.

Общим для инструкции invokedynamic является то, что результат первого процесса начальной загрузки будет связан с инструкцией LDC и использоваться во всех последующих исполнениях (поскольку он должен быть константой).

Интересное свойство этих динамических констант состоит в том, что они являются действительными статическими аргументами метода начальной загрузки для другой динамической константы или инструкции invokedynamic (пока между динамическими константами нет циклической зависимости).

Обратите внимание, что уже существует удобный класс, содержащий несколько готовых к использованию методов начальной загрузки для динамических констант.

person Holger    schedule 09.11.2018

ldc можно использовать для загрузки значений типа int, float, String, Class, MethodType или MethodHandle; ldc2_w поддерживает значения типа long и double. 1

Как сказано, в пределах В реализации Oracle JVM используется внутренний API Unsafe, который позволяет вносить исправления в объекты времени выполнения в качестве замены констант, но имеет несколько недостатков. Во-первых, очевидно, что он не является частью официального API, присутствует не во всех JVM и может даже исчезнуть (или изменить сигнатуры методов) в будущих JVM Oracle. Кроме того, среда ASM не будет знать о том, что вы собираетесь делать, и у нее возникнут трудности с созданием соответствующего байт-кода для последующих исправлений.

Ведь непонятно, в чем преимущество злоупотребления ldc для исполняемого объекта в вашем проекте. Генерация кода для передачи экземпляра в качестве параметра метода или конструктора и сохранения объекта в поле с ASM не очень сложна. А для программной логики неважно, используете ли вы ldc или, например, getstatic, прямо перед использованием значения.

person Holger    schedule 09.09.2014

Как было указано на плохой способ использования Unsafe (на самом деле это тоже не вариант, поскольку он требует анонимной загрузки классов):

Я предполагаю, что вы создаете класс во время сборки, но хотите внедрить в эти классы какой-то контекст времени выполнения, необходимый для запуска вашего инструментария. Вы можете, по крайней мере, эмулировать это, написав для своего приложения специализированный ClassLoader, который знает об этом контексте и явно инициализирует класс, например, с помощью аннотации.

Это означает, что вы инструментируете такой класс, как:

@Enhanced
class Foo {

   static EnhancementDelegate delegate;

   void instrumentedMethod() {
     // do something with delegate
   }
}

во время сборки, и вы инициализируете явно во время загрузки:

class EnhancementClassLoader extends ClassLoader {
  @Override
  protected Class<?> loadClass(String name) {
    Class<?> clazz = super.loadClass(name);
    if(clazz.isAnnotationPresent(Enhanced.class)) {
       // do initialization stuff
    }
    return clazz;
  }
}

Вам бы это помогло? Это своего рода предположение, чего вы пытаетесь достичь, но я думаю, что это может быть хорошим решением. Посмотрите мой проект Byte Buddy, который решает аналогичную проблему для прокси-классов, добавляя LoadedTypeInitializer.

person Rafael Winterhalter    schedule 09.09.2014