closure.lua 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. print "testing closures and coroutines"
  2. --[[
  3. local A,B = 0,{g=10}
  4. function f(x)
  5. local a = {}
  6. for i=1,1000 do
  7. local y = 0
  8. do
  9. a[i] = function () B.g = B.g+1; y = y+x; return y+A end
  10. end
  11. end
  12. local dummy = function () return a[A] end
  13. collectgarbage()
  14. A = 1; assert(dummy() == a[1]); A = 0;
  15. assert(a[1]() == x)
  16. assert(a[3]() == x)
  17. collectgarbage()
  18. assert(B.g == 12)
  19. return a
  20. end
  21. a = f(10)
  22. -- force a GC in this level
  23. local x = {[1] = {}} -- to detect a GC
  24. setmetatable(x, {__mode = 'kv'})
  25. while x[1] do -- repeat until GC
  26. local a = A..A..A..A -- create garbage
  27. A = A+1
  28. end
  29. assert(a[1]() == 20+A)
  30. assert(a[1]() == 30+A)
  31. assert(a[2]() == 10+A)
  32. collectgarbage()
  33. assert(a[2]() == 20+A)
  34. assert(a[2]() == 30+A)
  35. assert(a[3]() == 20+A)
  36. assert(a[8]() == 10+A)
  37. assert(getmetatable(x).__mode == 'kv')
  38. assert(B.g == 19)
  39. --]]
  40. -- testing closures with 'for' control variable
  41. a = {}
  42. for i=1,10 do
  43. a[i] = {set = function(x) i=x end, get = function () return i end}
  44. if i == 3 then break end
  45. end
  46. assert(a[4] == nil)
  47. a[1].set(10)
  48. assert(a[2].get() == 2)
  49. a[2].set('a')
  50. assert(a[3].get() == 3)
  51. assert(a[2].get() == 'a')
  52. a = {}
  53. for i, k in pairs{'a', 'b'} do
  54. a[i] = {set = function(x, y) i=x; k=y end,
  55. get = function () return i, k end}
  56. if i == 2 then break end
  57. end
  58. a[1].set(10, 20)
  59. local r,s = a[2].get()
  60. assert(r == 2 and s == 'b')
  61. r,s = a[1].get()
  62. assert(r == 10 and s == 20)
  63. a[2].set('a', 'b')
  64. r,s = a[2].get()
  65. assert(r == "a" and s == "b")
  66. -- testing closures with 'for' control variable x break
  67. for i=1,3 do
  68. f = function () return i end
  69. break
  70. end
  71. assert(f() == 1)
  72. for k, v in pairs{"a", "b"} do
  73. f = function () return k, v end
  74. break
  75. end
  76. assert(({f()})[1] == 1)
  77. assert(({f()})[2] == "a")
  78. -- testing closure x break x return x errors
  79. local b
  80. function f(x)
  81. local first = 1
  82. while 1 do
  83. if x == 3 and not first then return end
  84. local a = 'xuxu'
  85. b = function (op, y)
  86. if op == 'set' then
  87. a = x+y
  88. else
  89. return a
  90. end
  91. end
  92. if x == 1 then do break end
  93. elseif x == 2 then return
  94. else if x ~= 3 then error() end
  95. end
  96. first = nil
  97. end
  98. end
  99. for i=1,3 do
  100. f(i)
  101. assert(b('get') == 'xuxu')
  102. b('set', 10); assert(b('get') == 10+i)
  103. b = nil
  104. end
  105. pcall(f, 4);
  106. assert(b('get') == 'xuxu')
  107. b('set', 10); assert(b('get') == 14)
  108. local w
  109. -- testing multi-level closure
  110. function f(x)
  111. return function (y)
  112. return function (z) return w+x+y+z end
  113. end
  114. end
  115. y = f(10)
  116. w = 1.345
  117. assert(y(20)(30) == 60+w)
  118. -- testing closures x repeat-until
  119. local a = {}
  120. local i = 1
  121. repeat
  122. local x = i
  123. a[i] = function () i = x+1; return x end
  124. until i > 10 or a[i]() ~= x
  125. assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
  126. print'+'
  127. -- test for correctly closing upvalues in tail calls of vararg functions
  128. local function t ()
  129. local function c(a,b) assert(a=="test" and b=="OK") end
  130. local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
  131. local x = 1
  132. return v(function() return x end)
  133. end
  134. t()
  135. -- coroutine tests
  136. local f
  137. assert(coroutine.running() == nil)
  138. -- tests for global environment
  139. local function foo (a)
  140. setfenv(0, a)
  141. coroutine.yield(getfenv())
  142. assert(getfenv(0) == a)
  143. assert(getfenv(1) == _G)
  144. return getfenv(1)
  145. end
  146. f = coroutine.wrap(foo)
  147. local a = {}
  148. assert(f(a) == _G)
  149. local a,b = pcall(f)
  150. assert(a and b == _G)
  151. -- tests for multiple yield/resume arguments
  152. local function eqtab (t1, t2)
  153. assert(table.getn(t1) == table.getn(t2))
  154. for i,v in ipairs(t1) do
  155. assert(t2[i] == v)
  156. end
  157. end
  158. _G.x = nil -- declare x
  159. function foo (a, ...)
  160. assert(coroutine.running() == f)
  161. assert(coroutine.status(f) == "running")
  162. local arg = {...}
  163. for i=1,table.getn(arg) do
  164. _G.x = {coroutine.yield(unpack(arg[i]))}
  165. end
  166. return unpack(a)
  167. end
  168. f = coroutine.create(foo)
  169. assert(type(f) == "thread" and coroutine.status(f) == "suspended")
  170. assert(string.find(tostring(f), "thread"))
  171. local s,a,b,c,d
  172. s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
  173. assert(s and a == nil and coroutine.status(f) == "suspended")
  174. s,a,b,c,d = coroutine.resume(f)
  175. eqtab(_G.x, {})
  176. assert(s and a == 1 and b == nil)
  177. s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
  178. eqtab(_G.x, {1, 2, 3})
  179. assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
  180. s,a,b,c,d = coroutine.resume(f, "xuxu")
  181. eqtab(_G.x, {"xuxu"})
  182. assert(s and a == 1 and b == 2 and c == 3 and d == nil)
  183. assert(coroutine.status(f) == "dead")
  184. s, a = coroutine.resume(f, "xuxu")
  185. assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
  186. -- yields in tail calls
  187. local function foo (i) return coroutine.yield(i) end
  188. f = coroutine.wrap(function ()
  189. for i=1,10 do
  190. assert(foo(i) == _G.x)
  191. end
  192. return 'a'
  193. end)
  194. for i=1,10 do _G.x = i; assert(f(i) == i) end
  195. _G.x = 'xuxu'; assert(f('xuxu') == 'a')
  196. -- recursive
  197. function pf (n, i)
  198. coroutine.yield(n)
  199. pf(n*i, i+1)
  200. end
  201. f = coroutine.wrap(pf)
  202. local s=1
  203. for i=1,10 do
  204. assert(f(1, 1) == s)
  205. s = s*i
  206. end
  207. -- sieve
  208. function gen (n)
  209. return coroutine.wrap(function ()
  210. for i=2,n do coroutine.yield(i) end
  211. end)
  212. end
  213. function filter (p, g)
  214. return coroutine.wrap(function ()
  215. while 1 do
  216. local n = g()
  217. if n == nil then return end
  218. if math.mod(n, p) ~= 0 then coroutine.yield(n) end
  219. end
  220. end)
  221. end
  222. local x = gen(100)
  223. local a = {}
  224. while 1 do
  225. local n = x()
  226. if n == nil then break end
  227. table.insert(a, n)
  228. x = filter(n, x)
  229. end
  230. assert(table.getn(a) == 25 and a[table.getn(a)] == 97)
  231. -- errors in coroutines
  232. function foo ()
  233. assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
  234. assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
  235. coroutine.yield(3)
  236. error(foo)
  237. end
  238. function goo() foo() end
  239. x = coroutine.wrap(goo)
  240. assert(x() == 3)
  241. local a,b = pcall(x)
  242. assert(not a and b == foo)
  243. x = coroutine.create(goo)
  244. a,b = coroutine.resume(x)
  245. assert(a and b == 3)
  246. a,b = coroutine.resume(x)
  247. assert(not a and b == foo and coroutine.status(x) == "dead")
  248. a,b = coroutine.resume(x)
  249. assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
  250. -- co-routines x for loop
  251. function all (a, n, k)
  252. if k == 0 then coroutine.yield(a)
  253. else
  254. for i=1,n do
  255. a[k] = i
  256. all(a, n, k-1)
  257. end
  258. end
  259. end
  260. local a = 0
  261. for t in coroutine.wrap(function () all({}, 5, 4) end) do
  262. a = a+1
  263. end
  264. assert(a == 5^4)
  265. -- access to locals of collected corroutines
  266. --[[
  267. local C = {}; setmetatable(C, {__mode = "kv"})
  268. local x = coroutine.wrap (function ()
  269. local a = 10
  270. local function f () a = a+10; return a end
  271. while true do
  272. a = a+1
  273. coroutine.yield(f)
  274. end
  275. end)
  276. C[1] = x;
  277. local f = x()
  278. assert(f() == 21 and x()() == 32 and x() == f)
  279. x = nil
  280. collectgarbage()
  281. assert(C[1] == nil)
  282. assert(f() == 43 and f() == 53)
  283. --]]
  284. -- old bug: attempt to resume itself
  285. function co_func (current_co)
  286. assert(coroutine.running() == current_co)
  287. assert(coroutine.resume(current_co) == false)
  288. assert(coroutine.resume(current_co) == false)
  289. return 10
  290. end
  291. local co = coroutine.create(co_func)
  292. local a,b = coroutine.resume(co, co)
  293. assert(a == true and b == 10)
  294. assert(coroutine.resume(co, co) == false)
  295. assert(coroutine.resume(co, co) == false)
  296. -- access to locals of erroneous coroutines
  297. local x = coroutine.create (function ()
  298. local a = 10
  299. _G.f = function () a=a+1; return a end
  300. error('x')
  301. end)
  302. assert(not coroutine.resume(x))
  303. -- overwrite previous position of local `a'
  304. assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
  305. assert(_G.f() == 11)
  306. assert(_G.f() == 12)
  307. if not T then
  308. (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a')
  309. else
  310. local turn
  311. function fact (t, x)
  312. assert(turn == t)
  313. if x == 0 then return 1
  314. else return x*fact(t, x-1)
  315. end
  316. end
  317. local A,B,a,b = 0,0,0,0
  318. local x = coroutine.create(function ()
  319. T.setyhook("", 2)
  320. A = fact("A", 10)
  321. end)
  322. local y = coroutine.create(function ()
  323. T.setyhook("", 3)
  324. B = fact("B", 11)
  325. end)
  326. while A==0 or B==0 do
  327. if A==0 then turn = "A"; T.resume(x) end
  328. if B==0 then turn = "B"; T.resume(y) end
  329. end
  330. assert(B/A == 11)
  331. end
  332. -- leaving a pending coroutine open
  333. _X = coroutine.wrap(function ()
  334. local a = 10
  335. local x = function () a = a+1 end
  336. coroutine.yield()
  337. end)
  338. _X()
  339. -- coroutine environments
  340. co = coroutine.create(function ()
  341. coroutine.yield(getfenv(0))
  342. return loadstring("return a")()
  343. end)
  344. a = {a = 15}
  345. debug.setfenv(co, a)
  346. assert(debug.getfenv(co) == a)
  347. assert(select(2, coroutine.resume(co)) == a)
  348. assert(select(2, coroutine.resume(co)) == a.a)
  349. print'OK'