--- source: src/main.rs assertion_line: 136 expression: compiled input_file: test-data/lua5.3-tests/db.lua --- local debug = require("debug") local fn dostring(s) { return assert(load(s))() } print("testing debug library and debug information"); { local a = 1 } assert(!debug.gethook()); local testline = 19 global fn test(s, l, p) { collectgarbage(); local fn f(event, line) { assert(event == 'line'); local l = table.remove(l, 1) if p { print(l, line); } assert(l == line, "wrong trace!!"); } debug.sethook(f, "l"); load(s)(); debug.sethook(); assert(#l == 0); } { assert(!pcall(debug.getinfo, print, "X")); assert(!debug.getinfo(1000)); assert(!debug.getinfo(-1)); local a = debug.getinfo(print) assert(a.what == "C" && a.short_src == "[C]"); a = debug.getinfo(print, "L") assert(a.activelines == nil); local b = debug.getinfo(test, "SfL") assert(b.name == nil && b.what == "Lua" && b.linedefined == testline && b.lastlinedefined == b.linedefined + 10 && b.func == test && !string.find(b.short_src, "%[")); assert(b.activelines[(b.linedefined + 1)] && b.activelines[(b.lastlinedefined)]); assert(!b.activelines[(b.linedefined)] && !b.activelines[(b.lastlinedefined + 1)]); } a = "function f () end" local fn dostring(s, x) { return load(s, x)() } 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 loop { local g = { x = fn () { local a = debug.getinfo(2) assert(a.name == 'f' && a.namewhat == 'local'); a = debug.getinfo(1) assert(a.name == 'x' && a.namewhat == 'field'); return 'xixi' } } local f = fn () { return 1 + 1 && (!1 || g.x()) } assert(f() == 'xixi'); g = debug.getinfo(f) assert(g.what == "Lua" && g.func == f && g.namewhat == "" && !g.name); global fn f(x, name) { name = name || 'f' local a = debug.getinfo(1) assert(a.name == name && a.namewhat == 'local'); return x } if 3 > 4 { break } f(); if 3 < 4 { a = 1 } else { break } f(); while 1 { local x = 10 break } f(); local b = 1 if 3 > 4 { return math.sin(1) } f(); a = 3 < 4 f(); a = 3 < 4 || 1 f(); loop { local x = 20 if 4 > 3 { f(); } else { break } f(); } until 1 g = {} f(g).x = f(2) && f(10) + f(9) assert(g.x == f(19)); global fn g(x) { if !x { return 3 } return (x('a', 'x')) } 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 }); print('+'); assert(!pcall(debug.getlocal, 20, 1)); assert(!pcall(debug.setlocal, -1, 1, 10)); local fn foo(a, b, ...) { local d, e } local co = coroutine.create(foo) assert(debug.getlocal(foo, 1) == 'a'); assert(debug.getlocal(foo, 2) == 'b'); assert(!debug.getlocal(foo, 3)); assert(debug.getlocal(co, foo, 1) == 'a'); assert(debug.getlocal(co, foo, 2) == 'b'); assert(!debug.getlocal(co, foo, 3)); assert(!debug.getlocal(print, 1)); local fn foo(a, ...) { local t = table.pack(...) for i = 1, t.n { local n, v = debug.getlocal(1, -i) assert(n == "(*vararg)" && v == t[(i)]); } assert(!debug.getlocal(1, -(t.n + 1))); assert(!debug.setlocal(1, -(t.n + 1), 30)); if t.n > 0 { (fn (x) { assert(debug.setlocal(2, -1, x) == "(*vararg)"); assert(debug.setlocal(2, -t.n, x) == "(*vararg)"); })(430); assert(... == 430); } } foo(); foo(print); foo(200, 3, 4); local a = {} for i = 1, (_soft && 100 || 1000) { a[(i)] = i } foo(table.unpack(a)); a = nil local fn foo() { return debug.getlocal(1, -1) } assert(!foo(10)); { assert(!debug.gethook()); local count = 0 local fn f() { assert(debug.getinfo(1).namewhat == "hook"); local sndline = string.match(debug.traceback(), "\n(.-)\n") assert(string.find(sndline, "hook")); count = count + 1 } debug.sethook(f, "l"); local a = 0 _ENV.a = a a = 1 debug.sethook(); assert(count == 4); } a = {} L = nil local glob = 1 local oldglob = glob debug.sethook(fn (e, l) { collectgarbage(); local f, m, c = debug.gethook() assert(m == 'crl' && c == 0); if e == "line" { if glob != oldglob { L = l - 1 oldglob = glob } } elseif if e == "call" { local f = debug.getinfo(2, "f").func a[(f)] = 1 } else { assert(e == "return"); } }, "crl"); global fn f(a, b) { collectgarbage(); local _, x = debug.getlocal(1, 1) local _, y = debug.getlocal(1, 2) assert(x == a && 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 && x.what == "Lua" && x.name == 'g' && x.nups == 2 && 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); } global fn foo() { glob = glob + 1 assert(debug.getinfo(1, "l").currentline == L + 1); } foo(); _ = 'alo\ alo' .. ` ` assert(debug.getinfo(1, "l").currentline == L + 11); global fn g(...) { local arg = { ... } { local a, b, c a = math.sin(40) } local feijao local AAAA, B = "xuxu", "mamão" f(AAAA, B); assert(AAAA == "pera" && B == "maçã"); { local B = 13 local x, y = debug.getlocal(1, 5) assert(x == 'B' && y == 13); } } g(); assert(a[(f)] && a[(g)] && a[(assert)] && a[(debug.getlocal)] && !a[(print)]); local n, v = debug.getlocal(0, 1) assert(v == 0 && n == "(*temporary)"); local n, v = debug.getlocal(0, 2) assert(v == 2 && n == "(*temporary)"); assert(!debug.getlocal(0, 3)); assert(!debug.getlocal(0, 0)); global fn f() { assert(select(2, debug.getlocal(2, 3)) == 1); assert(!debug.getlocal(2, 4)); debug.setlocal(2, 3, 10); return 20 } global fn g(a, b) { return (a + 1) + f() } assert(g(0, 0) == 30); debug.sethook(nil); assert(debug.gethook() == nil); local fn collectlocals(level) { local tab = {} for i = 1, math.huge { local n, v = debug.getlocal(level + 1, i) if !(n && string.find(n, "^[a-zA-Z0-9_]+$")) { break } tab[(n)] = v } return tab } X = nil a = {} method a::f(a, b, ...) { local arg = { ... } local c = 13 } debug.sethook(fn (e) { assert(e == "call"); dostring("XX = 12"); assert(!pcall(load("a='joao'+1"))); debug.sethook(fn (e, l) { assert(debug.getinfo(2, "l").currentline == l); local f, m, c = debug.gethook() assert(e == "line"); assert(m == 'l' && c == 0); debug.sethook(nil); assert(!X); X = collectlocals(2) }, "l"); }, "c"); a::f(1, 2, 3, 4, 5); assert(X.self == a && X.a == 1 && X.b == 2 && X.c == nil); assert(XX == 12); assert(debug.gethook() == nil); { local fn foo(a, b) { { local x, y, z } local c, d = 10, 20 return } local fn aux() { if debug.getinfo(2).name == "foo" { foo = nil local tab = { a = 100, b = 200, c = 10, d = 20 } for n, v with pairs(collectlocals(2)) { assert(tab[(n)] == v); tab[(n)] = nil } assert(next(tab) == nil); } } debug.sethook(aux, "r"); foo(100, 200); debug.sethook(); assert(foo == nil); } local fn getupvalues(f) { local t = {} local i = 1 while true { local name, value = debug.getupvalue(f, i) if !name { break } assert(!t[(name)]); t[(name)] = value i = i + 1 } return t } local a, b, c = 1, 2, 3 local fn foo1(a) { b = a return c } local fn foo2(x) { a = x return c + b } assert(!debug.getupvalue(foo1, 3)); assert(!debug.getupvalue(foo1, 0)); assert(!debug.setupvalue(foo1, 3, "xuxu")); local t = getupvalues(foo1) assert(t.a == nil && t.b == 2 && t.c == 3); t = getupvalues(foo2) assert(t.a == 1 && t.b == 2 && t.c == 3); assert(debug.setupvalue(foo1, 1, "xuxu") == "b"); assert(({ debug.getupvalue(foo2, 3) })[(2)] == "xuxu"); assert(debug.getupvalue(string.gmatch("x", "x"), 1) == ""); local a = 0 debug.sethook(fn (e) { a = a + 1 }, "", 1); a = 0 for i = 1, 1000 { } assert(1000 < a && a < 1012); debug.sethook(fn (e) { a = a + 1 }, "", 4); a = 0 for i = 1, 1000 { } assert(250 < a && a < 255); local f, m, c = debug.gethook() assert(m == "" && c == 4); debug.sethook(fn (e) { a = a + 1 }, "", 4000); a = 0 for i = 1, 1000 { } assert(a == 0); { debug.sethook(print, "", 2 ^ 24 - 1); local f, m, c = debug.gethook() assert(({ debug.gethook() })[(3)] == 2 ^ 24 - 1); } debug.sethook(); local fn f(x) { if x { assert(debug.getinfo(1, "S").what == "Lua"); assert(debug.getinfo(1, "t").istailcall == true); local tail = debug.getinfo(2) assert(tail.func == g1 && tail.istailcall == true); assert(debug.getinfo(3, "S").what == "main"); print("+"); } } global fn g(x) { return f(x) } global fn g1(x) { g(x); } local fn h(x) { local f = g1 return f(x) } h(true); local b = {} debug.sethook(fn (e) { table.insert(b, e); }, "cr"); h(false); debug.sethook(); local res = { "return", "call", "tail call", "call", "tail call", "return", "return", "call" } for i = 1, #res { assert(res[(i)] == table.remove(b, 1)); } b = 0 debug.sethook(fn (e) { if e == "tail call" { b = b + 1 assert(debug.getinfo(2, "t").istailcall == true); } else { assert(debug.getinfo(2, "t").istailcall == false); } }, "c"); h(false); debug.sethook(); assert(b == 2); lim = _soft && 3000 || 30000 local fn foo(x) { if x == 0 { assert(debug.getinfo(2).what == "main"); local info = debug.getinfo(1) assert(info.istailcall == true && info.func == foo); } else { return foo(x - 1) } } foo(lim); print("+"); co = load([[ local A = function () return x end return ]]) local a = 0 debug.sethook(fn (e, l) { if l == 3 { a = a + 1 assert(debug.getlocal(2, 1) == "(*temporary)"); } elseif if l == 4 { a = a + 1 assert(debug.getlocal(2, 1) == "A"); } }, "l"); co(); debug.sethook(); assert(a == 2); 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(!string.find(debug.traceback("hi"), "'debug.traceback'")); assert(string.find(debug.traceback("hi", 0), "'debug.traceback'")); assert(string.find(debug.traceback(), "^stack traceback:\n")); { local st, msg = (fn () { return pcall })()(debug.traceback) assert(st == true && string.find(msg, "pcall")); } local t = debug.getinfo(print, "u") assert(t.isvararg == true && t.nparams == 0 && t.nups == 0); t = debug.getinfo(fn (a, b, c) { }, "u") assert(t.isvararg == false && t.nparams == 3 && t.nups == 0); t = debug.getinfo(fn (a, b, ...) { return t[(a)] }, "u") assert(t.isvararg == true && t.nparams == 2 && t.nups == 1); t = debug.getinfo(1) assert(t.isvararg == true && t.nparams == 0 && t.nups == 1 && debug.getupvalue(t.func, 1) == "_ENV"); local fn checktraceback(co, p, level) { local tb = debug.traceback(co, nil, level) local i = 0 for l with string.gmatch(tb, "[^\n]+\n?") { assert(i == 0 || string.find(l, p[(i)])); i = i + 1 } assert(p[(i)] == nil); } local fn f(n) { if n > 0 { f(n - 1); } else { coroutine.yield(); } } 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(fn (x) { local a = 1 coroutine.yield(debug.getinfo(1, "l")); coroutine.yield(debug.getinfo(1, "l").currentline); return a }) local tr = {} local foo = fn (e, l) { if l { table.insert(tr, l); } } debug.sethook(co, foo, "lcr"); local _, l = coroutine.resume(co, 10) local x = debug.getinfo(co, 1, "lfLS") assert(x.currentline == l.currentline && x.activelines[(x.currentline)]); assert(type(x.func) == "function"); for i = x.linedefined + 1, x.lastlinedefined { assert(x.activelines[(i)]); x.activelines[(i)] = nil } assert(next(x.activelines) == nil); assert(!debug.getinfo(co, 2)); local a, b = debug.getlocal(co, 1, 1) assert(a == "x" && b == 10); a, b = debug.getlocal(co, 1, 2) assert(a == "a" && b == 1); debug.setlocal(co, 1, 2, "hi"); assert(debug.gethook(co) == foo); assert(#tr == 2 && tr[(1)] == l.currentline - 1 && tr[(2)] == l.currentline); a, b, c = pcall(coroutine.resume, co) assert(a && b && c == l.currentline + 1); checktraceback(co, { "yield", "in function <" }); a, b = coroutine.resume(co) assert(a && b == "hi"); assert(#tr == 4 && tr[(4)] == l.currentline + 2); assert(debug.gethook(co) == foo); assert(!debug.gethook()); checktraceback(co, {}); co = coroutine.create(fn (x) { local a, b = coroutine.yield(x) assert(a == 100 && b == nil); return x }) a, b = coroutine.resume(co, 10) assert(a && b == 10); a, b = debug.getlocal(co, 1, 1) assert(a == "x" && b == 10); assert(!debug.getlocal(co, 1, 5)); assert(debug.setlocal(co, 1, 1, 30) == "x"); assert(!debug.setlocal(co, 1, 5, 40)); a, b = coroutine.resume(co, 100) assert(a && b == 30); global fn f(i) { if i == 0 { error(i); } else { coroutine.yield(); f(i - 1); } } co = coroutine.create(fn (x) { f(x); }) a, b = coroutine.resume(co, 3) t = { "'coroutine.yield'", "'f'", "in function <" } while coroutine.status(co) == "suspended" { checktraceback(co, t); a, b = coroutine.resume(co) table.insert(t, 2, "'f'"); } t[(1)] = "'error'" checktraceback(co, t); local fn g(x) { coroutine.yield(x); } local fn f(i) { debug.sethook(fn () { }, "l"); for j = 1, 1000 { g(i + j); } } local co = coroutine.wrap(f) co(10); pcall(co); pcall(co); assert(type(debug.getregistry()) == "table"); local a = {} local fn f(t) { local info = debug.getinfo(1) assert(info.namewhat == "metamethod"); a.op = info.name return info.name } 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" && a ^ 3 == "__pow" && a .. a == "__concat"); assert(a / 3 == "__div" && 3 % a == "__mod"); assert(a + 3 == "__add" && 3 - a == "__sub" && a * 3 == "__mul" && -a == "__unm" && #a == "__len" && a & 3 == "__band"); assert(a | 3 == "__bor" && 3 ^^ a == "__bxor" && a << 3 == "__shl" && a >> 1 == "__shr"); assert(a == b && a.op == "__eq"); assert(a >= b && a.op == "__le"); assert(a > b && a.op == "__lt"); assert(~a == "__bnot"); { local fn f() { assert(debug.getinfo(1).name == "for iterator"); } for i with f { } } { local name = nil setmetatable({}, { __gc = fn () { local t = debug.getinfo(2) assert(t.namewhat == "metamethod"); name = t.name } }); loop { local a = {} } until name assert(name == "__gc"); } { print("testing traceback sizes"); local fn countlines(s) { return select(2, string.gsub(s, "\n", "")) } local fn deep(lvl, n) { if lvl == 0 { return (debug.traceback("message", n)) } else { return (deep(lvl - 1, n)) } } local fn checkdeep(total, start) { local s = deep(total, start) local rest = string.match(s, "^message\nstack traceback:\n(.*)$") local cl = countlines(rest) assert(cl <= 10 + 11 + 1); local brk = string.find(rest, "%.%.%.") if brk { local rest1 = string.sub(rest, 1, brk) local rest2 = string.sub(rest, brk, #rest) assert(countlines(rest1) == 10 && countlines(rest2) == 11); } else { assert(cl == total - start + 2); } } for d = 1, 51, 10 { for l = 1, d { coroutine.wrap(checkdeep)(d, l); } } } 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 ` local f = assert(load(string.dump(load(prog), true))) assert(f() == 13); { 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)) local c = string.dump(p) assert(#c > 1000 && #c < 2000); local f = assert(load(c)) local g = f() local h = g(3) assert(h(5) == 8); assert(debug.getinfo(f).source == name && debug.getinfo(g).source == name && debug.getinfo(h).source == name); local c = string.dump(p, true) assert(#c < 500); local f = assert(load(c)) local g = f() local h = g(30) assert(h(50) == 80); assert(debug.getinfo(f).source == '=?' && debug.getinfo(g).source == '=?' && debug.getinfo(h).source == '=?'); } print("OK");