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