Clojure stack traces via source registry + native frame walk
A direct-link build emits (jolt-register-source! short-name ns name file line) once per fn def — at definition time, so zero per-call cost. On an uncaught error the reporter walks Chez's native continuation frames (jolt-throw captures the live continuation via call/cc; host conditions carry their own &continuation), maps each frame's procedure name through the registry, and prints a Clojure backtrace 'ns/name (file:line)'. Wired into both the cli and a built binary's launcher. Frames are keyed by the short munged fn name Chez actually reports (emit-fn's letrec self-binding), not jv$ns$name; a cross-namespace collision degrades to the bare frame name rather than a wrong attribution. The analyzer carries the original form's position through defn macroexpansion onto the def node. Calling a non-fn now throws a catchable ClassCastException (via jolt-throw) naming the operator, instead of a raw Chez error. Caveats (documented in source-registry.ss): names map only in direct-link/AOT closed-world builds — the open-world -e/repl/run path falls back to the top-level location; and pervasive TCO erases tail-call frames, so a mapped trace shows only the non-tail spine. JOLT_DEBUG_FRAMES dumps raw frame names. Re-mint (analyzer + backend); prelude byte-identical (direct-link off during mint). Corpus rows certified, build-smoke asserts the trace.
This commit is contained in:
parent
9bf3d1c80e
commit
57bab5d409
12 changed files with 288 additions and 105 deletions
|
|
@ -35,7 +35,13 @@
|
|||
;; throw raises the jolt value RAW (no envelope);
|
||||
;; catch (emitted as `guard`) binds it directly. Chez `raise` accepts any
|
||||
;; object, so a thrown number/map/ex-info all work; uncaught -> non-zero exit.
|
||||
(define (jolt-throw v) (raise v))
|
||||
;; Capture the live continuation at the throw site (identity-tagged with the
|
||||
;; thrown value) so an uncaught error can walk the native frames back to a Clojure
|
||||
;; stack trace (source-registry.ss). call/cc is paid only on a throw, never per
|
||||
;; call; the captured k is walked, never invoked.
|
||||
(define jolt-throw-cont (make-thread-parameter #f))
|
||||
(define (jolt-throw v)
|
||||
(call/cc (lambda (k) (jolt-throw-cont (cons v k)) (raise v))))
|
||||
;; ex-info builds the tagged map {:jolt/type :jolt/ex-info :message :data :cause}
|
||||
;; — a real jolt-hash-map, so the ex-data/ex-message/ex-cause tier fns read it
|
||||
;; via jolt-get for free. Arity 2 (msg data) or 3 (msg data cause).
|
||||
|
|
@ -408,3 +414,7 @@
|
|||
;; printing. Loads LAST so its set!-wraps of jolt-class/jolt=2/the printers sit
|
||||
;; outermost over every earlier extension.
|
||||
(load "host/chez/java/bigdec.ss")
|
||||
|
||||
;; Native stack traces: jv$ns$name -> source registry + continuation frame walk +
|
||||
;; uncaught-throwable renderer. After the printers/equality it relies on.
|
||||
(load "host/chez/source-registry.ss")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue