Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
with `setcar`/`setcdr`; the old code followed the cycle forever, causing a
stack overflow or hang. The fix borrows `GCMARKBIT` during traversal to detect
cycles and prints `...` in their place, then immediately clears the marks.
Covered by the `kernel/circular-print` test, which also pins mark restoration
(a leaked mark bit corrupts the pair's car pointer, so post-print walkability
is a direct check).
- **Comments terminated by `\r` now parse correctly** (upstream rxi/fe PR #25
partial). Source files with Windows-style `\r\n` line endings had their `\r`
swallowed into the next token after a `;` comment, corrupting the parse. The
Expand Down
22 changes: 22 additions & 0 deletions tests/kernel/kernel.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,25 @@
(deftest "kernel/string-escape-eof"
; backslash at EOF in a string must raise "unclosed string", not overflow
(check-err (read-string "\"\\")))

;; fe_write detects cycles by borrowing GCMARKBIT, then clears it in
;; unmarkpairs. The mark bit lives in the low byte of a pair's car field, so a
;; *leaked* mark corrupts the car pointer — making post-print walkability a
;; direct test of mark restoration (no GC needed). `repr` runs fe_write twice
;; internally (measure + fill), so a stale mark would also skew the two passes.
(deftest "kernel/circular-print"
; tail-cycle: x's last cdr points back at x. Prints finite, ends in "...".
(set x (list 1 2 3))
(setcdr (cdr (cdr x)) x)
(check (is (repr x) "(1 2 3 ...)"))
; head-cycle: car of the head is the head itself.
(set y (list 1 2 3))
(setcar y y)
(check (is (repr y) "(... 2 3)"))
; after printing, the cycle is intact and pointers are uncorrupted —
; proves unmarkpairs cleared every borrowed mark bit.
(check (is (cdr (cdr (cdr x))) x)) ; identity: the cycle still closes on x
(check (is (car (cdr (cdr (cdr x)))) 1)) ; walk through the back-edge → x[0]
(check (is (car x) 1))
; reprʼing twice yields the same string — marks don't accumulate.
(check (is (repr x) (repr x))))
Loading