Files
five/hbrt/thread.go
Charles KWON OhJun 7629f95235 fix(hbrt): variadic PValue support — snapshot pushed args per frame
Variadic Harbour functions (`FUNCTION foo(...)`) declare 0 params,
so Frame() copied no args into the locals slots PValue/CallerLocal
read from. Result: PValue(n) returned whatever happened to live in
the caller's LOCAL slot n (i.e. the first LOCAL declared after `(...)`,
which loops typically use as a counter). fivenode's AP_RPUTS, AP_ECHO
and any other variadic dispatcher that walks PValue() saw garbage —
fivenode_go shipped a workaround (AP_RPUTS collapsed to a single arg)
that this commit now lets us revert.

Mechanism

  CallFrame now carries `actualArgs []Value`. Frame() snapshots every
  value the caller pushed (the full pendingParams, not the clipped
  declared count) into this slice before moving sp. The locals[]
  declared-param region is unchanged so positional LOCAL access keeps
  working. CallerLocal reads from actualArgs first.

  Stack handling tightens slightly: t.sp now ends at `sp - pushedArgs`
  instead of `sp - localsCopy`, dropping the extra-args slots that
  variadic callers used to leave on the stack. They're no longer needed
  — actualArgs is the canonical home — and leaving them on the stack
  was the root of the original "PValue returns the caller's LOCAL"
  bug because the next push would overwrite them.

  Slice reuse: when capacity permits, we slice down rather than
  reallocating, so the hot path (0/1/few args) keeps its no-alloc
  characteristics.

Verified

  • Variadic 2/1/0-arg case and fixed-arg comparison all print the
    expected values (test_variadic_only.prg).
  • Full suite: go test ./compiler/... ./hbrt/... ./hbrtl/...,
    Compat 56/56, std.ch 17/17, FRB 7/7, FiveSql2 43/43 all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 17:17:48 +09:00

24 KiB