From aadee458066c5d623ae2a16b8957f51418139fcd Mon Sep 17 00:00:00 2001 From: unwox Date: Tue, 9 Sep 2025 17:21:12 +0600 Subject: small refactorings --- fennel.lua | 1 + lua.go | 119 ++++------------------------------------------------- lua_common.go | 95 ++++++++++++++++++++++++++++++++++++++++++ luajit.go | 129 ++++++++-------------------------------------------------- worker.go | 3 +- 5 files changed, 122 insertions(+), 225 deletions(-) diff --git a/fennel.lua b/fennel.lua index 29e7918..26615d4 100644 --- a/fennel.lua +++ b/fennel.lua @@ -1,3 +1,4 @@ +-- fennel.lua -- SPDX-License-Identifier: MIT -- SPDX-FileCopyrightText: Calvin Rose and contributors package.preload["fennel.repl"] = package.preload["fennel.repl"] or function(...) diff --git a/lua.go b/lua.go index fb0b72a..b6d7027 100644 --- a/lua.go +++ b/lua.go @@ -24,20 +24,18 @@ static inline void luna_push_function(lua_State *l, uintptr_t f) { import "C" import ( "errors" - "fmt" "runtime/cgo" - "time" "unsafe" ) type LuaRef C.int + // Lua is a wrapper around C Lua state with several conveniences. type Lua struct { l *C.lua_State running **Lua yield func() resume func() bool - tracebackHandler LuaRef } //export luna_run_go_func @@ -53,9 +51,9 @@ func (l *Lua) Start() { l.running = &l C.luaL_openlibs(l.l) - // Put debug handler into the registry for later copies to the stack. + // Put traceback function at the start of the stack so it's always + // possible to refer to it via stack index 1. l.PushGoFunction(TracebackHandler) - l.tracebackHandler = l.PopToRef() } // Close closes the Lua context. @@ -63,22 +61,6 @@ func (l *Lua) Close() { C.lua_close(l.l) } -// Require loads and executes the file pushing results onto the Lua stack. -// FIXME: traceback handler remains dangling on the stack, we need to remove -// it somehow. -func (l *Lua) Require(file string) error { - l.PushTracebackHandler() - err := l.LoadFile(file) - if err != nil { - return errors.New("could not open the file:\n" + err.Error()) - } - err = l.PCall(0, C.LUA_MULTRET, -2) - if err != nil { - return errors.New("could not execute the file:\n" + err.Error()) - } - return nil -} - // PCall calls a function with the stack index (-nargs-1) expecting nresults // number of results. func (l *Lua) PCall(nargs int, nresults int, errfunc int) error { @@ -138,6 +120,11 @@ func (l *Lua) Pop(n int) { C.lua_settop(l.l, C.int(-n - 1)) } +// SetTop sets head of the stack at top. +func (l *Lua) SetTop(top int) { + C.lua_settop(l.l, C.int(top)) +} + // PushNil pushes tnil onto the Lua stack. func (l *Lua) PushNil() { C.lua_pushnil(l.l) @@ -208,63 +195,6 @@ func (l *Lua) RawGetI(tableOffset, i int) { C.lua_rawgeti(l.l, C.int(tableOffset), C.longlong(i)) } -// PushAny pushes value v onto the stack. -func (l *Lua) PushAny(v any) error { - switch v.(type) { - case nil: - l.PushNil() - case string: - v, _ := v.(string) - l.PushString(v) - case func (l *Lua) int: - v, _ := v.(func (l *Lua) int) - l.PushGoFunction(v) - case int: - v, _ := v.(int) - l.PushNumber(v) - case int64: - v, _ := v.(int64) - l.PushNumber(int(v)) - case float64: - v, _ := v.(float64) - l.PushFloatNumber(v) - case bool: - v, _ := v.(bool) - l.PushBoolean(v) - case map[string]any: - v, _ := v.(map[string]any) - err := l.PushObject(v) - if err != nil { - return fmt.Errorf("array push errro: ", err) - } - case []any: - v, _ := v.([]any) - err := l.PushArray(v) - if err != nil { - return fmt.Errorf("array push errro: ", err) - } - case time.Time: - v, _ := v.(time.Time) - l.PushString(v.Format(time.DateTime)) - default: - return fmt.Errorf("unsupported value type: %T", v) - } - return nil -} - -// PushObject recursively pushes string->any Go table onto the stack. -func (l *Lua) PushObject(table map[string]any) error { - l.CreateTable(len(table)) - for k, v := range table { - err := l.PushAny(v) - if err != nil { - return err - } - l.SetTableItem(k) - } - return nil -} - // PushArray recursively pushes an array of Go values onto the stack. func (l *Lua) PushArray(array []any) error { l.CreateTable(len(array)) @@ -333,45 +263,21 @@ func (l *Lua) Next() bool { // LoadAndCall loads and calls the code in the current Lua context. Whatever // the code returns is stored in the stack. To drop the results use l.Pop. -// FIXME: traceback handler remains dangling on the stack, we need to remove -// it somehow. func (l *Lua) LoadAndCall(code string) error { cstr := C.CString(code) defer C.free(unsafe.Pointer(cstr)) - l.PushTracebackHandler() if C.luaL_loadstring(l.l, cstr) != C.LUA_OK { errMsg := l.ToString(-1) l.Pop(1) return errors.New(errMsg) } - err := l.PCall(0, C.LUA_MULTRET, -2) + err := l.PCall(0, C.LUA_MULTRET, 1) if err != nil { return err } return nil } -// RestoreStackFunc remembers the Lua stack size and then restores it when a -// returned function is called. It's a helper function to avoid stack leakage. -func (l *Lua) RestoreStackFunc() func () { - before := l.StackLen() - return func () { - after := l.StackLen() - diff := after - before - if diff == 0 { - return - } else if diff < 0 { - msg := fmt.Sprintf( - "too many stack pops: len before: %d, after: %d\n", - before, - after, - ) - panic(msg) - } - C.lua_settop(l.l, C.int(before)) - } -} - // SetGlobal sets a global value at the -1 stack index with the name. func (l *Lua) SetGlobal(name string) { cstr := C.CString(name) @@ -392,16 +298,9 @@ func (l *Lua) NewThread(yield func(), resume func() bool) *Lua { running: l.running, resume: resume, yield: yield, - tracebackHandler: l.tracebackHandler, } } -// PushTracebackHandler puts the traceback handler onto the stack for using -// as a errfunc argument when calling PCall. -func (l *Lua) PushTracebackHandler() { - l.PushFromRef(l.tracebackHandler) -} - // TracebackHandler handles stack trace formatting on errors. func TracebackHandler(l *Lua) int { msg := l.ToString(-1) diff --git a/lua_common.go b/lua_common.go index 4e58d69..29fdab9 100644 --- a/lua_common.go +++ b/lua_common.go @@ -5,8 +5,82 @@ import ( "slices" "fmt" "runtime/cgo" + "time" + "errors" ) +const LUA_MULTRET = -1 + +// Require loads and executes the file pushing results onto the Lua stack. +func (l *Lua) Require(file string) error { + err := l.LoadFile(file) + if err != nil { + return errors.New("could not open the file:\n" + err.Error()) + } + err = l.PCall(0, LUA_MULTRET, 1) + if err != nil { + return errors.New("could not execute the file:\n" + err.Error()) + } + return nil +} + +// PushObject recursively pushes string->any Go table onto the stack. +func (l *Lua) PushObject(table map[string]any) error { + l.CreateTable(len(table)) + for k, v := range table { + err := l.PushAny(v) + if err != nil { + return err + } + l.SetTableItem(k) + } + return nil +} + +// PushAny pushes value v onto the stack. +func (l *Lua) PushAny(v any) error { + switch v.(type) { + case nil: + l.PushNil() + case string: + v, _ := v.(string) + l.PushString(v) + case func (l *Lua) int: + v, _ := v.(func (l *Lua) int) + l.PushGoFunction(v) + case int: + v, _ := v.(int) + l.PushNumber(v) + case int64: + v, _ := v.(int64) + l.PushNumber(int(v)) + case float64: + v, _ := v.(float64) + l.PushFloatNumber(v) + case bool: + v, _ := v.(bool) + l.PushBoolean(v) + case map[string]any: + v, _ := v.(map[string]any) + err := l.PushObject(v) + if err != nil { + return fmt.Errorf("object push error: ", err) + } + case []any: + v, _ := v.([]any) + err := l.PushArray(v) + if err != nil { + return fmt.Errorf("array push error: ", err) + } + case time.Time: + v, _ := v.(time.Time) + l.PushString(v.Format(time.DateTime)) + default: + return fmt.Errorf("unsupported value type: %T", v) + } + return nil +} + // Scan scans values from the Lua stack into vars according to their types. func (l *Lua) Scan(vars ...any) error { slices.Reverse(vars) @@ -137,3 +211,24 @@ func (l *Lua) Scan(vars ...any) error { } return nil } + +// RestoreStackFunc remembers the Lua stack size and then restores it when a +// returned function is called. It's a helper function to avoid stack leakage. +func (l *Lua) RestoreStackFunc() func () { + before := l.StackLen() + return func () { + after := l.StackLen() + diff := after - before + if diff == 0 { + return + } else if diff < 0 { + msg := fmt.Sprintf( + "too many stack pops: len before: %d, after: %d\n", + before, + after, + ) + panic(msg) + } + l.SetTop(before) + } +} diff --git a/luajit.go b/luajit.go index 0c51f13..2ed3788 100644 --- a/luajit.go +++ b/luajit.go @@ -24,20 +24,18 @@ static inline void luna_push_function(lua_State *l, uintptr_t f) { import "C" import ( "errors" - "fmt" "runtime/cgo" - "time" "unsafe" ) type LuaRef C.int + // Lua is a wrapper around C Lua state with several conveniences. type Lua struct { l *C.lua_State running **Lua yield func() resume func() bool - tracebackHandler LuaRef } //export luna_run_go_func @@ -53,9 +51,9 @@ func (l *Lua) Start() { l.running = &l C.luaL_openlibs(l.l) - // Put debug handler into the registry for later copies to the stack. + // Put traceback function at the start of the stack so it's always + // possible to refer to it via stack index 1. l.PushGoFunction(TracebackHandler) - l.tracebackHandler = l.PopToRef() } // Close closes the Lua context. @@ -63,23 +61,6 @@ func (l *Lua) Close() { C.lua_close(l.l) } -// Require loads and executes the file pushing results onto the Lua stack. -// FIXME: traceback handler remains dangling on the stack, we need to remove -// it somehow. -func (l *Lua) Require(file string) error { - l.PushTracebackHandler() - if !l.LoadFile(file) { - errMsg := l.ToString(-1) - l.Pop(1) - return errors.New("could not open the file:\n" + errMsg) - } - err := l.PCall(0, C.LUA_MULTRET, -2) - if err != nil { - return errors.New("could not execute the file:\n" + err.Error()) - } - return nil -} - // PCall calls a function with the stack index (-nargs-1) expecting nresults // number of results. func (l *Lua) PCall(nargs int, nresults int, errfunc int) error { @@ -94,11 +75,16 @@ func (l *Lua) PCall(nargs int, nresults int, errfunc int) error { } // LoadFile loads the file code in the current Lua context. -func (l *Lua) LoadFile(file string) bool { +func (l *Lua) LoadFile(file string) error { cstr := C.CString(file) defer C.free(unsafe.Pointer(cstr)) res := C.luaL_loadfilex(l.l, cstr, nil) - return res == C.LUA_OK + if res != C.LUA_OK { + errMsg := l.ToString(-1) + l.Pop(1) + return errors.New(errMsg) + } + return nil } // StackLen returns the length of the Lua stack. @@ -132,6 +118,11 @@ func (l *Lua) Pop(n int) { C.lua_settop(l.l, C.int(-n - 1)) } +// SetTop sets head of the stack at top. +func (l *Lua) SetTop(top int) { + C.lua_settop(l.l, C.int(top)) +} + // PushNil pushes tnil onto the Lua stack. func (l *Lua) PushNil() { C.lua_pushnil(l.l) @@ -202,63 +193,6 @@ func (l *Lua) RawGetI(tableOffset, i int) { C.lua_rawgeti(l.l, C.int(tableOffset), C.int(i)) } -// PushAny pushes value v onto the stack. -func (l *Lua) PushAny(v any) error { - switch v.(type) { - case nil: - l.PushNil() - case string: - v, _ := v.(string) - l.PushString(v) - case func (l *Lua) int: - v, _ := v.(func (l *Lua) int) - l.PushGoFunction(v) - case int: - v, _ := v.(int) - l.PushNumber(v) - case int64: - v, _ := v.(int64) - l.PushNumber(int(v)) - case float64: - v, _ := v.(float64) - l.PushFloatNumber(v) - case bool: - v, _ := v.(bool) - l.PushBoolean(v) - case map[string]any: - v, _ := v.(map[string]any) - err := l.PushObject(v) - if err != nil { - return fmt.Errorf("object push error: ", err) - } - case []any: - v, _ := v.([]any) - err := l.PushArray(v) - if err != nil { - return fmt.Errorf("array push error: ", err) - } - case time.Time: - v, _ := v.(time.Time) - l.PushString(v.Format(time.DateTime)) - default: - return fmt.Errorf("unsupported value type: %T", v) - } - return nil -} - -// PushObject recursively pushes string->any Go table onto the stack. -func (l *Lua) PushObject(table map[string]any) error { - l.CreateTable(len(table)) - for k, v := range table { - err := l.PushAny(v) - if err != nil { - return err - } - l.SetTableItem(k) - } - return nil -} - // PushArray recursively pushes an array of Go values onto the stack. func (l *Lua) PushArray(array []any) error { l.CreateTable(len(array)) @@ -326,45 +260,21 @@ func (l *Lua) Next() bool { // LoadAndCall loads and calls the code in the current Lua context. Whatever // the code returns is stored in the stack. To drop the results use l.Pop. -// FIXME: traceback handler remains dangling on the stack, we need to remove -// it somehow. func (l *Lua) LoadAndCall(code string) error { cstr := C.CString(code) defer C.free(unsafe.Pointer(cstr)) - l.PushTracebackHandler() if C.luaL_loadstring(l.l, cstr) != C.LUA_OK { errMsg := l.ToString(-1) l.Pop(1) return errors.New(errMsg) } - err := l.PCall(0, C.LUA_MULTRET, -2) + err := l.PCall(0, C.LUA_MULTRET, 1) if err != nil { return err } return nil } -// RestoreStackFunc remembers the Lua stack size and then restores it when a -// returned function is called. It's a helper function to avoid stack leakage. -func (l *Lua) RestoreStackFunc() func () { - before := l.StackLen() - return func () { - after := l.StackLen() - diff := after - before - if diff == 0 { - return - } else if diff < 0 { - msg := fmt.Sprintf( - "too many stack pops: len before: %d, after: %d\n", - before, - after, - ) - panic(msg) - } - C.lua_settop(l.l, C.int(before)) - } -} - // SetGlobal sets a global value at the -1 stack index with the name. func (l *Lua) SetGlobal(name string) { cstr := C.CString(name) @@ -385,16 +295,9 @@ func (l *Lua) NewThread(yield func(), resume func() bool) *Lua { running: l.running, resume: resume, yield: yield, - tracebackHandler: l.tracebackHandler, } } -// PushTracebackHandler puts the traceback handler onto the stack for using -// as a errfunc argument when calling PCall. -func (l *Lua) PushTracebackHandler() { - l.PushFromRef(l.tracebackHandler) -} - // TracebackHandler handles stack trace formatting on errors. func TracebackHandler(l *Lua) int { msg := l.ToString(-1) diff --git a/worker.go b/worker.go index 8f6c342..d252708 100644 --- a/worker.go +++ b/worker.go @@ -152,7 +152,6 @@ func (w *Worker) Listen(queue chan *HTTPRequest) { return } - l.PushTracebackHandler() l.PushFromRef(w.routes[r.route]) req := r.request res := make(map[string]any) @@ -263,7 +262,7 @@ func (w *Worker) Listen(queue chan *HTTPRequest) { return } - err = l.PCall(1, 3, -3) + err = l.PCall(1, 3, 1) if err != nil { var body string if debug { -- cgit v1.2.3