mirror of
https://github.com/vxcontrol/golua.git
synced 2026-07-01 22:14:01 -04:00
Custom Debug Hook in Go via "State.SetHook" (#99)
With this commit it's now possible to define a custom debug Hook in Go via the new Function "State.SetHook". Currently there is only one Hook per State which means that a call to "State.SetHook" will always override the previously set hook. This hook is always of type "LUA_MASKCOUNT" Co-authored-by: xf <xf@b-schuessler.de>
This commit is contained in:
+3
-3
@@ -342,11 +342,11 @@ void clua_openbase(lua_State* L)
|
||||
void clua_hook_function(lua_State *L, lua_Debug *ar)
|
||||
{
|
||||
lua_checkstack(L, 2);
|
||||
lua_pushstring(L, "Lua execution quantum exceeded");
|
||||
lua_error(L);
|
||||
size_t gostateindex = clua_getgostate(L);
|
||||
golua_callgohook(gostateindex);
|
||||
}
|
||||
|
||||
void clua_setexecutionlimit(lua_State* L, int n)
|
||||
void clua_sethook(lua_State* L, int n)
|
||||
{
|
||||
lua_sethook(L, &clua_hook_function, LUA_MASKCOUNT, n);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ type Alloc func(ptr unsafe.Pointer, osize uint, nsize uint) unsafe.Pointer
|
||||
// This is the type of go function that can be registered as lua functions
|
||||
type LuaGoFunction func(L *State) int
|
||||
|
||||
// This is the type of a go function that can be used as a lua_Hook
|
||||
type HookFunction func(L *State)
|
||||
|
||||
// The errorstring used by State.SetExecutionLimit
|
||||
const ExecutionQuantumExceeded = "Lua execution quantum exceeded"
|
||||
|
||||
// Wrapper to keep cgo from complaining about incomplete ptr type
|
||||
//export State
|
||||
type State struct {
|
||||
@@ -41,6 +47,9 @@ type State struct {
|
||||
|
||||
// User self defined memory alloc func for the lua State
|
||||
allocfn *Alloc
|
||||
|
||||
// User defined hook function
|
||||
hookFn HookFunction
|
||||
}
|
||||
|
||||
var goStates map[uintptr]*State
|
||||
@@ -79,6 +88,14 @@ func golua_callgofunction(gostateindex uintptr, fid uint) int {
|
||||
return f(L1)
|
||||
}
|
||||
|
||||
//export golua_callgohook
|
||||
func golua_callgohook(gostateindex uintptr) {
|
||||
L1 := getGoState(gostateindex)
|
||||
if L1.hookFn != nil {
|
||||
L1.hookFn(L1)
|
||||
}
|
||||
}
|
||||
|
||||
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
||||
|
||||
//export golua_interface_newindex_callback
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ void clua_openpackage(lua_State* L);
|
||||
void clua_openstring(lua_State* L);
|
||||
void clua_opentable(lua_State* L);
|
||||
void clua_openos(lua_State* L);
|
||||
void clua_setexecutionlimit(lua_State* L, int n);
|
||||
void clua_sethook(lua_State* L, int n);
|
||||
|
||||
int clua_isgofunction(lua_State *L, int n);
|
||||
int clua_isgostruct(lua_State *L, int n);
|
||||
|
||||
+14
-4
@@ -56,7 +56,7 @@ type LuaStackEntry struct {
|
||||
}
|
||||
|
||||
func newState(L *C.lua_State) *State {
|
||||
newstate := &State{L, 0, make([]interface{}, 0, 8), make([]uint, 0, 8), nil}
|
||||
newstate := &State{L, 0, make([]interface{}, 0, 8), make([]uint, 0, 8), nil, nil}
|
||||
registerGoState(newstate)
|
||||
C.clua_setgostate(L, C.size_t(newstate.Index))
|
||||
C.clua_initstate(L)
|
||||
@@ -351,7 +351,7 @@ func (L *State) NewThread() *State {
|
||||
//TODO: should have same lists as parent
|
||||
// but may complicate gc
|
||||
s := C.lua_newthread(L.s)
|
||||
return &State{s, 0, nil, nil, nil}
|
||||
return &State{s, 0, nil, nil, nil, nil}
|
||||
}
|
||||
|
||||
// lua_next
|
||||
@@ -578,9 +578,19 @@ func (L *State) OpenOS() {
|
||||
C.clua_openos(L.s)
|
||||
}
|
||||
|
||||
// Sets the lua hook (lua_sethook).
|
||||
// This and SetExecutionLimit are mutual exclusive
|
||||
func (L *State) SetHook(f HookFunction, instrNumber int) {
|
||||
L.hookFn = f
|
||||
C.clua_sethook(L.s, C.int(instrNumber))
|
||||
}
|
||||
|
||||
// Sets the maximum number of operations to execute at instrNumber, after this the execution ends
|
||||
// This and SetHook are mutual exclusive
|
||||
func (L *State) SetExecutionLimit(instrNumber int) {
|
||||
C.clua_setexecutionlimit(L.s, C.int(instrNumber))
|
||||
L.SetHook(func(l *State) {
|
||||
l.RaiseError(ExecutionQuantumExceeded)
|
||||
}, instrNumber)
|
||||
}
|
||||
|
||||
// Returns the current stack trace
|
||||
@@ -611,7 +621,7 @@ func (L *State) StackTrace() []LuaStackEntry {
|
||||
func (L *State) RaiseError(msg string) {
|
||||
st := L.StackTrace()
|
||||
prefix := ""
|
||||
if len(st) >= 1 {
|
||||
if len(st) >= 2 {
|
||||
prefix = fmt.Sprintf("%s:%d: ", st[1].ShortSource, st[1].CurrentLine)
|
||||
}
|
||||
panic(&LuaError{0, prefix + msg, st})
|
||||
|
||||
@@ -411,3 +411,27 @@ func TestDumpAndLoad(t *testing.T) {
|
||||
t.Fatalf("Call error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomDebugHook(t *testing.T) {
|
||||
L := NewState()
|
||||
defer L.Close()
|
||||
|
||||
L.SetHook(func(l *State) {
|
||||
l.RaiseError("stop")
|
||||
}, 1)
|
||||
|
||||
err := L.DoString(`
|
||||
local x = 0
|
||||
while(1 ~= 0) do
|
||||
x = 2
|
||||
end
|
||||
`)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Script should have raised an error")
|
||||
} else {
|
||||
if err.Error() != "stop" {
|
||||
t.Fatal("Error should be coming from the hook")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user