diff --git a/_glua-tests/issues.lua b/_glua-tests/issues.lua index d2f2e158..22eb8ba2 100644 --- a/_glua-tests/issues.lua +++ b/_glua-tests/issues.lua @@ -65,4 +65,52 @@ assert(string.match("あいうえお", ".*あ.*") == "あいうえお") assert(string.match("あいうえお", "あいうえお") == "あいうえお") -- issue 47 -assert(string.gsub("A\nA", ".", "A") == "AAA") \ No newline at end of file +assert(string.gsub("A\nA", ".", "A") == "AAA") + +-- issue 62 +local function level4() error("error!") end +local function level3() level4() end +local function level2() level3() end +local function level1() level2() end +local ok, result = xpcall(level1, function(err) + return debug.traceback("msg", 10) +end) +assert(result == [[msg +stack traceback:]]) +ok, result = xpcall(level1, function(err) + return debug.traceback("msg", 9) +end) +assert(result == string.gsub([[msg +stack traceback: +@TAB@[G]: ?]], "@TAB@", "\t")) +local ok, result = xpcall(level1, function(err) + return debug.traceback("msg", 0) +end) + +assert(result == string.gsub([[msg +stack traceback: +@TAB@[G]: in function 'traceback' +@TAB@issues.lua:87: in function +@TAB@[G]: in function 'error' +@TAB@issues.lua:71: in function 'level4' +@TAB@issues.lua:72: in function 'level3' +@TAB@issues.lua:73: in function 'level2' +@TAB@issues.lua:74: in function +@TAB@[G]: in function 'xpcall' +@TAB@issues.lua:86: in main chunk +@TAB@[G]: ?]], "@TAB@", "\t")) + +local ok, result = xpcall(level1, function(err) + return debug.traceback("msg", 3) +end) + +assert(result == string.gsub([[msg +stack traceback: +@TAB@issues.lua:71: in function 'level4' +@TAB@issues.lua:72: in function 'level3' +@TAB@issues.lua:73: in function 'level2' +@TAB@issues.lua:74: in function +@TAB@[G]: in function 'xpcall' +@TAB@issues.lua:103: in main chunk +@TAB@[G]: ?]], "@TAB@", "\t")) + diff --git a/_state.go b/_state.go index 1903b366..d1815220 100644 --- a/_state.go +++ b/_state.go @@ -330,6 +330,7 @@ func newLState(options Options) *LState { currentFrame: nil, wrapped: false, uvcache: nil, + hasErrorFunc: false, } ls.Env = ls.G.Global return ls @@ -379,7 +380,9 @@ func (ls *LState) closeAllUpvalues() { // +inline-start } // +inline-end func (ls *LState) raiseError(level int, format string, args ...interface{}) { - ls.closeAllUpvalues() + if !ls.hasErrorFunc { + ls.closeAllUpvalues() + } message := format if len(args) > 0 { message = fmt.Sprintf(format, args...) @@ -453,11 +456,11 @@ func (ls *LState) stackTrace(include bool) string { } } buf = append(buf, fmt.Sprintf("\t%v: %v", "[G]", "?")) - if len(buf) > 10 { + if len(buf) > 20 { newbuf := make([]string, 0, 20) newbuf = append(newbuf, buf[0:7]...) newbuf = append(newbuf, "\t...") - newbuf = append(newbuf, buf[len(buf)-7:len(buf)-1]...) + newbuf = append(newbuf, buf[len(buf)-7:len(buf)]...) buf = newbuf } ret := strings.Join(buf, "\n") @@ -467,12 +470,12 @@ func (ls *LState) stackTrace(include bool) string { func (ls *LState) formattedFrameFuncName(fr *callFrame) string { name, ischunk := ls.frameFuncName(fr) if ischunk { - if name[0] != '(' && name[0] != '<' { - return fmt.Sprintf("function '%s'", name) - } - return fmt.Sprintf("function %s", name) + return name } - return name + if name[0] != '(' && name[0] != '<' { + return fmt.Sprintf("function '%s'", name) + } + return fmt.Sprintf("function %s", name) } func (ls *LState) rawFrameFuncName(fr *callFrame) string { @@ -1225,7 +1228,9 @@ func (ls *LState) Error(lv LValue, level int) { if str, ok := lv.(LString); ok { ls.raiseError(level, string(str)) } else { - ls.closeAllUpvalues() + if !ls.hasErrorFunc { + ls.closeAllUpvalues() + } ls.Push(lv) ls.Panic(ls) } @@ -1520,8 +1525,12 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { base := ls.reg.Top() - nargs - 1 oldpanic := ls.Panic ls.Panic = panicWithoutTraceback + if errfunc != nil { + ls.hasErrorFunc = true + } defer func() { ls.Panic = oldpanic + ls.hasErrorFunc = false rcv := recover() if rcv != nil { if _, ok := rcv.(*ApiError); !ok { diff --git a/debuglib.go b/debuglib.go index 678a89a2..b0aef482 100644 --- a/debuglib.go +++ b/debuglib.go @@ -2,6 +2,7 @@ package lua import ( "fmt" + "strings" ) func OpenDebug(L *LState) int { @@ -151,10 +152,15 @@ func debugSetUpvalue(L *LState) int { func debugTraceback(L *LState) int { msg := L.OptString(1, "") + level := L.OptInt(2, 1) + stacktrace := strings.TrimSpace(L.stackTrace(true)) + lines := strings.Split(stacktrace, "\n") + header := lines[0] + lst := lines[intMax(intMin(len(lines)-1, level), 0)+1 : len(lines)] + traceback := fmt.Sprintf("%s\n%s", header, strings.Join(lst, "\n")) if len(msg) > 0 { - L.Push(LString(fmt.Sprintf("%s\n%s\n", msg, L.stackTrace(false)))) - } else { - L.Push(LString(L.stackTrace(false))) + traceback = fmt.Sprintf("%s\n%s", msg, traceback) } + L.Push(LString(strings.TrimSpace(traceback))) return 1 } diff --git a/state.go b/state.go index 312b6352..3c351af0 100644 --- a/state.go +++ b/state.go @@ -334,6 +334,7 @@ func newLState(options Options) *LState { currentFrame: nil, wrapped: false, uvcache: nil, + hasErrorFunc: false, } ls.Env = ls.G.Global return ls @@ -383,7 +384,9 @@ func (ls *LState) closeAllUpvalues() { // +inline-start } // +inline-end func (ls *LState) raiseError(level int, format string, args ...interface{}) { - ls.closeAllUpvalues() + if !ls.hasErrorFunc { + ls.closeAllUpvalues() + } message := format if len(args) > 0 { message = fmt.Sprintf(format, args...) @@ -457,11 +460,11 @@ func (ls *LState) stackTrace(include bool) string { } } buf = append(buf, fmt.Sprintf("\t%v: %v", "[G]", "?")) - if len(buf) > 10 { + if len(buf) > 20 { newbuf := make([]string, 0, 20) newbuf = append(newbuf, buf[0:7]...) newbuf = append(newbuf, "\t...") - newbuf = append(newbuf, buf[len(buf)-7:len(buf)-1]...) + newbuf = append(newbuf, buf[len(buf)-7:len(buf)]...) buf = newbuf } ret := strings.Join(buf, "\n") @@ -471,12 +474,12 @@ func (ls *LState) stackTrace(include bool) string { func (ls *LState) formattedFrameFuncName(fr *callFrame) string { name, ischunk := ls.frameFuncName(fr) if ischunk { - if name[0] != '(' && name[0] != '<' { - return fmt.Sprintf("function '%s'", name) - } - return fmt.Sprintf("function %s", name) + return name } - return name + if name[0] != '(' && name[0] != '<' { + return fmt.Sprintf("function '%s'", name) + } + return fmt.Sprintf("function %s", name) } func (ls *LState) rawFrameFuncName(fr *callFrame) string { @@ -1310,7 +1313,9 @@ func (ls *LState) Error(lv LValue, level int) { if str, ok := lv.(LString); ok { ls.raiseError(level, string(str)) } else { - ls.closeAllUpvalues() + if !ls.hasErrorFunc { + ls.closeAllUpvalues() + } ls.Push(lv) ls.Panic(ls) } @@ -1605,8 +1610,12 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) { base := ls.reg.Top() - nargs - 1 oldpanic := ls.Panic ls.Panic = panicWithoutTraceback + if errfunc != nil { + ls.hasErrorFunc = true + } defer func() { ls.Panic = oldpanic + ls.hasErrorFunc = false rcv := recover() if rcv != nil { if _, ok := rcv.(*ApiError); !ok { diff --git a/value.go b/value.go index 3ad8c842..d8dbac19 100644 --- a/value.go +++ b/value.go @@ -214,6 +214,7 @@ type LState struct { currentFrame *callFrame wrapped bool uvcache *Upvalue + hasErrorFunc bool } func (ls *LState) String() string { return fmt.Sprintf("thread: %p", ls) }