0

I am using ASM (tree and util as well) and have faced a weird exception

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

I am trying to edit the bytecode of a .class file, to generate a new one. I have a for loop with some if branches inside and I try to modify it. I am posting below the code of the two for loops the initial one and the desired generated.

Initial

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]);
    }
  }
}

Desired

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;
}

In order to do that, I use the ASM tree ListIterator, to detect the for loop (I am doing that by checking if some commands are occurring the one after the other). I use the ASM bytecode extension for intellj, to compare the initial and the desired bytecode. Then, using ASM tree I remove and add in the bytecode list the instructions I want. (I have tried both ASM5 and ASM8) The problem is that after this operation when I run the generated .class file I get the Exception above. The point is I don't manually edit the LocalVariableTable and don't really know how to edit it. If I run the javap -v command in the desired code I cannot even see the LocalVariableTable, whereas if I run the javap -v in the generated .class file I see it.

I have tried another modification to see that my idea with ASM tree works in a simpler for loop without if and a unique array and everything worked fine there.

Also, I have tried using SKIP_DEBUG, but I cannot see the Label and LineNumber -Nodes and in my case I need to remove some of the LabelNode and LineNumberNode instructions.

EDIT:

I am giving a simple example of the way I am doing the transformation. I use a diff checker of the initial and the desired ASM bytecode and it produces something like the image below Diff checker of initial and desired bytecode So the code in order to manipulate that is

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);
Fotis koun
  • 19
  • 4
  • Have you tried using the Krakatau disassembler rather than `javap`? Krakatau should give you a more accurate picture of what is actually in your classfile. https://github.com/Storyyeller/Krakatau – Antimony Jun 09 '20 at 22:25
  • 1
    You leave stuff on the stack in the loop. – Johannes Kuhn Jun 09 '20 at 22:46
  • 2
    Why do you have to manipulate the line number debug information? This contradicts the task description you have given before that statement. Further, you should show what you actually did, in terms of the code performing the transformation. – Holger Jun 10 '20 at 08:18
  • @Antimony thank you for your suggestion, I will try Krakatau instead of javap in case I have clearer information – Fotis koun Jun 10 '20 at 10:04
  • @JohannesKuhn could you please explain a bit more what do you mean by "leaving stuff", thank you – Fotis koun Jun 10 '20 at 10:04
  • @Holger please check the edit section of the post – Fotis koun Jun 10 '20 at 10:06
  • 2
    This seems to be a tiny fragment of the code manipulation you’ve described. There is no invocation of `callAFunctionToDoSthing`, no redirection of `ar`/`ar2` accesses to `temp` and the code location for label `l45Ins` is not defined. Further, you didn’t show the way you parse the original class and generate the new class. – Holger Jun 10 '20 at 11:51
  • @Holger do you want me to post my whole ASM modifier function and the actual initial and desired loops? Would this help? Even if it is a bit too big file? – Fotis koun Jun 12 '20 at 12:13
  • 1
    We can’t say beforehand which part is the relevant one for the issue. If you have the suspicion that the problem is caused by a particular part of the transformation, just try to strip off everything else and test, whether the problem still occurs. Post it when you have the minimal code needed to reproduce the issue, regardless of how large it is. – Holger Jun 12 '20 at 12:24
  • I used the CheckClassAdapter in my TraceClassVisitor and I got the following exception `java.lang.IllegalArgumentException: Invalid end label (must be visited first)`. Does anyone know what end label must be visited first mean? – Fotis koun Jun 17 '20 at 13:27
  • @Fotiskoun Visiting a label means calling `visitLabel(label)` on the `MethodVisitor`. It would corelate to a `LabelNode`. The end-label is a label the specifies the end of scope for a variable. – JojOatXGME Jun 21 '20 at 22:07
  • @JojOatXGME thank you for your reply, it was very clear. The point though is that I am doing the changes using the ASM tree API. So everything is happening in visitEnd() by editing the insertion list. Although, I suspect this is caused because I remove some label instructions and create new ones. But I don't know how to move some nodes around. – Fotis koun Jun 22 '20 at 10:41
  • @Fotiskoun Every `Label` you use must be part of the instruction list using a `LabelNode`. If you remove some `LabelNode`s, you have to put the labels back into the instruction list or you have to change all places that use the label. I cannot say much more. Your question does not contain enough information to reproduce the issue. Anyway, maybe you have already enough information to enough out the problem yourself. – JojOatXGME Jun 23 '20 at 21:36

1 Answers1

0

I was able to finally figure out how to solve the problem above. The issue was on the LabelNodes. As you can see in the editing part of the question, I was removing LabelNode 39 and inserting a new LabelNode. I was also doing it in the final LabelNode after the Return statement of the main. ASM though was not able to detect the new LabelNode I was inserting and replace the old ones with that. More specifically, the global scope variables I had, in the LocalVariableTable were finishing on the last label of the program. Although, when I removed that LabelNode, they got a very big number which leads the program to crash. To solve that, I simply used the same LabelNodes from the initial version and just inserted new in my desired program only when there was no other choice. Of course, I still edited the LinenumberNode to mirror my desired line. For the example above the code that solves the issue is the following

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);
Fotis koun
  • 19
  • 4