op 1
This commit is contained in:
parent
e58f9a6028
commit
6b9e2a0e20
105
bytecode.go
105
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(
|
||||
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)
|
||||
})
|
||||
status := C.lua_pcall(s.L, 0, C.int(nresults), 0)
|
||||
if status != 0 {
|
||||
err := &LuaError{
|
||||
Code: int(status),
|
||||
Message: s.ToString(-1),
|
||||
}
|
||||
|
||||
// LoadAndRunBytecode loads and executes bytecode
|
||||
func (s *State) LoadAndRunBytecode(bytecode []byte, name string) error {
|
||||
if err := s.LoadBytecode(bytecode, name); err != nil {
|
||||
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
|
||||
|
|
147
wrapper.go
147
wrapper.go
|
@ -11,13 +11,7 @@ package luajit
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 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)
|
||||
status := C.execute_with_results(s.L, ccode, 1) // store_results=true
|
||||
if status != 0 {
|
||||
return status
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user