summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fennel.lua1
-rw-r--r--lua.go119
-rw-r--r--lua_common.go95
-rw-r--r--luajit.go129
-rw-r--r--worker.go3
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 {