1-- $Id: testes/coroutine.lua $
2-- See Copyright Notice in file all.lua
3
4print "testing coroutines"
5
6local debug = require'debug'
7
8local f
9
10local main, ismain = coroutine.running()
11assert(type(main) == "thread" and ismain)
12assert(not coroutine.resume(main))
13assert(not coroutine.isyieldable(main) and not coroutine.isyieldable())
14assert(not pcall(coroutine.yield))
15
16
17-- trivial errors
18assert(not pcall(coroutine.resume, 0))
19assert(not pcall(coroutine.status, 0))
20
21
22-- tests for multiple yield/resume arguments
23
24local function eqtab (t1, t2)
25 assert(#t1 == #t2)
26 for i = 1, #t1 do
27 local v = t1[i]
28 assert(t2[i] == v)
29 end
30end
31
32_G.x = nil -- declare x
33_G.f = nil -- declare f
34local function foo (a, ...)
35 local x, y = coroutine.running()
36 assert(x == f and y == false)
37 -- next call should not corrupt coroutine (but must fail,
38 -- as it attempts to resume the running coroutine)
39 assert(coroutine.resume(f) == false)
40 assert(coroutine.status(f) == "running")
41 local arg = {...}
42 assert(coroutine.isyieldable(x))
43 for i=1,#arg do
44 _G.x = {coroutine.yield(table.unpack(arg[i]))}
45 end
46 return table.unpack(a)
47end
48
49f = coroutine.create(foo)
50assert(coroutine.isyieldable(f))
51assert(type(f) == "thread" and coroutine.status(f) == "suspended")
52assert(string.find(tostring(f), "thread"))
53local s,a,b,c,d
54s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
55assert(coroutine.isyieldable(f))
56assert(s and a == nil and coroutine.status(f) == "suspended")
57s,a,b,c,d = coroutine.resume(f)
58eqtab(_G.x, {})
59assert(s and a == 1 and b == nil)
60assert(coroutine.isyieldable(f))
61s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
62eqtab(_G.x, {1, 2, 3})
63assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
64s,a,b,c,d = coroutine.resume(f, "xuxu")
65eqtab(_G.x, {"xuxu"})
66assert(s and a == 1 and b == 2 and c == 3 and d == nil)
67assert(coroutine.status(f) == "dead")
68s, a = coroutine.resume(f, "xuxu")
69assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
70
71_G.f = nil
72
73-- yields in tail calls
74local function foo (i) return coroutine.yield(i) end
75local f = coroutine.wrap(function ()
76 for i=1,10 do
77 assert(foo(i) == _G.x)
78 end
79 return 'a'
80end)
81for i=1,10 do _G.x = i; assert(f(i) == i) end
82_G.x = 'xuxu'; assert(f('xuxu') == 'a')
83
84_G.x = nil
85
86-- recursive
87local function pf (n, i)
88 coroutine.yield(n)
89 pf(n*i, i+1)
90end
91
92f = coroutine.wrap(pf)
93local s=1
94for i=1,10 do
95 assert(f(1, 1) == s)
96 s = s*i
97end
98
99-- sieve
100local function gen (n)
101 return coroutine.wrap(function ()
102 for i=2,n do coroutine.yield(i) end
103 end)
104end
105
106
107local function filter (p, g)
108 return coroutine.wrap(function ()
109 while 1 do
110 local n = g()
111 if n == nil then return end
112 if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
113 end
114 end)
115end
116
117local x = gen(80)
118local a = {}
119while 1 do
120 local n = x()
121 if n == nil then break end
122 table.insert(a, n)
123 x = filter(n, x)
124end
125
126assert(#a == 22 and a[#a] == 79)
127x, a = nil
128
129
130print("to-be-closed variables in coroutines")
131
132local function func2close (f)
133 return setmetatable({}, {__close = f})
134end
135
136do
137 -- ok to close a dead coroutine
138 local co = coroutine.create(print)
139 assert(coroutine.resume(co, "testing 'coroutine.close'"))
140 assert(coroutine.status(co) == "dead")
141 local st, msg = coroutine.close(co)
142 assert(st and msg == nil)
143 -- also ok to close it again
144 st, msg = coroutine.close(co)
145 assert(st and msg == nil)
146
147
148 -- cannot close the running coroutine
149 local st, msg = pcall(coroutine.close, coroutine.running())
150 assert(not st and string.find(msg, "running"))
151
152 local main = coroutine.running()
153
154 -- cannot close a "normal" coroutine
155 ;(coroutine.wrap(function ()
156 local st, msg = pcall(coroutine.close, main)
157 assert(not st and string.find(msg, "normal"))
158 end))()
159
160 -- cannot close a coroutine while closing it
161 do
162 local co
163 co = coroutine.create(
164 function()
165 local x <close> = func2close(function()
166 coroutine.close(co) -- try to close it again
167 end)
168 coroutine.yield(20)
169 end)
170 local st, msg = coroutine.resume(co)
171 assert(st and msg == 20)
172 st, msg = coroutine.close(co)
173 assert(not st and string.find(msg, "running coroutine"))
174 end
175
176 -- to-be-closed variables in coroutines
177 local X
178
179 -- closing a coroutine after an error
180 local co = coroutine.create(error)
181 local st, msg = coroutine.resume(co, 100)
182 assert(not st and msg == 100)
183 st, msg = coroutine.close(co)
184 assert(not st and msg == 100)
185 -- after closing, no more errors
186 st, msg = coroutine.close(co)
187 assert(st and msg == nil)
188
189 co = coroutine.create(function ()
190 local x <close> = func2close(function (self, err)
191 assert(err == nil); X = false
192 end)
193 X = true
194 coroutine.yield()
195 end)
196 coroutine.resume(co)
197 assert(X)
198 assert(coroutine.close(co))
199 assert(not X and coroutine.status(co) == "dead")
200
201 -- error closing a coroutine
202 local x = 0
203 co = coroutine.create(function()
204 local y <close> = func2close(function (self,err)
205 assert(err == 111)
206 x = 200
207 error(200)
208 end)
209 local x <close> = func2close(function (self, err)
210 assert(err == nil); error(111)
211 end)
212 coroutine.yield()
213 end)
214 coroutine.resume(co)
215 assert(x == 0)
216 local st, msg = coroutine.close(co)
217 assert(st == false and coroutine.status(co) == "dead" and msg == 200)
218 assert(x == 200)
219 -- after closing, no more errors
220 st, msg = coroutine.close(co)
221 assert(st and msg == nil)
222end
223
224do
225 -- <close> versus pcall in coroutines
226 local X = false
227 local Y = false
228 local function foo ()
229 local x <close> = func2close(function (self, err)
230 Y = debug.getinfo(2)
231 X = err
232 end)
233 error(43)
234 end
235 local co = coroutine.create(function () return pcall(foo) end)
236 local st1, st2, err = coroutine.resume(co)
237 assert(st1 and not st2 and err == 43)
238 assert(X == 43 and Y.what == "C")
239
240 -- recovering from errors in __close metamethods
241 local track = {}
242
243 local function h (o)
244 local hv <close> = o
245 return 1
246 end
247
248 local function foo ()
249 local x <close> = func2close(function(_,msg)
250 track[#track + 1] = msg or false
251 error(20)
252 end)
253 local y <close> = func2close(function(_,msg)
254 track[#track + 1] = msg or false
255 return 1000
256 end)
257 local z <close> = func2close(function(_,msg)
258 track[#track + 1] = msg or false
259 error(10)
260 end)
261 coroutine.yield(1)
262 h(func2close(function(_,msg)
263 track[#track + 1] = msg or false
264 error(2)
265 end))
266 end
267
268 local co = coroutine.create(pcall)
269
270 local st, res = coroutine.resume(co, foo) -- call 'foo' protected
271 assert(st and res == 1) -- yield 1
272 local st, res1, res2 = coroutine.resume(co) -- continue
273 assert(coroutine.status(co) == "dead")
274 assert(st and not res1 and res2 == 20) -- last error (20)
275 assert(track[1] == false and track[2] == 2 and track[3] == 10 and
276 track[4] == 10)
277end
278
279
280-- yielding across C boundaries
281
282local co = coroutine.wrap(function()
283 assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
284 assert(coroutine.isyieldable())
285 coroutine.yield(20)
286 return 30
287 end)
288
289assert(co() == 20)
290assert(co() == 30)
291
292
293local f = function (s, i) return coroutine.yield(i) end
294
295local f1 = coroutine.wrap(function ()
296 return xpcall(pcall, function (...) return ... end,
297 function ()
298 local s = 0
299 for i in f, nil, 1 do pcall(function () s = s + i end) end
300 error({s})
301 end)
302 end)
303
304f1()
305for i = 1, 10 do assert(f1(i) == i) end
306local r1, r2, v = f1(nil)
307assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
308
309
310local function f (a, b) a = coroutine.yield(a); error{a + b} end
311local function g(x) return x[1]*2 end
312
313co = coroutine.wrap(function ()
314 coroutine.yield(xpcall(f, g, 10, 20))
315 end)
316
317assert(co() == 10)
318local r, msg = co(100)
319assert(not r and msg == 240)
320
321
322-- unyieldable C call
323do
324 local function f (c)
325 assert(not coroutine.isyieldable())
326 return c .. c
327 end
328
329 local co = coroutine.wrap(function (c)
330 assert(coroutine.isyieldable())
331 local s = string.gsub("a", ".", f)
332 return s
333 end)
334 assert(co() == "aa")
335end
336
337
338
339do -- testing single trace of coroutines
340 local X
341 local co = coroutine.create(function ()
342 coroutine.yield(10)
343 return 20;
344 end)
345 local trace = {}
346 local function dotrace (event)
347 trace[#trace + 1] = event
348 end
349 debug.sethook(co, dotrace, "clr")
350 repeat until not coroutine.resume(co)
351 local correcttrace = {"call", "line", "call", "return", "line", "return"}
352 assert(#trace == #correcttrace)
353 for k, v in pairs(trace) do
354 assert(v == correcttrace[k])
355 end
356end
357
358-- errors in coroutines
359function foo ()
360 assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
361 assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
362 coroutine.yield(3)
363 error(foo)
364end
365
366function goo() foo() end
367x = coroutine.wrap(goo)
368assert(x() == 3)
369local a,b = pcall(x)
370assert(not a and b == foo)
371
372x = coroutine.create(goo)
373a,b = coroutine.resume(x)
374assert(a and b == 3)
375a,b = coroutine.resume(x)
376assert(not a and b == foo and coroutine.status(x) == "dead")
377a,b = coroutine.resume(x)
378assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
379
380goo = nil
381
382-- co-routines x for loop
383local function all (a, n, k)
384 if k == 0 then coroutine.yield(a)
385 else
386 for i=1,n do
387 a[k] = i
388 all(a, n, k-1)
389 end
390 end
391end
392
393local a = 0
394for t in coroutine.wrap(function () all({}, 5, 4) end) do
395 a = a+1
396end
397assert(a == 5^4)
398
399
400-- access to locals of collected corroutines
401local C = {}; setmetatable(C, {__mode = "kv"})
402local x = coroutine.wrap (function ()
403 local a = 10
404 local function f () a = a+10; return a end
405 while true do
406 a = a+1
407 coroutine.yield(f)
408 end
409 end)
410
411C[1] = x;
412
413local f = x()
414assert(f() == 21 and x()() == 32 and x() == f)
415x = nil
416collectgarbage()
417assert(C[1] == undef)
418assert(f() == 43 and f() == 53)
419
420
421-- old bug: attempt to resume itself
422
423local function co_func (current_co)
424 assert(coroutine.running() == current_co)
425 assert(coroutine.resume(current_co) == false)
426 coroutine.yield(10, 20)
427 assert(coroutine.resume(current_co) == false)
428 coroutine.yield(23)
429 return 10
430end
431
432local co = coroutine.create(co_func)
433local a,b,c = coroutine.resume(co, co)
434assert(a == true and b == 10 and c == 20)
435a,b = coroutine.resume(co, co)
436assert(a == true and b == 23)
437a,b = coroutine.resume(co, co)
438assert(a == true and b == 10)
439assert(coroutine.resume(co, co) == false)
440assert(coroutine.resume(co, co) == false)
441
442
443-- other old bug when attempting to resume itself
444-- (trigger C-code assertions)
445do
446 local A = coroutine.running()
447 local B = coroutine.create(function() return coroutine.resume(A) end)
448 local st, res = coroutine.resume(B)
449 assert(st == true and res == false)
450
451 local X = false
452 A = coroutine.wrap(function()
453 local _ <close> = func2close(function () X = true end)
454 return pcall(A, 1)
455 end)
456 st, res = A()
457 assert(not st and string.find(res, "non%-suspended") and X == true)
458end
459
460
461-- bug in 5.4.1
462do
463 -- coroutine ran close metamethods with invalid status during a
464 -- reset.
465 local co
466 co = coroutine.wrap(function()
467 local x <close> = func2close(function() return pcall(co) end)
468 error(111)
469 end)
470 local st, errobj = pcall(co)
471 assert(not st and errobj == 111)
472 st, errobj = pcall(co)
473 assert(not st and string.find(errobj, "dead coroutine"))
474end
475
476
477-- attempt to resume 'normal' coroutine
478local co1, co2
479co1 = coroutine.create(function () return co2() end)
480co2 = coroutine.wrap(function ()
481 assert(coroutine.status(co1) == 'normal')
482 assert(not coroutine.resume(co1))
483 coroutine.yield(3)
484 end)
485
486a,b = coroutine.resume(co1)
487assert(a and b == 3)
488assert(coroutine.status(co1) == 'dead')
489
490-- infinite recursion of coroutines
491a = function(a) coroutine.wrap(a)(a) end
492assert(not pcall(a, a))
493a = nil
494
495
496-- access to locals of erroneous coroutines
497local x = coroutine.create (function ()
498 local a = 10
499 _G.F = function () a=a+1; return a end
500 error('x')
501 end)
502
503assert(not coroutine.resume(x))
504-- overwrite previous position of local `a'
505assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
506assert(_G.F() == 11)
507assert(_G.F() == 12)
508_G.F = nil
509
510
511if not T then
512 (Message or print)
513 ('\n >>> testC not active: skipping coroutine API tests <<<\n')
514else
515 print "testing yields inside hooks"
516
517 local turn
518
519 local function fact (t, x)
520 assert(turn == t)
521 if x == 0 then return 1
522 else return x*fact(t, x-1)
523 end
524 end
525
526 local A, B = 0, 0
527
528 local x = coroutine.create(function ()
529 T.sethook("yield 0", "", 2)
530 A = fact("A", 6)
531 end)
532
533 local y = coroutine.create(function ()
534 T.sethook("yield 0", "", 3)
535 B = fact("B", 7)
536 end)
537
538 while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
539 if A==0 then turn = "A"; assert(T.resume(x)) end
540 if B==0 then turn = "B"; assert(T.resume(y)) end
541
542 -- check that traceback works correctly after yields inside hooks
543 debug.traceback(x)
544 debug.traceback(y)
545 end
546
547 assert(B // A == 7) -- fact(7) // fact(6)
548
549 do -- hooks vs. multiple values
550 local done
551 local function test (n)
552 done = false
553 return coroutine.wrap(function ()
554 local a = {}
555 for i = 1, n do a[i] = i end
556 -- 'pushint' just to perturb the stack
557 T.sethook("pushint 10; yield 0", "", 1) -- yield at each op.
558 local a1 = {table.unpack(a)} -- must keep top between ops.
559 assert(#a1 == n)
560 for i = 1, n do assert(a[i] == i) end
561 done = true
562 end)
563 end
564 -- arguments to the coroutine are just to perturb its stack
565 local co = test(0); while not done do co(30) end
566 co = test(1); while not done do co(20, 10) end
567 co = test(3); while not done do co() end
568 co = test(100); while not done do co() end
569 end
570
571 local line = debug.getinfo(1, "l").currentline + 2 -- get line number
572 local function foo ()
573 local x = 10 --<< this line is 'line'
574 x = x + 10
575 _G.XX = x
576 end
577
578 -- testing yields in line hook
579 local co = coroutine.wrap(function ()
580 T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
581
582 _G.XX = nil;
583 _G.X = nil; co(); assert(_G.X == line)
584 _G.X = nil; co(); assert(_G.X == line + 1)
585 _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
586 _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
587 assert(co() == 10)
588 _G.X = nil
589
590 -- testing yields in count hook
591 co = coroutine.wrap(function ()
592 T.sethook("yield 0", "", 1); foo(); return 10 end)
593
594 _G.XX = nil;
595 local c = 0
596 repeat c = c + 1; local a = co() until a == 10
597 assert(_G.XX == 20 and c >= 5)
598
599 co = coroutine.wrap(function ()
600 T.sethook("yield 0", "", 2); foo(); return 10 end)
601
602 _G.XX = nil;
603 local c = 0
604 repeat c = c + 1; local a = co() until a == 10
605 assert(_G.XX == 20 and c >= 5)
606 _G.X = nil; _G.XX = nil
607
608 do
609 -- testing debug library on a coroutine suspended inside a hook
610 -- (bug in 5.2/5.3)
611 c = coroutine.create(function (a, ...)
612 T.sethook("yield 0", "l") -- will yield on next two lines
613 local b = a
614 return ...
615 end)
616
617 assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
618 local n,v = debug.getlocal(c, 0, 1) -- check its local
619 assert(n == "a" and v == 1 and debug.getlocal(c, 0, 2) ~= "b")
620 assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
621 local t = debug.getinfo(c, 0) -- test 'getinfo'
622 assert(t.currentline == t.linedefined + 2)
623 assert(not debug.getinfo(c, 1)) -- no other level
624 assert(coroutine.resume(c)) -- run next line
625 local n,v = debug.getlocal(c, 0, 2) -- check next local
626 assert(n == "b" and v == 10)
627 v = {coroutine.resume(c)} -- finish coroutine
628 assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
629 assert(not coroutine.resume(c))
630 end
631
632 do
633 -- testing debug library on last function in a suspended coroutine
634 -- (bug in 5.2/5.3)
635 local c = coroutine.create(function () T.testC("yield 1", 10, 20) end)
636 local a, b = coroutine.resume(c)
637 assert(a and b == 20)
638 assert(debug.getinfo(c, 0).linedefined == -1)
639 a, b = debug.getlocal(c, 0, 2)
640 assert(b == 10)
641 end
642
643
644 print "testing coroutine API"
645
646 -- reusing a thread
647 assert(T.testC([[
648 newthread # create thread
649 pushvalue 2 # push body
650 pushstring 'a a a' # push argument
651 xmove 0 3 2 # move values to new thread
652 resume -1, 1 # call it first time
653 pushstatus
654 xmove 3 0 0 # move results back to stack
655 setglobal X # result
656 setglobal Y # status
657 pushvalue 2 # push body (to call it again)
658 pushstring 'b b b'
659 xmove 0 3 2
660 resume -1, 1 # call it again
661 pushstatus
662 xmove 3 0 0
663 return 1 # return result
664 ]], function (...) return ... end) == 'b b b')
665
666 assert(X == 'a a a' and Y == 'OK')
667
668 X, Y = nil
669
670
671 -- resuming running coroutine
672 C = coroutine.create(function ()
673 return T.testC([[
674 pushnum 10;
675 pushnum 20;
676 resume -3 2;
677 pushstatus
678 gettop;
679 return 3]], C)
680 end)
681 local a, b, c, d = coroutine.resume(C)
682 assert(a == true and string.find(b, "non%-suspended") and
683 c == "ERRRUN" and d == 4)
684
685 a, b, c, d = T.testC([[
686 rawgeti R 1 # get main thread
687 pushnum 10;
688 pushnum 20;
689 resume -3 2;
690 pushstatus
691 gettop;
692 return 4]])
693 assert(a == coroutine.running() and string.find(b, "non%-suspended") and
694 c == "ERRRUN" and d == 4)
695
696
697 -- using a main thread as a coroutine (dubious use!)
698 local state = T.newstate()
699
700 -- check that yielddable is working correctly
701 assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
702
703 -- main thread is not yieldable
704 assert(not T.testC(state, "rawgeti R 1; isyieldable -1; remove 1; return 1"))
705
706 T.testC(state, "settop 0")
707
708 T.loadlib(state)
709
710 assert(T.doremote(state, [[
711 coroutine = require'coroutine';
712 X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
713 return 'ok']]))
714
715 local t = table.pack(T.testC(state, [[
716 rawgeti R 1 # get main thread
717 pushstring 'XX'
718 getglobal X # get function for body
719 pushstring AA # arg
720 resume 1 1 # 'resume' shadows previous stack!
721 gettop
722 setglobal T # top
723 setglobal B # second yielded value
724 setglobal A # fist yielded value
725 rawgeti R 1 # get main thread
726 pushnum 5 # arg (noise)
727 resume 1 1 # after coroutine ends, previous stack is back
728 pushstatus
729 return *
730 ]]))
731 assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
732 assert(T.doremote(state, "return T") == '2')
733 assert(T.doremote(state, "return A") == 'AA')
734 assert(T.doremote(state, "return B") == 'BB')
735
736 T.closestate(state)
737
738 print'+'
739
740end
741
742
743-- leaving a pending coroutine open
744_G.TO_SURVIVE = coroutine.wrap(function ()
745 local a = 10
746 local x = function () a = a+1 end
747 coroutine.yield()
748 end)
749
750_G.TO_SURVIVE()
751
752
753if not _soft then
754 -- bug (stack overflow)
755 local lim = 1000000 -- stack limit; assume 32-bit machine
756 local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1, lim + 5}
757 for i = 1, #t do
758 local j = t[i]
759 local co = coroutine.create(function()
760 return table.unpack({}, 1, j)
761 end)
762 local r, msg = coroutine.resume(co)
763 -- must fail for unpacking larger than stack limit
764 assert(j < lim or not r)
765 end
766end
767
768
769assert(coroutine.running() == main)
770
771print"+"
772
773
774print"testing yields inside metamethods"
775
776local function val(x)
777 if type(x) == "table" then return x.x else return x end
778end
779
780local mt = {
781 __eq = function(a,b) coroutine.yield(nil, "eq"); return val(a) == val(b) end,
782 __lt = function(a,b) coroutine.yield(nil, "lt"); return val(a) < val(b) end,
783 __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
784 __add = function(a,b) coroutine.yield(nil, "add");
785 return val(a) + val(b) end,
786 __sub = function(a,b) coroutine.yield(nil, "sub"); return val(a) - val(b) end,
787 __mul = function(a,b) coroutine.yield(nil, "mul"); return val(a) * val(b) end,
788 __div = function(a,b) coroutine.yield(nil, "div"); return val(a) / val(b) end,
789 __idiv = function(a,b) coroutine.yield(nil, "idiv");
790 return val(a) // val(b) end,
791 __pow = function(a,b) coroutine.yield(nil, "pow"); return val(a) ^ val(b) end,
792 __mod = function(a,b) coroutine.yield(nil, "mod"); return val(a) % val(b) end,
793 __unm = function(a,b) coroutine.yield(nil, "unm"); return -val(a) end,
794 __bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~val(a) end,
795 __shl = function(a,b) coroutine.yield(nil, "shl");
796 return val(a) << val(b) end,
797 __shr = function(a,b) coroutine.yield(nil, "shr");
798 return val(a) >> val(b) end,
799 __band = function(a,b)
800 coroutine.yield(nil, "band")
801 return val(a) & val(b)
802 end,
803 __bor = function(a,b) coroutine.yield(nil, "bor");
804 return val(a) | val(b) end,
805 __bxor = function(a,b) coroutine.yield(nil, "bxor");
806 return val(a) ~ val(b) end,
807
808 __concat = function(a,b)
809 coroutine.yield(nil, "concat");
810 return val(a) .. val(b)
811 end,
812 __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
813 __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
814}
815
816
817local function new (x)
818 return setmetatable({x = x, k = {}}, mt)
819end
820
821
822local a = new(10)
823local b = new(12)
824local c = new"hello"
825
826local function run (f, t)
827 local i = 1
828 local c = coroutine.wrap(f)
829 while true do
830 local res, stat = c()
831 if res then assert(t[i] == undef); return res, t end
832 assert(stat == t[i])
833 i = i + 1
834 end
835end
836
837
838assert(run(function () if (a>=b) then return '>=' else return '<' end end,
839 {"le", "sub"}) == "<")
840assert(run(function () if (a<=b) then return '<=' else return '>' end end,
841 {"le", "sub"}) == "<=")
842assert(run(function () if (a==b) then return '==' else return '~=' end end,
843 {"eq"}) == "~=")
844
845assert(run(function () return a & b + a end, {"add", "band"}) == 2)
846
847assert(run(function () return 1 + a end, {"add"}) == 11)
848assert(run(function () return a - 25 end, {"sub"}) == -15)
849assert(run(function () return 2 * a end, {"mul"}) == 20)
850assert(run(function () return a ^ 2 end, {"pow"}) == 100)
851assert(run(function () return a / 2 end, {"div"}) == 5)
852assert(run(function () return a % 6 end, {"mod"}) == 4)
853assert(run(function () return a // 3 end, {"idiv"}) == 3)
854
855assert(run(function () return a + b end, {"add"}) == 22)
856assert(run(function () return a - b end, {"sub"}) == -2)
857assert(run(function () return a * b end, {"mul"}) == 120)
858assert(run(function () return a ^ b end, {"pow"}) == 10^12)
859assert(run(function () return a / b end, {"div"}) == 10/12)
860assert(run(function () return a % b end, {"mod"}) == 10)
861assert(run(function () return a // b end, {"idiv"}) == 0)
862
863-- repeat tests with larger constants (to use 'K' opcodes)
864local a1000 = new(1000)
865
866assert(run(function () return a1000 + 1000 end, {"add"}) == 2000)
867assert(run(function () return a1000 - 25000 end, {"sub"}) == -24000)
868assert(run(function () return 2000 * a end, {"mul"}) == 20000)
869assert(run(function () return a1000 / 1000 end, {"div"}) == 1)
870assert(run(function () return a1000 % 600 end, {"mod"}) == 400)
871assert(run(function () return a1000 // 500 end, {"idiv"}) == 2)
872
873
874
875assert(run(function () return a % b end, {"mod"}) == 10)
876
877assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12)
878assert(run(function () return a | b end, {"bor"}) == 10 | 12)
879assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12)
880assert(run(function () return a << b end, {"shl"}) == 10 << 12)
881assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
882
883assert(run(function () return 10 & b end, {"band"}) == 10 & 12)
884assert(run(function () return a | 2 end, {"bor"}) == 10 | 2)
885assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2)
886assert(run(function () return a >> 2 end, {"shr"}) == 10 >> 2)
887assert(run(function () return 1 >> a end, {"shr"}) == 1 >> 10)
888assert(run(function () return a << 2 end, {"shl"}) == 10 << 2)
889assert(run(function () return 1 << a end, {"shl"}) == 1 << 10)
890assert(run(function () return 2 ~ a end, {"bxor"}) == 2 ~ 10)
891
892
893assert(run(function () return a..b end, {"concat"}) == "1012")
894
895assert(run(function() return a .. b .. c .. a end,
896 {"concat", "concat", "concat"}) == "1012hello10")
897
898assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
899 {"concat", "concat", "concat"}) == "ab10chello12x")
900
901
902do -- a few more tests for comparison operators
903 local mt1 = {
904 __le = function (a,b)
905 coroutine.yield(10)
906 return (val(a) <= val(b))
907 end,
908 __lt = function (a,b)
909 coroutine.yield(10)
910 return val(a) < val(b)
911 end,
912 }
913 local mt2 = { __lt = mt1.__lt, __le = mt1.__le }
914
915 local function run (f)
916 local co = coroutine.wrap(f)
917 local res
918 repeat
919 res = co()
920 until res ~= 10
921 return res
922 end
923
924 local function test ()
925 local a1 = setmetatable({x=1}, mt1)
926 local a2 = setmetatable({x=2}, mt2)
927 assert(a1 < a2)
928 assert(a1 <= a2)
929 assert(1 < a2)
930 assert(1 <= a2)
931 assert(2 > a1)
932 assert(2 >= a2)
933 return true
934 end
935
936 run(test)
937
938end
939
940assert(run(function ()
941 a.BB = print
942 return a.BB
943 end, {"nidx", "idx"}) == print)
944
945-- getuptable & setuptable
946do local _ENV = _ENV
947 f = function () AAA = BBB + 1; return AAA end
948end
949local g = new(10); g.k.BBB = 10;
950debug.setupvalue(f, 1, g)
951assert(run(f, {"idx", "nidx", "idx"}) == 11)
952assert(g.k.AAA == 11)
953
954print"+"
955
956print"testing yields inside 'for' iterators"
957
958local f = function (s, i)
959 if i%2 == 0 then coroutine.yield(nil, "for") end
960 if i < s then return i + 1 end
961 end
962
963assert(run(function ()
964 local s = 0
965 for i in f, 4, 0 do s = s + i end
966 return s
967 end, {"for", "for", "for"}) == 10)
968
969
970
971-- tests for coroutine API
972if T==nil then
973 (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
974 print "OK"; return
975end
976
977print('testing coroutine API')
978
979local function apico (...)
980 local x = {...}
981 return coroutine.wrap(function ()
982 return T.testC(table.unpack(x))
983 end)
984end
985
986local a = {apico(
987[[
988 pushstring errorcode
989 pcallk 1 0 2;
990 invalid command (should not arrive here)
991]],
992[[return *]],
993"stackmark",
994error
995)()}
996assert(#a == 4 and
997 a[3] == "stackmark" and
998 a[4] == "errorcode" and
999 _G.status == "ERRRUN" and
1000 _G.ctx == 2) -- 'ctx' to pcallk
1001
1002local co = apico(
1003 "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
1004 coroutine.yield,
1005 "getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
1006 "getglobal status; getglobal ctx; return *")
1007
1008assert(co() == 10)
1009assert(co(20, 30) == 'a')
1010a = {co()}
1011assert(#a == 10 and
1012 a[2] == coroutine.yield and
1013 a[5] == 20 and a[6] == 30 and
1014 a[7] == "YIELD" and a[8] == 3 and
1015 a[9] == "YIELD" and a[10] == 4)
1016assert(not pcall(co)) -- coroutine is dead now
1017
1018
1019f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
1020co = coroutine.wrap(function ()
1021 assert(f() == 23); assert(f() == 23); return 10
1022end)
1023assert(co(23,16) == 5)
1024assert(co(23,16) == 5)
1025assert(co(23,16) == 10)
1026
1027
1028-- testing coroutines with C bodies
1029f = T.makeCfunc([[
1030 pushnum 102
1031 yieldk 1 U2
1032 cannot be here!
1033]],
1034[[ # continuation
1035 pushvalue U3 # accessing upvalues inside a continuation
1036 pushvalue U4
1037 return *
1038]], 23, "huu")
1039
1040x = coroutine.wrap(f)
1041assert(x() == 102)
1042eqtab({x()}, {23, "huu"})
1043
1044
1045f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
1046
1047a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
1048 pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
1049 return 4; ]], f)
1050
1051assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
1052
1053
1054-- testing chain of suspendable C calls
1055
1056local count = 3 -- number of levels
1057
1058f = T.makeCfunc([[
1059 remove 1; # remove argument
1060 pushvalue U3; # get selection function
1061 call 0 1; # call it (result is 'f' or 'yield')
1062 pushstring hello # single argument for selected function
1063 pushupvalueindex 2; # index of continuation program
1064 callk 1 -1 .; # call selected function
1065 errorerror # should never arrive here
1066]],
1067[[
1068 # continuation program
1069 pushnum 34 # return value
1070 return * # return all results
1071]],
1072function () -- selection function
1073 count = count - 1
1074 if count == 0 then return coroutine.yield
1075 else return f
1076 end
1077end
1078)
1079
1080co = coroutine.wrap(function () return f(nil) end)
1081assert(co() == "hello") -- argument to 'yield'
1082a = {co()}
1083-- three '34's (one from each pending C call)
1084assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
1085
1086
1087-- testing yields with continuations
1088
1089local y
1090
1091co = coroutine.wrap(function (...) return
1092 T.testC([[ # initial function
1093 yieldk 1 2
1094 cannot be here!
1095 ]],
1096 [[ # 1st continuation
1097 yieldk 0 3
1098 cannot be here!
1099 ]],
1100 [[ # 2nd continuation
1101 yieldk 0 4
1102 cannot be here!
1103 ]],
1104 [[ # 3th continuation
1105 pushvalue 6 # function which is last arg. to 'testC' here
1106 pushnum 10; pushnum 20;
1107 pcall 2 0 0 # call should throw an error and return to next line
1108 pop 1 # remove error message
1109 pushvalue 6
1110 getglobal status; getglobal ctx
1111 pcallk 2 2 5 # call should throw an error and jump to continuation
1112 cannot be here!
1113 ]],
1114 [[ # 4th (and last) continuation
1115 return *
1116 ]],
1117 -- function called by 3th continuation
1118 function (a,b) x=a; y=b; error("errmsg") end,
1119 ...
1120)
1121end)
1122
1123local a = {co(3,4,6)}
1124assert(a[1] == 6 and a[2] == undef)
1125a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 2)
1126a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 3)
1127a = {co(7,8)};
1128-- original arguments
1129assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
1130 type(a[3]) == 'string' and type(a[4]) == 'string' and
1131 type(a[5]) == 'string' and type(a[6]) == 'function')
1132-- arguments left from fist resume
1133assert(a[7] == 3 and a[8] == 4)
1134-- arguments to last resume
1135assert(a[9] == 7 and a[10] == 8)
1136-- error message and nothing more
1137assert(a[11]:find("errmsg") and #a == 11)
1138-- check arguments to pcallk
1139assert(x == "YIELD" and y == 4)
1140
1141assert(not pcall(co)) -- coroutine should be dead
1142
1143_G.ctx = nil
1144_G.status = nil
1145
1146
1147-- bug in nCcalls
1148local co = coroutine.wrap(function ()
1149 local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
1150 return pcall(assert, table.unpack(a))
1151end)
1152
1153local a = {co()}
1154assert(a[10] == "hi")
1155
1156print'OK'