Help us shape the future of Go! Take the 2026 Go Developer Survey

lua

package module
v1.3.3 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 26 Imported by: 0

README

Glasm

Glasm is a Lua 5.5.1 VM and compiler in Go, with a Lua → WebAssembly compiler for running untrusted scripts inside pure-Go daemons.

Credit where credit is due

Glasm is a fork of gopher-lua by Yusuke Inuzuka — a highly capable Lua VM and compiler in Go (implementing Lua 5.1). The parser, the register-VM interpreter, the code generator, and the Go embedding API documented below all originate in gopher-lua. Without gopher-lua, Glasm would not exist. Thank you, yuin.

Glasm builds on that foundation with:

  • the language core upgraded through 5.2 to Lua 5.5.1 semantics — the stock 5.5.1 instruction encoding (85 opcodes), _ENV, integer/float duality, goto/labels, <const>/<close> attributes, the 5.3+ standard library — with differential testing against stock C Lua 5.5.1 as the oracle;
  • a Lua → WebAssembly compiler (luawasm/) targeting a freestanding wasm32 runtime derived from stock C Lua 5.5.1 (runtime/); the host engine JIT-compiles the wasm to native code on supported architectures (arm64, amd64) — a large win on some workloads, a cost on others; cmd/luabench measures both sides;
  • the daemon-facing host/ package — a pure-Go (no cgo) embedding surface over the wazero wasm engine: untrusted scripts with memory budgets, deadlines, sandboxed globals, and Go host functions.

Glasm has the same goal as Lua: Be a scripting language with extensible semantics. It provides Go APIs that allow you to easily embed a scripting language in your Go host programs.

Note: the embedding-API sections below are inherited from gopher-lua's README and describe the interpreter's Go API. The 5.5 language surface, the wasm toolchain (cmd/luawasmc, cmd/luawasm-run, cmd/glua), and the production host/ package are documented in docs/ — start with docs/Lua-Wasm-Design-and-Test-Plan.md and docs/Upgrade-from-5.2-to-5.5-of-Lua.md.

Table of Contents

Design principle

  • Be a scripting language with extensible semantics.
  • User-friendly Go API
    • The stack based API like the one used in the original Lua implementation will cause a performance improvements in Glasm (It will reduce memory allocations and concrete type <-> interface conversions). Glasm API is not the stack based API. Glasm give preference to the user-friendliness over the performance.

How about performance?

Glasm runs Lua two ways, and which one wins depends on the shape of the script and how long it runs:

  • The interpreter — the register VM in Go (gopher-lua's). Compiling to bytecode and starting a state both cost microseconds; every instruction then pays Go dispatch.
  • The compiler — Glasm compiles the script to a WebAssembly module, and the host engine JIT-compiles that wasm to native machine code (arm64, amd64). Integer and float arithmetic run as straight native code in tight loops — no interpreter dispatch at all. Operations that miss a fast path (string-library calls, table growth, metamethods, coercion) cross into a compiled C Lua 5.5.1 runtime.

The trade is setup cost up front versus native speed afterwards. Compiling Lua → wasm is cheap (tens of microseconds for a typical script). What costs is standing up a fresh VM — instantiating the runtime blob, opening the standard library, linking the script: about 70 ms before the first Lua instruction runs on the machine below. So:

  • Short scripts, or scripts run once — the interpreter wins, massively. A script that does 100 µs of work does not repay a 70 ms setup cost.
  • Computationally intensive scripts — straight-line numeric and table-indexed loops run at native speed. On a wasmtime-class engine a sieve benchmark runs ~25% faster than the interpreter (360 ms vs ~460–530 ms) despite being fully sandboxed; the longer the script runs, the more that per-instruction gap compounds.
  • Scripts run a lot — pool the VM. A pooled VM compiles once and re-runs in place; the per-run cost drops back to microseconds and even a trivial script round-trips faster than building a fresh interpreter state (24 µs vs 51 µs below) — faster, in fact, than the native C lua binary itself (see "vs native C Lua 5.5.1" below). The host/ package is built around this posture.
Measured — production path (host/ on wazero), 2026-10-21

Medians on one Apple-silicon Mac (arm64, go1.27); the meaningful column is the ratio — absolute numbers move with machine and engine version. All runs output-checked identical across paths. (These lanes are call-thin — for the call-shaped post-U9c numbers and the post-U10 fast-path work see the by-shape table below, docs/U9-perf.md, and docs/U10-perf.md.)

script (shape) interp wasm, cold VM wasm, pooled VM
h_tiny (startup only) 51.4 µs 67.0 ms 24.0 µs
h_str (short string ops) 136 µs 73.5 ms 94 µs
h_strlong (150 KB text processing) 1.33 ms 74.0 ms 762 µs
h_numloop (4 M-iteration numeric) 127.1 ms 114.0 ms 41.5 ms
h_tablechurn (table alloc churn) 9.9 ms 107.1 ms 28.0 ms
h_concat (concat-heavy) 922 µs 74.8 ms 1.59 ms
h_hostfn (3000 Go-call round trips) 584 µs 71.6 ms 2.96 ms

Read the table as: cold = compile + instantiate + run once; pooled = already-instantiated VM, run again. The 70 ms column is the "short script you run a lot" penalty — pooling removes it.

vs native C Lua 5.5.1 — perfcmp, 2026-10-25

How does any of this compare against the original C implementation? cmd/perfcmp answers that directly: it builds a native stock C Lua 5.5.1 (runtime/build-native55.sh — the vendored 5.5.1 tree plus the stock lua.c driver, clang -O2) and races it against the glasm engines over the same scripts, same source bytes, outputs cross-checked every run (docs/Perf-vs-C-Lua.md for methodology and the full run; all numbers from one back-to-back session — this class of comparison does not survive cross-day machine drift).

Steady-state per-run medians, warm postures (pooled VM for the wasm engine; the C binary re-loadfiles per run, which is the best its CLI can do — it has no pooling surface):

script (shape) native C 5.5.1 glasm interp glasm wasm (wazero, pooled) wasm × C
h_tiny (startup only) 14.0 µs 39.5 µs 9.8 µs 0.7×
h_str (short string ops) 36 µs 109 µs 69 µs 1.9×
h_strlong (150 KB text processing) 387 µs 1.29 ms 729 µs 1.9×
h_concat (concat-heavy) 620 µs 753 µs 1.51 ms 2.4×
h_numloop (4 M-iteration numeric) 8.5 ms 130 ms 40 ms 4.7×
h_tablechurn (table alloc churn) 3.9 ms 10.7 ms 21 ms 5.5×
h_tco (tail-recursive countdown) 1.6 ms 11.3 ms 9.5 ms 5.9×
h_nsieve (numeric + table stores) 6.0 ms 62 ms 40 ms 6.7×
h_fib(30) (call-heavy recursion) 20.2 ms 154 ms 137 ms 6.8×

The headline: on the high-frequency EVAL shape — a small script run many times, which is exactly the daemon's workload — the pooled wasm VM is faster than the native C lua binary (9.8 µs vs 14 µs per run), because it re-runs an already-compiled, already-instantiated VM while the C interpreter re-parses and rebuilds a fresh lua_State each time. That advantage holds for any script whose cost is dominated by state setup rather than compute; as the compute share grows, -O2 C's per- instruction advantage takes over (string lanes ~2×, tight numeric loops and call-heavy recursion ~5–7×, with the gaps mapped to specific rt seams in docs/Perf-vs-C-Lua.md). Memory: the C binary runs in ~2–3 MB RSS; the pooled Go engine carries a flat ~52 MB floor (Go runtime + wazero JIT + runtime blob) plus a bounded guest heap — the price of the pure-Go, no-cgo constraint, paid once per VM rather than per script.

By shape — compiler vs interpreter

Process-time medians (3 runs) over the luabench scripts, re-measured 2026-10-21 after U9c (direct dispatch), U10 (arith/compare/table fast paths), U11 (string-lane probes), U11c (the concat fold probe), U11d (the string.format probe), U11e (the in-wasm // % / ^ arith arms), U11e-b (the _ENV GETTABUP/SETTABUP globals probes) and U11e-c (the gmatch iterator probe) — bench/2026-10-21-u11ec.txt (the U11/U11c runs: bench/2026-10-20-u11*.txt):

shape wasm on wasmtime wasm on wazero vs interpreter
fib(30) (call-heavy recursion)¹ 0.11 s 0.23 s interp 0.16 s: 1.5× faster on wasmtime, 1.4× slower on wazero
h_nsieve (numeric loop + table stores) 0.05 s 0.13 s interp 0.06 s: parity on wasmtime, ~2× slower on wazero
~150 KB text processing 0.04 s 0.10 s interp < 0.01 s: 4× / 10× slower — instantiation-dominated at this script size

¹ The call-path history: U9c's direct in-module dispatch (ledger row 104) took wasmtime fib from 14.2 s to interp parity, and U10 removed the per-op try_table enters (wazero clones the machine stack per enter, 10,880 B) that masqueraded as a codegen gap; U11 took the same probe posture to strings (short-string table keys, #, string.byte/string.find inline — docs/U11-perf.md). Process times here include ~70–100 ms instantiation; warm pooled-VM numbers are the table above.

One caveat, active work rather than a law of nature:

  • The string lanes reached interp parity. Numeric, call, and table lanes beat the interpreter on the pooled production path (numloop 3× faster, fib/nsieve/tco at parity or better; tablechurn's alloc churn still pays ~2.5×), and after U11e-b the string lanes are at or past parity too — short-string table keys, #, string.byte/string.find run inline with no protected-call enter, U11c took the allocating .. itself off the protected route (one-pass fold with a two-sided memory pre-flight), U11d folded string.format, U11e moved the last arith fallbacks in-wasm (%/// floor semantics, //^ float arms via plain never-raise helpers), U11e-b staged the _ENV globals cell plain so per-loop global reads ride the short-string probe, and U11e-c runs the string.gmatch iterator step itself in-wasm (lstrlib's pattern engine ported verbatim, pinned by construction, every raise-shaped step declining to the stock-protected path) — h_strlong 0.57× interp (was ~10× at U10, 0.73× at U11e-b), h_str 0.69× (docs/U11-perf.md). What still pays the protected C route: h_concat's allocating fold (1.7× interp), and the un-probed stdlib callees (sub, rep, math.min, table.concat, …). C callees beyond the probed set, __call chains and cross-module closures still take the host dispatch seam, and tailcalls ride the staged M5c path.

To reproduce: go run ./cmd/luabench (interp vs wasm cold/warm on the production engine, output-checked); see also cmd/luawasmc + cmd/luawasm-run -v for single-script timing on either engine.

Installation

$ go get github.com/pschlump/glasm

Usage

Glasm APIs perform in much the same way as Lua, but the stack is used only for passing arguments and receiving returned values.

Glasm supports channel operations. See "Goroutines" section.

Import a package.

import (
    "github.com/pschlump/glasm"
)

Run scripts in the VM.

L := lua.NewState()
defer L.Close()
if err := L.DoString(`print("hello")`); err != nil {
    panic(err)
}
L := lua.NewState()
defer L.Close()
if err := L.DoFile("hello.lua"); err != nil {
    panic(err)
}

Refer to Lua Reference Manual and Go doc for further information.

Note that elements that are not commented in Go doc equivalent to Lua Reference Manual , except Glasm uses objects instead of Lua stack indices.

Data model

All data in a Glasm program is an LValue . LValue is an interface type that has following methods.

  • String() string
  • Type() LValueType

Objects implement an LValue interface are

Type name Go type Type() value Constants
LNilType (constants) LTNil LNil
LBool (constants) LTBool LTrue, LFalse
LNumber float64 LTNumber -
LString string LTString -
LFunction struct pointer LTFunction -
LUserData struct pointer LTUserData -
LState struct pointer LTThread -
LTable struct pointer LTTable -
LChannel chan LValue LTChannel -

You can test an object type in Go way(type assertion) or using a Type() value.

lv := L.Get(-1) // get the value at the top of the stack
if str, ok := lv.(lua.LString); ok {
    // lv is LString
    fmt.Println(string(str))
}
if lv.Type() != lua.LTString {
    panic("string required.")
}
lv := L.Get(-1) // get the value at the top of the stack
if tbl, ok := lv.(*lua.LTable); ok {
    // lv is LTable
    fmt.Println(L.ObjLen(tbl))
}

Note that LBool , LNumber , LString is not a pointer.

To test LNilType and LBool, You must use pre-defined constants.

lv := L.Get(-1) // get the value at the top of the stack

if lv == lua.LTrue { // correct
}

if bl, ok := lv.(lua.LBool); ok && bool(bl) { // wrong
}

In Lua, both nil and false make a condition false. LVIsFalse and LVAsBool implement this specification.

lv := L.Get(-1) // get the value at the top of the stack
if lua.LVIsFalse(lv) { // lv is nil or false
}

if lua.LVAsBool(lv) { // lv is neither nil nor false
}

Objects that based on go structs(LFunction. LUserData, LTable) have some public methods and fields. You can use these methods and fields for performance and debugging, but there are some limitations.

  • Metatable does not work.
  • No error handlings.
Callstack & Registry size

The size of an LState's callstack controls the maximum call depth for Lua functions within a script (Go function calls do not count).

The registry of an LState implements stack storage for calling functions (both Lua and Go functions) and also for temporary variables in expressions. Its storage requirements will increase with callstack usage and also with code complexity.

Both the registry and the callstack can be set to either a fixed size or to auto size.

When you have a large number of LStates instantiated in a process, it's worth taking the time to tune the registry and callstack options.

Registry

The registry can have an initial size, a maximum size and a step size configured on a per LState basis. This will allow the registry to grow as needed. It will not shrink again after growing.

L := lua.NewState(lua.Options{
   RegistrySize: 1024 * 20,         // this is the initial size of the registry
   RegistryMaxSize: 1024 * 80,      // this is the maximum size that the registry can grow to. If set to `0` (the default) then the registry will not auto grow
   RegistryGrowStep: 32,            // this is how much to step up the registry by each time it runs out of space. The default is `32`.
})
defer L.Close()

A registry which is too small for a given script will ultimately result in a panic. A registry which is too big will waste memory (which can be significant if many LStates are instantiated). Auto growing registries incur a small performance hit at the point they are resized but will not otherwise affect performance.

Callstack

The callstack can operate in two different modes, fixed or auto size. A fixed size callstack has the highest performance and has a fixed memory overhead. An auto sizing callstack will allocate and release callstack pages on demand which will ensure the minimum amount of memory is in use at any time. The downside is it will incur a small performance impact every time a new page of callframes is allocated. By default an LState will allocate and free callstack frames in pages of 8, so the allocation overhead is not incurred on every function call. It is very likely that the performance impact of an auto resizing callstack will be negligible for most use cases.

L := lua.NewState(lua.Options{
    CallStackSize: 120,                 // this is the maximum callstack size of this LState
    MinimizeStackMemory: true,          // Defaults to `false` if not specified. If set, the callstack will auto grow and shrink as needed up to a max of `CallStackSize`. If not set, the callstack will be fixed at `CallStackSize`.
})
defer L.Close()
Option defaults

The above examples show how to customize the callstack and registry size on a per LState basis. You can also adjust some defaults for when options are not specified by altering the values of lua.RegistrySize, lua.RegistryGrowStep and lua.CallStackSize.

An LState object that has been created by *LState#NewThread() inherits the callstack & registry size from the parent LState object.

Miscellaneous lua.NewState options
  • Options.SkipOpenLibs bool(default false)
    • By default, Glasm opens all built-in libraries when new LState is created.
    • You can skip this behaviour by setting this to true .
    • Using the various OpenXXX(L *LState) int functions you can open only those libraries that you require, for an example see below.
  • Options.IncludeGoStackTrace bool(default false)
    • By default, Glasm does not show Go stack traces when panics occur.
    • You can get Go stack traces by setting this to true .
API

Refer to Lua Reference Manual and Go doc(LState methods) for further information.

Calling Go from Lua
func Double(L *lua.LState) int {
    lv := L.ToInt(1)             /* get argument */
    L.Push(lua.LNumber(lv * 2)) /* push result */
    return 1                     /* number of results */
}

func main() {
    L := lua.NewState()
    defer L.Close()
    L.SetGlobal("double", L.NewFunction(Double)) /* Original lua_setglobal uses stack... */
}
print(double(20)) -- > "40"

Any function registered with Glasm is a lua.LGFunction, defined in value.go

type LGFunction func(*LState) int

Working with coroutines.

co, _ := L.NewThread() /* create a new thread */
fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */
for {
    st, err, values := L.Resume(co, fn)
    if st == lua.ResumeError {
        fmt.Println("yield break(error)")
        fmt.Println(err.Error())
        break
    }

    for i, lv := range values {
        fmt.Printf("%v : %v\n", i, lv)
    }

    if st == lua.ResumeOK {
        fmt.Println("yield break(ok)")
        break
    }
}
Opening a subset of builtin modules

The following demonstrates how to open a subset of the built-in modules in Lua, say for example to avoid enabling modules with access to local files or system calls.

main.go

func main() {
    L := lua.NewState(lua.Options{SkipOpenLibs: true})
    defer L.Close()
    for _, pair := range []struct {
        n string
        f lua.LGFunction
    }{
        {lua.LoadLibName, lua.OpenPackage}, // Must be first
        {lua.BaseLibName, lua.OpenBase},
        {lua.TabLibName, lua.OpenTable},
    } {
        if err := L.CallByParam(lua.P{
            Fn:      L.NewFunction(pair.f),
            NRet:    0,
            Protect: true,
        }, lua.LString(pair.n)); err != nil {
            panic(err)
        }
    }
    if err := L.DoFile("main.lua"); err != nil {
        panic(err)
    }
}
Creating a module by Go

mymodule.go

package mymodule

import (
    "github.com/pschlump/glasm"
)

func Loader(L *lua.LState) int {
    // register functions to the table
    mod := L.SetFuncs(L.NewTable(), exports)
    // register other stuff
    L.SetField(mod, "name", lua.LString("value"))

    // returns the module
    L.Push(mod)
    return 1
}

var exports = map[string]lua.LGFunction{
    "myfunc": myfunc,
}

func myfunc(L *lua.LState) int {
    return 0
}

mymain.go

package main

import (
    "./mymodule"
    "github.com/pschlump/glasm"
)

func main() {
    L := lua.NewState()
    defer L.Close()
    L.PreloadModule("mymodule", mymodule.Loader)
    if err := L.DoFile("main.lua"); err != nil {
        panic(err)
    }
}

main.lua

local m = require("mymodule")
m.myfunc()
print(m.name)
Calling Lua from Go
L := lua.NewState()
defer L.Close()
if err := L.DoFile("double.lua"); err != nil {
    panic(err)
}
if err := L.CallByParam(lua.P{
    Fn: L.GetGlobal("double"),
    NRet: 1,
    Protect: true,
    }, lua.LNumber(10)); err != nil {
    panic(err)
}
ret := L.Get(-1) // returned value
L.Pop(1)  // remove received value

If Protect is false, Glasm will panic instead of returning an error value.

User-Defined types

You can extend Glasm with new types written in Go. LUserData is provided for this purpose.

type Person struct {
    Name string
}

const luaPersonTypeName = "person"

// Registers my person type to given L.
func registerPersonType(L *lua.LState) {
    mt := L.NewTypeMetatable(luaPersonTypeName)
    L.SetGlobal("person", mt)
    // static attributes
    L.SetField(mt, "new", L.NewFunction(newPerson))
    // methods
    L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), personMethods))
}

// Constructor
func newPerson(L *lua.LState) int {
    person := &Person{L.CheckString(1)}
    ud := L.NewUserData()
    ud.Value = person
    L.SetMetatable(ud, L.GetTypeMetatable(luaPersonTypeName))
    L.Push(ud)
    return 1
}

// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkPerson(L *lua.LState) *Person {
    ud := L.CheckUserData(1)
    if v, ok := ud.Value.(*Person); ok {
        return v
    }
    L.ArgError(1, "person expected")
    return nil
}

var personMethods = map[string]lua.LGFunction{
    "name": personGetSetName,
}

// Getter and setter for the Person#Name
func personGetSetName(L *lua.LState) int {
    p := checkPerson(L)
    if L.GetTop() == 2 {
        p.Name = L.CheckString(2)
        return 0
    }
    L.Push(lua.LString(p.Name))
    return 1
}

func main() {
    L := lua.NewState()
    defer L.Close()
    registerPersonType(L)
    if err := L.DoString(`
        p = person.new("Steeve")
        print(p:name()) -- "Steeve"
        p:name("Alice")
        print(p:name()) -- "Alice"
    `); err != nil {
        panic(err)
    }
}
Terminating a running LState

Glasm supports the Go Concurrency Patterns: Context .

L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// set the context to our LState
L.SetContext(ctx)
err := L.DoString(`
  local clock = os.clock
  function sleep(n)  -- seconds
    local t0 = clock()
    while clock() - t0 <= n do end
  end
  sleep(3)
`)
// err.Error() contains "context deadline exceeded"

With coroutines

L := lua.NewState()
defer L.Close()
ctx, cancel := context.WithCancel(context.Background())
L.SetContext(ctx)
defer cancel()
L.DoString(`
    function coro()
	  local i = 0
	  while true do
	    coroutine.yield(i)
		i = i+1
	  end
	  return i
    end
`)
co, cocancel := L.NewThread()
defer cocancel()
fn := L.GetGlobal("coro").(*LFunction)

_, err, values := L.Resume(co, fn) // err is nil

cancel() // cancel the parent context

_, err, values = L.Resume(co, fn) // err is NOT nil : child context was canceled

Note that using a context causes performance degradation.

time ./glua-with-context.exe fib.lua
9227465
0.01s user 0.11s system 1% cpu 7.505 total

time ./glua-without-context.exe fib.lua
9227465
0.01s user 0.01s system 0% cpu 5.306 total
Sharing Lua byte code between LStates

Calling DoFile will load a Lua script, compile it to byte code and run the byte code in a LState.

If you have multiple LStates which are all required to run the same script, you can share the byte code between them, which will save on memory. Sharing byte code is safe as it is read only and cannot be altered by lua scripts.

// CompileLua reads the passed lua file from disk and compiles it.
func CompileLua(filePath string) (*lua.FunctionProto, error) {
    file, err := os.Open(filePath)
    defer file.Close()
    if err != nil {
        return nil, err
    }
    reader := bufio.NewReader(file)
    chunk, err := parse.Parse(reader, filePath)
    if err != nil {
        return nil, err
    }
    proto, err := lua.Compile(chunk, filePath)
    if err != nil {
        return nil, err
    }
    return proto, nil
}

// DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
// to calling DoFile on the LState with the original source file.
func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {
    lfunc := L.NewFunctionFromProto(proto)
    L.Push(lfunc)
    return L.PCall(0, lua.MultRet, nil)
}

// Example shows how to share the compiled byte code from a lua script between multiple VMs.
func Example() {
    codeToShare, err := CompileLua("mylua.lua")
    if err != nil {
        panic(err)
    }
    a := lua.NewState()
    b := lua.NewState()
    c := lua.NewState()
    DoCompiledFile(a, codeToShare)
    DoCompiledFile(b, codeToShare)
    DoCompiledFile(c, codeToShare)
}
Goroutines

The LState is not goroutine-safe. It is recommended to use one LState per goroutine and communicate between goroutines by using channels.

Channels are represented by channel objects in Glasm. And a channel table provides functions for performing channel operations.

Some objects can not be sent over channels due to having non-goroutine-safe objects inside itself.

  • a thread(state)
  • a function
  • an userdata
  • a table with a metatable

You must not send these objects from Go APIs to channels.

func receiver(ch, quit chan lua.LValue) {
    L := lua.NewState()
    defer L.Close()
    L.SetGlobal("ch", lua.LChannel(ch))
    L.SetGlobal("quit", lua.LChannel(quit))
    if err := L.DoString(`
    local exit = false
    while not exit do
      channel.select(
        {"|<-", ch, function(ok, v)
          if not ok then
            print("channel closed")
            exit = true
          else
            print("received:", v)
          end
        end},
        {"|<-", quit, function(ok, v)
            print("quit")
            exit = true
        end}
      )
    end
  `); err != nil {
        panic(err)
    }
}

func sender(ch, quit chan lua.LValue) {
    L := lua.NewState()
    defer L.Close()
    L.SetGlobal("ch", lua.LChannel(ch))
    L.SetGlobal("quit", lua.LChannel(quit))
    if err := L.DoString(`
    ch:send("1")
    ch:send("2")
  `); err != nil {
        panic(err)
    }
    ch <- lua.LString("3")
    quit <- lua.LTrue
}

func main() {
    ch := make(chan lua.LValue)
    quit := make(chan lua.LValue)
    go receiver(ch, quit)
    go sender(ch, quit)
    time.Sleep(3 * time.Second)
}
Go API

ToChannel, CheckChannel, OptChannel are available.

Refer to Go doc(LState methods) for further information.

Lua API
  • channel.make([buf:int]) -> ch:channel

    • Create new channel that has a buffer size of buf. By default, buf is 0.
  • channel.select(case:table [, case:table, case:table ...]) -> {index:int, recv:any, ok}

    • Same as the select statement in Go. It returns the index of the chosen case and, if that case was a receive operation, the value received and a boolean indicating whether the channel has been closed.
    • case is a table that outlined below.
      • receiving: {"|<-", ch:channel [, handler:func(ok, data:any)]}
      • sending: {"<-|", ch:channel, data:any [, handler:func(data:any)]}
      • default: {"default" [, handler:func()]}

channel.select examples:

local idx, recv, ok = channel.select(
  {"|<-", ch1},
  {"|<-", ch2}
)
if not ok then
    print("closed")
elseif idx == 1 then -- received from ch1
    print(recv)
elseif idx == 2 then -- received from ch2
    print(recv)
end
channel.select(
  {"|<-", ch1, function(ok, data)
    print(ok, data)
  end},
  {"<-|", ch2, "value", function(data)
    print(data)
  end},
  {"default", function()
    print("default action")
  end}
)
  • channel:send(data:any)
    • Send data over the channel.
  • channel:receive() -> ok:bool, data:any
    • Receive some data over the channel.
  • channel:close()
    • Close the channel.
The LState pool pattern

To create per-thread LState instances, You can use the sync.Pool like mechanism.

type lStatePool struct {
    m     sync.Mutex
    saved []*lua.LState
}

func (pl *lStatePool) Get() *lua.LState {
    pl.m.Lock()
    defer pl.m.Unlock()
    n := len(pl.saved)
    if n == 0 {
        return pl.New()
    }
    x := pl.saved[n-1]
    pl.saved = pl.saved[0 : n-1]
    return x
}

func (pl *lStatePool) New() *lua.LState {
    L := lua.NewState()
    // setting the L up here.
    // load scripts, set global variables, share channels, etc...
    return L
}

func (pl *lStatePool) Put(L *lua.LState) {
    pl.m.Lock()
    defer pl.m.Unlock()
    pl.saved = append(pl.saved, L)
}

func (pl *lStatePool) Shutdown() {
    for _, L := range pl.saved {
        L.Close()
    }
}

// Global LState pool
var luaPool = &lStatePool{
    saved: make([]*lua.LState, 0, 4),
}

Now, you can get per-thread LState objects from the luaPool .

func MyWorker() {
   L := luaPool.Get()
   defer luaPool.Put(L)
   /* your code here */
}

func main() {
    defer luaPool.Shutdown()
    go MyWorker()
    go MyWorker()
    /* etc... */
}

Differences between Lua and Glasm

Inherited from the upstream 5.1-era README and partially superseded by the 5.5 upgrade — e.g. string.dump and debug hooks are implemented in Glasm (see docs/); cmd/glua remains the standalone interpreter.

Goroutines
  • Glasm supports channel operations.
    • Glasm has a type named channel.
    • The channel table provides functions for performing channel operations.
Unsupported functions
  • os.setlocale
  • lua_Debug.namewhat
  • package.loadlib
Miscellaneous notes
  • collectgarbage does not take any arguments and runs the garbage collector for the entire Go program.
  • file:setvbuf does not support a line buffering.
  • Daylight saving time is not supported.
  • Glasm has a function to set an environment variable : os.setenv(name, value)
  • Glasm support goto and ::label:: statement in Lua5.2.
    • goto is a keyword and not a valid variable name.

Standalone interpreter

Lua has an interpreter called lua . Glasm has an interpreter called glua .

go install github.com/pschlump/glasm/cmd/glua@latest

glua has same options as lua .

How to Contribute

See Guidelines for contributors .

Libraries for the gopher-lua ecosystem

These target gopher-lua's 5.1-era API; the Glasm interpreter retains that API surface where the 5.5 upgrade did not change it.

License

MIT

Authors

Yusuke Inuzuka (original Gopher-Lua)

Philip Schlump

Documentation

Overview

glasm: a Lua 5.5 VM, compiler, and Lua→wasm backend in Go — a fork of yuin/gopher-lua (upstream copyright and license in LICENSE).

Index

Constants

View Source
const (
	VarArgHidden uint8 = 1 // PF_VAHID — hidden extra args (stock default)
	VarArgTable  uint8 = 2 // PF_VATAB — materialized vararg table
)

Vararg model flags (U5b, stock lobject.h PF_*): every vararg function starts hidden-args (VARARGPREP copies func+params above the extras and shifts the frame); a whole-value use / index write / upvalue capture of the named parameter promotes the function to the table model at finish time (needvatab — VARARGPREP then builds {1..n, n=n} into the param slot, GETVARG rewrites to GETTABLE, VARARG gets k=1). NumParameters counts the fixed params only — the vararg parameter itself occupies the register AT NumParameters, reserved by parlist like stock's `adjustlocalvars(ls, 1)` + `luaK_reserveregs`.

View Source
const (
	// BaseLibName is here for consistency; the base functions have no namespace/library.
	BaseLibName = ""
	// LoadLibName is here for consistency; the loading system has no namespace/library.
	LoadLibName = "package"
	// TabLibName is the name of the table Library.
	TabLibName = "table"
	// IoLibName is the name of the io Library.
	IoLibName = "io"
	// OsLibName is the name of the os Library.
	OsLibName = "os"
	// StringLibName is the name of the string Library.
	StringLibName = "string"
	// MathLibName is the name of the math Library.
	MathLibName = "math"
	// DebugLibName is the name of the debug Library.
	DebugLibName = "debug"
	// ChannelLibName is the name of the channel Library.
	ChannelLibName = "channel"
	// CoroutineLibName is the name of the coroutine Library.
	CoroutineLibName = "coroutine"
	// UTF8LibName is the name of the utf8 Library (U3e).
	UTF8LibName = "utf8"
)
View Source
const (
	OP_MOVE       int = iota /*      A B     R[A] := R[B]                                        */
	OP_LOADI                 /*      A sBx   R[A] := sBx                                        */
	OP_LOADF                 /*      A sBx   R[A] := (float)sBx                                 */
	OP_LOADK                 /*      A Bx    R[A] := K[Bx]                                      */
	OP_LOADKX                /*      A       R[A] := K[extra arg]                               */
	OP_LOADFALSE             /*      A       R[A] := false                                      */
	OP_LFALSESKIP            /*      A       R[A] := false; pc++                                */
	OP_LOADTRUE              /*      A       R[A] := true                                       */
	OP_LOADNIL               /*      A B     R[A], R[A+1], ..., R[A+B] := nil                   */
	OP_GETUPVAL              /*      A B     R[A] := UpValue[B]                                 */
	OP_SETUPVAL              /*      A B     UpValue[B] := R[A]                                 */

	OP_GETTABUP /*      A B C   R[A] := UpValue[B][K[C]:string]                       */
	OP_GETTABLE /*      A B C   R[A] := R[B][R[C]]                                     */
	OP_GETI     /*      A B C   R[A] := R[B][C]                                       */
	OP_GETFIELD /*      A B C   R[A] := R[B][K[C]:string]                             */

	OP_SETTABUP /*      A B C   UpValue[A][K[B]:string] := RK(C)                      */
	OP_SETTABLE /*      A B C   R[A][R[B]] := RK(C)                                   */
	OP_SETI     /*      A B C   R[A][B] := RK(C)                                      */
	OP_SETFIELD /*      A B C   R[A][K[B]:string] := RK(C)                            */

	OP_NEWTABLE /*      A vB vC k  R[A] := {}                                          */

	OP_SELF /*      A B C   R[A+1] := R[B]; R[A] := R[B][K[C]:string]                  */

	OP_ADDI /*      A B sC   R[A] := R[B] + sC                                         */

	OP_ADDK  /*      A B C   R[A] := R[B] + K[C]:number                                */
	OP_SUBK  /*      A B C   R[A] := R[B] - K[C]:number                                */
	OP_MULK  /*      A B C   R[A] := R[B] * K[C]:number                                */
	OP_MODK  /*      A B C   R[A] := R[B] % K[C]:number                                */
	OP_POWK  /*      A B C   R[A] := R[B] ^ K[C]:number                                */
	OP_DIVK  /*      A B C   R[A] := R[B] / K[C]:number                                */
	OP_IDIVK /*      A B C   R[A] := R[B] // K[C]:number                               */

	OP_BANDK /*      A B C   R[A] := R[B] & K[C]:integer                               */
	OP_BORK  /*      A B C   R[A] := R[B] | K[C]:integer                               */
	OP_BXORK /*      A B C   R[A] := R[B] ~ K[C]:integer                               */

	OP_SHLI /*      A B sC   R[A] := sC << R[B]                                        */
	OP_SHRI /*      A B sC   R[A] := R[B] >> sC                                        */

	OP_ADD  /*      A B C   R[A] := R[B] + R[C]                                        */
	OP_SUB  /*      A B C   R[A] := R[B] - R[C]                                        */
	OP_MUL  /*      A B C   R[A] := R[B] * R[C]                                        */
	OP_MOD  /*      A B C   R[A] := R[B] % R[C]                                        */
	OP_POW  /*      A B C   R[A] := R[B] ^ R[C]                                        */
	OP_DIV  /*      A B C   R[A] := R[B] / R[C]                                        */
	OP_IDIV /*      A B C   R[A] := R[B] // R[C]                                       */

	OP_BAND /*      A B C   R[A] := R[B] & R[C]                                       */
	OP_BOR  /*      A B C   R[A] := R[B] | R[C]                                       */
	OP_BXOR /*      A B C   R[A] := R[B] ~ R[C]                                       */
	OP_SHL  /*      A B C   R[A] := R[B] << R[C]                                      */
	OP_SHR  /*      A B C   R[A] := R[B] >> R[C]                                      */

	OP_MMBIN  /*      A B C   call C metamethod over R[A] and R[B]                     */
	OP_MMBINI /*      A sB C k  call C metamethod over R[A] and sB                     */
	OP_MMBINK /*      A B C k   call C metamethod over R[A] and K[B]                   */

	OP_UNM  /*      A B     R[A] := -R[B]                                             */
	OP_BNOT /*      A B     R[A] := ~R[B]                                             */
	OP_NOT  /*      A B     R[A] := not R[B]                                          */
	OP_LEN  /*      A B     R[A] := #R[B]                                             */

	OP_CONCAT /*      A B     R[A] := R[A].. ... ..R[A + B - 1]                        */

	OP_CLOSE /*      A       close all upvalues >= R[A]                               */
	OP_TBC   /*      A       mark variable A "to be closed"                           */
	OP_JMP   /*      sJ      pc += sJ                                                */
	OP_EQ    /*      A B k   if ((R[A] == R[B]) ~= k) then pc++                       */
	OP_LT    /*      A B k   if ((R[A] <  R[B]) ~= k) then pc++                       */
	OP_LE    /*      A B k   if ((R[A] <= R[B]) ~= k) then pc++                       */

	OP_EQK /*      A B k   if ((R[A] == K[B]) ~= k) then pc++                          */
	OP_EQI /*      A sB k  if ((R[A] == sB) ~= k) then pc++                            */
	OP_LTI /*      A sB k  if ((R[A] < sB) ~= k) then pc++                             */
	OP_LEI /*      A sB k  if ((R[A] <= sB) ~= k) then pc++                            */
	OP_GTI /*      A sB k  if ((R[A] > sB) ~= k) then pc++                             */
	OP_GEI /*      A sB k  if ((R[A] >= sB) ~= k) then pc++                            */

	OP_TEST    /*      A k     if (not R[A] == k) then pc++                            */
	OP_TESTSET /*      A B k   if (not R[B] == k) then pc++ else R[A] := R[B]          */

	OP_CALL     /*      A B C   R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1])     */
	OP_TAILCALL /*      A B C k  return R[A](R[A+1], ... ,R[A+B-1])                    */

	OP_RETURN  /*      A B C k  return R[A], ... ,R[A+B-2]                             */
	OP_RETURN0 /*              return                                                 */
	OP_RETURN1 /*      A       return R[A]                                            */

	OP_FORLOOP /*      A Bx    update counters; if loop continues then pc-=Bx         */
	OP_FORPREP /*      A Bx    <check values and prepare counters>;
	        if not to run then pc+=Bx+1                                 */

	OP_TFORPREP /*      A Bx    create upvalue for R[A + 3]; pc+=Bx                   */
	OP_TFORCALL /*      A C     R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2])          */
	OP_TFORLOOP /*      A Bx    if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx }       */

	OP_SETLIST /*      A vB vC k  R[A][vC+i] := R[A+i], 1 <= i <= vB                  */

	OP_CLOSURE /*      A Bx    R[A] := closure(KPROTO[Bx])                             */

	OP_VARARG /*      A B C k R[A], ..., R[A+C-2] = varargs                           */

	OP_GETVARG /*      A B C   R[A] := R[B][R[C]], R[B] is vararg parameter           */

	OP_ERRNNIL /*      A Bx    raise error if R[A] ~= nil (K[Bx - 1] is global name)  */

	OP_VARARGPREP /*              (adjust varargs)                                    */

	OP_EXTRAARG /*      Ax      extra (larger) argument for previous opcode           */
)
View Source
const EnvironIndex = -10001
View Source
const FramesPerSegment = 8

FramesPerSegment should be a power of 2 constant for performance reasons. It will allow the go compiler to change the divs and mods into bitshifts. Max is 256 due to current use of uint8 to count how many frames in a segment are used.

View Source
const GlobalsIndex = -10002
View Source
const LNumberBit = 64
View Source
const LNumberScanFormat = "%f"
View Source
const LuaVersion = "Lua 5.5"

U5f: _VERSION re-pinned to the 5.5 core the engine now implements (the 5.2→5.5 program, U1..U5) — a script feature-detecting on it must see the integer/float split, _ENV, and the 5.5 libraries. _GLASM_VERSION keeps the implementation identity (renamed from the gopher-lua-era _GOPHER_LUA_VERSION at the D12 release).

View Source
const MultRet = -1
View Source
const PackageAuthors = "Yusuke Inuzuka"
View Source
const PackageName = "glasm"
View Source
const RegistryIndex = -10000
View Source
const VarArgAny = VarArgHidden | VarArgTable

VarArgAny is the isvararg(p) test mask (either model marks the proto).

Variables

View Source
var CallStackSize = 256
View Source
var FieldsPerFlush = 50
View Source
var LFalse = LBool(false)
View Source
var LNil = LValue(&LNilType{})
View Source
var LTrue = LBool(true)
View Source
var LuaDirSep string
View Source
var LuaExecDir = "!"
View Source
var LuaIgMark = "-"
View Source
var LuaLDir string
View Source
var LuaOS string
View Source
var LuaPath = "LUA_PATH"
View Source
var LuaPathDefault string
View Source
var LuaPathMark = "?"
View Source
var LuaPathSep = ";"
View Source
var MaxArrayIndex = 67108864
View Source
var MaxTableGetLoop = 100
View Source
var PackageCopyRight = PackageName + " " + PackageVersion + " (fork of GopherLua) Copyright (C) 2015 -2017 " + PackageAuthors

PackageCopyRight must stay a var: it is computed from PackageVersion at package init, after the linker has applied any -X override.

View Source
var PackageVersion = "1.3.3"

PackageVersion is overridden at link time by the Makefile (make build) via -ldflags "-X github.com/pschlump/glasm.PackageVersion=<version>"; the literal below is the fallback for plain go builds and tests.

View Source
var RegistryGrowStep = 32
View Source
var RegistrySize = 256 * 20

Functions

func FormatVarInfo

func FormatVarInfo(kind, name string) string

FormatVarInfo is formatVarInfo exported so the backend renders the identical " (%s '%s')" strings the interp raises.

func FuncNameFromCode

func FuncNameFromCode(p *FunctionProto, pc int) (kind, name string)

FuncNameFromCode is funcnamefromcode exported for the backend's call-error hints (the callee name of the instruction at pc).

func GetObjName

func GetObjName(p *FunctionProto, lastpc, reg int) (kind, name string)

GetObjName is getobjname exported for the wasm backend's varinfo hints (U5e, row 71): the backend precomputes the interp's debug-name answer at compile time — the raise site's lastpc follows the cf.Pc-1 convention (the instruction that raised; findSetReg itself steps back over an MMBIN* slot at that pc).

func LVAsBool

func LVAsBool(v LValue) bool

LVIsFalse returns false if a given LValue is a nil or false otherwise true.

func LVAsString

func LVAsString(v LValue) string

LVAsString returns string representation of a given LValue if the LValue is a string or number, otherwise an empty string.

func LVCanConvToString

func LVCanConvToString(v LValue) bool

LVCanConvToString returns true if a given LValue is a string or number otherwise false.

func LVIsFalse

func LVIsFalse(v LValue) bool

LVIsFalse returns true if a given LValue is a nil or false otherwise false.

func LocalNameAt

func LocalNameAt(p *FunctionProto, localnum, pc int) (string, bool)

LocalNameAt is localNameAt exported for the wasm backend's OP_TBC lowering (U4f): the non-closable raise names the variable, and the backend precomputes the interp's DbgLocals answer at compile time.

func OpCodeMax

func OpCodeMax() int

func OpCreateABCk

func OpCreateABCk(op, a, b, c, k int) uint32

func OpCreateABx

func OpCreateABx(op, a, bx int) uint32

func OpCreateAsBx

func OpCreateAsBx(op, a, sbx int) uint32

func OpCreateAx

func OpCreateAx(op, ax int) uint32

func OpCreateSJ

func OpCreateSJ(op, sj int) uint32

func OpCreatevABCk

func OpCreatevABCk(op, a, b, c, k int) uint32

func OpGetArgA

func OpGetArgA(inst uint32) int

func OpGetArgAx

func OpGetArgAx(inst uint32) int

func OpGetArgB

func OpGetArgB(inst uint32) int

func OpGetArgBx

func OpGetArgBx(inst uint32) int

func OpGetArgC

func OpGetArgC(inst uint32) int

func OpGetArgSbx

func OpGetArgSbx(inst uint32) int

func OpGetArgk

func OpGetArgk(inst uint32) int

func OpGetArgsJ

func OpGetArgsJ(inst uint32) int

func OpGetArgvB

func OpGetArgvB(inst uint32) int

func OpGetArgvC

func OpGetArgvC(inst uint32) int

func OpGetOpCode

func OpGetOpCode(inst uint32) int

func OpenBase

func OpenBase(L *LState) int

func OpenChannel

func OpenChannel(L *LState) int

func OpenCoroutine

func OpenCoroutine(L *LState) int

func OpenDebug

func OpenDebug(L *LState) int

func OpenIo

func OpenIo(L *LState) int

func OpenMath

func OpenMath(L *LState) int

func OpenOs

func OpenOs(L *LState) int

func OpenPackage

func OpenPackage(L *LState) int

func OpenString

func OpenString(L *LState) int

func OpenTable

func OpenTable(L *LState) int

func OpenUTF8

func OpenUTF8(L *LState) int

func UpvalName

func UpvalName(p *FunctionProto, uv int) string

UpvalName is upvalName exported for the backend's GETTABUP/SETTABUP object hints (the table operand is an upvalue cell — varinfo's getupvalname arm).

func UpvalueIndex

func UpvalueIndex(i int) int

Types

type ApiError

type ApiError struct {
	Type       ApiErrorType
	Object     LValue
	StackTrace string
	// Underlying error. This attribute is set only if the Type is ApiErrorFile or ApiErrorSyntax
	Cause error
}

func (*ApiError) Error

func (e *ApiError) Error() string

type ApiErrorType

type ApiErrorType int
const (
	ApiErrorSyntax ApiErrorType = iota
	ApiErrorFile
	ApiErrorRun
	ApiErrorError
	ApiErrorPanic
)

type CompileError

type CompileError struct {
	Line    int
	Message string
	// contains filtered or unexported fields
}

func (*CompileError) Error

func (e *CompileError) Error() string

type DbgCall

type DbgCall struct {
	Name string
	Pc   int
	// Method records a colon-syntax call site (`obj:name(...)`): stock's
	// luaL_argerror does not count the implicit self in the argument
	// number and has a dedicated "bad self" error for arg 1 (U3b).
	Method bool
}

type DbgLocalInfo

type DbgLocalInfo struct {
	Name    string
	StartPc int
	EndPc   int
}

type Debug

type Debug struct {
	Name            string
	NameWhat        string
	What            string
	Source          string
	CurrentLine     int
	NUpvalues       int
	LineDefined     int
	LastLineDefined int
	// U5c (stock lua_Debug): 'u' adds nparams/isvararg, 't' adds
	// istailcall/extraargs — extraargs is the __call-chain count (the
	// ci's MAX_CCMT bits, ldebug.c:359), NOT the vararg count: a plain
	// vararg call answers 0, a chain-fed call answers its hop count.
	NParams    int
	IsVarArg   bool
	IsTailCall bool
	ExtraArgs  int
	// 'r' ftransfer/ntransfer (no hooks in the fork: always 0) and 'L'
	// activelines (nil for C functions, collectvalidlines' table).
	FTransfer   int
	NTransfer   int
	ActiveLines *LTable
	// contains filtered or unexported fields
}

type FunctionProto

type FunctionProto struct {
	SourceName string
	// SourceIsNull marks stock's NULL source (U5f): a stripped binary
	// chunk carries no source string, and getinfo/where render "?"
	// shapes for it. A TEXT load with an explicit empty chunkname
	// (load(s, "")) is a REAL empty source — `[string ""]` (db.lua:73)
	// — so "" alone must not take the NULL path. Only the undump sets
	// the flag (dumping an empty TString is size 0, identical to NULL,
	// so every undumped empty source IS stock's NULL).
	SourceIsNull       bool
	LineDefined        int
	LastLineDefined    int
	NumUpvalues        uint8
	NumParameters      uint8
	IsVarArg           uint8
	NumUsedRegisters   uint8
	Code               []uint32
	Constants          []LValue
	FunctionPrototypes []*FunctionProto
	Upvalues           []Upvaldesc

	// EnvUpvalueIndex: the upvalue slot holding _ENV, the table global
	// accesses resolve through (5.2 scheme, upgrade plan D3). -1 when the
	// function references no globals. Used by the loader to seed the
	// main chunk's _ENV cell (GETTABUP/SETTABUP address the cell by the
	// B operand directly).
	EnvUpvalueIndex int8

	DbgSourcePositions []int
	DbgLocals          []*DbgLocalInfo
	DbgCalls           []DbgCall
	DbgUpvalues        []string
	// contains filtered or unexported fields
}

func Compile

func Compile(chunk []ast.Stmt, name string) (proto *FunctionProto, err error)

func (*FunctionProto) String

func (fp *FunctionProto) String() string

func (*FunctionProto) StringConstants

func (fp *FunctionProto) StringConstants() []string

StringConstants exposes the string-constants table used by the *KS opcodes and SETGLOBAL (backend access).

type Global

type Global struct {
	MainThread    *LState
	CurrentThread *LState
	Registry      *LTable
	Global        *LTable
	// contains filtered or unexported fields
}

type LBool

type LBool bool

func (LBool) String

func (bl LBool) String() string

func (LBool) Type

func (bl LBool) Type() LValueType

type LChannel

type LChannel chan LValue

func (LChannel) String

func (ch LChannel) String() string

func (LChannel) Type

func (ch LChannel) Type() LValueType

type LFunction

type LFunction struct {
	IsG       bool
	Env       *LTable
	Proto     *FunctionProto
	GFunction LGFunction
	Upvalues  []*Upvalue
}

func (*LFunction) LocalName

func (fn *LFunction) LocalName(regno, pc int) (string, bool)

func (*LFunction) String

func (fn *LFunction) String() string

func (*LFunction) Type

func (fn *LFunction) Type() LValueType

type LGFunction

type LGFunction func(*LState) int

type LInteger

type LInteger int64

LInteger is the 64-bit integer subtype of "number" (5.3+, U2/D1). Integer-ness is observable: tostring(2) == "2" while tostring(2.0) == "2.0" (once float printing re-pins at U5), table keys normalize t[2.0] to the integer 2, and //, shifts and integer for-loops require it. Arithmetic on mixed subtypes promotes to float.

func (LInteger) Format

func (nm LInteger) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LInteger) String

func (nm LInteger) String() string

func (LInteger) Type

func (nm LInteger) Type() LValueType

type LNilType

type LNilType struct{}

func (*LNilType) String

func (nl *LNilType) String() string

func (*LNilType) Type

func (nl *LNilType) Type() LValueType

type LNumber

type LNumber float64

func LVAsNumber

func LVAsNumber(v LValue) LNumber

LVAsNumber tries to convert a given LValue to a number (float view). The integer subtype converts to its float value; use parseNumber when the syntactic subtype of a string must be preserved.

func (LNumber) Format

func (nm LNumber) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LNumber) String

func (nm LNumber) String() string

String is the engine-wide float spelling: the stock tostringbuffFloat (%.15g, re-read, %.17g, ".0" on integer-looking results — U3i, the D7 re-pin pulled forward because math.lua's numeral round-trip asserts tostring(1.0) == "1.0"). The old Go dialect printed "1" there.

func (LNumber) Type

func (nm LNumber) Type() LValueType

type LState

type LState struct {
	G       *Global
	Parent  *LState
	Env     *LTable
	Panic   func(*LState)
	Dead    bool
	Options Options
	// contains filtered or unexported fields
}

func NewState

func NewState(opts ...Options) *LState

func (*LState) ArgError

func (ls *LState) ArgError(n int, message string)

func (*LState) Call

func (ls *LState) Call(nargs, nret int)

func (*LState) CallByParam

func (ls *LState) CallByParam(cp P, args ...LValue) error

func (*LState) CallMeta

func (ls *LState) CallMeta(obj LValue, event string) LValue

func (*LState) CheckAny

func (ls *LState) CheckAny(n int) LValue

func (*LState) CheckBool

func (ls *LState) CheckBool(n int) bool

func (*LState) CheckChannel

func (ls *LState) CheckChannel(n int) chan LValue

Checks whether the given index is an LChannel and returns this channel.

func (*LState) CheckFunction

func (ls *LState) CheckFunction(n int) *LFunction

func (*LState) CheckInt

func (ls *LState) CheckInt(n int) int

func (*LState) CheckInt64

func (ls *LState) CheckInt64(n int) int64

func (*LState) CheckNumber

func (ls *LState) CheckNumber(n int) LNumber

func (*LState) CheckOption

func (ls *LState) CheckOption(n int, options []string) int

func (*LState) CheckString

func (ls *LState) CheckString(n int) string

func (*LState) CheckTable

func (ls *LState) CheckTable(n int) *LTable

func (*LState) CheckThread

func (ls *LState) CheckThread(n int) *LState

func (*LState) CheckType

func (ls *LState) CheckType(n int, typ LValueType)

func (*LState) CheckTypes

func (ls *LState) CheckTypes(n int, typs ...LValueType)

func (*LState) CheckUserData

func (ls *LState) CheckUserData(n int) *LUserData

func (*LState) Close

func (ls *LState) Close()

func (*LState) Concat

func (ls *LState) Concat(values ...LValue) string

func (*LState) Context

func (ls *LState) Context() context.Context

Context returns the LState's context. To change the context, use WithContext.

func (*LState) CreateTable

func (ls *LState) CreateTable(acap, hcap int) *LTable

func (*LState) DoFile

func (ls *LState) DoFile(path string) error

func (*LState) DoString

func (ls *LState) DoString(source string) error

func (*LState) Equal

func (ls *LState) Equal(lhs, rhs LValue) bool

func (*LState) Error

func (ls *LState) Error(lv LValue, level int)

This function is equivalent to lua_error( http://www.lua.org/manual/5.1/manual.html#lua_error ).

func (*LState) FindTable

func (ls *LState) FindTable(obj *LTable, n string, size int) LValue

func (*LState) ForEach

func (ls *LState) ForEach(tb *LTable, cb func(LValue, LValue))

func (*LState) GPCall

func (ls *LState) GPCall(fn LGFunction, data LValue) error

func (*LState) Get

func (ls *LState) Get(idx int) LValue

func (*LState) GetFEnv

func (ls *LState) GetFEnv(obj LValue) LValue

func (*LState) GetField

func (ls *LState) GetField(obj LValue, skey string) LValue

func (*LState) GetGlobal

func (ls *LState) GetGlobal(name string) LValue

func (*LState) GetInfo

func (ls *LState) GetInfo(what string, dbg *Debug, fn LValue) (LValue, error)

GetInfo is lua_getinfo over the fork's Debug struct (U5c: stock's option-filtered fields). A leading '>' queries the given function instead of dbg.frame's — stock sets ci=NULL there, so the frame-relative options answer their no-frame defaults ('l' -1, 'n' empty, 't' false/0) while 'S'/'u' still read the function. Returns the 'f' payload (nil without 'f'); the activelines table lands in Debug.ActiveLines ('L'). An option outside "flnSrtuL" returns the invalid-option error the debug library converts to its argerror (stock lua_getinfo answers 0 and db_getinfo raises).

func (*LState) GetLocal

func (ls *LState) GetLocal(dbg *Debug, no int) (string, LValue)

func (*LState) GetMetaField

func (ls *LState) GetMetaField(obj LValue, event string) LValue

func (*LState) GetMetatable

func (ls *LState) GetMetatable(obj LValue) LValue

func (*LState) GetStack

func (ls *LState) GetStack(level int) (*Debug, bool)

GetStack is lua_getstack. Cross-thread queries over a SUSPENDED thread (U5c) run over stock's continuation model: 5.5 keeps the yielded C function's CallInfo alive while the thread is suspended, so level 0 is that C frame and the thread's own frames start at level 1. The fork's Go stack unwinds the yield's G frame instead — the running and normal-at-resume threads keep their boundary frame as a REAL frame, only the suspended ones need the shift. Level 0 hands back a synthetic G frame (C-shaped for 'S'/'u'/'l'/'t'; 'f' on it is not stock's yield function — unpinned corner).

func (*LState) GetTable

func (ls *LState) GetTable(obj LValue, key LValue) LValue

func (*LState) GetTop

func (ls *LState) GetTop() int

func (*LState) GetTypeMetatable

func (ls *LState) GetTypeMetatable(typ string) LValue

func (*LState) GetUpvalue

func (ls *LState) GetUpvalue(fn *LFunction, no int) (string, LValue)

func (*LState) Insert

func (ls *LState) Insert(value LValue, index int)

func (*LState) IsClosed

func (ls *LState) IsClosed() bool

func (*LState) LessThan

func (ls *LState) LessThan(lhs, rhs LValue) bool

func (*LState) Load

func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error)

Load parses, compiles, and wraps a chunk as a callable function. The chunk's _ENV upvalue (D3) is seeded with the thread's environment.

func (*LState) LoadEnv

func (ls *LState) LoadEnv(reader io.Reader, name string, env LValue) (*LFunction, error)

LoadEnv is Load with an explicit chunk environment (5.2 load's env argument): the compiled main chunk's _ENV upvalue cell is seeded with env, so the chunk (and every closure it creates) resolves globals there. A nil env falls back to the thread environment; 5.5 accepts ANY value for env (calls.lua:415's `load(..., 123)` — only actual global accesses care that it is a table).

func (*LState) LoadFile

func (ls *LState) LoadFile(path string) (*LFunction, error)

func (*LState) LoadString

func (ls *LState) LoadString(source string) (*LFunction, error)

func (*LState) NewClosure

func (ls *LState) NewClosure(fn LGFunction, upvalues ...LValue) *LFunction

func (*LState) NewFunction

func (ls *LState) NewFunction(fn LGFunction) *LFunction

func (*LState) NewFunctionFromProto

func (ls *LState) NewFunctionFromProto(proto *FunctionProto) *LFunction

func (*LState) NewTable

func (ls *LState) NewTable() *LTable

func (*LState) NewThread

func (ls *LState) NewThread() (*LState, context.CancelFunc)

NewThread returns a new LState that shares with the original state all global objects. If the original state has context.Context, the new state has a new child context of the original state and this function returns its cancel function.

func (*LState) NewTypeMetatable

func (ls *LState) NewTypeMetatable(typ string) *LTable

func (*LState) NewUserData

func (ls *LState) NewUserData() *LUserData

func (*LState) Next

func (ls *LState) Next(tb *LTable, key LValue) (LValue, LValue)

func (*LState) ObjLen

func (ls *LState) ObjLen(v1 LValue) int

func (*LState) OpenLibs

func (ls *LState) OpenLibs()

OpenLibs loads the built-in libraries. It is equivalent to running OpenLoad, then OpenBase, then iterating over the other OpenXXX functions in any order.

func (*LState) OptBool

func (ls *LState) OptBool(n int, d bool) bool

func (*LState) OptChannel

func (ls *LState) OptChannel(n int, ch chan LValue) chan LValue

If the given index is a LChannel, returns this channel. If this argument is absent or is nil, returns ch. Otherwise, raises an error.

func (*LState) OptFunction

func (ls *LState) OptFunction(n int, d *LFunction) *LFunction

func (*LState) OptInt

func (ls *LState) OptInt(n int, d int) int

func (*LState) OptInt64

func (ls *LState) OptInt64(n int, d int64) int64

func (*LState) OptNumber

func (ls *LState) OptNumber(n int, d LNumber) LNumber

func (*LState) OptString

func (ls *LState) OptString(n int, d string) string

func (*LState) OptTable

func (ls *LState) OptTable(n int, d *LTable) *LTable

func (*LState) OptUserData

func (ls *LState) OptUserData(n int, d *LUserData) *LUserData

func (*LState) PCall

func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error)

func (*LState) Pop

func (ls *LState) Pop(n int)

func (*LState) PreloadModule

func (ls *LState) PreloadModule(name string, loader LGFunction)

Set a module loader to the package.preload table.

func (*LState) Push

func (ls *LState) Push(value LValue)

func (*LState) RaiseError

func (ls *LState) RaiseError(format string, args ...interface{})

This function is equivalent to luaL_error( http://www.lua.org/manual/5.1/manual.html#luaL_error ). U2f: the position target is the CURRENT frame when raised from Lua code (luaG_runerror semantics — the VM's own raises) and the caller when raised from a G function (luaL_error semantics) — and a G caller yields NO position (stock's luaL_where over a C frame is empty; the old skipg walk skipped straight past it).

func (*LState) RawEqual

func (ls *LState) RawEqual(lhs, rhs LValue) bool

func (*LState) RawGet

func (ls *LState) RawGet(tb *LTable, key LValue) LValue

func (*LState) RawGetInt

func (ls *LState) RawGetInt(tb *LTable, key int) LValue

func (*LState) RawSet

func (ls *LState) RawSet(tb *LTable, key LValue, value LValue)

func (*LState) RawSetInt

func (ls *LState) RawSetInt(tb *LTable, key int, value LValue)

func (*LState) Register

func (ls *LState) Register(name string, fn LGFunction)

func (*LState) RegisterModule

func (ls *LState) RegisterModule(name string, funcs map[string]LGFunction) LValue

func (*LState) Remove

func (ls *LState) Remove(index int)

func (*LState) RemoveCallerFrame

func (ls *LState) RemoveCallerFrame() *callFrame

RemoveCallerFrame removes the stack frame above the current stack frame. This is useful in tail calls. It returns the new current frame.

func (*LState) RemoveContext

func (ls *LState) RemoveContext() context.Context

RemoveContext removes the context associated with this LState and returns this context.

func (*LState) Replace

func (ls *LState) Replace(idx int, value LValue)

func (*LState) Resume

func (ls *LState) Resume(th *LState, fn *LFunction, args ...LValue) (ResumeState, error, []LValue)

func (*LState) SetContext

func (ls *LState) SetContext(ctx context.Context)

SetContext set a context ctx to this LState. The provided ctx must be non-nil.

func (*LState) SetFEnv

func (ls *LState) SetFEnv(obj LValue, env LValue)

func (*LState) SetField

func (ls *LState) SetField(obj LValue, key string, value LValue)

func (*LState) SetFuncs

func (ls *LState) SetFuncs(tb *LTable, funcs map[string]LGFunction, upvalues ...LValue) *LTable

func (*LState) SetGlobal

func (ls *LState) SetGlobal(name string, value LValue)

func (*LState) SetLocal

func (ls *LState) SetLocal(dbg *Debug, no int, lv LValue) string

func (*LState) SetMetatable

func (ls *LState) SetMetatable(obj LValue, mt LValue)

func (*LState) SetMx

func (ls *LState) SetMx(mx int)

Set maximum memory size. This function can only be called from the main thread.

func (*LState) SetTable

func (ls *LState) SetTable(obj LValue, key LValue, value LValue)

func (*LState) SetTop

func (ls *LState) SetTop(idx int)

func (*LState) SetUpvalue

func (ls *LState) SetUpvalue(fn *LFunction, no int, lv LValue) string

func (*LState) Status

func (ls *LState) Status(th *LState) string

func (*LState) String

func (ls *LState) String() string

func (*LState) ToBool

func (ls *LState) ToBool(n int) bool

func (*LState) ToChannel

func (ls *LState) ToChannel(n int) chan LValue

Converts the Lua value at the given acceptable index to the chan LValue.

func (*LState) ToFunction

func (ls *LState) ToFunction(n int) *LFunction

func (*LState) ToInt

func (ls *LState) ToInt(n int) int

func (*LState) ToInt64

func (ls *LState) ToInt64(n int) int64

func (*LState) ToNumber

func (ls *LState) ToNumber(n int) LNumber

func (*LState) ToString

func (ls *LState) ToString(n int) string

func (*LState) ToStringMeta

func (ls *LState) ToStringMeta(lv LValue) LValue

ToStringMeta returns string representation of given LValue — the luaL_tolstring port (U3h): __tostring's result must be a string (a number converts, like lua_isstring); the default rendering for tables/functions/etc. is "kind: 0x..." with kind from a string __name metafield when present.

func (*LState) ToTable

func (ls *LState) ToTable(n int) *LTable

func (*LState) ToThread

func (ls *LState) ToThread(n int) *LState

func (*LState) ToUserData

func (ls *LState) ToUserData(n int) *LUserData

func (*LState) Type

func (ls *LState) Type() LValueType

func (*LState) TypeError

func (ls *LState) TypeError(n int, typ LValueType)

TypeError is luaL_typeerror with the fork's type-tag spelling; the actual-operand side honors __name and reports "no value" when the argument is absent.

func (*LState) Where

func (ls *LState) Where(level int) string

func (*LState) XMoveTo

func (ls *LState) XMoveTo(other *LState, n int)

func (*LState) Yield

func (ls *LState) Yield(values ...LValue) int

type LString

type LString string

func (LString) Format

func (st LString) Format(f fmt.State, c rune)

fmt.Formatter interface

func (LString) String

func (st LString) String() string

func (LString) Type

func (st LString) Type() LValueType

type LTable

type LTable struct {
	Metatable LValue
	// contains filtered or unexported fields
}

func (*LTable) Append

func (tb *LTable) Append(value LValue)

Append appends a given LValue to this LTable.

func (*LTable) ForEach

func (tb *LTable) ForEach(cb func(LValue, LValue))

ForEach iterates over this table of elements, yielding each in turn to a given function.

func (*LTable) Insert

func (tb *LTable) Insert(i int, value LValue)

Insert inserts a given LValue at position `i` in this table.

func (*LTable) Len

func (tb *LTable) Len() int

Len returns length of this LTable without using __len.

func (*LTable) MaxN

func (tb *LTable) MaxN() int

MaxN returns a maximum number key that nil value does not exist before it.

func (*LTable) Next

func (tb *LTable) Next(key LValue) (LValue, LValue)

This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ). Unlike RawGet/RawSet, the incoming key is NOT normalized: stock 5.5's findindex accepts array entries only for integer keys (keyinarray is ttisinteger-only) and hashes the raw key for the hash part, so the float spelling of an integer slot is an invalid key (baseNext screens it).

func (*LTable) RawGet

func (tb *LTable) RawGet(key LValue) LValue

RawGet returns an LValue associated with a given key without __index metamethod.

func (*LTable) RawGetH

func (tb *LTable) RawGetH(key LValue) LValue

RawGet returns an LValue associated with a given key without __index metamethod.

func (*LTable) RawGetInt

func (tb *LTable) RawGetInt(key int) LValue

RawGetInt returns an LValue at position `key` without __index metamethod. An out-of-array-range key reads the hash part (stock luaH_getint), so a {[math.mininteger]=10} table answers at negative indices too.

func (*LTable) RawGetString

func (tb *LTable) RawGetString(key string) LValue

RawGetString returns an LValue associated with a given key without __index metamethod.

func (*LTable) RawSet

func (tb *LTable) RawSet(key LValue, value LValue)

RawSet sets a given LValue to a given index without the __newindex metamethod. It is recommended to use `RawSetString` or `RawSetInt` for performance if you already know the given LValue is a string or number.

func (*LTable) RawSetH

func (tb *LTable) RawSetH(key LValue, value LValue)

RawSetH sets a given LValue to a given index without the __newindex metamethod.

func (*LTable) RawSetInt

func (tb *LTable) RawSetInt(key int, value LValue)

RawSetInt sets a given LValue at a position `key` without the __newindex metamethod.

func (*LTable) RawSetString

func (tb *LTable) RawSetString(key string, value LValue)

RawSetString sets a given LValue to a given string index without the __newindex metamethod.

func (*LTable) Remove

func (tb *LTable) Remove(pos int) LValue

Remove removes from this table the element at a given position.

func (*LTable) String

func (tb *LTable) String() string

func (*LTable) Type

func (tb *LTable) Type() LValueType

type LUserData

type LUserData struct {
	Value     interface{}
	Env       *LTable
	Metatable LValue
	// UserValue is the debug.getuservalue slot (U5c, stock uvalues[0]):
	// any Lua value, nil by default — distinct from Env, the legacy 5.1
	// environment table the fork's GetFEnv reads. UserValues is the slot
	// capacity (stock newuserdatauv's nuvalue): stock's Lua-visible
	// userdata — the io files — are created with ZERO slots, so
	// debug.get/setuservalue answer nil for them (db.lua:434 pins it);
	// NewUserData leaves 0 for the same posture.
	UserValue  LValue
	UserValues int8
	// Light marks a light userdata (U5c, stock LUA_VLIGHTUSERDATA): a
	// bare pointer in Lua's clothing. It still types as "userdata"
	// (5.5's typename table maps both tags to it), but full-userdata
	// argument checks reject it naming "light userdata" (luaL_typeerror
	// special case), base setmetatable refuses it, and its metatable is
	// the per-basic-type table (debug.setmetatable territory), never a
	// per-object one. debug.upvalueid is the only creator.
	Light bool
}

func (*LUserData) String

func (ud *LUserData) String() string

func (*LUserData) Type

func (ud *LUserData) Type() LValueType

type LValue

type LValue interface {
	String() string
	Type() LValueType
}

type LValueType

type LValueType int
const (
	LTNil LValueType = iota
	LTBool
	LTNumber
	LTString
	LTFunction
	LTUserData
	LTThread
	LTTable
	LTChannel
	// LTInteger is the 5.3+ integer subtype (U2/D1). It is a distinct
	// LValueType so type switches can tell int from float, but it names
	// "number": Lua's type() must keep reporting the basic type.
	LTInteger
)

func (LValueType) String

func (vt LValueType) String() string

type Options

type Options struct {
	// Call stack size. This defaults to `lua.CallStackSize`.
	CallStackSize int
	// Data stack size. This defaults to `lua.RegistrySize`.
	RegistrySize int
	// Allow the registry to grow from the registry size specified up to a value of RegistryMaxSize. A value of 0
	// indicates no growth is permitted. The registry will not shrink again after any growth.
	RegistryMaxSize int
	// If growth is enabled, step up by an additional `RegistryGrowStep` each time to avoid having to resize too often.
	// This defaults to `lua.RegistryGrowStep`
	RegistryGrowStep int
	// Controls whether or not libraries are opened by default
	SkipOpenLibs bool
	// Tells whether a Go stacktrace should be included in a Lua stacktrace when panics occur.
	IncludeGoStackTrace bool
	// If `MinimizeStackMemory` is set, the call stack will be automatically grown or shrank up to a limit of
	// `CallStackSize` in order to minimize memory usage. This does incur a slight performance penalty.
	MinimizeStackMemory bool
}

Options is a configuration that is used to create a new LState.

type P

type P struct {
	Fn      LValue
	NRet    int
	Protect bool
	Handler *LFunction
}

type ResumeState

type ResumeState int
const (
	ResumeOK ResumeState = iota
	ResumeYield
	ResumeError
)

type Upvaldesc

type Upvaldesc struct {
	InStack bool
	Idx     uint8
	Name    string
}

Upvaldesc is a compiled upvalue-capture descriptor (U2c, stock lobject.h Upvaldesc minus the kind field — <const> attributes arrive with U5): InStack captures the parent's register Idx, otherwise the parent's upvalue Idx. These replace the fork's old pseudo-instructions after OP_CLOSURE.

type Upvalue

type Upvalue struct {
	// contains filtered or unexported fields
}

func (*Upvalue) Close

func (uv *Upvalue) Close()

func (*Upvalue) IsClosed

func (uv *Upvalue) IsClosed() bool

func (*Upvalue) SetValue

func (uv *Upvalue) SetValue(value LValue)

func (*Upvalue) UpvalueID

func (uv *Upvalue) UpvalueID() *LUserData

UpvalueID returns the identity object for this upvalue cell — the same cell answers the same light userdata, different cells different ones (stock db_upvalueid's semantics over the Upvalue pointer).

func (*Upvalue) Value

func (uv *Upvalue) Value() LValue

Directories

Path Synopsis
cmd
clua55-run command
Command clua55-run runs a Lua script under stock C Lua 5.5.1 (the wasm oracle blob, lua55_sjlj.wasm) with the CLI-arg surface the other runners provide.
Command clua55-run runs a Lua script under stock C Lua 5.5.1 (the wasm oracle blob, lua55_sjlj.wasm) with the CLI-arg surface the other runners provide.
glua command
luabench command
Command luabench measures the production EVAL postures for high-frequency scripts (U7c: promoted from note/bench-vs-orig/hostbench — the 2026-09-23 reference numbers in note/bench-vs-orig/out-hostbench.txt were produced by that harness with the same interp/cold/warm rows).
Command luabench measures the production EVAL postures for high-frequency scripts (U7c: promoted from note/bench-vs-orig/hostbench — the 2026-09-23 reference numbers in note/bench-vs-orig/out-hostbench.txt were produced by that harness with the same interp/cold/warm rows).
luafuzz command
Command luafuzz is the M6e differential fuzzer CLI: a deterministic seeded generator driving the testdiff engines, with a chunk- resumable soak designed for a laptop that sleeps and moves between networks daily (all state under -dir; every interruption is safe).
Command luafuzz is the M6e differential fuzzer CLI: a deterministic seeded generator driving the testdiff engines, with a chunk- resumable soak designed for a laptop that sleeps and moves between networks daily (all state under -dir; every interruption is safe).
luawasm-run command
Command luawasm-run executes a module saved by cmd/luawasmc against the C-Lua rt_* runtime (embedded lua55_rt.wasm, ABI v4) on wasmtime — or on the pure-Go wazero production engine when GLUA_WASM_ENGINE=wazero (ledger row 32: one blob, two hosts).
Command luawasm-run executes a module saved by cmd/luawasmc against the C-Lua rt_* runtime (embedded lua55_rt.wasm, ABI v4) on wasmtime — or on the pure-Go wazero production engine when GLUA_WASM_ENGINE=wazero (ledger row 32: one blob, two hosts).
luawasmc command
Command luawasmc compiles a Lua 5.5 script to a WebAssembly module.
Command luawasmc compiles a Lua 5.5 script to a WebAssembly module.
perfcmp command
Command perfcmp compares the glasm engines against native stock C Lua 5.5.1 on the luabench script set — wall time AND process memory.
Command perfcmp compares the glasm engines against native stock C Lua 5.5.1 on the luabench script set — wall time AND process memory.
testdiff command
Command testdiff runs a corpus across engines and reports log diffs.
Command testdiff runs a corpus across engines and reports log diffs.
examples
eval command
Command eval is the minimal host-package demonstration (design §6.3): compile once, run with KEYS/ARGV, print the reply values — plus one host function so the redis.call seam is visible end to end.
Command eval is the minimal host-package demonstration (design §6.3): compile once, run with KEYS/ARGV, print the reply values — plus one host function so the redis.call seam is visible end to end.
evalserver command
Command evalserver is the thread-proofing demonstration (design §6.3): concurrent EVALs against a pool of locked VMs — the deployment shape a daemon actually uses.
Command evalserver is the thread-proofing demonstration (design §6.3): concurrent EVALs against a pool of locked VMs — the deployment shape a daemon actually uses.
sharedvm command
Command sharedvm is the single-shared-VM deployment mode (design §6.3): one Lua memory image, many goroutines — VM.Run serializes on the image lock (A9) and scripts observe each other's globals, exactly like Redis-classic single-state script execution.
Command sharedvm is the single-shared-VM deployment mode (design §6.3): one Lua memory image, many goroutines — VM.Run serializes on the image lock (A9) and scripts observe each other's globals, exactly like Redis-classic single-state script execution.
Package host is the public Go API a pure-Go daemon (the Ultima Redis clone) imports to run untrusted Lua scripts: compile to wasm, execute on wazero against the embedded production runtime blob, exchange values with in-script host functions (the redis.call seam).
Package host is the public Go API a pure-Go daemon (the Ultima Redis clone) imports to run untrusted Lua scripts: compile to wasm, execute on wazero against the embedded production runtime blob, exchange values with in-script host functions (the redis.call seam).
Package luafuzz is the M6e differential fuzzer: a deterministic Lua program generator (design doc §8.4 L5) plus an interruptible, chunk-resumable soak runner (m6 plan §4.5/D6).
Package luafuzz is the M6e differential fuzzer: a deterministic Lua program generator (design doc §8.4 L5) plus an interruptible, chunk-resumable soak runner (m6 plan §4.5/D6).
Package luawasm is the Lua→wasm backend (design doc §4): FunctionProto trees from the glasm frontend become wasm modules that share linear memory with the C runtime and call down through the rt_* ABI.
Package luawasm is the Lua→wasm backend (design doc §4): FunctionProto trees from the glasm frontend become wasm modules that share linear memory with the C runtime and call down through the rt_* ABI.
Lua pattern match functions for Go
Lua pattern match functions for Go
Package testdiff is the differential testing harness of the Lua→wasm project (docs/Lua-Wasm-Design-and-Test-Plan.md §7–§8, milestone M1).
Package testdiff is the differential testing harness of the Lua→wasm project (docs/Lua-Wasm-Design-and-Test-Plan.md §7–§8, milestone M1).
Package wasm is a minimal, dependency-free emitter for WebAssembly 1.0/2.0 core binary modules.
Package wasm is a minimal, dependency-free emitter for WebAssembly 1.0/2.0 core binary modules.

Jump to

Keyboard shortcuts

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