From 6b9e2a0e201bdd34fba0972441434354aa5c67c5 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Fri, 4 Apr 2025 21:29:55 -0500 Subject: [PATCH] op 1 --- bytecode.go | 117 +++++++++++++++++++++++++++++++---------- wrapper.go | 149 +++++++++++++++++++++++++++------------------------- 2 files changed, 167 insertions(+), 99 deletions(-) diff --git a/bytecode.go b/bytecode.go index 3c8f870..6dbc47b 100644 --- a/bytecode.go +++ b/bytecode.go @@ -38,6 +38,16 @@ int direct_bytecode_writer(lua_State *L, const void *p, size_t sz, void *ud) { data[1] = (void*)(current_size + sz); return 0; } + +// Combined load and run bytecode in a single call +int load_and_run_bytecode(lua_State *L, const unsigned char *buf, size_t len, + const char *name, int nresults) { + BytecodeReader reader = {buf, len, name}; + int status = lua_load(L, bytecode_reader, &reader, name); + if (status != 0) return status; + + return lua_pcall(L, 0, nresults, 0); +} */ import "C" import ( @@ -55,9 +65,10 @@ func (s *State) CompileBytecode(code string, name string) ([]byte, error) { data := [2]unsafe.Pointer{nil, nil} // Dump the function to bytecode - err := s.safeCall(func() C.int { - return C.lua_dump(s.L, (*[0]byte)(unsafe.Pointer(C.direct_bytecode_writer)), unsafe.Pointer(&data)) - }) + status := C.lua_dump(s.L, (*[0]byte)(unsafe.Pointer(C.direct_bytecode_writer)), unsafe.Pointer(&data)) + if status != 0 { + return nil, fmt.Errorf("failed to dump bytecode: status %d", status) + } // Get result var bytecode []byte @@ -70,10 +81,6 @@ func (s *State) CompileBytecode(code string, name string) ([]byte, error) { s.Pop(1) // Remove the function from stack - if err != nil { - return nil, fmt.Errorf("failed to dump bytecode: %w", err) - } - return bytecode, nil } @@ -87,17 +94,20 @@ func (s *State) LoadBytecode(bytecode []byte, name string) error { defer C.free(unsafe.Pointer(cname)) // Load the bytecode - err := s.safeCall(func() C.int { - return C.load_bytecode( - s.L, - (*C.uchar)(unsafe.Pointer(&bytecode[0])), - C.size_t(len(bytecode)), - cname, - ) - }) + status := C.load_bytecode( + s.L, + (*C.uchar)(unsafe.Pointer(&bytecode[0])), + C.size_t(len(bytecode)), + cname, + ) - if err != nil { - return fmt.Errorf("failed to load bytecode: %w", err) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err } return nil @@ -111,25 +121,76 @@ func (s *State) RunBytecode() error { // RunBytecodeWithResults executes bytecode and keeps nresults on the stack // Use LUA_MULTRET (-1) to keep all results func (s *State) RunBytecodeWithResults(nresults int) error { - return s.safeCall(func() C.int { - return C.lua_pcall(s.L, 0, C.int(nresults), 0) - }) -} - -// LoadAndRunBytecode loads and executes bytecode -func (s *State) LoadAndRunBytecode(bytecode []byte, name string) error { - if err := s.LoadBytecode(bytecode, name); err != nil { + status := C.lua_pcall(s.L, 0, C.int(nresults), 0) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message return err } - return s.RunBytecode() + return nil +} + +// LoadAndRunBytecode loads and executes bytecode in a single CGO transition +func (s *State) LoadAndRunBytecode(bytecode []byte, name string) error { + if len(bytecode) == 0 { + return fmt.Errorf("empty bytecode") + } + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + // Use combined load and run function + status := C.load_and_run_bytecode( + s.L, + (*C.uchar)(unsafe.Pointer(&bytecode[0])), + C.size_t(len(bytecode)), + cname, + 0, // No results + ) + + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + + return nil } // LoadAndRunBytecodeWithResults loads and executes bytecode, preserving results func (s *State) LoadAndRunBytecodeWithResults(bytecode []byte, name string, nresults int) error { - if err := s.LoadBytecode(bytecode, name); err != nil { + if len(bytecode) == 0 { + return fmt.Errorf("empty bytecode") + } + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + // Use combined load and run function + status := C.load_and_run_bytecode( + s.L, + (*C.uchar)(unsafe.Pointer(&bytecode[0])), + C.size_t(len(bytecode)), + cname, + C.int(nresults), + ) + + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message return err } - return s.RunBytecodeWithResults(nresults) + + return nil } // CompileAndRun compiles and immediately executes Lua code diff --git a/wrapper.go b/wrapper.go index f4e0a54..e8571c0 100644 --- a/wrapper.go +++ b/wrapper.go @@ -11,13 +11,7 @@ package luajit #include #include -// Optimized helpers for common operations -static int get_abs_index(lua_State *L, int idx) { - if (idx > 0 || idx <= LUA_REGISTRYINDEX) return idx; - return lua_gettop(L) + idx + 1; -} - -// Combined load and execute with no results +// Direct execution helpers to minimize CGO transitions static int do_string(lua_State *L, const char *s) { int status = luaL_loadstring(L, s); if (status == 0) { @@ -26,7 +20,6 @@ static int do_string(lua_State *L, const char *s) { return status; } -// Combined load and execute file static int do_file(lua_State *L, const char *filename) { int status = luaL_loadfile(L, filename); if (status == 0) { @@ -34,6 +27,12 @@ static int do_file(lua_State *L, const char *filename) { } return status; } + +static int execute_with_results(lua_State *L, const char *code, int store_results) { + int status = luaL_loadstring(L, code); + if (status != 0) return status; + return lua_pcall(L, 0, store_results ? LUA_MULTRET : 0, 0); +} */ import "C" import ( @@ -339,9 +338,16 @@ func (s *State) LoadString(code string) error { ccode := C.CString(code) defer C.free(unsafe.Pointer(ccode)) - return s.safeCall(func() C.int { - return C.luaL_loadstring(s.L, ccode) - }) + status := C.luaL_loadstring(s.L, ccode) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + return nil } // LoadFile loads a Lua chunk from a file without executing it @@ -349,16 +355,30 @@ func (s *State) LoadFile(filename string) error { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) - return s.safeCall(func() C.int { - return C.luaL_loadfile(s.L, cfilename) - }) + status := C.luaL_loadfile(s.L, cfilename) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + return nil } // Call calls a function with the given number of arguments and results func (s *State) Call(nargs, nresults int) error { - return s.safeCall(func() C.int { - return C.lua_pcall(s.L, C.int(nargs), C.int(nresults), 0) - }) + status := C.lua_pcall(s.L, C.int(nargs), C.int(nresults), 0) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + return nil } // DoString executes a Lua string and cleans up the stack @@ -366,9 +386,16 @@ func (s *State) DoString(code string) error { ccode := C.CString(code) defer C.free(unsafe.Pointer(ccode)) - return s.safeCall(func() C.int { - return C.do_string(s.L, ccode) - }) + status := C.do_string(s.L, ccode) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + return nil } // DoFile executes a Lua file and cleans up the stack @@ -376,9 +403,16 @@ func (s *State) DoFile(filename string) error { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) - return s.safeCall(func() C.int { - return C.do_file(s.L, cfilename) - }) + status := C.do_file(s.L, cfilename) + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), + } + s.Pop(1) // Remove error message + return err + } + return nil } // Execute executes a Lua string and returns the number of results left on the stack @@ -388,25 +422,17 @@ func (s *State) Execute(code string) (int, error) { ccode := C.CString(code) defer C.free(unsafe.Pointer(ccode)) - var nresults int - err := s.safeCall(func() C.int { - status := C.luaL_loadstring(s.L, ccode) - if status != 0 { - return status + status := C.execute_with_results(s.L, ccode, 1) // store_results=true + if status != 0 { + err := &LuaError{ + Code: int(status), + Message: s.ToString(-1), } - - status = C.lua_pcall(s.L, 0, C.LUA_MULTRET, 0) - if status == 0 { - nresults = s.GetTop() - baseTop - } - return status - }) - - if err != nil { + s.Pop(1) // Remove error message return 0, err } - return nresults, nil + return s.GetTop() - baseTop, nil } // ExecuteWithResult executes a Lua string and returns the first result @@ -426,6 +452,20 @@ func (s *State) ExecuteWithResult(code string) (any, error) { return s.ToValue(-nresults) } +// BatchExecute executes multiple statements with a single CGO transition +func (s *State) BatchExecute(statements []string) error { + // Join statements with semicolons + combinedCode := "" + for i, stmt := range statements { + combinedCode += stmt + if i < len(statements)-1 { + combinedCode += "; " + } + } + + return s.DoString(combinedCode) +} + // Package path operations // SetPackagePath sets the Lua package.path @@ -441,36 +481,3 @@ func (s *State) AddPackagePath(path string) error { code := fmt.Sprintf(`package.path = package.path .. ";%s"`, path) return s.DoString(code) } - -// Helper functions - -// checkStack ensures there is enough space on the Lua stack -func (s *State) checkStack(n int) error { - if C.lua_checkstack(s.L, C.int(n)) == 0 { - return fmt.Errorf("stack overflow (cannot allocate %d slots)", n) - } - return nil -} - -// safeCall wraps a potentially dangerous C call with stack checking -func (s *State) safeCall(f func() C.int) error { - // Ensure we have enough stack space - if err := s.checkStack(LUA_MINSTACK); err != nil { - return err - } - - // Make the call - status := f() - - // Check for errors - if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } - s.Pop(1) // Remove error message - return err - } - - return nil -}