23

I am trying to implement a subset of Java for an academic study. Well, I'm in the last stages (code generation) and I wrote a rather simple program to see how method arguments are handled:

class Main {
    public static void main(String[] args) {
        System.out.println(args.length);
    }
}

Then I built it, and ran 'Main.class' through an online disassembler I found at: http://www.cs.cornell.edu/People/egs/kimera/disassembler.html

I get the following implementation for the 'main' method: (the disassembled output is in Jasmin)

.method public static main([Ljava/lang/String;)V
    .limit locals 1
    .limit stack 2

    getstatic   java/lang/System/out Ljava/io/PrintStream;
    aload_0
    arraylength
    invokevirtual   java/io/PrintStream.println(I)V
    return
.end method

My problem with this is:
1. aload_0 is supposed to push 'this' on to the stack (thats what the JVM spec seems to say)
2. arraylength is supposed to return the length of the array whose reference is on the top-of-stack

So according to me the combination of 1 & 2 should not even work.

How/why is it working? Or is the disassembler buggy and the actual bytecode is something else?

ArjunShankar
  • 23,020
  • 5
  • 61
  • 83

1 Answers1

53

aload_0 is supposed to push 'this' on to the stack

Not quite … aload_0 reads the first reference argument (or, more generally, the first local reference variable) of the method and pushes it onto the stack.

In member functions, the first local variable happens to be the this reference.

But main is not a member function, it’s a static function so there is no this argument, and the true first argument of the method is args.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 3
    Does this hold true for *all* static methods? – ankh-morpork Jul 11 '15 at 12:18
  • @KonradRudolph - Don't think above statement is correct. `aload_0` reads the instance on which this method is invoked (i.e. `this` variable) and `aload_1` reads 1st argument to this method (from the local variable array of the current frame and pushed onto the operand stack). Ref JVM document for SE8 (also section 2.6) [https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial] – KGhatak Dec 21 '16 at 06:18
  • @BuckCherry And what happens for static methods? For clarification, see the first comment, by "grinch". – Konrad Rudolph Dec 21 '16 at 06:32
  • @grinch - I ready didn't get `this.foo(x,y) is really Foo.foo(this, x, y)`. Are you saying for static method, aload_0 reads current-class onto stack!! – KGhatak Dec 21 '16 at 10:13
  • @KonradRudolph - The meaning of aload_ is precisely, as the JVM doc says, to load reference typed value from local-array at index 'n' onto operand stack. For static methods, arguments are stored from index 0,1,.. as opposed to from 1,2,.. for non-static method. Either way, statements like- aload_0 reads 1st arg or 'this' are incorrect without referring to the static or no-static context. In fact, for a static method if 1st argument is not of 'reference' type, then there would be no aload_0 for that method. Btw, it would be nice if you could reflect that in your answer. – KGhatak Dec 21 '16 at 11:53
  • @BuckCherry I’ve made some clarifications. But your concern seems to be unrelated, and ultimately based on a misreading of the documentation: the JVM makes no difference between `this` and other arguments, and it makes no difference in its treatment of static and non-static methods (after invocation). – Konrad Rudolph Dec 21 '16 at 12:19
  • 2
    @KonradRudolph - Conceptually, a JVM doesn't need to bother about knowing which index stores what. However, JVM (SE8) does mandate the behavior and w.r.t. static and non-static aspects. Excerpts from sec. 2.6.1 of the spec. -*`"On class method invocation, any parameters are passed in consecutive local variables starting from local variable 0. On instance method invocation, local variable 0 is always used to pass a reference to the object on which the instance method is being invoked (this in the Java programming language)...."`* – KGhatak Dec 21 '16 at 14:20
  • @BuckCherry I know what the documentation says. It doesn’t support your claim. – Konrad Rudolph Dec 21 '16 at 14:23
  • @KonradRudolph - Would you clearly state which claim you're referring to! – KGhatak Dec 21 '16 at 14:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/131164/discussion-between-buckcherry-and-konrad-rudolph). – KGhatak Dec 21 '16 at 14:33
  • @KGhatak No, what I meant is that a member function call like `this.foo(x,y)` is really just treated as a static function that passes the `this` reference as a parameter. So it turns into `Foo.foo(this, x, y)` – grinch Dec 28 '16 at 15:14
  • @grinch Not sure if this way of explaining (conversion between static and non-static methods) has any basis! Seems to me not so right! – KGhatak Jan 02 '17 at 08:03
  • @KGhatak I'm just saying that at the bytecode level the code is more like a bunch of static method calls with a `this` parameter being passed around. – grinch Jan 03 '17 at 16:06
  • @grinch - I don't mean to dwell on this so long...but even at bytecode level we have certain differentiation between static and non-static e.g. getfield vs. getstatic, invokespecial vs. invokestatic are JVM instructions that do differentiate static and non-static contexts. – KGhatak Jan 04 '17 at 07:48
  • @KGhatak yes I understand there are differences between static and non static. When I made the comment 5 years ago I was just pointing out that the call in java: `this.foo(x,y)` is more like this in byte code: `Foo.foo(this, x, y)`. – grinch Jan 04 '17 at 17:17