Skip to content

fix: stabilize protected calls across direct, unwind, and yield paths - #32

Merged
wolfy-j merged 5 commits into
mainfrom
fix/xpcall-recover
Aug 31, 2026
Merged

wolfy-j merged 5 commits into
mainfrom
fix/xpcall-recover

Conversation

@skhaz

@skhaz skhaz commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Rebases the protected-call stability work onto current main after #39, preserving the original focused commits and removing stale compiler, CI, golden-fixture, and type-system drift.

Protected calls

  • Catch direct xpcall errors under DoString/PCall and invoke the message handler while throw-site frames remain inspectable.
  • Clear continuation and error-handler extensions from discarded pcall, xpcall, and API PCall frames before their stack indexes are reused.
  • Preserve continuation metadata when a yielding Go call is reached through return pcall(...) and its Lua caller is collapsed by a tail call.
  • Return results correctly when a Go function is used directly as a resumed thread's entry point.

Regression coverage includes direct and nested xpcall, handler failures, stale-frame reuse, nested CallK unwinds, yielding tail-pcall, and root Go-function resume.

Issue #37 stability coverage

Adds the Wippy-shaped resumed-thread + pcall + yielding-Go-call + register-pressure regression. It forces a real post-resume registry growth at the unchanged baseline size (256) and configured sizes 128 and 384. The hypothesized nil-register loss does not reproduce in upstream core under these conditions, so this PR deliberately avoids a speculative registry mutation.

Inspect

GetStackFrame now uses the *LFunction returned by GetInfo("...f...") for Go-function name resolution and Lua upvalue capture.

Verification

  • go test ./... -count=1
  • targeted protected-call/yield suite under -race
  • go vet ./...
  • git diff --check
  • repeated registry-pressure and tail-yield stress runs
  • all repository fuzz targets exercised locally

The compiler LOADNIL fix is intentionally not duplicated here; it landed separately in #39.

skhaz and others added 5 commits August 31, 2026 17:49
Why:
xpcall leaked its error and never invoked the message handler when invoked
outside a coroutine (e.g. under DoString/PCall), aborting the surrounding
chunk. basePCall was updated to wrap CallK in its own defer/recover, but
baseXPCall was left depending on threadRun's recover via handleProtectedError
— a layer that only exists inside coroutines.

What:
- Wrap baseXPCall's CallK in an inline defer/recover, mirroring basePCall.
- Invoke the error handler BEFORE resetting the stack so it can inspect the
  throw site (debug.getlocal / debug.traceback), matching standard Lua
  semantics (PCall's own errfunc path does the same).
- Add errorObjectFromRecover and invokeErrorHandler helpers.
- Add xpcall_direct_test.go covering direct-call catch, chunk preservation,
  success path, nesting under pcall, and the still-working coroutine path.
Why:
GetStackFrame never reported upvalues: it read funcTable["f"] for the frame
function, but GetInfo's 'f' flag RETURNS the *LFunction (it does not store it
under any table key). The funcTable was always empty, so the upvalue loop was
skipped and debug.getupvalue kept disagreeing with inspect.

What:
- Capture the *LFunction from GetInfo's return value and use it for both the
  Go-function name resolution and the upvalue walk.
- Drop the unused funcTable allocation.
- Add TestGetStackFrameCapturesUpvalues guarding against regression.
@wolfy-j
wolfy-j force-pushed the fix/xpcall-recover branch from 0ba54f6 to 802aa9e Compare August 31, 2026 21:51
@wolfy-j wolfy-j changed the title fix(xpcall): catch errors under direct PCall/DoString + inspect upvalues fix: stabilize protected calls across direct, unwind, and yield paths Aug 31, 2026
@wolfy-j
wolfy-j merged commit 5d134c1 into main Aug 31, 2026
6 checks passed
@wolfy-j
wolfy-j deleted the fix/xpcall-recover branch August 31, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants