Search code examples
clojurestack-trace

Clojure stacktraces and dynamically interned vars


I have Clojure code and evaluate it, and then intern it into a var:

(let [x (binding [*ns* my-ns] (eval m-code))]
  (intern my-ns my-sym x)) ; my-sym was declared beforehand. 

But when an error occurs within x, the trace makes no reference to x:

my-ns/eval27298/fn--27299
...

I duplicated the metadata of a more "standard" var, but this does not change the stack trace:

(alter-meta! my-var #(assoc % :line 1 :column 1 :file "my_ns.clj" :name my-sym :ns my-ns))

If not the metadata, what is used to determine the stack trace?


Solution

  • Unless I'm mistaken, here's how it works:

    1. The reader reads your code and assigns all location metadata to vars
    2. The code is then evaluated and that metadata is used not only to report the errors during code loading but also to report any errors that happen when the evaluated functionality is used after the evaluation itself

    Given that, alter-meta! cannot work because all the locations have already been processed and remembered. What can work is assigning the metadata between the steps 1 and 2 above:

    user=> (def code "#(throw (ex-info \"\" {}))")
    #'user/code
    user=> (def f (eval (-> (read-string code) (with-meta {:line 42, :clojure.core/eval-file "hello.clj"}))))
    #'user/f
    user=> (f)
    Execution error (ExceptionInfo) at user/eval175$fn (hello.clj:42).
    

    Note that I'm using :clojure.core/eval-file to specify the file instead of just :file.