vendor/luajit | ||
.gitignore | ||
bytecode_test.go | ||
bytecode.go | ||
DOCS.md | ||
functions_test.go | ||
functions.go | ||
go.mod | ||
LICENSE | ||
README.md | ||
stack.go | ||
table_test.go | ||
table.go | ||
types.go | ||
wrapper_bench_test.go | ||
wrapper_test.go | ||
wrapper.go |
LuaJIT Go Wrapper
Hey there! This is a Go wrapper for LuaJIT that makes it easy to embed Lua in your Go applications. We've focused on making it both safe and fast, while keeping the API clean and intuitive.
What's This For?
This wrapper lets you run Lua code from Go and easily pass data back and forth between the two languages. You might want this if you're:
- Adding scripting support to your application
- Building a game engine
- Creating a configuration system
- Writing an embedded rules engine
- Building test automation tools
Get Started
First, grab the package:
go get git.sharkk.net/Sky/LuaJIT-to-Go
You'll need LuaJIT's development files, but don't worry - we include libraries for Windows and Linux in the vendor directory.
Here's the simplest thing you can do:
L := luajit.NewSafe()
defer L.Close()
defer L.Cleanup()
err := L.DoString(`print("Hey from Lua!")`)
Stack Safety: Choose Your Adventure
One of the key decisions you'll make is whether to use stack-safe mode. Here's what that means:
Stack-Safe Mode (NewSafe())
L := luajit.NewSafe()
Think of this as driving with guardrails. It's perfect when:
- You're new to Lua or embedding scripting languages
- You're writing a server or long-running application
- You want to handle untrusted Lua code
- You'd rather have slightly slower code than mysterious crashes
The safe mode will:
- Prevent stack overflows
- Check types more thoroughly
- Clean up after messy Lua code
- Give you better error messages
Non-Stack-Safe Mode (New())
L := luajit.New()
This is like taking off the training wheels. Use it when:
- You know exactly how your Lua code behaves
- You've profiled your application and need more speed
- You're doing lots of rapid, simple Lua calls
- You're writing performance-critical code
The unsafe mode:
- Skips most safety checks
- Runs noticeably faster
- Gives you direct control over the stack
- Can crash spectacularly if you make a mistake
Most applications should start with stack-safe mode and only switch to unsafe mode if profiling shows it's necessary.
Working with Bytecode
Need even more performance? You can compile your Lua code to bytecode and reuse it:
// Compile once
bytecode, err := L.CompileBytecode(`
local function calculate(x)
return x * x + x + 1
end
return calculate(10)
`, "calc")
// Execute many times
for i := 0; i < 1000; i++ {
err := L.LoadBytecode(bytecode, "calc")
}
// Or do both at once
err := L.CompileAndLoad(`return "hello"`, "greeting")
When to Use Bytecode
Bytecode execution is consistently faster than direct execution:
- Simple operations: 20-60% faster
- String operations: Up to 60% speedup
- Loop-heavy code: 10-15% improvement
- Table operations: 10-15% faster
Some benchmark results on a typical system:
Operation Direct Exec Bytecode Exec
----------------------------------------
Simple Math 1.5M ops/sec 2.4M ops/sec
String Ops 370K ops/sec 600K ops/sec
Table Creation 127K ops/sec 146K ops/sec
Use bytecode when you:
- Have code that runs frequently
- Need maximum performance
- Want to precompile your Lua code
- Are distributing Lua code to many instances
Registering Go Functions
Want to call Go code from Lua? Easy:
// This function adds two numbers and returns the result
adder := func(s *luajit.State) int {
sum := s.ToNumber(1) + s.ToNumber(2)
s.PushNumber(sum)
return 1 // we're returning one value
}
L.RegisterGoFunction("add", adder)
Now in Lua:
result = add(40, 2) -- result = 42
Working with Tables
Lua tables are pretty powerful - they're like a mix of Go's maps and slices. We make it easy to work with them:
// Go → Lua
stuff := map[string]interface{}{
"name": "Arthur Dent",
"age": 30,
"items": []float64{1, 2, 3},
}
L.PushTable(stuff)
// Lua → Go
L.GetGlobal("some_table")
result, err := L.ToTable(-1)
Error Handling
We try to give you useful errors instead of mysterious panics:
if err := L.DoString("this isn't valid Lua!"); err != nil {
if luaErr, ok := err.(*luajit.LuaError); ok {
fmt.Printf("Oops: %s\n", luaErr.Message)
}
}
A Few Tips
- Always use those
defer L.Close()
anddefer L.Cleanup()
calls - they prevent memory leaks - Each Lua state should stick to one goroutine
- For concurrent stuff, create multiple states
- You can share functions between states safely
- Keep an eye on your stack in unsafe mode - it won't clean up after itself
- Start with stack-safe mode and measure before optimizing
- Use bytecode for frequently executed code paths
- Consider compiling critical Lua code to bytecode at startup
Need Help?
Check out the tests in the repository - they're full of examples. If you're stuck, open an issue! We're here to help.
License
MIT Licensed - do whatever you want with it!