1-- testing debug library
2
3local function dostring(s) return assert(loadstring(s))() end
4
5print"testing debug library and debug information"
6
7do
8local a=1
9end
10
11function test (s, l, p)
12  collectgarbage()   -- avoid gc during trace
13  local function f (event, line)
14    assert(event == 'line')
15    local l = table.remove(l, 1)
16    if p then print(l, line) end
17    assert(l == line, "wrong trace!!")
18  end
19  debug.sethook(f,"l"); loadstring(s)(); debug.sethook()
20  assert(table.getn(l) == 0)
21end
22
23
24do
25  local a = debug.getinfo(print)
26  assert(a.what == "C" and a.short_src == "[C]")
27  local b = debug.getinfo(test, "SfL")
28  assert(b.name == nil and b.what == "Lua" and b.linedefined == 11 and
29         b.lastlinedefined == b.linedefined + 10 and
30         b.func == test and not string.find(b.short_src, "%["))
31  assert(b.activelines[b.linedefined + 1] and
32         b.activelines[b.lastlinedefined])
33  assert(not b.activelines[b.linedefined] and
34         not b.activelines[b.lastlinedefined + 1])
35end
36
37
38-- test file and string names truncation
39a = "function f () end"
40local function dostring (s, x) return loadstring(s, x)() end
41dostring(a)
42assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a))
43dostring(a..string.format("; %s\n=1", string.rep('p', 400)))
44assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
45dostring("\n"..a)
46assert(debug.getinfo(f).short_src == '[string "..."]')
47dostring(a, "")
48assert(debug.getinfo(f).short_src == '[string ""]')
49dostring(a, "@xuxu")
50assert(debug.getinfo(f).short_src == "xuxu")
51dostring(a, "@"..string.rep('p', 1000)..'t')
52assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$"))
53dostring(a, "=xuxu")
54assert(debug.getinfo(f).short_src == "xuxu")
55dostring(a, string.format("=%s", string.rep('x', 500)))
56assert(string.find(debug.getinfo(f).short_src, "^x*"))
57dostring(a, "=")
58assert(debug.getinfo(f).short_src == "")
59a = nil; f = nil;
60
61
62repeat
63  local g = {x = function ()
64    local a = debug.getinfo(2)
65    assert(a.name == 'f' and a.namewhat == 'local')
66    a = debug.getinfo(1)
67    assert(a.name == 'x' and a.namewhat == 'field')
68    return 'xixi'
69  end}
70  local f = function () return 1+1 and (not 1 or g.x()) end
71  assert(f() == 'xixi')
72  g = debug.getinfo(f)
73  assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name)
74
75  function f (x, name)   -- local!
76    name = name or 'f'
77    local a = debug.getinfo(1)
78    assert(a.name == name and a.namewhat == 'local')
79    return x
80  end
81
82  -- breaks in different conditions
83  if 3>4 then break end; f()
84  if 3<4 then a=1 else break end; f()
85  while 1 do local x=10; break end; f()
86  local b = 1
87  if 3>4 then return math.sin(1) end; f()
88  a = 3<4; f()
89  a = 3<4 or 1; f()
90  repeat local x=20; if 4>3 then f() else break end; f() until 1
91  g = {}
92  f(g).x = f(2) and f(10)+f(9)
93  assert(g.x == f(19))
94  function g(x) if not x then return 3 end return (x('a', 'x')) end
95  assert(g(f) == 'a')
96until 1
97
98test([[if
99math.sin(1)
100then
101  a=1
102else
103  a=2
104end
105]], {2,4,7})
106
107test([[--
108if nil then
109  a=1
110else
111  a=2
112end
113]], {2,5,6})
114
115test([[a=1
116repeat
117  a=a+1
118until a==3
119]], {1,3,4,3,4})
120
121test([[ do
122  return
123end
124]], {2})
125
126test([[local a
127a=1
128while a<=3 do
129  a=a+1
130end
131]], {2,3,4,3,4,3,4,3,5})
132
133test([[while math.sin(1) do
134  if math.sin(1)
135  then
136    break
137  end
138end
139a=1]], {1,2,4,7})
140
141test([[for i=1,3 do
142  a=i
143end
144]], {1,2,1,2,1,2,1,3})
145
146test([[for i,v in pairs{'a','b'} do
147  a=i..v
148end
149]], {1,2,1,2,1,3})
150
151test([[for i=1,4 do a=1 end]], {1,1,1,1,1})
152
153
154
155print'+'
156
157a = {}; L = nil
158local glob = 1
159local oldglob = glob
160debug.sethook(function (e,l)
161  collectgarbage()   -- force GC during a hook
162  local f, m, c = debug.gethook()
163  assert(m == 'crl' and c == 0)
164  if e == "line" then
165    if glob ~= oldglob then
166      L = l-1   -- get the first line where "glob" has changed
167      oldglob = glob
168    end
169  elseif e == "call" then
170      local f = debug.getinfo(2, "f").func
171      a[f] = 1
172  else assert(e == "return")
173  end
174end, "crl")
175
176function f(a,b)
177  collectgarbage()
178  local _, x = debug.getlocal(1, 1)
179  local _, y = debug.getlocal(1, 2)
180  assert(x == a and y == b)
181  assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
182  assert(debug.setlocal(2, 4, "ma��") == "B")
183  x = debug.getinfo(2)
184  assert(x.func == g and x.what == "Lua" and x.name == 'g' and
185         x.nups == 0 and string.find(x.source, "^@.*db%.lua"))
186  glob = glob+1
187  assert(debug.getinfo(1, "l").currentline == L+1)
188  assert(debug.getinfo(1, "l").currentline == L+2)
189end
190
191function foo()
192  glob = glob+1
193  assert(debug.getinfo(1, "l").currentline == L+1)
194end; foo()  -- set L
195-- check line counting inside strings and empty lines
196
197_ = 'alo\
198alo' .. [[
199
200]]
201--[[
202]]
203assert(debug.getinfo(1, "l").currentline == L+11)  -- check count of lines
204
205
206function g(...)
207  do local a,b,c; a=math.sin(40); end
208  local feijao
209  local AAAA,B = "xuxu", "mam�o"
210  f(AAAA,B)
211  assert(AAAA == "pera" and B == "ma��")
212  do
213     local B = 13
214     local x,y = debug.getlocal(1,5)
215     assert(x == 'B' and y == 13)
216  end
217end
218
219g()
220
221
222assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print])
223
224
225-- tests for manipulating non-registered locals (C and Lua temporaries)
226
227local n, v = debug.getlocal(0, 1)
228assert(v == 0 and n == "(*temporary)")
229local n, v = debug.getlocal(0, 2)
230assert(v == 2 and n == "(*temporary)")
231assert(not debug.getlocal(0, 3))
232assert(not debug.getlocal(0, 0))
233
234function f()
235  assert(select(2, debug.getlocal(2,3)) == 1)
236  assert(not debug.getlocal(2,4))
237  debug.setlocal(2, 3, 10)
238  return 20
239end
240
241function g(a,b) return (a+1) + f() end
242
243assert(g(0,0) == 30)
244
245
246debug.sethook(nil);
247assert(debug.gethook() == nil)
248
249
250-- testing access to function arguments
251
252X = nil
253a = {}
254function a:f (a, b, ...) local c = 13 end
255debug.sethook(function (e)
256  assert(e == "call")
257  dostring("XX = 12")  -- test dostring inside hooks
258  -- testing errors inside hooks
259  assert(not pcall(loadstring("a='joao'+1")))
260  debug.sethook(function (e, l)
261    assert(debug.getinfo(2, "l").currentline == l)
262    local f,m,c = debug.gethook()
263    assert(e == "line")
264    assert(m == 'l' and c == 0)
265    debug.sethook(nil)  -- hook is called only once
266    assert(not X)       -- check that
267    X = {}; local i = 1
268    local x,y
269    while 1 do
270      x,y = debug.getlocal(2, i)
271      if x==nil then break end
272      X[x] = y
273      i = i+1
274    end
275  end, "l")
276end, "c")
277
278a:f(1,2,3,4,5)
279assert(X.self == a and X.a == 1   and X.b == 2 and X.arg.n == 3 and X.c == nil)
280assert(XX == 12)
281assert(debug.gethook() == nil)
282
283
284-- testing upvalue access
285local function getupvalues (f)
286  local t = {}
287  local i = 1
288  while true do
289    local name, value = debug.getupvalue(f, i)
290    if not name then break end
291    assert(not t[name])
292    t[name] = value
293    i = i + 1
294  end
295  return t
296end
297
298local a,b,c = 1,2,3
299local function foo1 (a) b = a; return c end
300local function foo2 (x) a = x; return c+b end
301assert(debug.getupvalue(foo1, 3) == nil)
302assert(debug.getupvalue(foo1, 0) == nil)
303assert(debug.setupvalue(foo1, 3, "xuxu") == nil)
304local t = getupvalues(foo1)
305assert(t.a == nil and t.b == 2 and t.c == 3)
306t = getupvalues(foo2)
307assert(t.a == 1 and t.b == 2 and t.c == 3)
308assert(debug.setupvalue(foo1, 1, "xuxu") == "b")
309assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu")
310-- cannot manipulate C upvalues from Lua
311assert(debug.getupvalue(io.read, 1) == nil)
312assert(debug.setupvalue(io.read, 1, 10) == nil)
313
314
315-- testing count hooks
316local a=0
317debug.sethook(function (e) a=a+1 end, "", 1)
318a=0; for i=1,1000 do end; assert(1000 < a and a < 1012)
319debug.sethook(function (e) a=a+1 end, "", 4)
320a=0; for i=1,1000 do end; assert(250 < a and a < 255)
321local f,m,c = debug.gethook()
322assert(m == "" and c == 4)
323debug.sethook(function (e) a=a+1 end, "", 4000)
324a=0; for i=1,1000 do end; assert(a == 0)
325debug.sethook(print, "", 2^24 - 1)   -- count upperbound
326local f,m,c = debug.gethook()
327assert(({debug.gethook()})[3] == 2^24 - 1)
328debug.sethook()
329
330
331-- tests for tail calls
332local function f (x)
333  if x then
334    assert(debug.getinfo(1, "S").what == "Lua")
335    local tail = debug.getinfo(2)
336    assert(not pcall(getfenv, 3))
337    assert(tail.what == "tail" and tail.short_src == "(tail call)" and
338           tail.linedefined == -1 and tail.func == nil)
339    assert(debug.getinfo(3, "f").func == g1)
340    assert(getfenv(3))
341    assert(debug.getinfo(4, "S").what == "tail")
342    assert(not pcall(getfenv, 5))
343    assert(debug.getinfo(5, "S").what == "main")
344    assert(getfenv(5))
345    print"+"
346    end
347end
348
349function g(x) return f(x) end
350
351function g1(x) g(x) end
352
353local function h (x) local f=g1; return f(x) end
354
355h(true)
356
357local b = {}
358debug.sethook(function (e) table.insert(b, e) end, "cr")
359h(false)
360debug.sethook()
361local res = {"return",   -- first return (from sethook)
362  "call", "call", "call", "call",
363  "return", "tail return", "return", "tail return",
364  "call",    -- last call (to sethook)
365}
366for _, k in ipairs(res) do assert(k == table.remove(b, 1)) end
367
368
369lim = 30000
370local function foo (x)
371  if x==0 then
372    assert(debug.getinfo(lim+2).what == "main")
373    for i=2,lim do assert(debug.getinfo(i, "S").what == "tail") end
374  else return foo(x-1)
375  end
376end
377
378foo(lim)
379
380
381print"+"
382
383
384-- testing traceback
385
386assert(debug.traceback(print) == print)
387assert(debug.traceback(print, 4) == print)
388assert(string.find(debug.traceback("hi", 4), "^hi\n"))
389assert(string.find(debug.traceback("hi"), "^hi\n"))
390assert(not string.find(debug.traceback("hi"), "'traceback'"))
391assert(string.find(debug.traceback("hi", 0), "'traceback'"))
392assert(string.find(debug.traceback(), "^stack traceback:\n"))
393
394-- testing debugging of coroutines
395
396local function checktraceback (co, p)
397  local tb = debug.traceback(co)
398  local i = 0
399  for l in string.gmatch(tb, "[^\n]+\n?") do
400    assert(i == 0 or string.find(l, p[i]))
401    i = i+1
402  end
403  assert(p[i] == nil)
404end
405
406
407local function f (n)
408  if n > 0 then return f(n-1)
409  else coroutine.yield() end
410end
411
412local co = coroutine.create(f)
413coroutine.resume(co, 3)
414checktraceback(co, {"yield", "db.lua", "tail", "tail", "tail"})
415
416
417co = coroutine.create(function (x)
418       local a = 1
419       coroutine.yield(debug.getinfo(1, "l"))
420       coroutine.yield(debug.getinfo(1, "l").currentline)
421       return a
422     end)
423
424local tr = {}
425local foo = function (e, l) table.insert(tr, l) end
426debug.sethook(co, foo, "l")
427
428local _, l = coroutine.resume(co, 10)
429local x = debug.getinfo(co, 1, "lfLS")
430assert(x.currentline == l.currentline and x.activelines[x.currentline])
431assert(type(x.func) == "function")
432for i=x.linedefined + 1, x.lastlinedefined do
433  assert(x.activelines[i])
434  x.activelines[i] = nil
435end
436assert(next(x.activelines) == nil)   -- no 'extra' elements
437assert(debug.getinfo(co, 2) == nil)
438local a,b = debug.getlocal(co, 1, 1)
439assert(a == "x" and b == 10)
440a,b = debug.getlocal(co, 1, 2)
441assert(a == "a" and b == 1)
442debug.setlocal(co, 1, 2, "hi")
443assert(debug.gethook(co) == foo)
444assert(table.getn(tr) == 2 and
445       tr[1] == l.currentline-1 and tr[2] == l.currentline)
446
447a,b,c = pcall(coroutine.resume, co)
448assert(a and b and c == l.currentline+1)
449checktraceback(co, {"yield", "in function <"})
450
451a,b = coroutine.resume(co)
452assert(a and b == "hi")
453assert(table.getn(tr) == 4 and tr[4] == l.currentline+2)
454assert(debug.gethook(co) == foo)
455assert(debug.gethook() == nil)
456checktraceback(co, {})
457
458
459-- check traceback of suspended (or dead with error) coroutines
460
461function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end
462
463co = coroutine.create(function (x) f(x) end)
464a, b = coroutine.resume(co, 3)
465t = {"'yield'", "'f'", "in function <"}
466while coroutine.status(co) == "suspended" do
467  checktraceback(co, t)
468  a, b = coroutine.resume(co)
469  table.insert(t, 2, "'f'")   -- one more recursive call to 'f'
470end
471t[1] = "'error'"
472checktraceback(co, t)
473
474
475-- test acessing line numbers of a coroutine from a resume inside
476-- a C function (this is a known bug in Lua 5.0)
477
478local function g(x)
479    coroutine.yield(x)
480end
481
482local function f (i)
483  debug.sethook(function () end, "l")
484  for j=1,1000 do
485    g(i+j)
486  end
487end
488
489local co = coroutine.wrap(f)
490co(10)
491pcall(co)
492pcall(co)
493
494
495assert(type(debug.getregistry()) == "table")
496
497
498print"OK"
499
500