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:
Xaver Fischer
2021-05-07 15:07:08 +02:00
committed by GitHub
parent f22fa65a4e
commit 11106aa577
5 changed files with 59 additions and 8 deletions
+3 -3
View File
@@ -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);
}
+17
View File
@@ -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
View File
@@ -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
View File
@@ -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})
+24
View File
@@ -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")
}
}
}