Documentation
¶
Overview ¶
Package bytecode emits a stack-based instruction stream for luascript (Lua 5.4 syntax). The VM model is conventional stack-based: every expression leaves a single value on top of the stack unless it is a "multi-value" producer (call, method call, vararg) appearing in a last-position context that requests variable results.
Index ¶
- Constants
- Variables
- func Disassemble(sets []*InstructionSet) string
- func SerializeChunk(w io.Writer, main *InstructionSet) error
- type Generator
- type Instruction
- func (i *Instruction) ActionName() string
- func (i *Instruction) AnchorLine() int
- func (i *Instruction) CacheGen() uint32
- func (i *Instruction) CacheVal() Value
- func (i *Instruction) Inspect() string
- func (i *Instruction) Line() int
- func (i *Instruction) SetCache(gen uint32, val Value)
- func (i *Instruction) SourceLine() int
- type InstructionSet
- type UpvalueDesc
- type Value
Constants ¶
const ( Program = "ProgramStart" // top-level chunk FunctionDef = "Function" // any function body, including the main chunk's )
Instruction-set names used as the `Type` of an InstructionSet.
const ( // --- constants & literals --- LoadNil uint8 = iota // params: count ; pushes `count` nils LoadTrue // pushes true LoadFalse // pushes false LoadInt // params: int64 ; pushes integer LoadFloat // params: float64 ; pushes float LoadString // params: string ; pushes string LoadVararg // params: count (-1 = all) ; expands current frame's `...` Closure // params: protoIndex ; pushes closure built from FunctionProto[i] // --- variables --- GetLocal // params: slot SetLocal // params: slot GetUpvalue // params: index SetUpvalue // params: index GetGlobal // params: name SetGlobal // params: name // --- tables --- NewTable // params: arrayHint, hashHint ; pushes new empty table GetTable // pops key, table ; pushes table[key] SetTable // pops value, key, table ; table[key]=value GetField // params: key (string) ; pops table; pushes table[key] SetField // params: key (string) ; pops value, table; table[key]=value Self // params: key (string) ; for `obj:m`. pops obj; pushes obj[key], obj SetList // params: count, offset ; bulk-fill array part of a table built below the values // --- arithmetic --- Add Sub Mul Div FloorDiv Mod Pow Neg // unary - // --- bitwise (Lua 5.3+) --- BitAnd BitOr BitXor Shl Shr BitNot // unary ~ // --- string / length --- Concat // params: count ; concatenates top `count` values Len // unary # // --- comparison --- Eq NotEq Lt Le Gt Ge // --- logical --- Not // unary not // --- control flow --- Jump // params: targetLine JumpIfFalse // params: targetLine ; pops; jump if falsy JumpIfTrue // params: targetLine ; pops; jump if truthy JumpIfFalseKeep // params: targetLine ; peeks; if falsy keep & jump, else pop & continue (for `and`) JumpIfTrueKeep // params: targetLine ; peeks; if truthy keep & jump, else pop & continue (for `or`) // --- calls / returns --- Call // params: nargs, nresults (-1 = all) Return // params: count (-1 = all from base to top) // --- numeric for --- // baseSlot..baseSlot+2 are hidden control slots (index, limit, step); // baseSlot+3 is the visible loop variable, which both opcodes refresh from // the hidden index at the top of each iteration. Keeping the counter hidden // is what makes assigning to the loop variable inside the body harmless. ForPrep // params: baseSlot, targetLine ; uses 3 stack values: start, limit, step ForLoop // params: baseSlot, targetLine // --- generic for --- TForCall // params: baseSlot, nresults TForLoop // params: baseSlot, targetLine // --- stack utility --- Pop // params: count Dup // duplicates top of stack // --- frame --- Leave // ends instruction set (REPL friendly fallback / chunk terminator) // MarkArgs records the current stack height onto the VM's call-mark // stack. Emitted by compileCall/compileMethodCall ONLY when the call // has a multi-value last argument (call, methodcall, or vararg in // last position). The matching Call opcode encoded with nargs=-1 // pops the mark to recover the args base — needed because the spread // length isn't known until runtime. MarkArgs // Defer pops a closure off the stack and registers it on the current // frame's deferred list. The VM runs a frame's deferred closures in // last-in-first-out order when the frame unwinds (normal return, // fall-off-end, or an error caught by pcall). No params. Defer // CloseUpvalues closes every open upvalue whose slot is at or above the // local slot in param A (relative to the current frame base). Emitted at // the end of a loop iteration whose body captured a local in a closure, so // each iteration's variables are captured independently (Lua 5.4 gives // every iteration a fresh loop variable). No stack effect. CloseUpvalues // Try installs an error handler on the current frame: param A is the // instruction index of the matching catch clause. If any error is raised // while the handler is installed, the VM unwinds back to this frame, // truncates the stack to the height Try observed, pushes the error value, // and resumes at A. Handlers nest — the innermost one wins. No stack // effect. Try // EndTry pops the innermost `count` (param A) handlers off the current // frame without running them. Emitted on every path that leaves a // protected region other than by raising: the fall-through end of a `try` // body, and any `break`/`continue` that jumps out of one. No stack effect. // // A `return` out of a `try` needs no EndTry — handlers live on the frame, // so unwinding the frame discards them. EndTry // Throw pops a value and raises it as a Lua error, identical to what the // `error` builtin does (which propagates its argument verbatim, with no // position prefix). It is an opcode rather than a lowering to a call so // that `throw` cannot be re-pointed by shadowing the name `error`. Throw InstructionCount )
Opcodes for the Lua-flavored stack VM.
const SerialVersion = 2
SerialVersion is the on-disk bytecode layout version. Bump on any change to the encoding below — and on any change to what an existing opcode's operands mean, since a cached chunk carries no other record of that. Version 2 moved the numeric-for loop variable to baseSlot+3 (behind the hidden counter).
Variables ¶
var InstructionNameTable = []string{ LoadNil: "loadnil", LoadTrue: "loadtrue", LoadFalse: "loadfalse", LoadInt: "loadint", LoadFloat: "loadfloat", LoadString: "loadstring", LoadVararg: "loadvararg", Closure: "closure", GetLocal: "getlocal", SetLocal: "setlocal", GetUpvalue: "getupvalue", SetUpvalue: "setupvalue", GetGlobal: "getglobal", SetGlobal: "setglobal", NewTable: "newtable", GetTable: "gettable", SetTable: "settable", GetField: "getfield", SetField: "setfield", Self: "self", SetList: "setlist", Add: "add", Sub: "sub", Mul: "mul", Div: "div", FloorDiv: "floordiv", Mod: "mod", Pow: "pow", Neg: "neg", BitAnd: "band", BitOr: "bor", BitXor: "bxor", Shl: "shl", Shr: "shr", BitNot: "bnot", Concat: "concat", Len: "len", Eq: "eq", NotEq: "neq", Lt: "lt", Le: "le", Gt: "gt", Ge: "ge", Not: "not", Jump: "jump", JumpIfFalse: "jumpiffalse", JumpIfTrue: "jumpiftrue", JumpIfFalseKeep: "jumpiffalsekeep", JumpIfTrueKeep: "jumpiftruekeep", Call: "call", Return: "return", ForPrep: "forprep", ForLoop: "forloop", TForCall: "tforcall", TForLoop: "tforloop", Pop: "pop", Dup: "dup", Leave: "leave", MarkArgs: "markargs", CloseUpvalues: "closeupvalues", Defer: "defer", Try: "try", EndTry: "endtry", Throw: "throw", }
InstructionNameTable maps each opcode to a readable mnemonic.
Functions ¶
func Disassemble ¶
func Disassemble(sets []*InstructionSet) string
func SerializeChunk ¶
func SerializeChunk(w io.Writer, main *InstructionSet) error
SerializeChunk writes main (and, recursively, its nested protos) to w.
Types ¶
type Generator ¶
type Generator struct {
REPL bool
// contains filtered or unexported fields
}
Generator drives bytecode emission for an entire program.
func (*Generator) Err ¶
Err returns the first generation error, or nil. An unresolved forward goto would otherwise keep its anchor at line 0 and silently compile into a jump to the start of the function.
func (*Generator) GenerateInstructions ¶
func (g *Generator) GenerateInstructions(stmts []ast.Statement) []*InstructionSet
GenerateInstructions emits bytecode for the supplied chunk body and returns every instruction set produced (main chunk first, nested function protos follow in declaration order).
func (*Generator) InitTopLevelScope ¶
InitTopLevelScope opens the main-chunk function context. The Lua reference implementation models a chunk as `function(...) <chunk body> end`, so the main chunk is vararg with zero declared parameters.
func (*Generator) ResetInstructionSets ¶
func (g *Generator) ResetInstructionSets()
ResetInstructionSets clears prior emission output. Useful between REPL inputs.
type Instruction ¶
type Instruction struct {
Opcode uint8
A int32 // primary int param (slot, count, jump target, name index, ...)
B int32 // secondary int param (Call nresults, NewTable hashHint, SetList offset, jump target for For*)
BoxedAny any // LoadInt/LoadFloat/LoadString payload — preserves the shared-box optimization (see vm dispatch comment)
StrA string
Params []any
// contains filtered or unexported fields
}
Instruction is one emitted opcode plus its parameters and source line.
Hot-path fields A / B / StrA / BoxedAny are pre-decoded from Params at emission time so the VM dispatch doesn't pay a per-instruction type assertion + slice bounds check. The exact mapping of an opcode's arguments onto these fields lives in encodeParams below. Params is retained for the disassembler, Inspect, and the bytecode test suite — the VM hot loop reads only the typed fields.
Inline-cache state (cacheGen / cacheVal) is owned by the VM, written on a GetGlobal miss and read on subsequent hits. Per-Instruction monomorphic caching: each call site memoises its own global.
func (*Instruction) ActionName ¶
func (i *Instruction) ActionName() string
ActionName returns the mnemonic for this instruction.
func (*Instruction) AnchorLine ¶
func (i *Instruction) AnchorLine() int
AnchorLine returns the resolved target line of this instruction's anchor. Panics if the instruction has no anchor.
func (*Instruction) CacheGen ¶
func (i *Instruction) CacheGen() uint32
CacheGen returns the generation observed at the last successful inline-cache lookup against this instruction (zero before first use).
func (*Instruction) CacheVal ¶
func (i *Instruction) CacheVal() Value
CacheVal returns the value memoised at the last cache miss.
func (*Instruction) Inspect ¶
func (i *Instruction) Inspect() string
Inspect renders the instruction in a human-readable form.
func (*Instruction) Line ¶
func (i *Instruction) Line() int
Line returns the instruction's index inside its instruction set.
func (*Instruction) SetCache ¶
func (i *Instruction) SetCache(gen uint32, val Value)
SetCache records (gen, val) on this instruction for the next hit.
func (*Instruction) SourceLine ¶
func (i *Instruction) SourceLine() int
SourceLine returns the source-code line where the instruction originated.
type InstructionSet ¶
type InstructionSet struct {
Instructions []*Instruction
// Function-proto data — only populated for functions (and the main chunk).
NumParams int
IsVararg bool
NumLocals int
Upvalues []UpvalueDesc
Constants []any // reserved for future constant-pool use
Protos []*InstructionSet // nested function instruction sets
// contains filtered or unexported fields
}
InstructionSet is the body of one function (or the top-level chunk).
func DeserializeChunk ¶
func DeserializeChunk(r io.Reader) (*InstructionSet, error)
DeserializeChunk reads a chunk previously written by SerializeChunk.
func (*InstructionSet) HasTry ¶
func (is *InstructionSet) HasTry() bool
HasTry reports whether this body installs any error handler, i.e. whether it contains a `try`. Only meaningful once the proto's one-time scan has run — see SetHasTry and vm.callClosure.
func (*InstructionSet) LocalsResolved ¶
func (is *InstructionSet) LocalsResolved() bool
LocalsResolved reports whether the VM has already reconciled NumLocals.
func (*InstructionSet) MarkLocalsResolved ¶
func (is *InstructionSet) MarkLocalsResolved()
MarkLocalsResolved records that NumLocals is now authoritative.
func (*InstructionSet) Name ¶
func (is *InstructionSet) Name() string
Name returns the instruction set's name.
func (*InstructionSet) SetHasTry ¶
func (is *InstructionSet) SetHasTry(b bool)
SetHasTry records the scan's finding. Called alongside MarkLocalsResolved.
func (*InstructionSet) Type ¶
func (is *InstructionSet) Type() string
Type returns the instruction set's category (Program / FunctionDef).
type UpvalueDesc ¶
UpvalueDesc describes how a function captures one upvalue.
InStack == true -> Index is a local slot in the immediately enclosing function InStack == false -> Index is an upvalue index in the immediately enclosing function