Исключение при анализе байт-кода Java 16 с библиотекой ASM

Я пытаюсь разобрать байт-код из Java 16 (версия байт-кода 60), используя ObjectWeb ASM. Я написал следующий код:

@Test
void parseBytecode() {
    try (InputStream stream = this.getClass()
         .getClassLoader()
         .getResourceAsStream("MethodByte.class")) {
         //MethodByte.class has 60 version (Java 16)
        new ClassReader(stream).accept(new ClassPrinter(), 0);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

private class ClassPrinter extends ClassVisitor {
    protected ClassPrinter() {
        super(Opcodes.ASM9);
    }
}

Когда я запускаю код, я получаю IllegalArgumentException:

Caused by: java.lang.IllegalArgumentException
   at org.objectweb.asm.ClassReader.<init>(ClassReader.java:262)
   at org.objectweb.asm.ClassReader.<init>(ClassReader.java:180)
   at org.objectweb.asm.ClassReader.<init>(ClassReader.java:166)
   at org.objectweb.asm.ClassReader.<init>(ClassReader.java:287)

Версия библиотеки ASM 9.5 (от pom.xml):

<dependency>
  <groupId>org.ow2.asm</groupId>
  <artifactId>asm</artifactId>
  <version>9.5</version>
  <scope>compile</scope>
</dependency>

Кроме того, стоит отметить, что я проверил, что MethodByte.class лежит в правильном месте. Я распечатал его содержимое, чтобы убедиться, что оно доступно в тесте.

Насколько я понимаю, ASM 9 должен поддерживать java 16. Я подозреваю, что ошибка может быть связана с версией ASM, которую я использую, и ее несовместимостью с байт-кодом Java 16, поэтому я попробовал Opcodes.V16 вместо Opcodes.ASM9, и это не помогло.

Также я попытался разобрать байт-код с 52 версией (Java 8), и это тоже не помогло.

Кто-нибудь сталкивался с этим раньше?


Обновление: как я получаю MethodByte.class.

Я использую maven для сборки проекта и запуска тестов. Итак, у меня есть следующий конвейер:

  1. Я создаю класс Java со следующим содержимым:
package org.eolang.jeo;

public class MethodByte {
    public static void main(String[] args) {
        System.out.println(method());
        System.out.println("passed");
    }
    static byte method() {
        return 52;
    }
}
  1. Я бегу mvn clean compile, чтобы построить этот класс. Для этого я использую следующие свойства конфигурации maven:
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>16</maven.compiler.source>
  <maven.compiler.target>16</maven.compiler.target>
</properties>
  1. Затем я копирую этот класс в каталог ресурсов:
cp target/classes/org/eolang/jeo/MethodByte.class src/test/resources/.
  1. Наконец, я запускаю тесты и получаю исключение, о котором я упоминал выше.

Работает ли это для классов, скомпилированных в более ранней версии?

Olivier 14.08.2023 18:36

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

kriegaex 15.08.2023 02:51

@Olivier Я попытался запустить еще один пример с версией 52 (Java 8), и исключение такое же.

Volodya Lombrozo 15.08.2023 09:09

@kriegaex Я добавил немного контекста. Спасибо за ваш комментарий.

Volodya Lombrozo 15.08.2023 09:24
Этот путь используется только в том случае, если он не является допустимым файлом класса.
Johannes Kuhn 15.08.2023 11:03

@JohannesKuhn Знаете ли вы, как я могу проверить «действительность»? Я пытался использовать javap, и он успешно анализирует байт-код

Volodya Lombrozo 15.08.2023 11:53

Отладка. Вы пытались установить точку останова в том месте, где возникает исключение? Какова ценность тега? Какой это индекс? Что javap говорит об этом постоянном индексе пула?

Johannes Kuhn 15.08.2023 12:26

@JohannesKuhn Место, где выдается исключение: currentCpInfoOffset = 10 тег (classFileBuffer[currentCpInfoOffset]) = -65 На самом деле первые двенадцать байтов отрицательные: [-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 0, 0, 0, 60, 0, 41, 10, 0, +more]

Volodya Lombrozo 15.08.2023 12:57

А как же индекс? Что javap говорит о постоянном пуле по этому индексу? Кроме того, файл класса должен начинаться с 0xCAFEBABE.

Johannes Kuhn 15.08.2023 13:23

@JohannesKuhn javap постоянная запись в пул: #10 = Utf8 java/lang/System

Volodya Lombrozo 15.08.2023 13:32

@JohannesKuhn hexdump -n 16 -C MethodByte.class дает мне: 00000000 ca fe ba be 00 00 00 3c 00 29 0a 00 02 00 03 07

Volodya Lombrozo 15.08.2023 13:37

Мне это кажется неправильным. По какой-то причине шестнадцатеричный дамп не соответствует содержимому показанного вами массива. Шестнадцатеричный дамп выглядит как действительный файл класса, массив - нет.

Johannes Kuhn 15.08.2023 14:03

Что непонятного в моем комментарии? MCVE предназначен для полного воспроизведения. Боюсь, «немного контекста» здесь не работает. Где файл класса, который вы пытаетесь проанализировать, или исходный код, который вы скомпилировали для его создания? Это не дискуссионный форум, где проблемы должны обсуждаться в нескольких итерациях. Просто задайте четкий вопрос и будьте рады получить прямой и точный ответ.

kriegaex 15.08.2023 14:12

@JohannesKuhn Я добавил контент класса MethodByte и шаги, которые я выполняю. Может быть, это поможет?

Volodya Lombrozo 15.08.2023 16:16

@kriegaex Я старался сделать вопрос как можно меньше ценой неполноты. Итак, я добавил исходный код файла, чтобы сделать пример полностью завершенным. Надеюсь, это поможет. Спасибо за ваш комментарий.

Volodya Lombrozo 15.08.2023 16:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
15
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как упомянул Йоханнес Кун , двоичное представление исходного класса не соответствовало массиву байтов, переданному в класс org.objectweb.asm.ClassReader. Корень проблемы кроется в механизме Maven Resource Filtering. Проще говоря, Maven запускает цель resources:3.3.1:testResources во время фазы generate-test-sources, которая заменяет заполнители в файлах ресурсов фактическими значениями. Когда Maven пытается применить эту фильтрацию к двоичным файлам, он может столкнуться с проблемами кодирования, что приводит к исключению, о котором я упоминал в своем вопросе.

Хотя папка src/test/resources содержала правильный байт-код, она была повреждена, когда я запускал тесты (используя команду mvn clean test), что впоследствии вызывало ошибку IllegalArgumentException.

Чтобы решить эту проблему, я исключил .class файлы из фильтрации:

<build>
  <testResources>
    <testResource>
      <directory>src/test/resources</directory>
      <filtering>false</filtering>
      <includes>
        <include>**/*.class</include>
      </includes>
    </testResource>
  </testResources>
</build>

С этой конфигурацией теперь все работает как положено без исключений.

Другие вопросы по теме