bytecode

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 10 Imported by: 0

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

View Source
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.

View Source
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.

View Source
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

View Source
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 NewGenerator

func NewGenerator() *Generator

NewGenerator creates a fresh generator.

func (*Generator) Err

func (g *Generator) Err() error

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

func (g *Generator) InitTopLevelScope(_ *ast.Program)

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

type UpvalueDesc struct {
	Name    string
	InStack bool
	Index   int
}

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

type Value

type Value = any

Value is re-declared here as `any` to avoid an import cycle with vm/. The cacheVal field stores a vm.Value (which is `any`) — equivalent at the language level, and the cache is only ever read/written by the VM.

Jump to

Keyboard shortcuts

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