The break loop used to discard the pointer it was handed, so the buffer could name a BoundsError's fields and never show 648. Now the snapshot stashes it, flan_agent_condition hands it back on the stopped thread, and a daemon-built thunk — locals pointed at the condition — renders each field. Delivered at-stop, so a resume-and-restop cannot get the old type read over the new pointer. The trap sites publish their loc around the hook call, the snapshot copies it, and break answers :site with the line's text as :source — the frame lines say where each call was; this is the only record of the indexing itself. Compiler temps are hidden from the locals listing rather than refused as s4; a shadowing rebind strips its ~N except where the outer binding is on the same list, where both keep their raw spelling.
44 lines
1.8 KiB
Plaintext
44 lines
1.8 KiB
Plaintext
;;;; A program that stops with something worth looking at in the frame.
|
|
;;;;
|
|
;;;; dev-break.flan proves an editor can find out *that* a program stopped and
|
|
;;;; choose a restart; this one is about what the frame holds while it is
|
|
;;;; stopped. One local of each shape the structural printer has an arm for —
|
|
;;;; a parameter, a string, a struct, a fixed array, a bool — plus one that is
|
|
;;;; bound only *after* the error, which is the case that must come back
|
|
;;;; refused rather than rendered: its slot is storage nothing has written yet.
|
|
(import agent "vendor:agent")
|
|
|
|
(defstruct Point [x f32 y f32])
|
|
(defstruct Boom [why i32])
|
|
|
|
(defn look [n i64 label string] i64
|
|
(let [p (Point {.x 1.5 .y 2.5})
|
|
xs [10 20 30]
|
|
flag (> n 0)]
|
|
;; A loop, for its hidden bound: [dotimes] allocates a slot nobody named,
|
|
;; and the listing must *hide* it rather than refuse it by an invented
|
|
;; name — [s6] is not a variable anyone can find in this file.
|
|
(dotimes [hop 0] (print ""))
|
|
;; And a shadowing rebind. The checker suffixes the repeat as [label~2]
|
|
;; so the debug info never claims one binding is the other; the listing
|
|
;; keeps both raw spellings, because two rows both called [label] with
|
|
;; nothing to tell them apart would be worse.
|
|
(let [label "inner"]
|
|
(restart-case
|
|
(do (error (Boom {.why 7}))
|
|
;; Never reached before the break, so [after] is a slot with nothing
|
|
;; in it: the frame records a null for it and this is what "not bound
|
|
;; yet" has to mean.
|
|
(let [after (i64 99)] after))
|
|
(carry-on [] 5)))))
|
|
|
|
(defvar ticks i64)
|
|
|
|
(defn main [] i32
|
|
(agent/start "/tmp/flan-dev-locals-fallback.sock")
|
|
(print (look 3 "hello")) (println "")
|
|
(dotimes [i 4000]
|
|
(agent/wait 5)
|
|
(set ticks (+ ticks 1)))
|
|
0)
|