Currently, the Clojure compiler doesn't emit an entry in the line number table for a constructor callsite, unlike for a method callsite.
For example, let's say you have code like the following:
(ns example.core)
(defn f []
(StringBuilder.))
This code will generate the following bytecode and line number table:
public static java.lang.Object invokeStatic();
descriptor: ()Ljava/lang/Object;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: new #13 // class java/lang/StringBuilder
3: dup
4: invokespecial #14 // Method java/lang/StringBuilder."<init>":()V
7: areturn
LineNumberTable:
line 3: 0
// line 4: 4 <- this entry should be here
Note that the entry line 4: 4
should be in the line number table, but it actually does not.
In fact, we can confirm that the Compiler doesn't call visitLineNumber
at all in the NewExpr
's implementation of emit()
.
This can be a real problem when you are writing a function that may throw more than one exception explicitly:
(ns example.core)
(defn f [x]
(if (>= x 0)
(throw (IllegalArgumentException.))
(throw (ArithmeticException.))))
In such a case, the stack trace of the exception raised will not indicate the exact location where the exception occurred:
user=> (f 1)
Execution error (IllegalArgumentException) at example.core/f (core.clj:4).
null
user=> (pst)
IllegalArgumentException
example.core/f (core.clj:4) ;; <- should be "core.clj:5"
example.core/f (core.clj:3)
user/eval160 (NO_SOURCE_FILE:1)
...
nil
user=> (f -1)
Execution error (ArithmeticException) at example.core/f (core.clj:4).
null
user=> (pst)
ArithmeticException
example.core/f (core.clj:4) ;; <- should be "core.clj:6"
example.core/f (core.clj:3)
user/eval164 (NO_SOURCE_FILE:1)
...
nil
user=>
Is there any reason that constructor callsites don't have their own entry in the line number table?