diff --git a/bytecode.go b/bytecode.go index 6dbc47b..b28a30d 100644 --- a/bytecode.go +++ b/bytecode.go @@ -7,46 +7,46 @@ package luajit #include typedef struct { - const unsigned char *buf; - size_t size; - const char *name; + const unsigned char *buf; + size_t size; + const char *name; } BytecodeReader; const char *bytecode_reader(lua_State *L, void *ud, size_t *size) { - BytecodeReader *r = (BytecodeReader *)ud; - (void)L; // unused - if (r->size == 0) return NULL; - *size = r->size; - r->size = 0; // Only read once - return (const char *)r->buf; + BytecodeReader *r = (BytecodeReader *)ud; + (void)L; // unused + if (r->size == 0) return NULL; + *size = r->size; + r->size = 0; // Only read once + return (const char *)r->buf; } int load_bytecode(lua_State *L, const unsigned char *buf, size_t len, const char *name) { - BytecodeReader reader = {buf, len, name}; - return lua_load(L, bytecode_reader, &reader, name); + BytecodeReader reader = {buf, len, name}; + return lua_load(L, bytecode_reader, &reader, name); } // Direct bytecode dumping without intermediate buffer - more efficient int direct_bytecode_writer(lua_State *L, const void *p, size_t sz, void *ud) { - void **data = (void **)ud; - size_t current_size = (size_t)data[1]; - void *newbuf = realloc(data[0], current_size + sz); - if (newbuf == NULL) return 1; + void **data = (void **)ud; + size_t current_size = (size_t)data[1]; + void *newbuf = realloc(data[0], current_size + sz); + if (newbuf == NULL) return 1; - memcpy((unsigned char*)newbuf + current_size, p, sz); - data[0] = newbuf; - data[1] = (void*)(current_size + sz); - return 0; + memcpy((unsigned char*)newbuf + current_size, p, sz); + data[0] = newbuf; + 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; + 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); + return lua_pcall(L, 0, nresults, 0); } */ import "C" @@ -102,10 +102,7 @@ func (s *State) LoadBytecode(bytecode []byte, name string) error { ) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("LoadBytecode(%s)", name)) s.Pop(1) // Remove error message return err } @@ -123,10 +120,7 @@ func (s *State) RunBytecode() error { func (s *State) RunBytecodeWithResults(nresults int) error { status := C.lua_pcall(s.L, 0, C.int(nresults), 0) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("RunBytecode(nresults=%d)", nresults)) s.Pop(1) // Remove error message return err } @@ -152,10 +146,7 @@ func (s *State) LoadAndRunBytecode(bytecode []byte, name string) error { ) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("LoadAndRunBytecode(%s)", name)) s.Pop(1) // Remove error message return err } @@ -182,10 +173,7 @@ func (s *State) LoadAndRunBytecodeWithResults(bytecode []byte, name string, nres ) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("LoadAndRunBytecodeWithResults(%s, nresults=%d)", name, nresults)) s.Pop(1) // Remove error message return err } diff --git a/stack.go b/stack.go index f0e0173..eba38e1 100644 --- a/stack.go +++ b/stack.go @@ -5,16 +5,45 @@ package luajit #include */ import "C" -import "fmt" +import ( + "fmt" + "strings" +) -// LuaError represents an error from the Lua state +// LuaError represents an enhanced error from the Lua state type LuaError struct { - Code int - Message string + Code int + Message string + File string + Line int + StackTrace string + Context string } func (e *LuaError) Error() string { - return fmt.Sprintf("lua error (code=%d): %s", e.Code, e.Message) + var parts []string + + if e.File != "" && e.Line > 0 { + parts = append(parts, fmt.Sprintf("%s:%d", e.File, e.Line)) + } + + if e.Context != "" { + parts = append(parts, fmt.Sprintf("[%s]", e.Context)) + } + + parts = append(parts, e.Message) + + if e.Code != 0 { + parts = append(parts, fmt.Sprintf("(code=%d)", e.Code)) + } + + result := strings.Join(parts, " ") + + if e.StackTrace != "" { + result += "\n" + e.StackTrace + } + + return result } // Stack management constants from lua.h @@ -45,3 +74,50 @@ func (s *State) GetStackTrace() string { return trace } + +// GetErrorInfo extracts detailed error information from the Lua stack +func (s *State) GetErrorInfo(context string) *LuaError { + if s.GetTop() == 0 { + return &LuaError{ + Message: "unknown error (empty stack)", + Context: context, + } + } + + message := s.ToString(-1) + + // Parse file:line from common Lua error format + var file string + var line int + + if colonPos := strings.Index(message, ":"); colonPos > 0 { + beforeColon := message[:colonPos] + afterColon := message[colonPos+1:] + + if secondColonPos := strings.Index(afterColon, ":"); secondColonPos > 0 { + file = beforeColon + if n, err := fmt.Sscanf(afterColon[:secondColonPos], "%d", &line); n == 1 && err == nil { + // Strip the file:line part from message for cleaner display + message = strings.TrimSpace(afterColon[secondColonPos+1:]) + } + } + } + + // Get stack trace + stackTrace := s.GetStackTrace() + + return &LuaError{ + Message: message, + File: file, + Line: line, + StackTrace: stackTrace, + Context: context, + } +} + +// CreateLuaError creates a LuaError with full context information +func (s *State) CreateLuaError(code int, context string) *LuaError { + err := s.GetErrorInfo(context) + err.Code = code + return err +} diff --git a/wrapper.go b/wrapper.go index 1067037..11e5ae9 100644 --- a/wrapper.go +++ b/wrapper.go @@ -13,25 +13,25 @@ package luajit // 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) { - status = lua_pcall(L, 0, 0, 0); - } - return status; + int status = luaL_loadstring(L, s); + if (status == 0) { + status = lua_pcall(L, 0, 0, 0); + } + return status; } static int do_file(lua_State *L, const char *filename) { - int status = luaL_loadfile(L, filename); - if (status == 0) { - status = lua_pcall(L, 0, 0, 0); - } - return status; + int status = luaL_loadfile(L, filename); + if (status == 0) { + status = lua_pcall(L, 0, 0, 0); + } + 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); + int status = luaL_loadstring(L, code); + if (status != 0) return status; + return lua_pcall(L, 0, store_results ? LUA_MULTRET : 0, 0); } */ import "C" @@ -342,10 +342,7 @@ func (s *State) LoadString(code string) error { status := C.luaL_loadstring(s.L, ccode) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), "LoadString") s.Pop(1) // Remove error message return err } @@ -359,10 +356,7 @@ func (s *State) LoadFile(filename string) error { status := C.luaL_loadfile(s.L, cfilename) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("LoadFile(%s)", filename)) s.Pop(1) // Remove error message return err } @@ -373,10 +367,7 @@ func (s *State) LoadFile(filename string) error { func (s *State) Call(nargs, nresults int) error { 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), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("Call(%d,%d)", nargs, nresults)) s.Pop(1) // Remove error message return err } @@ -390,10 +381,7 @@ func (s *State) DoString(code string) error { status := C.do_string(s.L, ccode) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), "DoString") s.Pop(1) // Remove error message return err } @@ -407,10 +395,7 @@ func (s *State) DoFile(filename string) error { status := C.do_file(s.L, cfilename) if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), fmt.Sprintf("DoFile(%s)", filename)) s.Pop(1) // Remove error message return err } @@ -426,10 +411,7 @@ func (s *State) Execute(code string) (int, error) { status := C.execute_with_results(s.L, ccode, 1) // store_results=true if status != 0 { - err := &LuaError{ - Code: int(status), - Message: s.ToString(-1), - } + err := s.CreateLuaError(int(status), "Execute") s.Pop(1) // Remove error message return 0, err }