Как изменить инструкции в Java BootstrapMethods с помощью ASM?

После компиляции с Java 8 и сброса полученных файлов классов с помощью javap я вижу это, из которых я показываю только первые 2 элемента:

BootstrapMethods:

  0: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:

      #175 (Ljava/lang/Object;)Z

      #179 invokestatic llllll/lallll.lambda$printPersons$0:(Lllllll/lallll;)Z

      #180 (Lllllll/lallll;)Z

  1: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

    Method arguments:

      #175 (Ljava/lang/Object;)Z

      #191 invokestatic llllll/lallll.lambda$printPersons$1:(Lllllll/lallll;)Z

      #180 (Lllllll/lallll;)Z

  2: #174 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/inv

Могу ли я использовать ASM для просмотра инструкций в этих методах начальной загрузки и изменить, скажем, имена вызываемых методов с помощью приведенных выше инструкций invokestatic?

По-видимому, эти методы не являются частью обычных методов класса, и мне не удалось посетить их, используя стандартные методы классов и методов ASM.

Если не использовать ASM, можно ли найти и изменить эти инструкции с помощью другой среды манипулирования файлами байт-кода/класса Java?

Я читал документы стандартного формата файла класса для Java и не вижу прямого описания инструкций для методов начальной загрузки, хотя я вижу то, что я буду описывать как метаданные для методов начальной загрузки.

Благодарность,

-Дэйвид


person David Dunkle    schedule 29.11.2017    source источник


Ответы (1)


Атрибут BootstrapMethods содержит ссылки на другие методы. В вашем файле класса нет методов начальной загрузки (обычно), и вы на самом деле не хотите изменять метод начальной загрузки, который в вашем примере является методом metafactory в классе java.lang.invoke.LambdaMetafactory.

Что вы действительно хотите сделать (очевидно), так это изменить свойства invokedynamic инструкция, которая создаст экземпляр для лямбда-выражения или ссылки на метод. Для этой задачи вам уже поможет ASM.

При использовании API посетителей необходимо переопределить visitInvokeDynamicInsn. В этом месте ASM уже декодирует указанную запись атрибута BootstrapMethods, предоставляет эти значения методу посещения и будет (повторно) создавать соответствующий атрибут BootstrapMethods, когда вы передаете эти, возможно измененные, значения методу visitInvokeDynamicInsn метода ClassWriter. посетитель.

В переопределенном visitInvokeDynamicInsn вам сначала нужно убедиться, что эта инструкция invokedynamic действительно является сайтом создания лямбда. Аргумент bsm должен быть Handle. владельцем которого является java/lang/invoke/LambdaMetafactory, а метод должен быть либо metafactory или altMetafactory. Если нет, просто передайте все в super.visitInvokeDynamicInsn (передав полномочия автору без изменений), так как это другое использование invokedynamic feature (например, Java 9 будет использовать ее для объединения строк).

Если это сайт создания лямбда-выражений, вы можете интерпретировать аргументы в соответствии с соглашениями, указанными в документацию LambdaMetafactory. Массив bsmArgs соответствует атрибуту, который вы разместили в своем вопросе. Элемент массива с индексом 1 будет целевым методом, снова представленным как Handle в ASM. Вы можете изменить его на другой дескриптор, который, по-видимому, является вашей предполагаемой операцией. Целевой функциональный интерфейс — это возвращаемый тип, закодированный в аргументе desc, а имя метода функционального интерфейса предоставляется в виде аргумента name (аргумент имени invokedName метода начальной загрузки). Дополнительные сведения см. во всеобъемлющей документации LambdaMetafactory. .

person Holger    schedule 30.11.2017
comment
Метод getOwner объекта Handle, который является сайтом создания лямбда-выражений, не возвращает java/lang/invoke/LambdaMetafactory. Есть ли какой-то другой способ проверить владельца? - person David Dunkle; 01.12.2017
comment
Ты смотришь на правую ручку? Это аргумент bsm, который должен указывать на LambdaMetafactory. Дескриптор в массиве bsmArgs args указывает на целевой метод ссылки на метод/лямбда-выражения. - person Holger; 01.12.2017