| -- $Id: testes/db.lua $ |
| -- See Copyright Notice in file all.lua |
| |
| -- testing debug library |
| |
| local debug = require "debug" |
| |
| local function dostring(s) return assert(load(s))() end |
| |
| print"testing debug library and debug information" |
| |
| do |
| local a=1 |
| end |
| |
| assert(not debug.gethook()) |
| |
| local testline = 19 -- line where 'test' is defined |
| function test (s, l, p) -- this must be line 19 |
| collectgarbage() -- avoid gc during trace |
| local function f (event, line) |
| assert(event == 'line') |
| local l = table.remove(l, 1) |
| if p then print(l, line) end |
| assert(l == line, "wrong trace!!") |
| end |
| debug.sethook(f,"l"); load(s)(); debug.sethook() |
| assert(#l == 0) |
| end |
| |
| |
| do |
| assert(not pcall(debug.getinfo, print, "X")) -- invalid option |
| assert(not debug.getinfo(1000)) -- out of range level |
| assert(not debug.getinfo(-1)) -- out of range level |
| local a = debug.getinfo(print) |
| assert(a.what == "C" and a.short_src == "[C]") |
| a = debug.getinfo(print, "L") |
| assert(a.activelines == nil) |
| local b = debug.getinfo(test, "SfL") |
| assert(b.name == nil and b.what == "Lua" and b.linedefined == testline and |
| b.lastlinedefined == b.linedefined + 10 and |
| b.func == test and not string.find(b.short_src, "%[")) |
| assert(b.activelines[b.linedefined + 1] and |
| b.activelines[b.lastlinedefined]) |
| assert(not b.activelines[b.linedefined] and |
| not b.activelines[b.lastlinedefined + 1]) |
| end |
| |
| |
| -- test file and string names truncation |
| a = "function f () end" |
| local function dostring (s, x) return load(s, x)() end |
| dostring(a) |
| assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a)) |
| dostring(a..string.format("; %s\n=1", string.rep('p', 400))) |
| assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) |
| dostring(a..string.format("; %s=1", string.rep('p', 400))) |
| assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) |
| dostring("\n"..a) |
| assert(debug.getinfo(f).short_src == '[string "..."]') |
| dostring(a, "") |
| assert(debug.getinfo(f).short_src == '[string ""]') |
| dostring(a, "@xuxu") |
| assert(debug.getinfo(f).short_src == "xuxu") |
| dostring(a, "@"..string.rep('p', 1000)..'t') |
| assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$")) |
| dostring(a, "=xuxu") |
| assert(debug.getinfo(f).short_src == "xuxu") |
| dostring(a, string.format("=%s", string.rep('x', 500))) |
| assert(string.find(debug.getinfo(f).short_src, "^x*$")) |
| dostring(a, "=") |
| assert(debug.getinfo(f).short_src == "") |
| a = nil; f = nil; |
| |
| |
| repeat |
| local g = {x = function () |
| local a = debug.getinfo(2) |
| assert(a.name == 'f' and a.namewhat == 'local') |
| a = debug.getinfo(1) |
| assert(a.name == 'x' and a.namewhat == 'field') |
| return 'xixi' |
| end} |
| local f = function () return 1+1 and (not 1 or g.x()) end |
| assert(f() == 'xixi') |
| g = debug.getinfo(f) |
| assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name) |
| |
| function f (x, name) -- local! |
| name = name or 'f' |
| local a = debug.getinfo(1) |
| assert(a.name == name and a.namewhat == 'local') |
| return x |
| end |
| |
| -- breaks in different conditions |
| if 3>4 then break end; f() |
| if 3<4 then a=1 else break end; f() |
| while 1 do local x=10; break end; f() |
| local b = 1 |
| if 3>4 then return math.sin(1) end; f() |
| a = 3<4; f() |
| a = 3<4 or 1; f() |
| repeat local x=20; if 4>3 then f() else break end; f() until 1 |
| g = {} |
| f(g).x = f(2) and f(10)+f(9) |
| assert(g.x == f(19)) |
| function g(x) if not x then return 3 end return (x('a', 'x')) end |
| assert(g(f) == 'a') |
| until 1 |
| |
| test([[if |
| math.sin(1) |
| then |
| a=1 |
| else |
| a=2 |
| end |
| ]], {2,3,4,7}) |
| |
| test([[-- |
| if nil then |
| a=1 |
| else |
| a=2 |
| end |
| ]], {2,5,6}) |
| |
| test([[a=1 |
| repeat |
| a=a+1 |
| until a==3 |
| ]], {1,3,4,3,4}) |
| |
| test([[ do |
| return |
| end |
| ]], {2}) |
| |
| test([[local a |
| a=1 |
| while a<=3 do |
| a=a+1 |
| end |
| ]], {1,2,3,4,3,4,3,4,3,5}) |
| |
| test([[while math.sin(1) do |
| if math.sin(1) |
| then break |
| end |
| end |
| a=1]], {1,2,3,6}) |
| |
| test([[for i=1,3 do |
| a=i |
| end |
| ]], {1,2,1,2,1,2,1,3}) |
| |
| test([[for i,v in pairs{'a','b'} do |
| a=tostring(i) .. v |
| end |
| ]], {1,2,1,2,1,3}) |
| |
| test([[for i=1,4 do a=1 end]], {1,1,1,1,1}) |
| |
| |
| do -- testing line info/trace with large gaps in source |
| |
| local a = {1, 2, 3, 10, 124, 125, 126, 127, 128, 129, 130, |
| 255, 256, 257, 500, 1000} |
| local s = [[ |
| local b = {10} |
| a = b[1] X + Y b[1] |
| b = 4 |
| ]] |
| for _, i in ipairs(a) do |
| local subs = {X = string.rep("\n", i)} |
| for _, j in ipairs(a) do |
| subs.Y = string.rep("\n", j) |
| local s = string.gsub(s, "[XY]", subs) |
| test(s, {1, 2 + i, 2 + i + j, 2 + i, 2 + i + j, 3 + i + j}) |
| end |
| end |
| end |
| |
| print'+' |
| |
| -- invalid levels in [gs]etlocal |
| assert(not pcall(debug.getlocal, 20, 1)) |
| assert(not pcall(debug.setlocal, -1, 1, 10)) |
| |
| |
| -- parameter names |
| local function foo (a,b,...) local d, e end |
| local co = coroutine.create(foo) |
| |
| assert(debug.getlocal(foo, 1) == 'a') |
| assert(debug.getlocal(foo, 2) == 'b') |
| assert(not debug.getlocal(foo, 3)) |
| assert(debug.getlocal(co, foo, 1) == 'a') |
| assert(debug.getlocal(co, foo, 2) == 'b') |
| assert(not debug.getlocal(co, foo, 3)) |
| |
| assert(not debug.getlocal(print, 1)) |
| |
| |
| local function foo () return (debug.getlocal(1, -1)) end |
| assert(not foo(10)) |
| |
| |
| -- varargs |
| local function foo (a, ...) |
| local t = table.pack(...) |
| for i = 1, t.n do |
| local n, v = debug.getlocal(1, -i) |
| assert(n == "(*vararg)" and v == t[i]) |
| end |
| assert(not debug.getlocal(1, -(t.n + 1))) |
| assert(not debug.setlocal(1, -(t.n + 1), 30)) |
| if t.n > 0 then |
| (function (x) |
| assert(debug.setlocal(2, -1, x) == "(*vararg)") |
| assert(debug.setlocal(2, -t.n, x) == "(*vararg)") |
| end)(430) |
| assert(... == 430) |
| end |
| end |
| |
| foo() |
| foo(print) |
| foo(200, 3, 4) |
| local a = {} |
| for i = 1, (_soft and 100 or 1000) do a[i] = i end |
| foo(table.unpack(a)) |
| a = nil |
| |
| |
| |
| do -- test hook presence in debug info |
| assert(not debug.gethook()) |
| local count = 0 |
| local function f () |
| assert(debug.getinfo(1).namewhat == "hook") |
| local sndline = string.match(debug.traceback(), "\n(.-)\n") |
| assert(string.find(sndline, "hook")) |
| count = count + 1 |
| end |
| debug.sethook(f, "l") |
| local a = 0 |
| _ENV.a = a |
| a = 1 |
| debug.sethook() |
| assert(count == 4) |
| end |
| |
| |
| a = {}; L = nil |
| local glob = 1 |
| local oldglob = glob |
| debug.sethook(function (e,l) |
| collectgarbage() -- force GC during a hook |
| local f, m, c = debug.gethook() |
| assert(m == 'crl' and c == 0) |
| if e == "line" then |
| if glob ~= oldglob then |
| L = l-1 -- get the first line where "glob" has changed |
| oldglob = glob |
| end |
| elseif e == "call" then |
| local f = debug.getinfo(2, "f").func |
| a[f] = 1 |
| else assert(e == "return") |
| end |
| end, "crl") |
| |
| |
| function f(a,b) |
| collectgarbage() |
| local _, x = debug.getlocal(1, 1) |
| local _, y = debug.getlocal(1, 2) |
| assert(x == a and y == b) |
| assert(debug.setlocal(2, 3, "pera") == "AA".."AA") |
| assert(debug.setlocal(2, 4, "maçã") == "B") |
| x = debug.getinfo(2) |
| assert(x.func == g and x.what == "Lua" and x.name == 'g' and |
| x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) |
| glob = glob+1 |
| assert(debug.getinfo(1, "l").currentline == L+1) |
| assert(debug.getinfo(1, "l").currentline == L+2) |
| end |
| |
| function foo() |
| glob = glob+1 |
| assert(debug.getinfo(1, "l").currentline == L+1) |
| end; foo() -- set L |
| -- check line counting inside strings and empty lines |
| |
| _ = 'alo\ |
| alo' .. [[ |
| |
| ]] |
| --[[ |
| ]] |
| assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines |
| |
| |
| function g (...) |
| local arg = {...} |
| do local a,b,c; a=math.sin(40); end |
| local feijao |
| local AAAA,B = "xuxu", "mamão" |
| f(AAAA,B) |
| assert(AAAA == "pera" and B == "maçã") |
| do |
| local B = 13 |
| local x,y = debug.getlocal(1,5) |
| assert(x == 'B' and y == 13) |
| end |
| end |
| |
| g() |
| |
| |
| assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print]) |
| |
| |
| -- tests for manipulating non-registered locals (C and Lua temporaries) |
| |
| local n, v = debug.getlocal(0, 1) |
| assert(v == 0 and n == "(*temporary)") |
| local n, v = debug.getlocal(0, 2) |
| assert(v == 2 and n == "(*temporary)") |
| assert(not debug.getlocal(0, 3)) |
| assert(not debug.getlocal(0, 0)) |
| |
| function f() |
| assert(select(2, debug.getlocal(2,3)) == 1) |
| assert(not debug.getlocal(2,4)) |
| debug.setlocal(2, 3, 10) |
| return 20 |
| end |
| |
| function g(a,b) return (a+1) + f() end |
| |
| assert(g(0,0) == 30) |
| |
| |
| debug.sethook(nil); |
| assert(debug.gethook() == nil) |
| |
| |
| -- minimal tests for setuservalue/getuservalue |
| do |
| assert(debug.setuservalue(io.stdin, 10) == nil) |
| local a, b = debug.getuservalue(io.stdin, 10) |
| assert(a == nil and not b) |
| end |
| |
| -- testing iteraction between multiple values x hooks |
| do |
| local function f(...) return 3, ... end |
| local count = 0 |
| local a = {} |
| for i = 1, 100 do a[i] = i end |
| debug.sethook(function () count = count + 1 end, "", 1) |
| local t = {table.unpack(a)} |
| assert(#t == 100) |
| t = {table.unpack(a, 1, 3)} |
| assert(#t == 3) |
| t = {f(table.unpack(a, 1, 30))} |
| assert(#t == 31) |
| end |
| |
| |
| -- testing access to function arguments |
| |
| local function collectlocals (level) |
| local tab = {} |
| for i = 1, math.huge do |
| local n, v = debug.getlocal(level + 1, i) |
| if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then |
| break -- consider only real variables |
| end |
| tab[n] = v |
| end |
| return tab |
| end |
| |
| |
| X = nil |
| a = {} |
| function a:f (a, b, ...) local arg = {...}; local c = 13 end |
| debug.sethook(function (e) |
| assert(e == "call") |
| dostring("XX = 12") -- test dostring inside hooks |
| -- testing errors inside hooks |
| assert(not pcall(load("a='joao'+1"))) |
| debug.sethook(function (e, l) |
| assert(debug.getinfo(2, "l").currentline == l) |
| local f,m,c = debug.gethook() |
| assert(e == "line") |
| assert(m == 'l' and c == 0) |
| debug.sethook(nil) -- hook is called only once |
| assert(not X) -- check that |
| X = collectlocals(2) |
| end, "l") |
| end, "c") |
| |
| a:f(1,2,3,4,5) |
| assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil) |
| assert(XX == 12) |
| assert(debug.gethook() == nil) |
| |
| |
| -- testing access to local variables in return hook (bug in 5.2) |
| do |
| local X = false |
| |
| local function foo (a, b, ...) |
| do local x,y,z end |
| local c, d = 10, 20 |
| return |
| end |
| |
| local function aux () |
| if debug.getinfo(2).name == "foo" then |
| X = true -- to signal that it found 'foo' |
| local tab = {a = 100, b = 200, c = 10, d = 20} |
| for n, v in pairs(collectlocals(2)) do |
| assert(tab[n] == v) |
| tab[n] = undef |
| end |
| assert(next(tab) == nil) -- 'tab' must be empty |
| end |
| end |
| |
| debug.sethook(aux, "r"); foo(100, 200); debug.sethook() |
| assert(X) |
| |
| end |
| |
| |
| local function eqseq (t1, t2) |
| assert(#t1 == #t2) |
| for i = 1, #t1 do |
| assert(t1[i] == t2[i]) |
| end |
| end |
| |
| |
| do print("testing inspection of parameters/returned values") |
| local on = false |
| local inp, out |
| |
| local function hook (event) |
| if not on then return end |
| local ar = debug.getinfo(2, "ruS") |
| local t = {} |
| for i = ar.ftransfer, ar.ftransfer + ar.ntransfer - 1 do |
| local _, v = debug.getlocal(2, i) |
| t[#t + 1] = v |
| end |
| if event == "return" then |
| out = t |
| else |
| inp = t |
| end |
| end |
| |
| debug.sethook(hook, "cr") |
| |
| on = true; math.sin(3); on = false |
| eqseq(inp, {3}); eqseq(out, {math.sin(3)}) |
| |
| on = true; select(2, 10, 20, 30, 40); on = false |
| eqseq(inp, {2, 10, 20, 30, 40}); eqseq(out, {20, 30, 40}) |
| |
| local function foo (a, ...) return ... end |
| local function foo1 () on = not on; return foo(20, 10, 0) end |
| foo1(); on = false |
| eqseq(inp, {20}); eqseq(out, {10, 0}) |
| |
| debug.sethook() |
| end |
| |
| |
| |
| -- testing upvalue access |
| local function getupvalues (f) |
| local t = {} |
| local i = 1 |
| while true do |
| local name, value = debug.getupvalue(f, i) |
| if not name then break end |
| assert(not t[name]) |
| t[name] = value |
| i = i + 1 |
| end |
| return t |
| end |
| |
| local a,b,c = 1,2,3 |
| local function foo1 (a) b = a; return c end |
| local function foo2 (x) a = x; return c+b end |
| assert(not debug.getupvalue(foo1, 3)) |
| assert(not debug.getupvalue(foo1, 0)) |
| assert(not debug.setupvalue(foo1, 3, "xuxu")) |
| local t = getupvalues(foo1) |
| assert(t.a == nil and t.b == 2 and t.c == 3) |
| t = getupvalues(foo2) |
| assert(t.a == 1 and t.b == 2 and t.c == 3) |
| assert(debug.setupvalue(foo1, 1, "xuxu") == "b") |
| assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu") |
| -- upvalues of C functions are allways "called" "" (the empty string) |
| assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "") |
| |
| |
| -- testing count hooks |
| local a=0 |
| debug.sethook(function (e) a=a+1 end, "", 1) |
| a=0; for i=1,1000 do end; assert(1000 < a and a < 1012) |
| debug.sethook(function (e) a=a+1 end, "", 4) |
| a=0; for i=1,1000 do end; assert(250 < a and a < 255) |
| local f,m,c = debug.gethook() |
| assert(m == "" and c == 4) |
| debug.sethook(function (e) a=a+1 end, "", 4000) |
| a=0; for i=1,1000 do end; assert(a == 0) |
| |
| do |
| debug.sethook(print, "", 2^24 - 1) -- count upperbound |
| local f,m,c = debug.gethook() |
| assert(({debug.gethook()})[3] == 2^24 - 1) |
| end |
| |
| debug.sethook() |
| |
| |
| -- tests for tail calls |
| local function f (x) |
| if x then |
| assert(debug.getinfo(1, "S").what == "Lua") |
| assert(debug.getinfo(1, "t").istailcall == true) |
| local tail = debug.getinfo(2) |
| assert(tail.func == g1 and tail.istailcall == true) |
| assert(debug.getinfo(3, "S").what == "main") |
| print"+" |
| end |
| end |
| |
| function g(x) return f(x) end |
| |
| function g1(x) g(x) end |
| |
| local function h (x) local f=g1; return f(x) end |
| |
| h(true) |
| |
| local b = {} |
| debug.sethook(function (e) table.insert(b, e) end, "cr") |
| h(false) |
| debug.sethook() |
| local res = {"return", -- first return (from sethook) |
| "call", "tail call", "call", "tail call", |
| "return", "return", |
| "call", -- last call (to sethook) |
| } |
| for i = 1, #res do assert(res[i] == table.remove(b, 1)) end |
| |
| b = 0 |
| debug.sethook(function (e) |
| if e == "tail call" then |
| b = b + 1 |
| assert(debug.getinfo(2, "t").istailcall == true) |
| else |
| assert(debug.getinfo(2, "t").istailcall == false) |
| end |
| end, "c") |
| h(false) |
| debug.sethook() |
| assert(b == 2) -- two tail calls |
| |
| lim = _soft and 3000 or 30000 |
| local function foo (x) |
| if x==0 then |
| assert(debug.getinfo(2).what == "main") |
| local info = debug.getinfo(1) |
| assert(info.istailcall == true and info.func == foo) |
| else return foo(x-1) |
| end |
| end |
| |
| foo(lim) |
| |
| |
| print"+" |
| |
| |
| -- testing local function information |
| co = load[[ |
| local A = function () |
| return x |
| end |
| return |
| ]] |
| |
| local a = 0 |
| -- 'A' should be visible to debugger only after its complete definition |
| debug.sethook(function (e, l) |
| if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(*temporary)") |
| elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A") |
| end |
| end, "l") |
| co() -- run local function definition |
| debug.sethook() -- turn off hook |
| assert(a == 2) -- ensure all two lines where hooked |
| |
| -- testing traceback |
| |
| assert(debug.traceback(print) == print) |
| assert(debug.traceback(print, 4) == print) |
| assert(string.find(debug.traceback("hi", 4), "^hi\n")) |
| assert(string.find(debug.traceback("hi"), "^hi\n")) |
| assert(not string.find(debug.traceback("hi"), "'debug.traceback'")) |
| assert(string.find(debug.traceback("hi", 0), "'debug.traceback'")) |
| assert(string.find(debug.traceback(), "^stack traceback:\n")) |
| |
| do -- C-function names in traceback |
| local st, msg = (function () return pcall end)()(debug.traceback) |
| assert(st == true and string.find(msg, "pcall")) |
| end |
| |
| |
| -- testing nparams, nups e isvararg |
| local t = debug.getinfo(print, "u") |
| assert(t.isvararg == true and t.nparams == 0 and t.nups == 0) |
| |
| t = debug.getinfo(function (a,b,c) end, "u") |
| assert(t.isvararg == false and t.nparams == 3 and t.nups == 0) |
| |
| t = debug.getinfo(function (a,b,...) return t[a] end, "u") |
| assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) |
| |
| t = debug.getinfo(1) -- main |
| assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and |
| debug.getupvalue(t.func, 1) == "_ENV") |
| |
| |
| |
| |
| -- testing debugging of coroutines |
| |
| local function checktraceback (co, p, level) |
| local tb = debug.traceback(co, nil, level) |
| local i = 0 |
| for l in string.gmatch(tb, "[^\n]+\n?") do |
| assert(i == 0 or string.find(l, p[i])) |
| i = i+1 |
| end |
| assert(p[i] == undef) |
| end |
| |
| |
| local function f (n) |
| if n > 0 then f(n-1) |
| else coroutine.yield() end |
| end |
| |
| local co = coroutine.create(f) |
| coroutine.resume(co, 3) |
| checktraceback(co, {"yield", "db.lua", "db.lua", "db.lua", "db.lua"}) |
| checktraceback(co, {"db.lua", "db.lua", "db.lua", "db.lua"}, 1) |
| checktraceback(co, {"db.lua", "db.lua", "db.lua"}, 2) |
| checktraceback(co, {"db.lua"}, 4) |
| checktraceback(co, {}, 40) |
| |
| |
| co = coroutine.create(function (x) |
| local a = 1 |
| coroutine.yield(debug.getinfo(1, "l")) |
| coroutine.yield(debug.getinfo(1, "l").currentline) |
| return a |
| end) |
| |
| local tr = {} |
| local foo = function (e, l) if l then table.insert(tr, l) end end |
| debug.sethook(co, foo, "lcr") |
| |
| local _, l = coroutine.resume(co, 10) |
| local x = debug.getinfo(co, 1, "lfLS") |
| assert(x.currentline == l.currentline and x.activelines[x.currentline]) |
| assert(type(x.func) == "function") |
| for i=x.linedefined + 1, x.lastlinedefined do |
| assert(x.activelines[i]) |
| x.activelines[i] = undef |
| end |
| assert(next(x.activelines) == nil) -- no 'extra' elements |
| assert(not debug.getinfo(co, 2)) |
| local a,b = debug.getlocal(co, 1, 1) |
| assert(a == "x" and b == 10) |
| a,b = debug.getlocal(co, 1, 2) |
| assert(a == "a" and b == 1) |
| debug.setlocal(co, 1, 2, "hi") |
| assert(debug.gethook(co) == foo) |
| assert(#tr == 2 and |
| tr[1] == l.currentline-1 and tr[2] == l.currentline) |
| |
| a,b,c = pcall(coroutine.resume, co) |
| assert(a and b and c == l.currentline+1) |
| checktraceback(co, {"yield", "in function <"}) |
| |
| a,b = coroutine.resume(co) |
| assert(a and b == "hi") |
| assert(#tr == 4 and tr[4] == l.currentline+2) |
| assert(debug.gethook(co) == foo) |
| assert(not debug.gethook()) |
| checktraceback(co, {}) |
| |
| |
| -- check get/setlocal in coroutines |
| co = coroutine.create(function (x) |
| local a, b = coroutine.yield(x) |
| assert(a == 100 and b == nil) |
| return x |
| end) |
| a, b = coroutine.resume(co, 10) |
| assert(a and b == 10) |
| a, b = debug.getlocal(co, 1, 1) |
| assert(a == "x" and b == 10) |
| assert(not debug.getlocal(co, 1, 5)) |
| assert(debug.setlocal(co, 1, 1, 30) == "x") |
| assert(not debug.setlocal(co, 1, 5, 40)) |
| a, b = coroutine.resume(co, 100) |
| assert(a and b == 30) |
| |
| |
| -- check traceback of suspended (or dead with error) coroutines |
| |
| function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end |
| |
| co = coroutine.create(function (x) f(x) end) |
| a, b = coroutine.resume(co, 3) |
| t = {"'coroutine.yield'", "'f'", "in function <"} |
| while coroutine.status(co) == "suspended" do |
| checktraceback(co, t) |
| a, b = coroutine.resume(co) |
| table.insert(t, 2, "'f'") -- one more recursive call to 'f' |
| end |
| t[1] = "'error'" |
| checktraceback(co, t) |
| |
| |
| -- test acessing line numbers of a coroutine from a resume inside |
| -- a C function (this is a known bug in Lua 5.0) |
| |
| local function g(x) |
| coroutine.yield(x) |
| end |
| |
| local function f (i) |
| debug.sethook(function () end, "l") |
| for j=1,1000 do |
| g(i+j) |
| end |
| end |
| |
| local co = coroutine.wrap(f) |
| co(10) |
| pcall(co) |
| pcall(co) |
| |
| |
| assert(type(debug.getregistry()) == "table") |
| |
| |
| -- test tagmethod information |
| local a = {} |
| local function f (t) |
| local info = debug.getinfo(1); |
| assert(info.namewhat == "metamethod") |
| a.op = info.name |
| return info.name |
| end |
| setmetatable(a, { |
| __index = f; __add = f; __div = f; __mod = f; __concat = f; __pow = f; |
| __mul = f; __idiv = f; __unm = f; __len = f; __sub = f; |
| __shl = f; __shr = f; __bor = f; __bxor = f; |
| __eq = f; __le = f; __lt = f; __unm = f; __len = f; __band = f; |
| __bnot = f; |
| }) |
| |
| local b = setmetatable({}, getmetatable(a)) |
| |
| assert(a[3] == "index" and a^3 == "pow" and a..a == "concat") |
| assert(a/3 == "div" and 3%a == "mod") |
| assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and |
| -a == "unm" and #a == "len" and a&3 == "band") |
| assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shift" and |
| a>>1 == "shift") |
| assert (a==b and a.op == "eq") |
| assert (a>=b and a.op == "order") |
| assert (a>b and a.op == "order") |
| assert(~a == "bnot") |
| |
| do -- testing for-iterator name |
| local function f() |
| assert(debug.getinfo(1).name == "for iterator") |
| end |
| |
| for i in f do end |
| end |
| |
| |
| do -- testing debug info for finalizers |
| local name = nil |
| |
| -- create a piece of garbage with a finalizer |
| setmetatable({}, {__gc = function () |
| local t = debug.getinfo(2) -- get callee information |
| assert(t.namewhat == "metamethod") |
| name = t.name |
| end}) |
| |
| -- repeat until previous finalizer runs (setting 'name') |
| repeat local a = {} until name |
| assert(name == "__gc") |
| end |
| |
| |
| do |
| print("testing traceback sizes") |
| |
| local function countlines (s) |
| return select(2, string.gsub(s, "\n", "")) |
| end |
| |
| local function deep (lvl, n) |
| if lvl == 0 then |
| return (debug.traceback("message", n)) |
| else |
| return (deep(lvl-1, n)) |
| end |
| end |
| |
| local function checkdeep (total, start) |
| local s = deep(total, start) |
| local rest = string.match(s, "^message\nstack traceback:\n(.*)$") |
| local cl = countlines(rest) |
| -- at most 10 lines in first part, 11 in second, plus '...' |
| assert(cl <= 10 + 11 + 1) |
| local brk = string.find(rest, "%.%.%.") |
| if brk then -- does message have '...'? |
| local rest1 = string.sub(rest, 1, brk) |
| local rest2 = string.sub(rest, brk, #rest) |
| assert(countlines(rest1) == 10 and countlines(rest2) == 11) |
| else |
| assert(cl == total - start + 2) |
| end |
| end |
| |
| for d = 1, 51, 10 do |
| for l = 1, d do |
| -- use coroutines to ensure complete control of the stack |
| coroutine.wrap(checkdeep)(d, l) |
| end |
| end |
| |
| end |
| |
| |
| print("testing debug functions on chunk without debug info") |
| prog = [[-- program to be loaded without debug information |
| local debug = require'debug' |
| local a = 12 -- a local variable |
| |
| local n, v = debug.getlocal(1, 1) |
| assert(n == "(*temporary)" and v == debug) -- unkown name but known value |
| n, v = debug.getlocal(1, 2) |
| assert(n == "(*temporary)" and v == 12) -- unkown name but known value |
| |
| -- a function with an upvalue |
| local f = function () local x; return a end |
| n, v = debug.getupvalue(f, 1) |
| assert(n == "(*no name)" and v == 12) |
| assert(debug.setupvalue(f, 1, 13) == "(*no name)") |
| assert(a == 13) |
| |
| local t = debug.getinfo(f) |
| assert(t.name == nil and t.linedefined > 0 and |
| t.lastlinedefined == t.linedefined and |
| t.short_src == "?") |
| assert(debug.getinfo(1).currentline == -1) |
| |
| t = debug.getinfo(f, "L").activelines |
| assert(next(t) == nil) -- active lines are empty |
| |
| -- dump/load a function without debug info |
| f = load(string.dump(f)) |
| |
| t = debug.getinfo(f) |
| assert(t.name == nil and t.linedefined > 0 and |
| t.lastlinedefined == t.linedefined and |
| t.short_src == "?") |
| assert(debug.getinfo(1).currentline == -1) |
| |
| return a |
| ]] |
| |
| |
| -- load 'prog' without debug info |
| local f = assert(load(string.dump(load(prog), true))) |
| |
| assert(f() == 13) |
| |
| do -- tests for 'source' in binary dumps |
| local prog = [[ |
| return function (x) |
| return function (y) |
| return x + y |
| end |
| end |
| ]] |
| local name = string.rep("x", 1000) |
| local p = assert(load(prog, name)) |
| -- load 'p' as a binary chunk with debug information |
| local c = string.dump(p) |
| assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump |
| local f = assert(load(c)) |
| local g = f() |
| local h = g(3) |
| assert(h(5) == 8) |
| assert(debug.getinfo(f).source == name and -- all functions have 'source' |
| debug.getinfo(g).source == name and |
| debug.getinfo(h).source == name) |
| -- again, without debug info |
| local c = string.dump(p, true) |
| assert(#c < 500) -- no 'source' in dump |
| local f = assert(load(c)) |
| local g = f() |
| local h = g(30) |
| assert(h(50) == 80) |
| assert(debug.getinfo(f).source == '=?' and -- no function has 'source' |
| debug.getinfo(g).source == '=?' and |
| debug.getinfo(h).source == '=?') |
| end |
| |
| print"OK" |
| |