|
|
|
Advertisement
|
HopAround: A Java virtual machine simulation
The applet below demonstrates a Java virtual machine executing a
sequence of bytecodes. The bytecode sequence in the simulation was
generated by the javac compiler for the
hopAround() method of the class shown below:
class Clown {
static int hopAround() {
int i = 0;
while (true) {
try {
try {
i = 1;
}
finally { // the first finally clause
i = 2;
}
i = 3;
return i; // this never completes, because of the continue
}
finally { // the second finally clause
if (i == 3) {
continue; // this continue overrides the return statement
}
}
}
}
}
The bytecodes generated by javac for the
hopAround() method are shown below:
0 iconst_0 // Push constant 0
1 istore_0 // Pop into local var 0: int i = 0;
// Both try blocks start here (see exception table, below).
2 iconst_1 // Push constant 1
3 istore_0 // Pop into local var 0: i = 1;
4 jsr 16 // Jump to mini-subroutine at offset 16 (the first finally clause)
7 goto 22 // Jump to offset 22 (to just below first finally clause)
// Catch clause for the first finally clause:
10 astore_3 // Pop the reference to thrown exception, store in local variable 3
11 jsr 16 // Jump to mini-subroutine at offset 16 (the first finally clause)
14 aload_3 // Push the reference (to thrown exception) from local variable 3
15 athrow // Rethrow the same exception
// The first finally clause:
16 astore 4 // Store the return address in local variable 4
18 iconst_2 // Push constant 2
19 istore_0 // Pop into local var 0: i = 2;
20 ret 4 // Jump to return address stored in local variable 4
// Bytecodes for the code just after the first finally clause:
22 iconst_3 // Push constant 3
23 istore_0 // Pop into local var 0: int i = 3;
// Bytecodes for the return statment:
24 iload_0 // Push the int from local variable 0 (i, which is 3)
25 istore_3 // Pop and store the int into local variable 3 (the return value, i)
26 jsr 37 // Jump to mini-subroutine at offset 37 (the second finally clause)
29 iload_3 // Push the int from local variable 3 (the return value)
30 ireturn // Return the int on the top of the stack
// Catch clause for the second finally clause:
31 astore_1 // Pop the reference to thrown exception, store in local variable 1
32 jsr 37 // Jump to mini-subroutine at offset 37 (the second finally clause)
35 aload_1 // Push the reference (to thrown exception) from local variable 1
36 athrow // Rethrow the same exception
// The second finally clause:
37 astore_2 // Store the return address in local variable 2
38 iload_0 // Push the int from local variable 0 (i)
39 iconst_3 // Push constant 3
40 if_icmpne 46 // If the top two ints on the stack are unequal, jump to offset 46: if (i == 3) {
43 goto 2 // Jump to offset 2 (the top of the while block): continue;
46 ret 2 // Jump to return address stored in local variable 2
Exception table:
from to target type
2 4 10 any
2 31 31 any
The hopAround() method returns from the first
finally clause by executing past the closing curly brace,
but returns from the second finally clause by executing a
continue statement. The first finally clause,
therefore, exits via its ret instruction. But because the
second finally clause exits via a continue,
its ret instruction is never executed. The
continue statement causes the JVM to jump
to the top of the while loop again. This results in an
endless loop, even though it is a return statement that
originally causes the second finally clause to be executed
in the first place. The continue statement in the finally
clause supersedes the return statement, so the method
never returns.
Note that the bytecodes that implement the return
statement store a copy of the return value into local variable 3 before
jumping to the miniature subroutine that represents the second
finally clause. Then, after the miniature subroutine returns
(in this case it never does, because the continue is always
executed), the return value is retrieved from local variable 3 and
returned. This highlights the way the JVM returns
values when finally clauses are also executed. Rather than returning
the value of i after the finally clause is
executed, the JVM will return the value that i
had just before the finally clause was executed. This
means that even if the finally clause changes the value of
i, the method will still return the value that i
had when the return statement was first reached, before
the finally clause was invoked. If you wanted the
finally clause to be able to change the return value of
the method, you would have to put an actual return
statement with the new return value into the finally
clause itself.
To drive the simulation, just press the Step button. Each press of the Step button will cause the Java virtual machine to execute one bytecode instruction. To start the simulation over, press the Reset button. To cause the Java virtual machine to repeatedly execute bytecodes with no further coaxing on your part, press the Run button. The Java virtual machine will then execute the bytecodes until the Stop button is pressed. The text area at the bottom of the applet describes the next instruction to be executed. Happy clicking.
About the author
Bill Venners has been writing software professionally for 12 years.
Based in Silicon Valley, he provides software consulting and training
services under the name Artima Software Company.
Over the years he has developed software for the consumer electronics,
education, semiconductor, and life insurance industries. He has
programmed in many languages on many platforms: assembly language
on various microprocessors, C on Unix, C++ on Windows, Java on the Web.
He is author of the book: Inside the Java
Virtual Machine, published by McGraw-Hill. You can reach him at bv@artima.com.
This article was first published under the name Under the Hood: Try-finally clauses defined and demonstrated in JavaWorld, a division of Web Publishing, Inc., February 1997.
|
Sponsored Links
|