diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-07-09 12:33:01 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-07-09 12:33:01 -0300 |
| commit | 7c519dfbd0c68b952f0849e01deaa3750e1f8153 (patch) | |
| tree | dde3ddbba310877db725df37a0d9f2cbe4e2a8f9 /testes/db.lua | |
| parent | f59e6a93c0ad38a27a420e51abf8f13d962446b5 (diff) | |
| download | lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.tar.gz lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.tar.bz2 lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.zip | |
Added manual and tests for version 5.4-w2
Diffstat (limited to 'testes/db.lua')
| -rw-r--r-- | testes/db.lua | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/testes/db.lua b/testes/db.lua new file mode 100644 index 00000000..36d1cdaa --- /dev/null +++ b/testes/db.lua | |||
| @@ -0,0 +1,948 @@ | |||
| 1 | -- $Id: db.lua,v 1.90 2018/04/02 17:55:58 roberto Exp $ | ||
| 2 | -- See Copyright Notice in file all.lua | ||
| 3 | |||
| 4 | -- testing debug library | ||
| 5 | |||
| 6 | local debug = require "debug" | ||
| 7 | |||
| 8 | local function dostring(s) return assert(load(s))() end | ||
| 9 | |||
| 10 | print"testing debug library and debug information" | ||
| 11 | |||
| 12 | do | ||
| 13 | local a=1 | ||
| 14 | end | ||
| 15 | |||
| 16 | assert(not debug.gethook()) | ||
| 17 | |||
| 18 | local testline = 19 -- line where 'test' is defined | ||
| 19 | function test (s, l, p) -- this must be line 19 | ||
| 20 | collectgarbage() -- avoid gc during trace | ||
| 21 | local function f (event, line) | ||
| 22 | assert(event == 'line') | ||
| 23 | local l = table.remove(l, 1) | ||
| 24 | if p then print(l, line) end | ||
| 25 | assert(l == line, "wrong trace!!") | ||
| 26 | end | ||
| 27 | debug.sethook(f,"l"); load(s)(); debug.sethook() | ||
| 28 | assert(#l == 0) | ||
| 29 | end | ||
| 30 | |||
| 31 | |||
| 32 | do | ||
| 33 | assert(not pcall(debug.getinfo, print, "X")) -- invalid option | ||
| 34 | assert(not debug.getinfo(1000)) -- out of range level | ||
| 35 | assert(not debug.getinfo(-1)) -- out of range level | ||
| 36 | local a = debug.getinfo(print) | ||
| 37 | assert(a.what == "C" and a.short_src == "[C]") | ||
| 38 | a = debug.getinfo(print, "L") | ||
| 39 | assert(a.activelines == nil) | ||
| 40 | local b = debug.getinfo(test, "SfL") | ||
| 41 | assert(b.name == nil and b.what == "Lua" and b.linedefined == testline and | ||
| 42 | b.lastlinedefined == b.linedefined + 10 and | ||
| 43 | b.func == test and not string.find(b.short_src, "%[")) | ||
| 44 | assert(b.activelines[b.linedefined + 1] and | ||
| 45 | b.activelines[b.lastlinedefined]) | ||
| 46 | assert(not b.activelines[b.linedefined] and | ||
| 47 | not b.activelines[b.lastlinedefined + 1]) | ||
| 48 | end | ||
| 49 | |||
| 50 | |||
| 51 | -- test file and string names truncation | ||
| 52 | a = "function f () end" | ||
| 53 | local function dostring (s, x) return load(s, x)() end | ||
| 54 | dostring(a) | ||
| 55 | assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a)) | ||
| 56 | dostring(a..string.format("; %s\n=1", string.rep('p', 400))) | ||
| 57 | assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) | ||
| 58 | dostring(a..string.format("; %s=1", string.rep('p', 400))) | ||
| 59 | assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) | ||
| 60 | dostring("\n"..a) | ||
| 61 | assert(debug.getinfo(f).short_src == '[string "..."]') | ||
| 62 | dostring(a, "") | ||
| 63 | assert(debug.getinfo(f).short_src == '[string ""]') | ||
| 64 | dostring(a, "@xuxu") | ||
| 65 | assert(debug.getinfo(f).short_src == "xuxu") | ||
| 66 | dostring(a, "@"..string.rep('p', 1000)..'t') | ||
| 67 | assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$")) | ||
| 68 | dostring(a, "=xuxu") | ||
| 69 | assert(debug.getinfo(f).short_src == "xuxu") | ||
| 70 | dostring(a, string.format("=%s", string.rep('x', 500))) | ||
| 71 | assert(string.find(debug.getinfo(f).short_src, "^x*$")) | ||
| 72 | dostring(a, "=") | ||
| 73 | assert(debug.getinfo(f).short_src == "") | ||
| 74 | a = nil; f = nil; | ||
| 75 | |||
| 76 | |||
| 77 | repeat | ||
| 78 | local g = {x = function () | ||
| 79 | local a = debug.getinfo(2) | ||
| 80 | assert(a.name == 'f' and a.namewhat == 'local') | ||
| 81 | a = debug.getinfo(1) | ||
| 82 | assert(a.name == 'x' and a.namewhat == 'field') | ||
| 83 | return 'xixi' | ||
| 84 | end} | ||
| 85 | local f = function () return 1+1 and (not 1 or g.x()) end | ||
| 86 | assert(f() == 'xixi') | ||
| 87 | g = debug.getinfo(f) | ||
| 88 | assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name) | ||
| 89 | |||
| 90 | function f (x, name) -- local! | ||
| 91 | name = name or 'f' | ||
| 92 | local a = debug.getinfo(1) | ||
| 93 | assert(a.name == name and a.namewhat == 'local') | ||
| 94 | return x | ||
| 95 | end | ||
| 96 | |||
| 97 | -- breaks in different conditions | ||
| 98 | if 3>4 then break end; f() | ||
| 99 | if 3<4 then a=1 else break end; f() | ||
| 100 | while 1 do local x=10; break end; f() | ||
| 101 | local b = 1 | ||
| 102 | if 3>4 then return math.sin(1) end; f() | ||
| 103 | a = 3<4; f() | ||
| 104 | a = 3<4 or 1; f() | ||
| 105 | repeat local x=20; if 4>3 then f() else break end; f() until 1 | ||
| 106 | g = {} | ||
| 107 | f(g).x = f(2) and f(10)+f(9) | ||
| 108 | assert(g.x == f(19)) | ||
| 109 | function g(x) if not x then return 3 end return (x('a', 'x')) end | ||
| 110 | assert(g(f) == 'a') | ||
| 111 | until 1 | ||
| 112 | |||
| 113 | test([[if | ||
| 114 | math.sin(1) | ||
| 115 | then | ||
| 116 | a=1 | ||
| 117 | else | ||
| 118 | a=2 | ||
| 119 | end | ||
| 120 | ]], {2,3,4,7}) | ||
| 121 | |||
| 122 | test([[-- | ||
| 123 | if nil then | ||
| 124 | a=1 | ||
| 125 | else | ||
| 126 | a=2 | ||
| 127 | end | ||
| 128 | ]], {2,5,6}) | ||
| 129 | |||
| 130 | test([[a=1 | ||
| 131 | repeat | ||
| 132 | a=a+1 | ||
| 133 | until a==3 | ||
| 134 | ]], {1,3,4,3,4}) | ||
| 135 | |||
| 136 | test([[ do | ||
| 137 | return | ||
| 138 | end | ||
| 139 | ]], {2}) | ||
| 140 | |||
| 141 | test([[local a | ||
| 142 | a=1 | ||
| 143 | while a<=3 do | ||
| 144 | a=a+1 | ||
| 145 | end | ||
| 146 | ]], {1,2,3,4,3,4,3,4,3,5}) | ||
| 147 | |||
| 148 | test([[while math.sin(1) do | ||
| 149 | if math.sin(1) | ||
| 150 | then break | ||
| 151 | end | ||
| 152 | end | ||
| 153 | a=1]], {1,2,3,6}) | ||
| 154 | |||
| 155 | test([[for i=1,3 do | ||
| 156 | a=i | ||
| 157 | end | ||
| 158 | ]], {1,2,1,2,1,2,1,3}) | ||
| 159 | |||
| 160 | test([[for i,v in pairs{'a','b'} do | ||
| 161 | a=tostring(i) .. v | ||
| 162 | end | ||
| 163 | ]], {1,2,1,2,1,3}) | ||
| 164 | |||
| 165 | test([[for i=1,4 do a=1 end]], {1,1,1,1,1}) | ||
| 166 | |||
| 167 | |||
| 168 | do -- testing line info/trace with large gaps in source | ||
| 169 | |||
| 170 | local a = {1, 2, 3, 10, 124, 125, 126, 127, 128, 129, 130, | ||
| 171 | 255, 256, 257, 500, 1000} | ||
| 172 | local s = [[ | ||
| 173 | local b = {10} | ||
| 174 | a = b[1] X + Y b[1] | ||
| 175 | b = 4 | ||
| 176 | ]] | ||
| 177 | for _, i in ipairs(a) do | ||
| 178 | local subs = {X = string.rep("\n", i)} | ||
| 179 | for _, j in ipairs(a) do | ||
| 180 | subs.Y = string.rep("\n", j) | ||
| 181 | local s = string.gsub(s, "[XY]", subs) | ||
| 182 | test(s, {1, 2 + i, 2 + i + j, 2 + i, 2 + i + j, 3 + i + j}) | ||
| 183 | end | ||
| 184 | end | ||
| 185 | end | ||
| 186 | |||
| 187 | print'+' | ||
| 188 | |||
| 189 | -- invalid levels in [gs]etlocal | ||
| 190 | assert(not pcall(debug.getlocal, 20, 1)) | ||
| 191 | assert(not pcall(debug.setlocal, -1, 1, 10)) | ||
| 192 | |||
| 193 | |||
| 194 | -- parameter names | ||
| 195 | local function foo (a,b,...) local d, e end | ||
| 196 | local co = coroutine.create(foo) | ||
| 197 | |||
| 198 | assert(debug.getlocal(foo, 1) == 'a') | ||
| 199 | assert(debug.getlocal(foo, 2) == 'b') | ||
| 200 | assert(not debug.getlocal(foo, 3)) | ||
| 201 | assert(debug.getlocal(co, foo, 1) == 'a') | ||
| 202 | assert(debug.getlocal(co, foo, 2) == 'b') | ||
| 203 | assert(not debug.getlocal(co, foo, 3)) | ||
| 204 | |||
| 205 | assert(not debug.getlocal(print, 1)) | ||
| 206 | |||
| 207 | |||
| 208 | local function foo () return (debug.getlocal(1, -1)) end | ||
| 209 | assert(not foo(10)) | ||
| 210 | |||
| 211 | |||
| 212 | -- varargs | ||
| 213 | local function foo (a, ...) | ||
| 214 | local t = table.pack(...) | ||
| 215 | for i = 1, t.n do | ||
| 216 | local n, v = debug.getlocal(1, -i) | ||
| 217 | assert(n == "(*vararg)" and v == t[i]) | ||
| 218 | end | ||
| 219 | assert(not debug.getlocal(1, -(t.n + 1))) | ||
| 220 | assert(not debug.setlocal(1, -(t.n + 1), 30)) | ||
| 221 | if t.n > 0 then | ||
| 222 | (function (x) | ||
| 223 | assert(debug.setlocal(2, -1, x) == "(*vararg)") | ||
| 224 | assert(debug.setlocal(2, -t.n, x) == "(*vararg)") | ||
| 225 | end)(430) | ||
| 226 | assert(... == 430) | ||
| 227 | end | ||
| 228 | end | ||
| 229 | |||
| 230 | foo() | ||
| 231 | foo(print) | ||
| 232 | foo(200, 3, 4) | ||
| 233 | local a = {} | ||
| 234 | for i = 1, (_soft and 100 or 1000) do a[i] = i end | ||
| 235 | foo(table.unpack(a)) | ||
| 236 | a = nil | ||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | do -- test hook presence in debug info | ||
| 241 | assert(not debug.gethook()) | ||
| 242 | local count = 0 | ||
| 243 | local function f () | ||
| 244 | assert(debug.getinfo(1).namewhat == "hook") | ||
| 245 | local sndline = string.match(debug.traceback(), "\n(.-)\n") | ||
| 246 | assert(string.find(sndline, "hook")) | ||
| 247 | count = count + 1 | ||
| 248 | end | ||
| 249 | debug.sethook(f, "l") | ||
| 250 | local a = 0 | ||
| 251 | _ENV.a = a | ||
| 252 | a = 1 | ||
| 253 | debug.sethook() | ||
| 254 | assert(count == 4) | ||
| 255 | end | ||
| 256 | |||
| 257 | |||
| 258 | a = {}; L = nil | ||
| 259 | local glob = 1 | ||
| 260 | local oldglob = glob | ||
| 261 | debug.sethook(function (e,l) | ||
| 262 | collectgarbage() -- force GC during a hook | ||
| 263 | local f, m, c = debug.gethook() | ||
| 264 | assert(m == 'crl' and c == 0) | ||
| 265 | if e == "line" then | ||
| 266 | if glob ~= oldglob then | ||
| 267 | L = l-1 -- get the first line where "glob" has changed | ||
| 268 | oldglob = glob | ||
| 269 | end | ||
| 270 | elseif e == "call" then | ||
| 271 | local f = debug.getinfo(2, "f").func | ||
| 272 | a[f] = 1 | ||
| 273 | else assert(e == "return") | ||
| 274 | end | ||
| 275 | end, "crl") | ||
| 276 | |||
| 277 | |||
| 278 | function f(a,b) | ||
| 279 | collectgarbage() | ||
| 280 | local _, x = debug.getlocal(1, 1) | ||
| 281 | local _, y = debug.getlocal(1, 2) | ||
| 282 | assert(x == a and y == b) | ||
| 283 | assert(debug.setlocal(2, 3, "pera") == "AA".."AA") | ||
| 284 | assert(debug.setlocal(2, 4, "maçã") == "B") | ||
| 285 | x = debug.getinfo(2) | ||
| 286 | assert(x.func == g and x.what == "Lua" and x.name == 'g' and | ||
| 287 | x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) | ||
| 288 | glob = glob+1 | ||
| 289 | assert(debug.getinfo(1, "l").currentline == L+1) | ||
| 290 | assert(debug.getinfo(1, "l").currentline == L+2) | ||
| 291 | end | ||
| 292 | |||
| 293 | function foo() | ||
| 294 | glob = glob+1 | ||
| 295 | assert(debug.getinfo(1, "l").currentline == L+1) | ||
| 296 | end; foo() -- set L | ||
| 297 | -- check line counting inside strings and empty lines | ||
| 298 | |||
| 299 | _ = 'alo\ | ||
| 300 | alo' .. [[ | ||
| 301 | |||
| 302 | ]] | ||
| 303 | --[[ | ||
| 304 | ]] | ||
| 305 | assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines | ||
| 306 | |||
| 307 | |||
| 308 | function g (...) | ||
| 309 | local arg = {...} | ||
| 310 | do local a,b,c; a=math.sin(40); end | ||
| 311 | local feijao | ||
| 312 | local AAAA,B = "xuxu", "mamão" | ||
| 313 | f(AAAA,B) | ||
| 314 | assert(AAAA == "pera" and B == "maçã") | ||
| 315 | do | ||
| 316 | local B = 13 | ||
| 317 | local x,y = debug.getlocal(1,5) | ||
| 318 | assert(x == 'B' and y == 13) | ||
| 319 | end | ||
| 320 | end | ||
| 321 | |||
| 322 | g() | ||
| 323 | |||
| 324 | |||
| 325 | assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print]) | ||
| 326 | |||
| 327 | |||
| 328 | -- tests for manipulating non-registered locals (C and Lua temporaries) | ||
| 329 | |||
| 330 | local n, v = debug.getlocal(0, 1) | ||
| 331 | assert(v == 0 and n == "(*temporary)") | ||
| 332 | local n, v = debug.getlocal(0, 2) | ||
| 333 | assert(v == 2 and n == "(*temporary)") | ||
| 334 | assert(not debug.getlocal(0, 3)) | ||
| 335 | assert(not debug.getlocal(0, 0)) | ||
| 336 | |||
| 337 | function f() | ||
| 338 | assert(select(2, debug.getlocal(2,3)) == 1) | ||
| 339 | assert(not debug.getlocal(2,4)) | ||
| 340 | debug.setlocal(2, 3, 10) | ||
| 341 | return 20 | ||
| 342 | end | ||
| 343 | |||
| 344 | function g(a,b) return (a+1) + f() end | ||
| 345 | |||
| 346 | assert(g(0,0) == 30) | ||
| 347 | |||
| 348 | |||
| 349 | debug.sethook(nil); | ||
| 350 | assert(debug.gethook() == nil) | ||
| 351 | |||
| 352 | |||
| 353 | -- minimal tests for setuservalue/getuservalue | ||
| 354 | do | ||
| 355 | assert(debug.setuservalue(io.stdin, 10) == nil) | ||
| 356 | local a, b = debug.getuservalue(io.stdin, 10) | ||
| 357 | assert(a == nil and not b) | ||
| 358 | end | ||
| 359 | |||
| 360 | -- testing iteraction between multiple values x hooks | ||
| 361 | do | ||
| 362 | local function f(...) return 3, ... end | ||
| 363 | local count = 0 | ||
| 364 | local a = {} | ||
| 365 | for i = 1, 100 do a[i] = i end | ||
| 366 | debug.sethook(function () count = count + 1 end, "", 1) | ||
| 367 | local t = {table.unpack(a)} | ||
| 368 | assert(#t == 100) | ||
| 369 | t = {table.unpack(a, 1, 3)} | ||
| 370 | assert(#t == 3) | ||
| 371 | t = {f(table.unpack(a, 1, 30))} | ||
| 372 | assert(#t == 31) | ||
| 373 | end | ||
| 374 | |||
| 375 | |||
| 376 | -- testing access to function arguments | ||
| 377 | |||
| 378 | local function collectlocals (level) | ||
| 379 | local tab = {} | ||
| 380 | for i = 1, math.huge do | ||
| 381 | local n, v = debug.getlocal(level + 1, i) | ||
| 382 | if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then | ||
| 383 | break -- consider only real variables | ||
| 384 | end | ||
| 385 | tab[n] = v | ||
| 386 | end | ||
| 387 | return tab | ||
| 388 | end | ||
| 389 | |||
| 390 | |||
| 391 | X = nil | ||
| 392 | a = {} | ||
| 393 | function a:f (a, b, ...) local arg = {...}; local c = 13 end | ||
| 394 | debug.sethook(function (e) | ||
| 395 | assert(e == "call") | ||
| 396 | dostring("XX = 12") -- test dostring inside hooks | ||
| 397 | -- testing errors inside hooks | ||
| 398 | assert(not pcall(load("a='joao'+1"))) | ||
| 399 | debug.sethook(function (e, l) | ||
| 400 | assert(debug.getinfo(2, "l").currentline == l) | ||
| 401 | local f,m,c = debug.gethook() | ||
| 402 | assert(e == "line") | ||
| 403 | assert(m == 'l' and c == 0) | ||
| 404 | debug.sethook(nil) -- hook is called only once | ||
| 405 | assert(not X) -- check that | ||
| 406 | X = collectlocals(2) | ||
| 407 | end, "l") | ||
| 408 | end, "c") | ||
| 409 | |||
| 410 | a:f(1,2,3,4,5) | ||
| 411 | assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil) | ||
| 412 | assert(XX == 12) | ||
| 413 | assert(debug.gethook() == nil) | ||
| 414 | |||
| 415 | |||
| 416 | -- testing access to local variables in return hook (bug in 5.2) | ||
| 417 | do | ||
| 418 | local X = false | ||
| 419 | |||
| 420 | local function foo (a, b, ...) | ||
| 421 | do local x,y,z end | ||
| 422 | local c, d = 10, 20 | ||
| 423 | return | ||
| 424 | end | ||
| 425 | |||
| 426 | local function aux () | ||
| 427 | if debug.getinfo(2).name == "foo" then | ||
| 428 | X = true -- to signal that it found 'foo' | ||
| 429 | local tab = {a = 100, b = 200, c = 10, d = 20} | ||
| 430 | for n, v in pairs(collectlocals(2)) do | ||
| 431 | assert(tab[n] == v) | ||
| 432 | tab[n] = undef | ||
| 433 | end | ||
| 434 | assert(next(tab) == nil) -- 'tab' must be empty | ||
| 435 | end | ||
| 436 | end | ||
| 437 | |||
| 438 | debug.sethook(aux, "r"); foo(100, 200); debug.sethook() | ||
| 439 | assert(X) | ||
| 440 | |||
| 441 | end | ||
| 442 | |||
| 443 | |||
| 444 | local function eqseq (t1, t2) | ||
| 445 | assert(#t1 == #t2) | ||
| 446 | for i = 1, #t1 do | ||
| 447 | assert(t1[i] == t2[i]) | ||
| 448 | end | ||
| 449 | end | ||
| 450 | |||
| 451 | |||
| 452 | do print("testing inspection of parameters/returned values") | ||
| 453 | local on = false | ||
| 454 | local inp, out | ||
| 455 | |||
| 456 | local function hook (event) | ||
| 457 | if not on then return end | ||
| 458 | local ar = debug.getinfo(2, "ruS") | ||
| 459 | local t = {} | ||
| 460 | for i = ar.ftransfer, ar.ftransfer + ar.ntransfer - 1 do | ||
| 461 | local _, v = debug.getlocal(2, i) | ||
| 462 | t[#t + 1] = v | ||
| 463 | end | ||
| 464 | if event == "return" then | ||
| 465 | out = t | ||
| 466 | else | ||
| 467 | inp = t | ||
| 468 | end | ||
| 469 | end | ||
| 470 | |||
| 471 | debug.sethook(hook, "cr") | ||
| 472 | |||
| 473 | on = true; math.sin(3); on = false | ||
| 474 | eqseq(inp, {3}); eqseq(out, {math.sin(3)}) | ||
| 475 | |||
| 476 | on = true; select(2, 10, 20, 30, 40); on = false | ||
| 477 | eqseq(inp, {2, 10, 20, 30, 40}); eqseq(out, {20, 30, 40}) | ||
| 478 | |||
| 479 | local function foo (a, ...) return ... end | ||
| 480 | local function foo1 () on = not on; return foo(20, 10, 0) end | ||
| 481 | foo1(); on = false | ||
| 482 | eqseq(inp, {20}); eqseq(out, {10, 0}) | ||
| 483 | |||
| 484 | debug.sethook() | ||
| 485 | end | ||
| 486 | |||
| 487 | |||
| 488 | |||
| 489 | -- testing upvalue access | ||
| 490 | local function getupvalues (f) | ||
| 491 | local t = {} | ||
| 492 | local i = 1 | ||
| 493 | while true do | ||
| 494 | local name, value = debug.getupvalue(f, i) | ||
| 495 | if not name then break end | ||
| 496 | assert(not t[name]) | ||
| 497 | t[name] = value | ||
| 498 | i = i + 1 | ||
| 499 | end | ||
| 500 | return t | ||
| 501 | end | ||
| 502 | |||
| 503 | local a,b,c = 1,2,3 | ||
| 504 | local function foo1 (a) b = a; return c end | ||
| 505 | local function foo2 (x) a = x; return c+b end | ||
| 506 | assert(not debug.getupvalue(foo1, 3)) | ||
| 507 | assert(not debug.getupvalue(foo1, 0)) | ||
| 508 | assert(not debug.setupvalue(foo1, 3, "xuxu")) | ||
| 509 | local t = getupvalues(foo1) | ||
| 510 | assert(t.a == nil and t.b == 2 and t.c == 3) | ||
| 511 | t = getupvalues(foo2) | ||
| 512 | assert(t.a == 1 and t.b == 2 and t.c == 3) | ||
| 513 | assert(debug.setupvalue(foo1, 1, "xuxu") == "b") | ||
| 514 | assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu") | ||
| 515 | -- upvalues of C functions are allways "called" "" (the empty string) | ||
| 516 | assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "") | ||
| 517 | |||
| 518 | |||
| 519 | -- testing count hooks | ||
| 520 | local a=0 | ||
| 521 | debug.sethook(function (e) a=a+1 end, "", 1) | ||
| 522 | a=0; for i=1,1000 do end; assert(1000 < a and a < 1012) | ||
| 523 | debug.sethook(function (e) a=a+1 end, "", 4) | ||
| 524 | a=0; for i=1,1000 do end; assert(250 < a and a < 255) | ||
| 525 | local f,m,c = debug.gethook() | ||
| 526 | assert(m == "" and c == 4) | ||
| 527 | debug.sethook(function (e) a=a+1 end, "", 4000) | ||
| 528 | a=0; for i=1,1000 do end; assert(a == 0) | ||
| 529 | |||
| 530 | do | ||
| 531 | debug.sethook(print, "", 2^24 - 1) -- count upperbound | ||
| 532 | local f,m,c = debug.gethook() | ||
| 533 | assert(({debug.gethook()})[3] == 2^24 - 1) | ||
| 534 | end | ||
| 535 | |||
| 536 | debug.sethook() | ||
| 537 | |||
| 538 | |||
| 539 | -- tests for tail calls | ||
| 540 | local function f (x) | ||
| 541 | if x then | ||
| 542 | assert(debug.getinfo(1, "S").what == "Lua") | ||
| 543 | assert(debug.getinfo(1, "t").istailcall == true) | ||
| 544 | local tail = debug.getinfo(2) | ||
| 545 | assert(tail.func == g1 and tail.istailcall == true) | ||
| 546 | assert(debug.getinfo(3, "S").what == "main") | ||
| 547 | print"+" | ||
| 548 | end | ||
| 549 | end | ||
| 550 | |||
| 551 | function g(x) return f(x) end | ||
| 552 | |||
| 553 | function g1(x) g(x) end | ||
| 554 | |||
| 555 | local function h (x) local f=g1; return f(x) end | ||
| 556 | |||
| 557 | h(true) | ||
| 558 | |||
| 559 | local b = {} | ||
| 560 | debug.sethook(function (e) table.insert(b, e) end, "cr") | ||
| 561 | h(false) | ||
| 562 | debug.sethook() | ||
| 563 | local res = {"return", -- first return (from sethook) | ||
| 564 | "call", "tail call", "call", "tail call", | ||
| 565 | "return", "return", | ||
| 566 | "call", -- last call (to sethook) | ||
| 567 | } | ||
| 568 | for i = 1, #res do assert(res[i] == table.remove(b, 1)) end | ||
| 569 | |||
| 570 | b = 0 | ||
| 571 | debug.sethook(function (e) | ||
| 572 | if e == "tail call" then | ||
| 573 | b = b + 1 | ||
| 574 | assert(debug.getinfo(2, "t").istailcall == true) | ||
| 575 | else | ||
| 576 | assert(debug.getinfo(2, "t").istailcall == false) | ||
| 577 | end | ||
| 578 | end, "c") | ||
| 579 | h(false) | ||
| 580 | debug.sethook() | ||
| 581 | assert(b == 2) -- two tail calls | ||
| 582 | |||
| 583 | lim = _soft and 3000 or 30000 | ||
| 584 | local function foo (x) | ||
| 585 | if x==0 then | ||
| 586 | assert(debug.getinfo(2).what == "main") | ||
| 587 | local info = debug.getinfo(1) | ||
| 588 | assert(info.istailcall == true and info.func == foo) | ||
| 589 | else return foo(x-1) | ||
| 590 | end | ||
| 591 | end | ||
| 592 | |||
| 593 | foo(lim) | ||
| 594 | |||
| 595 | |||
| 596 | print"+" | ||
| 597 | |||
| 598 | |||
| 599 | -- testing local function information | ||
| 600 | co = load[[ | ||
| 601 | local A = function () | ||
| 602 | return x | ||
| 603 | end | ||
| 604 | return | ||
| 605 | ]] | ||
| 606 | |||
| 607 | local a = 0 | ||
| 608 | -- 'A' should be visible to debugger only after its complete definition | ||
| 609 | debug.sethook(function (e, l) | ||
| 610 | if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(*temporary)") | ||
| 611 | elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A") | ||
| 612 | end | ||
| 613 | end, "l") | ||
| 614 | co() -- run local function definition | ||
| 615 | debug.sethook() -- turn off hook | ||
| 616 | assert(a == 2) -- ensure all two lines where hooked | ||
| 617 | |||
| 618 | -- testing traceback | ||
| 619 | |||
| 620 | assert(debug.traceback(print) == print) | ||
| 621 | assert(debug.traceback(print, 4) == print) | ||
| 622 | assert(string.find(debug.traceback("hi", 4), "^hi\n")) | ||
| 623 | assert(string.find(debug.traceback("hi"), "^hi\n")) | ||
| 624 | assert(not string.find(debug.traceback("hi"), "'debug.traceback'")) | ||
| 625 | assert(string.find(debug.traceback("hi", 0), "'debug.traceback'")) | ||
| 626 | assert(string.find(debug.traceback(), "^stack traceback:\n")) | ||
| 627 | |||
| 628 | do -- C-function names in traceback | ||
| 629 | local st, msg = (function () return pcall end)()(debug.traceback) | ||
| 630 | assert(st == true and string.find(msg, "pcall")) | ||
| 631 | end | ||
| 632 | |||
| 633 | |||
| 634 | -- testing nparams, nups e isvararg | ||
| 635 | local t = debug.getinfo(print, "u") | ||
| 636 | assert(t.isvararg == true and t.nparams == 0 and t.nups == 0) | ||
| 637 | |||
| 638 | t = debug.getinfo(function (a,b,c) end, "u") | ||
| 639 | assert(t.isvararg == false and t.nparams == 3 and t.nups == 0) | ||
| 640 | |||
| 641 | t = debug.getinfo(function (a,b,...) return t[a] end, "u") | ||
| 642 | assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) | ||
| 643 | |||
| 644 | t = debug.getinfo(1) -- main | ||
| 645 | assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and | ||
| 646 | debug.getupvalue(t.func, 1) == "_ENV") | ||
| 647 | |||
| 648 | |||
| 649 | |||
| 650 | |||
| 651 | -- testing debugging of coroutines | ||
| 652 | |||
| 653 | local function checktraceback (co, p, level) | ||
| 654 | local tb = debug.traceback(co, nil, level) | ||
| 655 | local i = 0 | ||
| 656 | for l in string.gmatch(tb, "[^\n]+\n?") do | ||
| 657 | assert(i == 0 or string.find(l, p[i])) | ||
| 658 | i = i+1 | ||
| 659 | end | ||
| 660 | assert(p[i] == undef) | ||
| 661 | end | ||
| 662 | |||
| 663 | |||
| 664 | local function f (n) | ||
| 665 | if n > 0 then f(n-1) | ||
| 666 | else coroutine.yield() end | ||
| 667 | end | ||
| 668 | |||
| 669 | local co = coroutine.create(f) | ||
| 670 | coroutine.resume(co, 3) | ||
| 671 | checktraceback(co, {"yield", "db.lua", "db.lua", "db.lua", "db.lua"}) | ||
| 672 | checktraceback(co, {"db.lua", "db.lua", "db.lua", "db.lua"}, 1) | ||
| 673 | checktraceback(co, {"db.lua", "db.lua", "db.lua"}, 2) | ||
| 674 | checktraceback(co, {"db.lua"}, 4) | ||
| 675 | checktraceback(co, {}, 40) | ||
| 676 | |||
| 677 | |||
| 678 | co = coroutine.create(function (x) | ||
| 679 | local a = 1 | ||
| 680 | coroutine.yield(debug.getinfo(1, "l")) | ||
| 681 | coroutine.yield(debug.getinfo(1, "l").currentline) | ||
| 682 | return a | ||
| 683 | end) | ||
| 684 | |||
| 685 | local tr = {} | ||
| 686 | local foo = function (e, l) if l then table.insert(tr, l) end end | ||
| 687 | debug.sethook(co, foo, "lcr") | ||
| 688 | |||
| 689 | local _, l = coroutine.resume(co, 10) | ||
| 690 | local x = debug.getinfo(co, 1, "lfLS") | ||
| 691 | assert(x.currentline == l.currentline and x.activelines[x.currentline]) | ||
| 692 | assert(type(x.func) == "function") | ||
| 693 | for i=x.linedefined + 1, x.lastlinedefined do | ||
| 694 | assert(x.activelines[i]) | ||
| 695 | x.activelines[i] = undef | ||
| 696 | end | ||
| 697 | assert(next(x.activelines) == nil) -- no 'extra' elements | ||
| 698 | assert(not debug.getinfo(co, 2)) | ||
| 699 | local a,b = debug.getlocal(co, 1, 1) | ||
| 700 | assert(a == "x" and b == 10) | ||
| 701 | a,b = debug.getlocal(co, 1, 2) | ||
| 702 | assert(a == "a" and b == 1) | ||
| 703 | debug.setlocal(co, 1, 2, "hi") | ||
| 704 | assert(debug.gethook(co) == foo) | ||
| 705 | assert(#tr == 2 and | ||
| 706 | tr[1] == l.currentline-1 and tr[2] == l.currentline) | ||
| 707 | |||
| 708 | a,b,c = pcall(coroutine.resume, co) | ||
| 709 | assert(a and b and c == l.currentline+1) | ||
| 710 | checktraceback(co, {"yield", "in function <"}) | ||
| 711 | |||
| 712 | a,b = coroutine.resume(co) | ||
| 713 | assert(a and b == "hi") | ||
| 714 | assert(#tr == 4 and tr[4] == l.currentline+2) | ||
| 715 | assert(debug.gethook(co) == foo) | ||
| 716 | assert(not debug.gethook()) | ||
| 717 | checktraceback(co, {}) | ||
| 718 | |||
| 719 | |||
| 720 | -- check get/setlocal in coroutines | ||
| 721 | co = coroutine.create(function (x) | ||
| 722 | local a, b = coroutine.yield(x) | ||
| 723 | assert(a == 100 and b == nil) | ||
| 724 | return x | ||
| 725 | end) | ||
| 726 | a, b = coroutine.resume(co, 10) | ||
| 727 | assert(a and b == 10) | ||
| 728 | a, b = debug.getlocal(co, 1, 1) | ||
| 729 | assert(a == "x" and b == 10) | ||
| 730 | assert(not debug.getlocal(co, 1, 5)) | ||
| 731 | assert(debug.setlocal(co, 1, 1, 30) == "x") | ||
| 732 | assert(not debug.setlocal(co, 1, 5, 40)) | ||
| 733 | a, b = coroutine.resume(co, 100) | ||
| 734 | assert(a and b == 30) | ||
| 735 | |||
| 736 | |||
| 737 | -- check traceback of suspended (or dead with error) coroutines | ||
| 738 | |||
| 739 | function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end | ||
| 740 | |||
| 741 | co = coroutine.create(function (x) f(x) end) | ||
| 742 | a, b = coroutine.resume(co, 3) | ||
| 743 | t = {"'coroutine.yield'", "'f'", "in function <"} | ||
| 744 | while coroutine.status(co) == "suspended" do | ||
| 745 | checktraceback(co, t) | ||
| 746 | a, b = coroutine.resume(co) | ||
| 747 | table.insert(t, 2, "'f'") -- one more recursive call to 'f' | ||
| 748 | end | ||
| 749 | t[1] = "'error'" | ||
| 750 | checktraceback(co, t) | ||
| 751 | |||
| 752 | |||
| 753 | -- test acessing line numbers of a coroutine from a resume inside | ||
| 754 | -- a C function (this is a known bug in Lua 5.0) | ||
| 755 | |||
| 756 | local function g(x) | ||
| 757 | coroutine.yield(x) | ||
| 758 | end | ||
| 759 | |||
| 760 | local function f (i) | ||
| 761 | debug.sethook(function () end, "l") | ||
| 762 | for j=1,1000 do | ||
| 763 | g(i+j) | ||
| 764 | end | ||
| 765 | end | ||
| 766 | |||
| 767 | local co = coroutine.wrap(f) | ||
| 768 | co(10) | ||
| 769 | pcall(co) | ||
| 770 | pcall(co) | ||
| 771 | |||
| 772 | |||
| 773 | assert(type(debug.getregistry()) == "table") | ||
| 774 | |||
| 775 | |||
| 776 | -- test tagmethod information | ||
| 777 | local a = {} | ||
| 778 | local function f (t) | ||
| 779 | local info = debug.getinfo(1); | ||
| 780 | assert(info.namewhat == "metamethod") | ||
| 781 | a.op = info.name | ||
| 782 | return info.name | ||
| 783 | end | ||
| 784 | setmetatable(a, { | ||
| 785 | __index = f; __add = f; __div = f; __mod = f; __concat = f; __pow = f; | ||
| 786 | __mul = f; __idiv = f; __unm = f; __len = f; __sub = f; | ||
| 787 | __shl = f; __shr = f; __bor = f; __bxor = f; | ||
| 788 | __eq = f; __le = f; __lt = f; __unm = f; __len = f; __band = f; | ||
| 789 | __bnot = f; | ||
| 790 | }) | ||
| 791 | |||
| 792 | local b = setmetatable({}, getmetatable(a)) | ||
| 793 | |||
| 794 | assert(a[3] == "index" and a^3 == "pow" and a..a == "concat") | ||
| 795 | assert(a/3 == "div" and 3%a == "mod") | ||
| 796 | assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and | ||
| 797 | -a == "unm" and #a == "len" and a&3 == "band") | ||
| 798 | assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shift" and | ||
| 799 | a>>1 == "shift") | ||
| 800 | assert (a==b and a.op == "eq") | ||
| 801 | assert (a>=b and a.op == "order") | ||
| 802 | assert (a>b and a.op == "order") | ||
| 803 | assert(~a == "bnot") | ||
| 804 | |||
| 805 | do -- testing for-iterator name | ||
| 806 | local function f() | ||
| 807 | assert(debug.getinfo(1).name == "for iterator") | ||
| 808 | end | ||
| 809 | |||
| 810 | for i in f do end | ||
| 811 | end | ||
| 812 | |||
| 813 | |||
| 814 | do -- testing debug info for finalizers | ||
| 815 | local name = nil | ||
| 816 | |||
| 817 | -- create a piece of garbage with a finalizer | ||
| 818 | setmetatable({}, {__gc = function () | ||
| 819 | local t = debug.getinfo(2) -- get callee information | ||
| 820 | assert(t.namewhat == "metamethod") | ||
| 821 | name = t.name | ||
| 822 | end}) | ||
| 823 | |||
| 824 | -- repeat until previous finalizer runs (setting 'name') | ||
| 825 | repeat local a = {} until name | ||
| 826 | assert(name == "__gc") | ||
| 827 | end | ||
| 828 | |||
| 829 | |||
| 830 | do | ||
| 831 | print("testing traceback sizes") | ||
| 832 | |||
| 833 | local function countlines (s) | ||
| 834 | return select(2, string.gsub(s, "\n", "")) | ||
| 835 | end | ||
| 836 | |||
| 837 | local function deep (lvl, n) | ||
| 838 | if lvl == 0 then | ||
| 839 | return (debug.traceback("message", n)) | ||
| 840 | else | ||
| 841 | return (deep(lvl-1, n)) | ||
| 842 | end | ||
| 843 | end | ||
| 844 | |||
| 845 | local function checkdeep (total, start) | ||
| 846 | local s = deep(total, start) | ||
| 847 | local rest = string.match(s, "^message\nstack traceback:\n(.*)$") | ||
| 848 | local cl = countlines(rest) | ||
| 849 | -- at most 10 lines in first part, 11 in second, plus '...' | ||
| 850 | assert(cl <= 10 + 11 + 1) | ||
| 851 | local brk = string.find(rest, "%.%.%.") | ||
| 852 | if brk then -- does message have '...'? | ||
| 853 | local rest1 = string.sub(rest, 1, brk) | ||
| 854 | local rest2 = string.sub(rest, brk, #rest) | ||
| 855 | assert(countlines(rest1) == 10 and countlines(rest2) == 11) | ||
| 856 | else | ||
| 857 | assert(cl == total - start + 2) | ||
| 858 | end | ||
| 859 | end | ||
| 860 | |||
| 861 | for d = 1, 51, 10 do | ||
| 862 | for l = 1, d do | ||
| 863 | -- use coroutines to ensure complete control of the stack | ||
| 864 | coroutine.wrap(checkdeep)(d, l) | ||
| 865 | end | ||
| 866 | end | ||
| 867 | |||
| 868 | end | ||
| 869 | |||
| 870 | |||
| 871 | print("testing debug functions on chunk without debug info") | ||
| 872 | prog = [[-- program to be loaded without debug information | ||
| 873 | local debug = require'debug' | ||
| 874 | local a = 12 -- a local variable | ||
| 875 | |||
| 876 | local n, v = debug.getlocal(1, 1) | ||
| 877 | assert(n == "(*temporary)" and v == debug) -- unkown name but known value | ||
| 878 | n, v = debug.getlocal(1, 2) | ||
| 879 | assert(n == "(*temporary)" and v == 12) -- unkown name but known value | ||
| 880 | |||
| 881 | -- a function with an upvalue | ||
| 882 | local f = function () local x; return a end | ||
| 883 | n, v = debug.getupvalue(f, 1) | ||
| 884 | assert(n == "(*no name)" and v == 12) | ||
| 885 | assert(debug.setupvalue(f, 1, 13) == "(*no name)") | ||
| 886 | assert(a == 13) | ||
| 887 | |||
| 888 | local t = debug.getinfo(f) | ||
| 889 | assert(t.name == nil and t.linedefined > 0 and | ||
| 890 | t.lastlinedefined == t.linedefined and | ||
| 891 | t.short_src == "?") | ||
| 892 | assert(debug.getinfo(1).currentline == -1) | ||
| 893 | |||
| 894 | t = debug.getinfo(f, "L").activelines | ||
| 895 | assert(next(t) == nil) -- active lines are empty | ||
| 896 | |||
| 897 | -- dump/load a function without debug info | ||
| 898 | f = load(string.dump(f)) | ||
| 899 | |||
| 900 | t = debug.getinfo(f) | ||
| 901 | assert(t.name == nil and t.linedefined > 0 and | ||
| 902 | t.lastlinedefined == t.linedefined and | ||
| 903 | t.short_src == "?") | ||
| 904 | assert(debug.getinfo(1).currentline == -1) | ||
| 905 | |||
| 906 | return a | ||
| 907 | ]] | ||
| 908 | |||
| 909 | |||
| 910 | -- load 'prog' without debug info | ||
| 911 | local f = assert(load(string.dump(load(prog), true))) | ||
| 912 | |||
| 913 | assert(f() == 13) | ||
| 914 | |||
| 915 | do -- tests for 'source' in binary dumps | ||
| 916 | local prog = [[ | ||
| 917 | return function (x) | ||
| 918 | return function (y) | ||
| 919 | return x + y | ||
| 920 | end | ||
| 921 | end | ||
| 922 | ]] | ||
| 923 | local name = string.rep("x", 1000) | ||
| 924 | local p = assert(load(prog, name)) | ||
| 925 | -- load 'p' as a binary chunk with debug information | ||
| 926 | local c = string.dump(p) | ||
| 927 | assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump | ||
| 928 | local f = assert(load(c)) | ||
| 929 | local g = f() | ||
| 930 | local h = g(3) | ||
| 931 | assert(h(5) == 8) | ||
| 932 | assert(debug.getinfo(f).source == name and -- all functions have 'source' | ||
| 933 | debug.getinfo(g).source == name and | ||
| 934 | debug.getinfo(h).source == name) | ||
| 935 | -- again, without debug info | ||
| 936 | local c = string.dump(p, true) | ||
| 937 | assert(#c < 500) -- no 'source' in dump | ||
| 938 | local f = assert(load(c)) | ||
| 939 | local g = f() | ||
| 940 | local h = g(30) | ||
| 941 | assert(h(50) == 80) | ||
| 942 | assert(debug.getinfo(f).source == '=?' and -- no function has 'source' | ||
| 943 | debug.getinfo(g).source == '=?' and | ||
| 944 | debug.getinfo(h).source == '=?') | ||
| 945 | end | ||
| 946 | |||
| 947 | print"OK" | ||
| 948 | |||
