Documentation
¶
Overview ¶
Package vm executes the bytecode produced by compiler/bytecode. The model is a conventional Lua 5.4 stack VM: every value lives on a single operand stack, locals are slots inside the current frame, upvalues capture enclosing-function locals, and globals live in a single _G table.
Lua values map to Go types as follows:
nil → Lua nil bool → Lua boolean (see boolean.go) int64 → Lua integer (see integer.go) float64 → Lua float (see float.go) string → Lua string (see string.go) *Table → Lua table (see table.go) *Closure → Lua-defined fn (see closure.go) *GoFunc → Go-defined fn (see closure.go)
Index ¶
- func Equal(a, b Value) bool
- func FloatArg(name string, n int, args []Value) float64
- func IntArg(name string, n int, args []Value) int64
- func IsTruthy(v Value) bool
- func NumArg(name string, n int, args []Value) (int64, float64, bool, bool)
- func OptInt(name string, n int, args []Value, dflt int64) int64
- func OptString(name string, n int, args []Value, dflt string) string
- func PatternGSub(s, pat string, repl Value, maxN int, ...) (string, int)
- func PatternHasSpecials(pat string) bool
- func RegisterPreload(v *VM, name string, loader func(*VM, []Value) []Value)
- func StringArg(name string, n int, args []Value) string
- func ToFloat(v Value) (float64, bool)
- func ToInteger(v Value) (int64, bool)
- func ToNumber(v Value) (intVal int64, floatVal float64, isInt bool, ok bool)
- func ToString(v Value) string
- func ToStringMM(v *VM, val Value) string
- func TypeName(v Value) string
- type CallFrame
- type Closure
- type Coroutine
- type GMatchIter
- type GoFunc
- type LuaError
- type Table
- type Thread
- type Upvalue
- type VM
- func (v *VM) AddScriptDir(dir string)
- func (v *VM) CallFrames() []*CallFrame
- func (v *VM) CallValue(fn Value, args []Value, nresults int) []Value
- func (v *VM) Run(main *bytecode.InstructionSet) (err error)
- func (v *VM) RunMainChunkWithResults(main *bytecode.InstructionSet) (results []Value, err error)
- func (v *VM) SafeCall(fn Value, args []Value) (rs []Value, errVal Value, failed bool)
- func (v *VM) SetGlobal(name string, val Value)
- type Value
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Equal ¶
Equal implements Lua-level == for two values. Numbers cross-compare across int/float subtype; everything else compares within type. Tables/functions compare by reference. Metatable __eq is layered on top of this in vm.go.
func IsTruthy ¶
IsTruthy reports whether v is treated as true by Lua control-flow rules. Only nil and false are falsy; every other value (including 0 and "") is truthy.
func NumArg ¶
NumArg accepts any value coercible to a Lua number (int or float). The (i, f, isInt, ok) tuple lets callers preserve integer-vs-float subtype. `ok` is always true on return — a non-coercible arg panics directly.
func OptInt ¶
OptInt returns the integer arg at position n or `dflt` if the arg is absent or nil. A present, non-numeric, non-nil value raises.
func OptString ¶
OptString returns the string arg at position n or `dflt` if the arg is absent or nil. A present, non-string, non-nil value raises.
func PatternGSub ¶
func PatternGSub( s, pat string, repl Value, maxN int, callFn func(fn Value, args []Value) []Value, ) (string, int)
PatternGSub implements string.gsub. repl is the substitution source (string, *Table, or *GoFunc/*Closure); the function form is called back through callFn (the caller injects vm.CallValue to keep this file VM-API-free). maxN <= 0 means "unlimited".
func PatternHasSpecials ¶
PatternHasSpecials reports whether pat contains any pattern-magic characters. string.find's `plain` flag short-circuits when this is false, falling back to strings.Index without engaging the matcher.
func RegisterPreload ¶
RegisterPreload installs a host loader under `package.preload[name]`. The first `require(name)` invokes `loader` and caches the result in `package.loaded`; later requires return the cache.
Every native module went through the same 12-line dance of fetching package / preload / creating a GoFunc — this helper centralizes it so each module's Register* function is a one-liner.
If `package` is missing (the loader subsystem hasn't been wired into this VM), the call is a silent no-op — matching the pre-existing behavior of each native module's Register*Preload.
func StringArg ¶
StringArg accepts strings, and (per Lua) coerces numbers via the standard integer/float formatter.
func ToInteger ¶
ToInteger coerces v to an int64 by Lua rules. Floats only convert if they are mathematically integer-valued and fit in int64.
func ToNumber ¶
ToNumber attempts to coerce v to a Lua number. It reports the value's concrete Lua subtype: an int64 is an integer, a float64 is a float — even when the float is mathematically integral (e.g. 2.0 stays a float, so `2.0 + 3` is the float 5.0, per Lua 5.4's int/float subtype rules). Returns the value, an "is integer" flag, and an "ok" flag (false if v cannot be a number). Strings follow tonumber's rules: "2" parses to an integer, "2.0" to a float.
func ToString ¶
ToString renders v in Lua's print/tostring format. Per-type formatters live in their respective per-type files.
func ToStringMM ¶
ToStringMM renders val to its string form, honouring the `__tostring` metamethod on tables and other values that have one. Numeric/string/bool/ nil values skip the lookup and route to ToString directly — they cannot carry metatables in current.lsc. If `__tostring` is defined but does not return a string, Lua's rule applies: it is an error. Here we surface that as a panic with a Lua-style message so it lands as a runtime error.
Callers must pass a non-nil *VM. A nil VM falls back to ToString (used by disassembly / debug paths that shouldn't trigger Lua calls).
Types ¶
type CallFrame ¶
type CallFrame struct {
Closure *Closure
IP int
Base int // Stack index where this frame's locals begin
Top int // Stack index just past this frame's last allocated local
NResults int // -1 means "all"
Varargs []Value
// Deferred holds the closures registered by `defer` in this activation.
// They run in last-in-first-out order when the frame unwinds (see
// VM.runDeferred). nil for the overwhelmingly common frame that defers
// nothing, so the slice costs nothing until a `defer` actually fires.
Deferred []*Closure
// contains filtered or unexported fields
}
CallFrame is one activation record. Base is the absolute index of the frame's first local slot on the operand stack; locals 0..NumLocals-1 live at Stack[Base..Base+NumLocals]. The returnTo state is captured by the caller frame; nresults says how many values the caller wants when the callee returns.
type Closure ¶
type Closure struct {
Proto *bytecode.InstructionSet
Upvalues []*Upvalue
}
Closure is a Lua-defined function: a function prototype plus the upvalues captured at the moment the closure was created.
type Coroutine ¶
type Coroutine struct {
// contains filtered or unexported fields
}
Coroutine wraps a closure plus the channel handshake needed to drive it. `status` follows Lua's vocabulary: "suspended" (waiting for resume), "running" (currently executing), "normal" (suspended because it resumed another coroutine — not modeled in v1), and "dead" (returned or errored).
type GMatchIter ¶
type GMatchIter struct {
// contains filtered or unexported fields
}
PatternGMatchNext drives one step of a gmatch iterator. `state` is the (string, pattern, position) iterator state stored as a Lua table; the caller advances `pos` between calls.
func NewGMatchIter ¶
func NewGMatchIter(s, pat string, init int) *GMatchIter
NewGMatchIter sets up a string.gmatch iterator state. `init` is the Lua 5.4 optional third argument: a 1-based start position (negative counts from the end, clamped to [1, #s+1]).
func (*GMatchIter) Next ¶
func (g *GMatchIter) Next() []Value
Next produces the next match's captures (or whole-match), or nil when exhausted. Per Lua 5.4 (`e != lastmatch` in gmatch_aux), an empty match abutting the previous match's end is rejected and the scan resumes one byte later — that both avoids infinite loops on patterns like "a*" and stops a spurious empty match right after a non-empty one. The matchState (and its capture buffer) is reused across calls, so a gmatch loop allocates per match only for the returned values.
type GoFunc ¶
GoFunc is a host-provided function callable from Lua. Args is read-only; the function returns its results as a slice (may be empty).
type LuaError ¶
type LuaError string
LuaError is a typed string used when the VM raises a runtime error. It is recovered by pcall().
type Table ¶
type Table struct {
// contains filtered or unexported fields
}
Table is a Lua table, split into an array part keyed 1..n and a hash part for everything else. Hash iteration is insertion-ordered (Lua doesn't guarantee an order, but determinism makes pairs() predictable for tests).
The metatable field, if non-nil, gives this table its metamethods (see vm/metatable.go). Use Metatable / SetMetatable for external access.
func NewTable ¶
NewTable returns an empty table. The hints are used only when non-zero — a {} literal that ends up empty (a common pattern for "carrier" objects) stays at zero allocations. Set() and the array/hash growth paths handle nil-slice/map promotion lazily.
func NilOrTableArg ¶
NilOrTableArg accepts nil (returned as Go nil *Table) or a *Table. Used by setmetatable's second argument.
func TableOrStringArg ¶
TableOrStringArg accepts either a string or a *Table — used by rawlen. Exactly one of the returned `s`/`t` will be meaningful; the other is the zero value. Callers should branch on `isString`.
func (*Table) Append ¶
Append pushes v onto the end of the array part. Used by SetList to bulk-fill.
func (*Table) EntryCount ¶
EntryCount returns the total number of key/value pairs stored in the table: the non-nil entries in the array part plus every live entry in the hash part. Unlike Len it counts past array holes and includes non-integer keys, so it reflects the table's actual occupancy rather than its Lua border length.
func (*Table) Len ¶
Len returns the table's array-part length. This is Lua's "border" length for the common case where the array part has no holes.
func (*Table) Next ¶
Next returns the (k, v) pair that follows key in iteration order. Calling with key==nil starts iteration. When iteration is exhausted both returns are nil. Iteration first walks the array part, then the hash part in insertion order.
func (*Table) Set ¶
Set assigns t[key]=value. A nil value removes the binding. Setting a nil or NaN key panics with a Lua-style error.
func (*Table) SetMetatable ¶
SetMetatable assigns mt as the metatable. Pass nil to clear.
type Thread ¶
type Thread struct {
Stack []Value
Frames []*CallFrame
OpenUpvs []*Upvalue
// CallMarks is the pending MarkArgs stack. A thread can legitimately
// suspend between a MarkArgs and its matching Call (any
// `f(coroutine.yield())` shape), so the marks are per-thread state —
// leaving them on the VM would let another thread pop them against the
// wrong stack.
CallMarks []int
}
Thread is the per-coroutine stack/frame state. The active thread's fields always match the VM's live Stack/frames/openUpvs; on every resume/yield boundary we save the live state into the outgoing thread and load the incoming thread's state into the live fields.
type Upvalue ¶
type Upvalue struct {
Stack *[]Value // non-nil while open
Index int // stack slot while open
Closed Value // value while closed (Stack==nil)
}
Upvalue is a captured local. While the enclosing function is still on the stack the upvalue is "open" and reads/writes the live stack slot through `Stack[Index]`. When the enclosing function returns, the VM closes the upvalue: it copies the value into Closed and clears Stack. Subsequent accesses then go through Closed.
Closing happens inside VM.closeUpvaluesAbove, called on every Return whose frame contains open upvalues.
type VM ¶
VM is the top-level interpreter state. It owns the operand stack, the frame stack, the globals table, and the open-upvalue list.
When coroutines are in use, the VM's live Stack/frames/openUpvs always belong to the *currently executing* coroutine (or the main thread). On every resume/yield boundary the outgoing thread's state is saved into its Thread snapshot and the incoming thread's snapshot is loaded; see vm/coroutine.go.
func (*VM) AddScriptDir ¶
AddScriptDir prepends search rules rooted at `dir` to package.path so `require` can find modules sitting next to the script being run, no matter what the process's current working directory is. The CLI calls this once it knows the main script's location; the REPL (no script) and the bundled .exe (script lives in the binary, not on disk) never do.
Entries are prepended so a module next to the script shadows a same-named one in the cwd — "modules near me win", the least-surprising default. A no-op if the loader/package table is missing or dir is empty.
func (*VM) CallFrames ¶
CallFrames returns the live call-frame stack, outermost first. The slice shares backing storage with the VM — callers must not mutate or retain it past the current callback. Exposed for the `debug` native module so it can implement traceback / getinfo without living inside this package.
func (*VM) CallValue ¶
CallValue invokes `fn` with `args` from Go code. Useful for embedding and for stdlib helpers that need to call back into Lua (e.g. ipairs internals).
func (*VM) Run ¶
func (v *VM) Run(main *bytecode.InstructionSet) (err error)
Run loads `main` as the top-level chunk and executes it. The chunk runs with no arguments and discards results. Any runtime error surfaces as a non-nil error.
func (*VM) RunMainChunkWithResults ¶
func (v *VM) RunMainChunkWithResults(main *bytecode.InstructionSet) (results []Value, err error)
RunMainChunkWithResults is like Run but keeps every value the chunk returned and hands them back to the caller. The REPL uses it to print the value of bare expressions like Lua's interactive `lua` does.
func (*VM) SafeCall ¶
SafeCall invokes fn with args, recovering any Lua error or runtime panic and fully unwinding the VM (frames, stack, and any open upvalues the failing call created) so the VM stays usable afterwards. It returns the call results, the Lua error value (valid only when failed is true), and whether the call failed. Hosts that run user callbacks outside a pcall — e.g. the http server's per-request handler dispatch — should route through this so one bad callback can't corrupt the shared VM.
type Value ¶
type Value = any
Value is any Lua runtime value. The valid concrete types are documented at the top of this file.
func AnyArg ¶
AnyArg simply asserts presence — for "value expected" sites where the callee handles every Value type itself (type(), pcall(), error()).
func PatternFind ¶
PatternFind searches s for pat starting from init (1-based, like Lua; 0/negative values clamp). Returns (startByte, endByte, captures, ok). startByte / endByte are 1-based byte offsets, matching Lua's string.find return shape.
func PatternMatch ¶
PatternMatch is like PatternFind but only returns the captures (or the whole match if there are no explicit captures), matching string.match's contract.