improve error reporting

This commit is contained in:
Sky Johnson 2025-05-22 08:54:59 -05:00
parent 2bdf7cfd4a
commit 6a2eb9df63
3 changed files with 127 additions and 81 deletions

View File

@ -7,46 +7,46 @@ package luajit
#include <string.h> #include <string.h>
typedef struct { typedef struct {
const unsigned char *buf; const unsigned char *buf;
size_t size; size_t size;
const char *name; const char *name;
} BytecodeReader; } BytecodeReader;
const char *bytecode_reader(lua_State *L, void *ud, size_t *size) { const char *bytecode_reader(lua_State *L, void *ud, size_t *size) {
BytecodeReader *r = (BytecodeReader *)ud; BytecodeReader *r = (BytecodeReader *)ud;
(void)L; // unused (void)L; // unused
if (r->size == 0) return NULL; if (r->size == 0) return NULL;
*size = r->size; *size = r->size;
r->size = 0; // Only read once r->size = 0; // Only read once
return (const char *)r->buf; return (const char *)r->buf;
} }
int load_bytecode(lua_State *L, const unsigned char *buf, size_t len, const char *name) { int load_bytecode(lua_State *L, const unsigned char *buf, size_t len, const char *name) {
BytecodeReader reader = {buf, len, name}; BytecodeReader reader = {buf, len, name};
return lua_load(L, bytecode_reader, &reader, name); return lua_load(L, bytecode_reader, &reader, name);
} }
// Direct bytecode dumping without intermediate buffer - more efficient // Direct bytecode dumping without intermediate buffer - more efficient
int direct_bytecode_writer(lua_State *L, const void *p, size_t sz, void *ud) { int direct_bytecode_writer(lua_State *L, const void *p, size_t sz, void *ud) {
void **data = (void **)ud; void **data = (void **)ud;
size_t current_size = (size_t)data[1]; size_t current_size = (size_t)data[1];
void *newbuf = realloc(data[0], current_size + sz); void *newbuf = realloc(data[0], current_size + sz);
if (newbuf == NULL) return 1; if (newbuf == NULL) return 1;
memcpy((unsigned char*)newbuf + current_size, p, sz); memcpy((unsigned char*)newbuf + current_size, p, sz);
data[0] = newbuf; data[0] = newbuf;
data[1] = (void*)(current_size + sz); data[1] = (void*)(current_size + sz);
return 0; return 0;
} }
// Combined load and run bytecode in a single call // Combined load and run bytecode in a single call
int load_and_run_bytecode(lua_State *L, const unsigned char *buf, size_t len, int load_and_run_bytecode(lua_State *L, const unsigned char *buf, size_t len,
const char *name, int nresults) { const char *name, int nresults) {
BytecodeReader reader = {buf, len, name}; BytecodeReader reader = {buf, len, name};
int status = lua_load(L, bytecode_reader, &reader, name); int status = lua_load(L, bytecode_reader, &reader, name);
if (status != 0) return status; if (status != 0) return status;
return lua_pcall(L, 0, nresults, 0); return lua_pcall(L, 0, nresults, 0);
} }
*/ */
import "C" import "C"
@ -102,10 +102,7 @@ func (s *State) LoadBytecode(bytecode []byte, name string) error {
) )
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("LoadBytecode(%s)", name))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -123,10 +120,7 @@ func (s *State) RunBytecode() error {
func (s *State) RunBytecodeWithResults(nresults int) error { func (s *State) RunBytecodeWithResults(nresults int) error {
status := C.lua_pcall(s.L, 0, C.int(nresults), 0) status := C.lua_pcall(s.L, 0, C.int(nresults), 0)
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("RunBytecode(nresults=%d)", nresults))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -152,10 +146,7 @@ func (s *State) LoadAndRunBytecode(bytecode []byte, name string) error {
) )
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("LoadAndRunBytecode(%s)", name))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -182,10 +173,7 @@ func (s *State) LoadAndRunBytecodeWithResults(bytecode []byte, name string, nres
) )
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("LoadAndRunBytecodeWithResults(%s, nresults=%d)", name, nresults))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }

View File

@ -5,16 +5,45 @@ package luajit
#include <lauxlib.h> #include <lauxlib.h>
*/ */
import "C" 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 { type LuaError struct {
Code int Code int
Message string Message string
File string
Line int
StackTrace string
Context string
} }
func (e *LuaError) Error() 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 // Stack management constants from lua.h
@ -45,3 +74,50 @@ func (s *State) GetStackTrace() string {
return trace 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
}

View File

@ -13,25 +13,25 @@ package luajit
// Direct execution helpers to minimize CGO transitions // Direct execution helpers to minimize CGO transitions
static int do_string(lua_State *L, const char *s) { static int do_string(lua_State *L, const char *s) {
int status = luaL_loadstring(L, s); int status = luaL_loadstring(L, s);
if (status == 0) { if (status == 0) {
status = lua_pcall(L, 0, 0, 0); status = lua_pcall(L, 0, 0, 0);
} }
return status; return status;
} }
static int do_file(lua_State *L, const char *filename) { static int do_file(lua_State *L, const char *filename) {
int status = luaL_loadfile(L, filename); int status = luaL_loadfile(L, filename);
if (status == 0) { if (status == 0) {
status = lua_pcall(L, 0, 0, 0); status = lua_pcall(L, 0, 0, 0);
} }
return status; return status;
} }
static int execute_with_results(lua_State *L, const char *code, int store_results) { static int execute_with_results(lua_State *L, const char *code, int store_results) {
int status = luaL_loadstring(L, code); int status = luaL_loadstring(L, code);
if (status != 0) return status; if (status != 0) return status;
return lua_pcall(L, 0, store_results ? LUA_MULTRET : 0, 0); return lua_pcall(L, 0, store_results ? LUA_MULTRET : 0, 0);
} }
*/ */
import "C" import "C"
@ -342,10 +342,7 @@ func (s *State) LoadString(code string) error {
status := C.luaL_loadstring(s.L, ccode) status := C.luaL_loadstring(s.L, ccode)
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), "LoadString")
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -359,10 +356,7 @@ func (s *State) LoadFile(filename string) error {
status := C.luaL_loadfile(s.L, cfilename) status := C.luaL_loadfile(s.L, cfilename)
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("LoadFile(%s)", filename))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -373,10 +367,7 @@ func (s *State) LoadFile(filename string) error {
func (s *State) Call(nargs, nresults int) error { func (s *State) Call(nargs, nresults int) error {
status := 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 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("Call(%d,%d)", nargs, nresults))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -390,10 +381,7 @@ func (s *State) DoString(code string) error {
status := C.do_string(s.L, ccode) status := C.do_string(s.L, ccode)
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), "DoString")
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err return err
} }
@ -407,10 +395,7 @@ func (s *State) DoFile(filename string) error {
status := C.do_file(s.L, cfilename) status := C.do_file(s.L, cfilename)
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), fmt.Sprintf("DoFile(%s)", filename))
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return err 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 status := C.execute_with_results(s.L, ccode, 1) // store_results=true
if status != 0 { if status != 0 {
err := &LuaError{ err := s.CreateLuaError(int(status), "Execute")
Code: int(status),
Message: s.ToString(-1),
}
s.Pop(1) // Remove error message s.Pop(1) // Remove error message
return 0, err return 0, err
} }