Skip to content

Commit 7fb1e29

Browse files
committed
Condense deep runtime stack trace rendering
1 parent 9fc0fa3 commit 7fb1e29

3 files changed

Lines changed: 60 additions & 5 deletions

File tree

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ Goal: improve day-to-day developer productivity and interpreter robustness.
328328

329329
- [ ] Profile evaluator hotspots and optimize dispatch paths.
330330
- [ ] Reduce allocations in common value transformations.
331-
- [ ] Improve error rendering for deeply nested call stacks.
331+
- [x] Improve error rendering for deeply nested call stacks.
332332
- [x] Add fuzz tests for parser and runtime edge cases.
333333

334334
### CI and Release Engineering

vibes/execution.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ func (e *assertionFailureError) Error() string {
108108
const (
109109
runtimeErrorTypeBase = "RuntimeError"
110110
runtimeErrorTypeAssertion = "AssertionError"
111+
runtimeErrorFrameHead = 8
112+
runtimeErrorFrameTail = 8
111113
)
112114

113115
var (
@@ -123,18 +125,32 @@ func (re *RuntimeError) Error() string {
123125
b.WriteString("\n")
124126
b.WriteString(re.CodeFrame)
125127
}
126-
for _, frame := range re.Frames {
127-
// Show position if line number is valid (1-based)
128+
renderFrame := func(frame StackFrame) {
128129
if frame.Pos.Line > 0 && frame.Pos.Column > 0 {
129130
fmt.Fprintf(&b, "\n at %s (%d:%d)", frame.Function, frame.Pos.Line, frame.Pos.Column)
130131
} else if frame.Pos.Line > 0 {
131-
// Line valid but column missing
132132
fmt.Fprintf(&b, "\n at %s (line %d)", frame.Function, frame.Pos.Line)
133133
} else {
134-
// No position information available
135134
fmt.Fprintf(&b, "\n at %s", frame.Function)
136135
}
137136
}
137+
138+
if len(re.Frames) <= runtimeErrorFrameHead+runtimeErrorFrameTail {
139+
for _, frame := range re.Frames {
140+
renderFrame(frame)
141+
}
142+
return b.String()
143+
}
144+
145+
for _, frame := range re.Frames[:runtimeErrorFrameHead] {
146+
renderFrame(frame)
147+
}
148+
omitted := len(re.Frames) - (runtimeErrorFrameHead + runtimeErrorFrameTail)
149+
fmt.Fprintf(&b, "\n ... %d frames omitted ...", omitted)
150+
for _, frame := range re.Frames[len(re.Frames)-runtimeErrorFrameTail:] {
151+
renderFrame(frame)
152+
}
153+
138154
return b.String()
139155
}
140156

vibes/runtime_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,45 @@ func TestRuntimeErrorStackTrace(t *testing.T) {
809809
}
810810
}
811811

812+
func TestRuntimeErrorCondensesDeepStackRendering(t *testing.T) {
813+
engine := MustNewEngine(Config{RecursionLimit: 128})
814+
script, err := engine.Compile(`
815+
def recurse(n)
816+
if n <= 0
817+
1 / 0
818+
end
819+
recurse(n - 1)
820+
end
821+
822+
def run()
823+
recurse(40)
824+
end
825+
`)
826+
if err != nil {
827+
t.Fatalf("compile error: %v", err)
828+
}
829+
830+
_, err = script.Call(context.Background(), "run", nil, CallOptions{})
831+
if err == nil {
832+
t.Fatalf("expected runtime error")
833+
}
834+
835+
var rtErr *RuntimeError
836+
if !errors.As(err, &rtErr) {
837+
t.Fatalf("expected RuntimeError, got %T", err)
838+
}
839+
if len(rtErr.Frames) <= 16 {
840+
t.Fatalf("expected deep frame set, got %d", len(rtErr.Frames))
841+
}
842+
rendered := rtErr.Error()
843+
if !strings.Contains(rendered, "frames omitted") {
844+
t.Fatalf("expected deep stack output to include omitted-frame marker: %s", rendered)
845+
}
846+
if !strings.Contains(rendered, "at recurse") {
847+
t.Fatalf("expected deep stack output to include recurse frames: %s", rendered)
848+
}
849+
}
850+
812851
func TestIntTimes(t *testing.T) {
813852
script := compileScript(t, `
814853
def collect(n)

0 commit comments

Comments
 (0)