Reputation: 2765
Is there any clear documentation on Java stack trace line numbers?
How are they "calculated" when printing a stack-trace (the logic behind it, not the implementaton)?
To show you why I'm confused, take the following code snippet:
public static void main(String[] args) {
String evilString = null;
System.out.println(new StringBuilder()
.append(evilString.toLowerCase()));
evilString.toUpperCase();
}
It gives: Exception in thread "main" java.lang.NullPointerException at be.company.training.ocjp6.App.main(App.java:28)
While the following piece of code:
public static void main(String[] args) {
String evilString = null;
System.out.println(new StringBuilder()
.append("".toLowerCase()));
evilString.toUpperCase();
}
Gives: Exception in thread "main" java.lang.NullPointerException at be.company.training.ocjp6.App.main(App.java:30)
So I understand that running the chain of StringBuilder
methods makes it being treated as 1 line (the StringBuilder code starts at line 28 in my editor). But if the error happens in the evilString.toUpperCase() snippet, we're back on track with line 30.
I want to know so that when I see a stacktrace, I can know for sure on what line the error happened (chaining methods (on multiple lines) is very common in the code I'm looking at).
Upvotes: 11
Views: 3494
Reputation: 1716
Seems like this is compiler dependent as @kdgregory noted.
Here's my java -version:
java version "1.6.0_29"
Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-10M3527)
Java HotSpot(TM) Client VM (build 20.4-b02-402, mixed mode)
With this code, I NPE on line 9 per the stack trace (which matches the physical line number in the source)
public static void main(String[] args) {
String evilString = null;
System.out.println(new StringBuilder()
.append(
evilString.toLowerCase())); // <--- NPE here (line 9)
}
So I dug a little deeper using javap:
This is the line number table for main as shown by javap -l
(line number table shows source line: instruction-offset
public static void main(java.lang.String[]);
LineNumberTable:
line 6: 0
line 7: 2
line 9: 12
line 8: 16
line 7: 19
line 10: 22
source line 9 starts at offset 12.
javap -c to disassemble shows this:
public static void main(java.lang.String[]);
Code:
0: aconst_null
1: astore_1
2: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
5: new #22; //class java/lang/StringBuilder
8: dup
9: invokespecial #24; //Method java/lang/StringBuilder."<init>":()V
12: aload_1 <--- closest line in the line number table
13: invokevirtual #25; //Method java/lang/String.toLowerCase:()Ljava/lang/String; <--- NPE here
16: invokevirtual #31; //Method java/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #35; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
22: return
my guess: when the exception is hit at the invokevirtual at offset 13, the jvm looks up the closest prior entry in the line number table and puts that in the stack trace.
Upvotes: 11
Reputation: 3123
are you sure that ""
equals null
in Java? I believe that this is the source of your confusion. have you tried writing "".toUpperCase();
on line 30?
Upvotes: 0