state

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package api defines the Lua state, global state, and call frame types.

LuaState is the per-thread (coroutine) state. GlobalState is shared across all threads in a Lua instance. CallInfo represents a single activation record in the call chain.

Reference: .analysis/04-call-return-error.md §1, .analysis/07-runtime-infrastructure.md §2

LuaState object pool — reuses dead thread structs to reduce allocation pressure.

Coroutines are the most expensive objects to create in go-lua because each LuaState includes a large stack slice (~200 StackValues). Using sync.Pool lets us reuse both the struct and the stack slice for short-lived coroutines.

LuaState initialization and stack management.

Reference: lua-master/lstate.c

Index

Constants

View Source
const (
	StatusOK        = 0 // LUA_OK
	StatusYield     = 1 // LUA_YIELD
	StatusErrRun    = 2 // LUA_ERRRUN
	StatusErrSyntax = 3 // LUA_ERRSYNTAX
	StatusErrMem    = 4 // LUA_ERRMEM
	StatusErrErr    = 5 // LUA_ERRERR

	// StatusCloseKTop: special status for closing TBC variables without
	// resetting L.Top. Used by OP_RETURN k-bit path where return values
	// sit on the stack above the TBC variables.
	// Matches C Lua's CLOSEKTOP = LUA_ERRERR + 1 in lfunc.h.
	StatusCloseKTop = 6
)

--------------------------------------------------------------------------- Thread status codes (matches C TStatus) ---------------------------------------------------------------------------

View Source
const (
	CISTNResults  uint32 = 0xFF    // bits 0-7: nresults + 1
	CISTC         uint32 = 1 << 15 // call is running a C function
	CISTFresh     uint32 = 1 << 16 // fresh luaV_execute frame
	CISTClsRet    uint32 = 1 << 17 // closing tbc variables on return
	CISTTBC       uint32 = 1 << 18 // has to-be-closed variables
	CISTOAH       uint32 = 1 << 19 // original allowhook value
	CISTHooked    uint32 = 1 << 20 // running a debug hook
	CISTYPCall    uint32 = 1 << 21 // yieldable protected call
	CISTTail      uint32 = 1 << 22 // call was tail called
	CISTHookYield uint32 = 1 << 23 // last hook call yielded
	CISTFin       uint32 = 1 << 24 // function called a finalizer

	CISTCCMTShift  = 8 // bits 8-11: __call metamethod count
	CISTCCMTMask   = 0xF << CISTCCMTShift
	CISTRecstShift = 12 // bits 12-14: recovery status
)

--------------------------------------------------------------------------- CallInfo status flags (packed into CallStatus uint32) Matches C exactly — verified against lstate.h ---------------------------------------------------------------------------

View Source
const (
	HookCall     = 0
	HookReturn   = 1
	HookLine     = 2
	HookCount    = 3
	HookTailCall = 4
)

Hook event constants (matches C LUA_HOOK*)

View Source
const (
	MaskCall  = 1 << 0 // LUA_MASKCALL
	MaskRet   = 1 << 1 // LUA_MASKRET
	MaskLine  = 1 << 2 // LUA_MASKLINE
	MaskCount = 1 << 3 // LUA_MASKCOUNT
)

--------------------------------------------------------------------------- Hook mask constants (matches C LUA_MASK*) ---------------------------------------------------------------------------

View Source
const (
	RegistryIndexGlobals    = 2 // LUA_RIDX_GLOBALS
	RegistryIndexMainThread = 3 // LUA_RIDX_MAINTHREAD
	RegistryIndexLast       = 3 // LUA_RIDX_LAST
)

--------------------------------------------------------------------------- Registry indices (matches C LUA_RIDX_*) ---------------------------------------------------------------------------

View Source
const BasicStackSize = 200

BasicStackSize is the initial stack allocation. Go pays higher cost for stack growth (alloc+copy+GC) than C, so we start larger to avoid reallocStack calls in typical programs like Fibonacci(20).

View Source
const ExtraStack = 5

ExtraStack is reserved space beyond stack_last for error handling.

View Source
const MaxCCalls = 200

MaxCCalls is the maximum C call depth before stack overflow.

View Source
const MaxResults = 250

MaxResults is the maximum number of results a function can return.

View Source
const MaxStack = 1_000_000

MaxStack is the maximum Lua stack size.

View Source
const MultiRet = -1

MultiRet signals "all results" in call/return (LUA_MULTRET = -1).

Variables

This section is empty.

Functions

func CloseState

func CloseState(L *LuaState)

CloseState cleans up a Lua state. In Go, most cleanup is handled by GC, but we nil out references to help the collector. Mirrors C Lua's close_state: runs all pending __gc finalizers before closing.

func DefaultWarnHandler

func DefaultWarnHandler(g *GlobalState)

DefaultWarnHandler is the standard warning handler installed by luaL_newstate. It supports @on/@off control messages and prints warnings to stderr. The handler uses a state machine with three modes:

  • warnOff: warnings are off (only @on control message is processed)
  • warnOn: ready for a new message (checks control messages, prints prefix)
  • warnCont: continuing a multi-part message

State is stored via closure over a *warnState.

func EnsureStack

func EnsureStack(L *LuaState, n int)

EnsureStack ensures at least n free stack slots, growing if needed. This is the primary function other modules should call.

func FreeCI

func FreeCI(L *LuaState)

FreeCI frees all CallInfo nodes after the current one. Mirrors: freeCI in lstate.c

func GrowStack

func GrowStack(L *LuaState, n int)

GrowStack ensures there are at least n free stack slots above L.Top. If not enough space, reallocates the stack. Mirrors: luaD_growstack / luaD_reallocstack in ldo.c

func PushValue

func PushValue(L *LuaState, v object.TValue)

PushValue pushes a TValue onto the stack and increments Top. Panics if stack overflow.

func PutLuaState

func PutLuaState(L *LuaState)

PutLuaState returns a LuaState to the pool for reuse. Called by the GC sweep phase when a dead thread is unlinked. Clears all reference fields before pooling to help Go's GC, but retains the Stack and CISlab slice capacities for reuse.

func ShrinkCI

func ShrinkCI(L *LuaState)

ShrinkCI frees half of the unused CallInfo nodes. Mirrors: luaE_shrinkCI in lstate.c

func StackCheck

func StackCheck(L *LuaState, n int) bool

StackCheck checks that there are at least n free stack slots. Returns true if enough space exists, false otherwise.

Types

type CFunction

type CFunction func(L *LuaState) int

--------------------------------------------------------------------------- CFunction is the CANONICAL type for Go functions callable from Lua. It receives the Lua state and returns the number of results pushed. All other modules should reference this type (or alias it). ---------------------------------------------------------------------------

type CallInfo

type CallInfo struct {
	Func int       // stack index of function slot
	Top  int       // stack index of top for this call
	Prev *CallInfo // caller's CallInfo
	Next *CallInfo // callee's CallInfo (allocated lazily)

	// Lua function fields (valid when CallStatus & CISTC == 0)
	SavedPC    int  // index into Proto.Code (next instruction)
	Trap       bool // tracing active (hooks)
	NExtraArgs int  // extra vararg arguments

	// C function fields (valid when CallStatus & CISTC != 0)
	K          KFunction // continuation for yields
	OldErrFunc int       // saved error function stack index
	Ctx        int       // continuation context

	// Ephemeral union (reused for different purposes)
	NYield  int // number of values yielded
	NRes    int // number of values returned
	FuncIdx int // saved function index for pcall recovery (mirrors ci->u2.funcidx)

	CallStatus uint32 // CIST_* flags
}

--------------------------------------------------------------------------- CallInfo is the activation record for a single function call. Forms a doubly-linked list. The Lua/C distinction is handled by checking CallStatus & CISTC. ---------------------------------------------------------------------------

func NewCI

func NewCI(L *LuaState) *CallInfo

NewCI allocates or reuses the next CallInfo in the chain. Mirrors: luaE_extendCI in lstate.c

func (*CallInfo) GetRecst

func (ci *CallInfo) GetRecst() int

GetRecst retrieves the recovery status from bits 12-14 of CallStatus. Mirrors: getcistrecst in C Lua (lstate.h)

func (*CallInfo) IsLua

func (ci *CallInfo) IsLua() bool

IsLua returns true if this call frame is running a Lua function.

func (*CallInfo) IsLuaCode

func (ci *CallInfo) IsLuaCode() bool

IsLuaCode returns true if running Lua bytecode (not C, not hook).

func (*CallInfo) NResults

func (ci *CallInfo) NResults() int

NResults returns the number of results the caller expects. Returns MultiRet (-1) if caller wants all results.

func (*CallInfo) SetNResults

func (ci *CallInfo) SetNResults(n int)

SetNResults encodes the expected result count into CallStatus.

func (*CallInfo) SetRecst

func (ci *CallInfo) SetRecst(status int)

SetRecst stores a recovery status in bits 12-14 of CallStatus. Mirrors: setcistrecst in C Lua (lstate.h)

type GlobalState

type GlobalState struct {
	Registry object.TValue // the registry table
	Seed     uint32        // randomized hash seed

	// I1 FIX: Concrete types where no circular dependency exists
	TMNames [25]*object.LuaString // metamethod name strings
	MT      [9]any                // metatables for basic types (9 = NumTypes, any to avoid Table import cycle)

	MainThread *LuaState         // the main thread
	Panic      CFunction         // unprotected error handler
	MemErrMsg  *object.LuaString // pre-allocated "not enough memory" string

	// String interning table (typed in luastring module)
	StringTable any

	// API string cache (typed in luastring module)
	StringCache any

	// MemoryLimit is the maximum number of bytes Lua objects may use.
	// 0 means no limit. When GCTotalBytes exceeds this after a full GC,
	// further allocations panic with StatusErrMem ("not enough memory").
	MemoryLimit int64

	// GCTotalBytes tracks total Lua-level object allocations (bytes).
	// Mirrors C Lua's gettotalbytes(g) for collectgarbage("count").
	// Incremented on allocation, decremented by V5 GC sweep when dead
	// objects are removed from allgc. Single-threaded access only.
	GCTotalBytes int64

	// GCAllocCount counts table allocations since the last GC cycle.
	// Used to trigger periodic GC during tight allocation loops.
	GCAllocCount int64

	// GCCountdown is a countdown counter for periodic GC triggering.
	// Starts at 5000, decrements each checkGC call. When ≤ 0, triggers GC
	// and resets. Uses countdown instead of modulo for lower inline cost.
	GCCountdown int64

	// GCStepFn is set by the API layer to run a full GC cycle.
	// The VM calls this periodically during allocation-heavy loops.
	// Signature: func(L *LuaState) — runs GCCollect.
	GCStepFn func(L *LuaState)

	// GCDrainFn is set by the API layer to run a lightweight GC cycle.
	// Signature: func(L *LuaState) — runs GCCollect.
	GCDrainFn func(L *LuaState)

	// SweepStringFn is called by GC sweep when a dead short string is found.
	// It removes the string from the string interning table.
	// Set by the API layer to avoid circular imports (gc → luastring).
	SweepStringFn func(obj object.GCObject)

	// GCClosed is set true when the state is closing — suppresses further GC.
	GCClosed bool
	// GCStopped is set true by collectgarbage("stop") — suppresses periodic GC.
	GCStopped bool
	// GCRunning is true while a GC cycle is executing — prevents re-entrant GC.
	// Mirrors C Lua's g->gcrunning flag.
	GCRunning bool
	// GCRunningFinalizer is true while __gc finalizers are being called.
	// Prevents reentrant finalization (mirrors C Lua's GCSTPGC flag in gcstp).
	GCRunningFinalizer bool
	// GCExplicit is true when GC is triggered by explicit collectgarbage() call.
	// When true, traverseThread uses th.Top (precise — dead registers not marked).
	// When false (periodic GC), traverseThread uses maxTop (conservative).
	GCExplicit bool

	// GCMode tracks the current GC mode: "incremental" (default) or "generational".
	// Mirrors C Lua's g->gckind (KGC_INC / KGC_GEN).
	GCMode string

	// GCParams stores GC tuning parameters (pause, stepmul, stepsize, etc.).
	// Go's GC doesn't use these, but we store them so Lua code that
	// reads/writes them (e.g., gc.lua tests) works correctly.
	// Keys: "pause", "stepmul", "stepsize", "minormul", "majorminor", "minormajor"
	GCParams map[string]int64

	// Object chains
	Allgc   object.GCObject // head of all collectable objects
	FinObj  object.GCObject // objects with __gc (separated at setmetatable time)
	TobeFnz object.GCObject // objects whose __gc should run this cycle
	FixedGC object.GCObject // permanent objects (never collected)

	// GC phase
	GCState      byte // current phase (GCSpause, GCSpropagate, etc.)
	CurrentWhite byte // current white bit (flips each cycle)

	// Gray lists (slice-based for cache locality; replaces intrusive GCList)
	Gray          []object.GCObject // objects to be traversed (stack: pop from end)
	GrayAgain     []object.GCObject // barrier-back objects (re-traverse in atomic)
	Weak          []object.GCObject // weak-value tables to clear after mark
	AllWeak       []object.GCObject // all-weak (kv) tables
	Ephemeron     []object.GCObject // ephemeron tables (weak-key) — pending convergence
	EphemeronDone []object.GCObject // ephemeron tables already converged (for clearByKeys)

	// EphemeronCount tracks how many weak-key (ephemeron) tables were
	// encountered during the current GC mark phase. When zero, the
	// expensive clearDeadKeysAllEphemerons walk can be skipped entirely.
	EphemeronCount int

	// Incremental GC state
	GCDebt     int64            // bytes "owed" — positive = credit, triggers step when <= 0
	GCEstimate int64            // estimate of non-garbage bytes (for setpause)
	SweepGC    *object.GCObject // current position in sweep list (nil = not sweeping)

	// GC tuning parameters (used by debt-based pacing)
	GCPause    int // pause% (default 200 = collect when memory reaches 2x live)
	GCStepMul  int // step multiplier (default 200)
	GCStepSize int // log2 of step size in bytes (default 13 = 8KB)

	// Generational GC state
	GCKind byte // KGC_INC=0, KGC_GENMINOR=1, KGC_GENMAJOR=2

	// Generation list boundary pointers within allgc chain.
	// The allgc chain is ordered: [new objects] → Survival → Old1 → ReallyOld → nil
	// These pointers mark the boundaries between generations.
	Survival  object.GCObject // first survival-age object in allgc
	Old1      object.GCObject // first old1-age object in allgc
	ReallyOld object.GCObject // first really-old object in allgc
	FirstOld1 object.GCObject // optimization: first OLD1 in list

	// Same boundaries for finobj chain
	FinObjSur  object.GCObject // survival boundary in finobj
	FinObjOld1 object.GCObject // old1 boundary in finobj
	FinObjROld object.GCObject // really-old boundary in finobj

	// GCMinorMul is the generational minor collection multiplier.
	// Controls debt after minor collections: debt = GCMajorMinor * GCMinorMul / 100.
	GCMinorMul int // default 20 (LUAI_GENMINORMUL)

	// GCMinorMajor controls when minor collections shift to major.
	// A major collection triggers when bytes-becoming-old exceed
	// GCMajorMinor * GCMinorMajor / 100. 0 = never promote.
	GCMinorMajor int // default 70 (LUAI_MINORMAJOR)

	// GCMajorMinor tracks bytes marked in the last major collection.
	// Used as the baseline for minor→major promotion decisions.
	GCMajorMinor int64

	// GCMarked tracks bytes that became old since the last major collection.
	// In minor mode, this accumulates addedold1 from each YoungCollection.
	GCMarked int64

	// WarnF is the warning handler function. Called with (message, tocont).
	// tocont=true means the message is a continuation (more parts follow).
	// When nil, warnings are silently discarded.
	WarnF func(ud any, message string, tocont bool)

	// WarnUd is the user data passed to WarnF (mirrors C Lua's ud_warn).
	WarnUd any

	// WarnBuf accumulates multi-part warning messages (tocont=true parts).
	WarnBuf string
}

--------------------------------------------------------------------------- GlobalState is shared across all threads in a Lua instance. ---------------------------------------------------------------------------

func (*GlobalState) LinkGC

func (g *GlobalState) LinkGC(obj object.GCObject)

--------------------------------------------------------------------------- LinkGC links a new collectable object into the allgc chain and sets its initial white mark. This is the Go equivalent of C Lua's luaC_newobj. Must be called for every new collectable object immediately after creation. ---------------------------------------------------------------------------

func (*GlobalState) SetWarnF

func (g *GlobalState) SetWarnF(f func(ud any, msg string, tocont bool), ud any)

SetWarnF sets the warning handler function and its user data. Mirrors C Lua's lua_setwarnf.

func (*GlobalState) TrackAllocation

func (g *GlobalState) TrackAllocation(n int64)

TrackAllocation increments GCTotalBytes and decrements GCDebt by n bytes. Used for debt-based GC pacing: when GCDebt reaches 0, a GC step triggers. Lua is single-threaded so no atomics needed.

If a MemoryLimit is set and GCTotalBytes exceeds it, a full GC is attempted (via GCDrainFn). If still over limit after GC, panics with StatusErrMem.

func (*GlobalState) WarnError

func (g *GlobalState) WarnError(where string, errMsg string)

WarnError generates a warning from a __gc error. Produces: "error in __gc (errmsg)" Mirrors C Lua's luaE_warnerror (lstate.c).

func (*GlobalState) Warning

func (g *GlobalState) Warning(msg string, tocont bool)

Warning issues a warning message through the registered handler. If tocont is true, the message is a continuation (more parts follow). Mirrors C Lua's luaE_warning (lstate.c).

type KFunction

type KFunction func(L *LuaState, status int, ctx int) int

KFunction is a continuation function for yieldable C calls.

type LuaBaseLevel

type LuaBaseLevel struct {
	Status int // status from resetthread (usually StatusOK)
}

--------------------------------------------------------------------------- LuaBaseLevel is thrown by CloseThread when a coroutine closes itself. Mirrors C Lua's luaD_throwbaselevel: bypasses all inner error handlers (pcall) and propagates directly to the outermost RunProtected (Resume). RunProtected catches this and re-panics; Resume catches it as termination. ---------------------------------------------------------------------------

type LuaError

type LuaError struct {
	Status  int           // error code (StatusErrRun, etc.)
	Message object.TValue // error object (usually a string)
}

--------------------------------------------------------------------------- LuaError is the CANONICAL error type used for Lua runtime errors. Lua errors are propagated via panic(LuaError{...}). All modules should use this type (not define their own). ---------------------------------------------------------------------------

func (LuaError) Error

func (e LuaError) Error() string

type LuaState

type LuaState struct {
	object.GCHeader // GC metadata
	// C3 FIX: Stack uses []StackValue (not []TValue) for TBC support.
	// Each slot has a TBCDelta field for the to-be-closed linked list.
	Stack  []object.StackValue // the value stack
	Top    int                 // index of first free slot
	CI     *CallInfo           // current call info
	BaseCI CallInfo            // embedded base CallInfo (C host level)

	Global    *GlobalState // shared global state
	OpenUpval any          // head of open upvalue list (typed in closure module)
	TBCList   int          // stack index of top to-be-closed variable (-1 = none)

	Status    int    // thread status (StatusOK, StatusYield, etc.)
	AllowHook bool   // hook enable flag
	NCCalls   uint32 // C call depth (low 16) + non-yieldable count (high 16)
	NCI       int    // number of CallInfo nodes in the list

	ErrFunc int // error handler stack index
	OldPC   int // last traced PC

	// CallInfo slab allocation — reduces GC pressure by allocating
	// CallInfos in batches of ciSlabSize instead of individually.
	CISlab    []CallInfo // slab for CallInfo allocation
	CISlabIdx int        // next free index in slab

	// C10 FIX: Debug hook fields (needed by debug.sethook/gethook)
	Hook          any // debug hook function (typed later)
	BaseHookCount int // base hook count setting
	HookCount     int // current hook countdown
	HookMask      int // which hooks active (call, return, line, count)
	FTransfer     int // hook value transfer: index of first value
	NTransfer     int // hook value transfer: number of values
	HookEvent     int // current hook event (HookCall, HookReturn, etc.)
	HookLine      int // current hook line number (-1 if not a line hook)
	// Saved state for hook yield resume (survives panic/yield)
	HookSavedTop   int // L.Top before hook dispatch
	HookSavedCITop int // ci.Top before hook dispatch

	// APIState caches the public api.State wrapper for this LuaState.
	// This avoids allocating a new &State{Internal: ls} on every C function call.
	// Set once during state creation; type is any to avoid circular import.
	APIState any
}

--------------------------------------------------------------------------- LuaState is the per-thread (coroutine) state. Each coroutine has its own LuaState with its own stack. ---------------------------------------------------------------------------

func NewState

func NewState() *LuaState

NewState creates a new Lua state with stack, base CI, registry, globals, string table, and TM names. This is the Go equivalent of lua_newstate.

func NewThread

func NewThread(L *LuaState) *LuaState

NewThread creates a new Lua thread (coroutine) sharing the same GlobalState.

func (*LuaState) CCalls

func (L *LuaState) CCalls() int

CCalls returns the current C call depth.

func (*LuaState) GC

func (L *LuaState) GC() *object.GCHeader

GC returns the GC header for this thread.

func (*LuaState) StackLast

func (L *LuaState) StackLast() int

StackLast returns the index of the last usable stack slot.

func (*LuaState) Yieldable

func (L *LuaState) Yieldable() bool

Yieldable returns true if the current coroutine can yield.

type LuaYield

type LuaYield struct {
	NResults int // number of results to yield
}

--------------------------------------------------------------------------- LuaYield is the type used for coroutine yield via panic/recover. Distinct from LuaError so recover() can distinguish error from yield. I5 FIX: Added for coroutine support. ---------------------------------------------------------------------------

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL