Модификация байт-кода Java с использованием ASM выдает ClassFormatError: Invalid length XXX in LocalVariableTable

Я использую ASM (также дерево и утилиту) и столкнулся со странным исключением.

Exception in thread "main" java.lang.ClassFormatError: Invalid length 65526 in LocalVariableTable in class file 

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

Исходный

int[] ar = new int[]{1,2,3,4,5};
int[] ar2 = new int[]{8,9,9,94,3,2};
MyClass myar = new MyClass(ar);
MyClass myar2 = new MyClass(ar2);
int sum=0;
for(int i=0; i<ar.length; i++)
  if(ar[i]>3) {
    if(ar2[i]>8) {
      sum+=(ar[i]+ar2[i]);
    }
  }
}

Желаемый

int[] ar = new int[]{1,2,3,4,5};
int[] ar2 = new int[]{8,9,9,94,3,2};
MyClass myar = new MyClass(ar);
MyClass myar2 = new MyClass(ar2);
int sum=0;
for(int i=0; i<ar.length; i++)
  int[] temp = callAFunctionToDoSthing(myar, myar2);
  if(temp[0]>3) {
    if(temp[1]>8) {
      sum+=(temp[0]+temp[1]);
    }else i+=1;
  }else i+=2;
}

Чтобы сделать это, я использую ListIterator дерева ASM, чтобы обнаружить цикл for (я делаю это, проверяя, выполняются ли некоторые команды одна за другой). Я использую расширение байт-кода ASM для intellj, чтобы сравнить исходный и желаемый байт-код. Затем, используя ASM-дерево, я удаляю и добавляю в список байт-кодов нужные мне инструкции. (Я пробовал и ASM5, и ASM8). Проблема в том, что после этой операции, когда я запускаю сгенерированный файл .class, я получаю исключение выше. Дело в том, что я не редактирую LocalVariableTable вручную и не знаю, как ее редактировать. Если я запускаю команду javap -v в нужном коде, я даже не вижу LocalVariableTable, тогда как если я запускаю javap -v в сгенерированном файле .class, я его вижу.

Я попробовал другую модификацию, чтобы убедиться, что моя идея с ASM-деревом работает в более простом цикле for без if и уникального массива, и там все работает нормально.

Кроме того, я пытался использовать SKIP_DEBUG, но не вижу Label и LineNumber -Nodes, и в моем случае мне нужно удалить некоторые инструкции LabelNode и LineNumberNode.

ИЗМЕНИТЬ:

Я даю простой пример того, как я делаю преобразование. Я использую средство проверки различий исходного и желаемого байт-кода ASM, и оно выдает что-то вроде изображения ниже < img src="https://i.stack.imgur.com/LJeFW.png" alt="Проверка различий исходного и желаемого байт-кода"> Таким образом, код для управления этим

IntInsnNode bp7 = (IntInsnNode) aload17.getNext();
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
instructions.remove(bp7.getNext());
InsnList l4 = new InsnList();
LabelNode l45Ins = new LabelNode();
l4.add(new JumpInsnNode(Opcodes.IF_ICMPGT, l45Ins));
LabelNode l46Ins = new LabelNode();
l4.add(l46Ins);
l4.add(new LineNumberNode(178, l46Ins));
l4.add(new VarInsnNode(Opcodes.ALOAD, 20));
l4.add(new InsnNode(Opcodes.ICONST_0));
instructions.insert(bp7, l4);

person Fotis koun    schedule 09.06.2020    source источник
comment
Вы пробовали использовать дизассемблер Krakatau вместо javap? Krakatau должен дать вам более точную картину того, что на самом деле находится в вашем файле класса. github.com/Storyyeller/Krakatau   -  person Antimony    schedule 10.06.2020
comment
Вы оставляете вещи в стеке в цикле.   -  person Johannes Kuhn    schedule 10.06.2020
comment
Почему вам нужно манипулировать отладочной информацией номера строки? Это противоречит описанию задачи, которое вы дали перед этим утверждением. Кроме того, вы должны показать, что вы на самом деле сделали, с точки зрения кода, выполняющего преобразование.   -  person Holger    schedule 10.06.2020
comment
@Antimony спасибо за ваше предложение, я попробую Krakatau вместо javap, если у меня будет более четкая информация   -  person Fotis koun    schedule 10.06.2020
comment
@JohannesKuhn, не могли бы вы объяснить немного подробнее, что вы имеете в виду, говоря о том, чтобы оставить вещи, спасибо   -  person Fotis koun    schedule 10.06.2020
comment
@Holger, пожалуйста, проверьте раздел редактирования сообщения   -  person Fotis koun    schedule 10.06.2020
comment
Кажется, это крошечный фрагмент манипуляции с кодом, который вы описали. Нет вызова callAFunctionToDoSthing, нет перенаправления доступа ar/ar2 к temp, а расположение кода для метки l45Ins не определено. Кроме того, вы не показали, как вы анализируете исходный класс и генерируете новый класс.   -  person Holger    schedule 10.06.2020
comment
@Holger, вы хотите, чтобы я опубликовал всю свою функцию модификатора ASM, а также фактические начальные и желаемые циклы? Поможет ли это? Даже если это слишком большой файл?   -  person Fotis koun    schedule 12.06.2020
comment
Мы не можем заранее сказать, какая часть имеет отношение к проблеме. Если у вас есть подозрение, что проблема вызвана какой-то определенной частью преобразования, просто попробуйте удалить все остальное и проверить, возникает ли проблема. Опубликуйте его, когда у вас будет минимальный код, необходимый для воспроизведения проблемы, независимо от того, насколько она велика.   -  person Holger    schedule 12.06.2020
comment
Я использовал CheckClassAdapter в своем TraceClassVisitor и получил следующее исключение java.lang.IllegalArgumentException: Invalid end label (must be visited first). Кто-нибудь знает, что означает конечная метка, которую нужно посетить в первую очередь?   -  person Fotis koun    schedule 17.06.2020
comment
@Fotiskoun Посещение лейбла означает вызов visitLabel(label) на MethodVisitor. Это будет коррелировать с LabelNode. Конечная метка — это метка, указывающая конец области видимости переменной.   -  person JojOatXGME    schedule 22.06.2020
comment
@JojOatXGME спасибо за ваш ответ, это было очень ясно. Дело в том, что я делаю изменения, используя API дерева ASM. Так что все происходит в visitEnd() путем редактирования списка вставки. Хотя я подозреваю, что это вызвано тем, что я удаляю некоторые инструкции на этикетках и создаю новые. Но я не знаю, как перемещать некоторые узлы.   -  person Fotis koun    schedule 22.06.2020
comment
@Fotiskoun Каждый используемый вами Label должен быть частью списка инструкций с использованием LabelNode. Если вы удалите некоторые LabelNode, вам придется поместить метки обратно в список инструкций или изменить все места, которые используют метку. Я не могу сказать намного больше. В вашем вопросе недостаточно информации для воспроизведения проблемы. В любом случае, возможно, у вас уже достаточно информации, чтобы решить проблему самостоятельно.   -  person JojOatXGME    schedule 24.06.2020


Ответы (1)


Я смог, наконец, выяснить, как решить проблему выше. Проблема была в LabelNodes. Как вы можете видеть в части редактирования вопроса, я удалял LabelNode 39 и вставлял новый LabelNode. Я также делал это в последнем LabelNode после оператора Return основного. Однако ASM не смог обнаружить новый LabelNode, который я вставлял, и заменить им старые. В частности, переменные глобальной области видимости, которые у меня были в LocalVariableTable, заканчивались на последней метке программы. Хотя, когда я удалил этот LabelNode, они получили очень большое число, которое приводит к сбою программы. Чтобы решить эту проблему, я просто использовал те же LabelNodes из начальной версии и просто вставлял новые в нужную мне программу только тогда, когда не было другого выбора. Конечно, я все же отредактировал LinenumberNode, чтобы отразить желаемую строку. В приведенном выше примере код, решающий проблему, выглядит следующим образом.

IntInsnNode bp7 = (IntInsnNode) aload17.getNext();
instructions.remove(bp7.getNext());

LabelNode l39 = (LabelNode) bp7.getNext();
LineNumberNode lnl39 = (LineNumberNode) l39.getNext();
lnl39.line = 178;
instructions.remove(lnl39.getNext());
instructions.remove(lnl39.getNext());

InsnList l4 = new InsnList();
LabelNode l45Ins = new LabelNode();
l4.add(new JumpInsnNode(Opcodes.IF_ICMPGT, l45Ins));
instructions.insert(bp7, l4);

InsnList l5 = new InsnList();
l5.add(new VarInsnNode(Opcodes.ALOAD, 20));
l5.add(new InsnNode(Opcodes.ICONST_0));
instructions.insert(lnl39, l5);
person Fotis koun    schedule 04.07.2020