Документация по номерам строк в трассировке стека

Есть ли четкая документация по номерам строк трассировки стека Java?
Как они "вычисляются" при печати трассировки стека (логика за ней, а не реализация)?

Чтобы показать вам, почему я запутался, сделайте следующий фрагмент кода:

public static void main(String[] args) {
        String evilString = null;
        System.out.println(new StringBuilder()
                .append(evilString.toLowerCase()));
        evilString.toUpperCase();
    }

Он дает: Исключение в потоке "main" java.lang.NullPointerException   at be.company.training.ocjp6.App.main(App.java: 28)

В то время как следующий фрагмент кода:

public static void main(String[] args) {
        String evilString = null;
        System.out.println(new StringBuilder()
                .append("".toLowerCase()));
        evilString.toUpperCase();
    }

дает: Исключение в потоке "main" java.lang.NullPointerException   at be.company.training.ocjp6.App.main(App.java: 30)

Итак, я понимаю, что запуск цепочки методов StringBuilder делает ее обрабатываемой как 1 строка (код StringBuilder начинается с строки 28 в моем редакторе). Но если ошибка произошла в фрагменте evilString.toUpperCase(), мы вернулись на дорожку с строкой 30.

Я хочу знать, что, когда я вижу стек, я точно знаю, в какой строке произошла ошибка (методы цепочки (на нескольких строках) очень распространены в коде, который я просматриваю).

Ответ 1

Похоже, что это компилятор, зависящий от @kdgregory.

Здесь моя java -версия:

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)

С помощью этого кода я NPE в строке 9 на трассировку стека (который соответствует номеру физической линии в источнике)

public static void main(String[] args) { 
    String evilString = null;
    System.out.println(new StringBuilder()
        .append(
                evilString.toLowerCase()));  // <--- NPE here (line 9)
}

Итак, я немного глуб с помощью javap:

Это таблица номеров строк для основного, как показано javap -l

(таблица номеров строк показывает исходную строку: смещение команды

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

строка источника 9 начинается со смещения 12.

javap -c для демонстрации показывает следующее:

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

Мое предположение: когда исключение попадает в invokevirtual со смещением 13, jvm просматривает ближайшую предыдущую запись в таблице номеров строк и помещает ее в трассировку стека.

Ответ 2

Вы уверены, что "" равно null в Java? Я считаю, что это источник вашей путаницы. вы пробовали написать "".toUpperCase(); в строке 30?