# LuaJIT Go Wrapper 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 performant and developer-friendly, with an API that feels natural to use. ## 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: ```bash 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: ```go L := luajit.New() defer L.Close() defer L.Cleanup() err := L.DoString(`print("Hey from Lua!")`) ``` ## Working with Bytecode Need even more performance? You can compile your Lua code to bytecode and reuse it: ```go // 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.LoadAndRunBytecode(bytecode, "calc") } // Or do both at once err := L.CompileAndRun(`return "hello"`, "greeting") ``` ### When to Use Bytecode Bytecode execution is consistently faster than direct execution: ``` Benchmark Ops/sec Comparison ---------------------------------------------------------------------------- BenchmarkSimpleDoString 2,561,012 Base BenchmarkSimplePrecompiledBytecode 3,828,841 +49.5% faster BenchmarkFunctionCallDoString 2,021,098 Base BenchmarkFunctionCallPrecompiled 3,482,074 +72.3% faster BenchmarkLoopDoString 188,119 Base BenchmarkLoopPrecompiled 211,081 +12.2% faster BenchmarkTableOperationsDoString 84,086 Base BenchmarkTableOperationsPrecompiled 93,655 +11.4% faster BenchmarkComplexScript 33,133 Base BenchmarkComplexScriptPrecompiled 41,044 +23.9% faster ``` 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? It's straightforward: ```go // 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: ```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 // 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 provide useful errors instead of mysterious panics: ```go if err := L.DoString("this isn't valid Lua!"); err != nil { if luaErr, ok := err.(*luajit.LuaError); ok { fmt.Printf("Error: %s\n", luaErr.Message) } } ``` ## Memory Management The wrapper uses a custom table pooling system to reduce GC pressure when handling many tables: ```go // Tables are pooled and reused internally for better performance for i := 0; i < 1000; i++ { L.GetGlobal("table") table, _ := L.ToTable(-1) // Use table... L.Pop(1) // Table is automatically returned to pool } ``` ## Best Practices - Always use `defer L.Close()` and `defer L.Cleanup()` to prevent memory leaks - Each Lua state should stick to one goroutine - For concurrent operations, create multiple states - You can share functions between states safely - Keep an eye on your stack management - pop as many items as you push - Use bytecode for frequently executed code paths - Consider compiling critical Lua code to bytecode at startup ## Advanced Features ### Bytecode Serialization You can serialize bytecode for distribution or caching: ```go // Compile once bytecode, _ := L.CompileBytecode(complexScript, "module") // Save to file ioutil.WriteFile("module.luac", bytecode, 0644) // Later, load from file bytecode, _ := ioutil.ReadFile("module.luac") L.LoadAndRunBytecode(bytecode, "module") ``` ### Closures and Upvalues Bytecode properly preserves closures and upvalues: ```go code := ` local counter = 0 return function() counter = counter + 1 return counter end ` bytecode, _ := L.CompileBytecode(code, "counter") L.LoadAndRunBytecodeWithResults(bytecode, "counter", 1) L.SetGlobal("increment") // Later... L.GetGlobal("increment") L.Call(0, 1) // Returns 1 L.Pop(1) L.GetGlobal("increment") L.Call(0, 1) // Returns 2 ``` ## 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!