coroutine.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. print "testing coroutines"
  2. -- local debug = require'debug'
  3. local weakTableSupported = false
  4. local f
  5. local main, ismain = coroutine.running()
  6. assert(type(main) == "thread" and ismain)
  7. assert(not coroutine.resume(main))
  8. assert(not pcall(coroutine.yield))
  9. -- tests for multiple yield/resume arguments
  10. local function eqtab (t1, t2)
  11. assert(#t1 == #t2)
  12. for i = 1, #t1 do
  13. local v = t1[i]
  14. assert(t2[i] == v)
  15. end
  16. end
  17. _G.x = nil -- declare x
  18. function foo (a, ...)
  19. local x, y = coroutine.running()
  20. assert(x == f and y == false)
  21. assert(coroutine.status(f) == "running")
  22. local arg = {...}
  23. for i=1,#arg do
  24. _G.x = {coroutine.yield(table.unpack(arg[i]))}
  25. end
  26. return table.unpack(a)
  27. end
  28. f = coroutine.create(foo)
  29. assert(type(f) == "thread" and coroutine.status(f) == "suspended")
  30. assert(string.find(tostring(f), "thread"))
  31. local s,a,b,c,d
  32. s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
  33. assert(s and a == nil and coroutine.status(f) == "suspended")
  34. s,a,b,c,d = coroutine.resume(f)
  35. eqtab(_G.x, {})
  36. assert(s and a == 1 and b == nil)
  37. s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
  38. eqtab(_G.x, {1, 2, 3})
  39. assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
  40. s,a,b,c,d = coroutine.resume(f, "xuxu")
  41. eqtab(_G.x, {"xuxu"})
  42. assert(s and a == 1 and b == 2 and c == 3 and d == nil)
  43. assert(coroutine.status(f) == "dead")
  44. s, a = coroutine.resume(f, "xuxu")
  45. assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
  46. -- yields in tail calls
  47. local function foo (i) return coroutine.yield(i) end
  48. f = coroutine.wrap(function ()
  49. for i=1,10 do
  50. assert(foo(i) == _G.x)
  51. end
  52. return 'a'
  53. end)
  54. for i=1,10 do _G.x = i; assert(f(i) == i) end
  55. _G.x = 'xuxu'; assert(f('xuxu') == 'a')
  56. -- recursive
  57. function pf (n, i)
  58. coroutine.yield(n)
  59. pf(n*i, i+1)
  60. end
  61. f = coroutine.wrap(pf)
  62. local s=1
  63. for i=1,10 do
  64. assert(f(1, 1) == s)
  65. s = s*i
  66. end
  67. -- sieve
  68. function gen (n)
  69. return coroutine.wrap(function ()
  70. for i=2,n do coroutine.yield(i) end
  71. end)
  72. end
  73. function filter (p, g)
  74. return coroutine.wrap(function ()
  75. while 1 do
  76. local n = g()
  77. if n == nil then return end
  78. if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
  79. end
  80. end)
  81. end
  82. local x = gen(100)
  83. local a = {}
  84. while 1 do
  85. local n = x()
  86. if n == nil then break end
  87. table.insert(a, n)
  88. x = filter(n, x)
  89. end
  90. assert(#a == 25 and a[#a] == 97)
  91. -- yielding across C boundaries
  92. co = coroutine.wrap(function()
  93. assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
  94. coroutine.yield(20)
  95. return 30
  96. end)
  97. assert(co() == 20)
  98. assert(co() == 30)
  99. local f = function (s, i) return coroutine.yield(i) end
  100. local f1 = coroutine.wrap(function ()
  101. return xpcall(pcall, function (...) return ... end,
  102. function ()
  103. local s = 0
  104. for i in f, nil, 1 do pcall(function () s = s + i end) end
  105. error({s})
  106. end)
  107. end)
  108. f1()
  109. for i = 1, 10 do assert(f1(i) == i) end
  110. local r1, r2, v = f1(nil)
  111. assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
  112. function f (a, b) a = coroutine.yield(a); error{a + b} end
  113. function g(x) return x[1]*2 end
  114. co = coroutine.wrap(function ()
  115. coroutine.yield(xpcall(f, g, 10, 20))
  116. end)
  117. assert(co() == 10)
  118. r, msg = co(100)
  119. assert(not r and msg == 240)
  120. -- errors in coroutines
  121. function foo ()
  122. -- assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
  123. -- assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
  124. coroutine.yield(3)
  125. error(foo)
  126. end
  127. function goo() foo() end
  128. x = coroutine.wrap(goo)
  129. assert(x() == 3)
  130. local a,b = pcall(x)
  131. assert(not a and b == foo)
  132. x = coroutine.create(goo)
  133. a,b = coroutine.resume(x)
  134. assert(a and b == 3)
  135. a,b = coroutine.resume(x)
  136. assert(not a and b == foo and coroutine.status(x) == "dead")
  137. a,b = coroutine.resume(x)
  138. assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
  139. -- co-routines x for loop
  140. function all (a, n, k)
  141. if k == 0 then coroutine.yield(a)
  142. else
  143. for i=1,n do
  144. a[k] = i
  145. all(a, n, k-1)
  146. end
  147. end
  148. end
  149. local a = 0
  150. for t in coroutine.wrap(function () all({}, 5, 4) end) do
  151. a = a+1
  152. end
  153. assert(a == 5^4)
  154. -- access to locals of collected corroutines
  155. local C = {}; setmetatable(C, {__mode = "kv"})
  156. local x = coroutine.wrap (function ()
  157. local a = 10
  158. local function f () a = a+10; return a end
  159. while true do
  160. a = a+1
  161. coroutine.yield(f)
  162. end
  163. end)
  164. C[1] = x;
  165. local f = x()
  166. assert(f() == 21 and x()() == 32 and x() == f)
  167. x = nil
  168. if weakTableSupported then
  169. collectgarbage()
  170. assert(C[1] == nil)
  171. end
  172. assert(f() == 43 and f() == 53)
  173. -- old bug: attempt to resume itself
  174. function co_func (current_co)
  175. assert(coroutine.running() == current_co)
  176. assert(coroutine.resume(current_co) == false)
  177. assert(coroutine.resume(current_co) == false)
  178. return 10
  179. end
  180. local co = coroutine.create(co_func)
  181. local a,b = coroutine.resume(co, co)
  182. assert(a == true and b == 10)
  183. assert(coroutine.resume(co, co) == false)
  184. assert(coroutine.resume(co, co) == false)
  185. -- attempt to resume 'normal' coroutine
  186. co1 = coroutine.create(function () return co2() end)
  187. co2 = coroutine.wrap(function ()
  188. assert(coroutine.status(co1) == 'normal')
  189. assert(not coroutine.resume(co1))
  190. coroutine.yield(3)
  191. end)
  192. a,b = coroutine.resume(co1)
  193. assert(a and b == 3)
  194. assert(coroutine.status(co1) == 'dead')
  195. -- infinite recursion of coroutines
  196. -- C# cannot catch stack overflow exception
  197. if false then
  198. local a = coroutine.wrap(function () a() end)
  199. assert(not pcall(a))
  200. end
  201. -- access to locals of erroneous coroutines
  202. local x = coroutine.create (function ()
  203. local a = 10
  204. _G.f = function () a=a+1; return a end
  205. error('x')
  206. end)
  207. assert(not coroutine.resume(x))
  208. -- overwrite previous position of local `a'
  209. assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
  210. assert(_G.f() == 11)
  211. assert(_G.f() == 12)
  212. if not T then
  213. (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a')
  214. else
  215. print "testing yields inside hooks"
  216. local turn
  217. function fact (t, x)
  218. assert(turn == t)
  219. if x == 0 then return 1
  220. else return x*fact(t, x-1)
  221. end
  222. end
  223. local A,B,a,b = 0,0,0,0
  224. local x = coroutine.create(function ()
  225. T.sethook("yield 0", "", 2)
  226. A = fact("A", 10)
  227. end)
  228. local y = coroutine.create(function ()
  229. T.sethook("yield 0", "", 3)
  230. B = fact("B", 11)
  231. end)
  232. while A==0 or B==0 do
  233. if A==0 then turn = "A"; assert(T.resume(x)) end
  234. if B==0 then turn = "B"; assert(T.resume(y)) end
  235. end
  236. assert(B/A == 11)
  237. -- local line = debug.getinfo(1, "l").currentline + 2 -- get line number
  238. local function foo ()
  239. local x = 10 --<< this line is 'line'
  240. x = x + 10
  241. _G.XX = x
  242. end
  243. -- testing yields in line hook
  244. local co = coroutine.wrap(function ()
  245. T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
  246. _G.XX = nil;
  247. -- _G.X = nil; co(); assert(_G.X == line)
  248. -- _G.X = nil; co(); assert(_G.X == line + 1)
  249. -- _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
  250. -- _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
  251. assert(co() == 10)
  252. -- testing yields in count hook
  253. co = coroutine.wrap(function ()
  254. T.sethook("yield 0", "", 1); foo(); return 10 end)
  255. _G.XX = nil;
  256. local c = 0
  257. repeat c = c + 1; local a = co() until a == 10
  258. assert(_G.XX == 20 and c == 10)
  259. co = coroutine.wrap(function ()
  260. T.sethook("yield 0", "", 2); foo(); return 10 end)
  261. _G.XX = nil;
  262. local c = 0
  263. repeat c = c + 1; local a = co() until a == 10
  264. assert(_G.XX == 20 and c == 5)
  265. _G.X = nil; _G.XX = nil
  266. print "testing coroutine API"
  267. -- reusing a thread
  268. assert(T.testC([[
  269. newthread # create thread
  270. pushvalue 2 # push body
  271. pushstring 'a a a' # push argument
  272. xmove 0 3 2 # move values to new thread
  273. resume -1, 1 # call it first time
  274. pushstatus
  275. xmove 3 0 0 # move results back to stack
  276. setglobal X # result
  277. setglobal Y # status
  278. pushvalue 2 # push body (to call it again)
  279. pushstring 'b b b'
  280. xmove 0 3 2
  281. resume -1, 1 # call it again
  282. pushstatus
  283. xmove 3 0 0
  284. return 1 # return result
  285. ]], function (...) return ... end) == 'b b b')
  286. assert(X == 'a a a' and Y == 'OK')
  287. -- resuming running coroutine
  288. C = coroutine.create(function ()
  289. return T.testC([[
  290. pushnum 10;
  291. pushnum 20;
  292. resume -3 2;
  293. pushstatus
  294. gettop;
  295. return 3]], C)
  296. end)
  297. local a, b, c, d = coroutine.resume(C)
  298. assert(a == true and string.find(b, "non%-suspended") and
  299. c == "ERRRUN" and d == 4)
  300. a, b, c, d = T.testC([[
  301. rawgeti R 1 # get main thread
  302. pushnum 10;
  303. pushnum 20;
  304. resume -3 2;
  305. pushstatus
  306. gettop;
  307. return 4]])
  308. assert(a == coroutine.running() and string.find(b, "non%-suspended") and
  309. c == "ERRRUN" and d == 4)
  310. -- using a main thread as a coroutine
  311. local state = T.newstate()
  312. T.loadlib(state)
  313. assert(T.doremote(state, [[
  314. coroutine = require'coroutine';
  315. X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
  316. return 'ok']]))
  317. t = table.pack(T.testC(state, [[
  318. rawgeti R 1 # get main thread
  319. pushstring 'XX'
  320. getglobal X # get function for body
  321. pushstring AA # arg
  322. resume 1 1 # 'resume' shadows previous stack!
  323. gettop
  324. setglobal T # top
  325. setglobal B # second yielded value
  326. setglobal A # fist yielded value
  327. rawgeti R 1 # get main thread
  328. pushnum 5 # arg (noise)
  329. resume 1 1 # after coroutine ends, previous stack is back
  330. pushstatus
  331. gettop
  332. return .
  333. ]]))
  334. assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
  335. assert(T.doremote(state, "return T") == '2')
  336. assert(T.doremote(state, "return A") == 'AA')
  337. assert(T.doremote(state, "return B") == 'BB')
  338. T.closestate(state)
  339. print'+'
  340. end
  341. -- leaving a pending coroutine open
  342. _X = coroutine.wrap(function ()
  343. local a = 10
  344. local x = function () a = a+1 end
  345. coroutine.yield()
  346. end)
  347. _X()
  348. if not _soft then
  349. -- bug (stack overflow)
  350. local j = 2^9
  351. local lim = 1000000 -- (C stack limit; assume 32-bit machine)
  352. local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
  353. for i = 1, #t do
  354. local j = t[i]
  355. co = coroutine.create(function()
  356. local t = {}
  357. for i = 1, j do t[i] = i end
  358. return table.unpack(t)
  359. end)
  360. local r, msg = coroutine.resume(co)
  361. assert(not r)
  362. end
  363. end
  364. assert(coroutine.running() == main)
  365. print"+"
  366. print"testing yields inside metamethods"
  367. local mt = {
  368. __eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end,
  369. __lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end,
  370. __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
  371. __add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end,
  372. __sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end,
  373. __concat = function(a,b)
  374. coroutine.yield(nil, "concat");
  375. a = type(a) == "table" and a.x or a
  376. b = type(b) == "table" and b.x or b
  377. return a .. b
  378. end,
  379. __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
  380. __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
  381. }
  382. local function new (x)
  383. return setmetatable({x = x, k = {}}, mt)
  384. end
  385. local a = new(10)
  386. local b = new(12)
  387. local c = new"hello"
  388. local function run (f, t)
  389. local i = 1
  390. local c = coroutine.wrap(f)
  391. while true do
  392. local res, stat = c()
  393. if res then assert(t[i] == nil); return res, t end
  394. assert(stat == t[i])
  395. i = i + 1
  396. end
  397. end
  398. assert(run(function () if (a>=b) then return '>=' else return '<' end end,
  399. {"le", "sub"}) == "<")
  400. -- '<=' using '<'
  401. mt.__le = nil
  402. assert(run(function () if (a<=b) then return '<=' else return '>' end end,
  403. {"lt"}) == "<=")
  404. assert(run(function () if (a==b) then return '==' else return '~=' end end,
  405. {"eq"}) == "~=")
  406. assert(run(function () return a..b end, {"concat"}) == "1012")
  407. assert(run(function() return a .. b .. c .. a end,
  408. {"concat", "concat", "concat"}) == "1012hello10")
  409. assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
  410. {"concat", "concat", "concat"}) == "ab10chello12x")
  411. assert(run(function ()
  412. a.BB = print
  413. return a.BB
  414. end, {"nidx", "idx"}) == print)
  415. -- getuptable & setuptable
  416. -- do local _ENV = _ENV
  417. -- f = function () AAA = BBB + 1; return AAA end
  418. -- end
  419. -- g = new(10); g.k.BBB = 10;
  420. -- debug.setupvalue(f, 1, g)
  421. -- assert(run(f, {"idx", "nidx", "idx"}) == 11)
  422. -- assert(g.k.AAA == 11)
  423. -- print"+"
  424. -- print"testing yields inside 'for' iterators"
  425. -- local f = function (s, i)
  426. -- if i%2 == 0 then coroutine.yield(nil, "for") end
  427. -- if i < s then return i + 1 end
  428. -- end
  429. -- assert(run(function ()
  430. -- local s = 0
  431. -- for i in f, 4, 0 do s = s + i end
  432. -- return s
  433. -- end, {"for", "for", "for"}) == 10)
  434. -- tests for coroutine API
  435. if T==nil then
  436. (Message or print)('\a\n >>> testC not active: skipping coroutine API tests <<<\n\a')
  437. return
  438. end
  439. print('testing coroutine API')
  440. local function apico (...)
  441. local x = {...}
  442. return coroutine.wrap(function ()
  443. return T.testC(table.unpack(x))
  444. end)
  445. end
  446. local a = {apico(
  447. [[
  448. pushstring errorcode
  449. pcallk 1 0 2;
  450. invalid command (should not arrive here)
  451. ]],
  452. [[getctx; gettop; return .]],
  453. "stackmark",
  454. error
  455. )()}
  456. assert(#a == 6 and
  457. a[3] == "stackmark" and
  458. a[4] == "errorcode" and
  459. a[5] == "ERRRUN" and
  460. a[6] == 2) -- 'ctx' to pcallk
  461. local co = apico(
  462. "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
  463. coroutine.yield,
  464. "getctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
  465. "getctx; gettop; return .")
  466. assert(co() == 10)
  467. assert(co(20, 30) == 'a')
  468. a = {co()}
  469. assert(#a == 10 and
  470. a[2] == coroutine.yield and
  471. a[5] == 20 and a[6] == 30 and
  472. a[7] == "YIELD" and a[8] == 3 and
  473. a[9] == "YIELD" and a[10] == 4)
  474. assert(not pcall(co)) -- coroutine is dead now
  475. f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
  476. co = coroutine.wrap(function ()
  477. assert(f() == 23); assert(f() == 23); return 10
  478. end)
  479. assert(co(23,16) == 5)
  480. assert(co(23,16) == 5)
  481. assert(co(23,16) == 10)
  482. -- testing coroutines with C bodies
  483. f = T.makeCfunc([[
  484. pushnum 102
  485. yieldk 1 U2
  486. return 2
  487. ]],
  488. [[
  489. pushnum 23 # continuation
  490. gettop
  491. return .
  492. ]])
  493. x = coroutine.wrap(f)
  494. assert(x() == 102)
  495. assert(x() == 23)
  496. f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
  497. a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
  498. pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
  499. return 4; ]], f)
  500. assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
  501. -- testing chain of suspendable C calls
  502. local count = 3 -- number of levels
  503. f = T.makeCfunc([[
  504. remove 1; # remove argument
  505. pushvalue U3; # get selection function
  506. call 0 1; # call it (result is 'f' or 'yield')
  507. pushstring hello # single argument for selected function
  508. pushupvalueindex 2; # index of continuation program
  509. callk 1 -1 .; # call selected function
  510. errorerror # should never arrive here
  511. ]],
  512. [[
  513. # continuation program
  514. pushnum 34 # return value
  515. gettop
  516. return . # return all results
  517. ]],
  518. function () -- selection function
  519. count = count - 1
  520. if count == 0 then return coroutine.yield
  521. else return f
  522. end
  523. end
  524. )
  525. co = coroutine.wrap(function () return f(nil) end)
  526. assert(co() == "hello") -- argument to 'yield'
  527. a = {co()}
  528. -- three '34's (one from each pending C call)
  529. assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
  530. -- testing yields with continuations
  531. co = coroutine.wrap(function (...) return
  532. T.testC([[
  533. getctx
  534. yieldk 3 2
  535. nonexec error
  536. ]],
  537. [[ # continuation
  538. getctx
  539. yieldk 2 3
  540. ]],
  541. [[ # continuation
  542. getctx
  543. yieldk 2 4
  544. ]],
  545. [[ # continuation
  546. pushvalue 6; pushnum 10; pushnum 20;
  547. pcall 2 0 # call should throw an error and execution continues
  548. pop 1 # remove error message
  549. pushvalue 6
  550. getctx
  551. pcallk 2 2 5 # call should throw an error and jump to continuation
  552. cannot be here!
  553. ]],
  554. [[ # continuation
  555. gettop
  556. return .
  557. ]],
  558. function (a,b) x=a; y=b; error("errmsg") end,
  559. ...
  560. )
  561. end)
  562. local a = {co(3,4,6)}; assert(a[1] == 6 and a[2] == "OK" and a[3] == 0)
  563. a = {co()}; assert(a[1] == "YIELD" and a[2] == 2)
  564. a = {co()}; assert(a[1] == "YIELD" and a[2] == 3)
  565. a = {co(7,8)};
  566. -- original arguments
  567. assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
  568. type(a[3]) == 'string' and type(a[4]) == 'string' and
  569. type(a[5]) == 'string' and type(a[6]) == 'function')
  570. -- arguments left from fist resume
  571. assert(a[7] == 3 and a[8] == 4)
  572. -- arguments to last resume
  573. assert(a[9] == 7 and a[10] == 8)
  574. -- error message and nothing more
  575. assert(a[11]:find("errmsg") and #a == 11)
  576. -- check arguments to pcallk
  577. assert(x == "YIELD" and y == 4)
  578. assert(not pcall(co)) -- coroutine should be dead
  579. -- testing ctx
  580. a,b = T.testC(
  581. [[ pushstring print; pcallk 0 0 12 # error
  582. getctx; return 2 ]])
  583. assert(a == "OK" and b == 0) -- no ctx outside continuations
  584. -- bug in nCcalls
  585. local co = coroutine.wrap(function ()
  586. local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
  587. return pcall(assert, table.unpack(a))
  588. end)
  589. local a = {co()}
  590. assert(a[10] == "hi")
  591. print'OK'