diff options
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 | |||