rafoo
rafoo

Reputation: 1642

Why this ASM instructions load 10?

I was testing with ASM framework, and I barely can't understand why this fragment of code produce the int value 10. For more details: the method returns an int and I printed the int back.

// This is the complete ASM visit of this method
// ...
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null);
mv.visitCode();
mv.visitIntInsn(Opcodes.BIPUSH, 0);

mv.visitIntInsn(Opcodes.BIPUSH , 0);
mv.visitIntInsn(Opcodes.ISTORE, 2);

mv.visitIntInsn(Opcodes.ILOAD, 1);
mv.visitInsn(Opcodes.IRETURN);

mv.visitMaxs(0, 0);
mv.visitEnd();
// ...

The decompiler show even some weird code. Decompiler output:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class HelloWorld implements Add {
    public HelloWorld() {
    }

    public int add(int var1, int var2) {
        boolean var3 = false;
        return var1;
    }
}
  1. Why does the function returns 10 ? Is it a bug ? Does this invoke a sort of "undefined behaviour", because variables are loaded into "self" because pushed back?

  2. How does JVM handle the variables and how to allocate it, is it stack based ? If so, then the aload will just push duplicate value of data

stack before aload: var1 var2 var3
stack after aload_1: var1 var2 var3 var1

Or am I totally wrong? It looks like a bit overworks instead of using registries.

Upvotes: 0

Views: 202

Answers (1)

kriegaex
kriegaex

Reputation: 67377

Who wrote that byte code? What is it supposed to do? What it actually does is:

  • push (byte) 0 onto operand stack as an integer
  • push (byte) 0 onto operand stack one more time
  • pop top-most 0 from stack and store as integer in local variable table, slot #2
  • load integer from local variable table slot #1 (note: different slot than before) onto the stack
  • return top-most stack element as method result

You also need to know that in the local variable table

  • #0 is this for the called method, i.e. a reference to the HelloWorld instance,
  • #1 is the first method parameter var1,
  • #2 is the second method parameter var2.

So if the method returns 10, it means that you must have called the method with a var1 value of 10 because you return the content of slot #1 unchanged. There is no clue that you add anything here like the method name implies. What it does instead is push 2x 0 onto the stack, but only using one of them, overwriting var2 for whatever reason before returning var1, as I said. The decompiler then tries to make sense of it, doing a pretty good job.


Update: If you want to implement this

public int add(int var1, int var2) {
  return var1 + var2;
}

then your byte code ought to be like this:

ILOAD 1
ILOAD 2
IADD
IRETURN

Upvotes: 2

Related Questions