locals.lua (30087B)
1 -- $Id: testes/locals.lua $ 2 -- See Copyright Notice in file all.lua 3 4 print('testing local variables and environments') 5 6 local debug = require"debug" 7 8 local tracegc = require"tracegc" 9 10 11 -- bug in 5.1: 12 13 local function f(x) x = nil; return x end 14 assert(f(10) == nil) 15 16 local function f() local x; return x end 17 assert(f(10) == nil) 18 19 local function f(x) x = nil; local y; return x, y end 20 assert(f(10) == nil and select(2, f(20)) == nil) 21 22 do 23 local i = 10 24 do local i = 100; assert(i==100) end 25 do local i = 1000; assert(i==1000) end 26 assert(i == 10) 27 if i ~= 10 then 28 local i = 20 29 else 30 local i = 30 31 assert(i == 30) 32 end 33 end 34 35 36 37 f = nil 38 39 local f 40 local x = 1 41 42 a = nil 43 load('local a = {}')() 44 assert(a == nil) 45 46 function f (a) 47 local _1, _2, _3, _4, _5 48 local _6, _7, _8, _9, _10 49 local x = 3 50 local b = a 51 local c,d = a,b 52 if (d == b) then 53 local x = 'q' 54 x = b 55 assert(x == 2) 56 else 57 assert(nil) 58 end 59 assert(x == 3) 60 local f = 10 61 end 62 63 local b=10 64 local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3 65 66 67 assert(x == 1) 68 69 f(2) 70 assert(type(f) == 'function') 71 72 73 local function getenv (f) 74 local a,b = debug.getupvalue(f, 1) 75 assert(a == '_ENV') 76 return b 77 end 78 79 -- test for global table of loaded chunks 80 assert(getenv(load"a=3") == _G) 81 local c = {}; local f = load("a = 3", nil, nil, c) 82 assert(getenv(f) == c) 83 assert(c.a == nil) 84 f() 85 assert(c.a == 3) 86 87 -- old test for limits for special instructions 88 do 89 local i = 2 90 local p = 4 -- p == 2^i 91 repeat 92 for j=-3,3 do 93 assert(load(string.format([[local a=%s; 94 a=a+%s; 95 assert(a ==2^%s)]], j, p-j, i), '')) () 96 assert(load(string.format([[local a=%s; 97 a=a-%s; 98 assert(a==-2^%s)]], -j, p-j, i), '')) () 99 assert(load(string.format([[local a,b=0,%s; 100 a=b-%s; 101 assert(a==-2^%s)]], -j, p-j, i), '')) () 102 end 103 p = 2 * p; i = i + 1 104 until p <= 0 105 end 106 107 print'+' 108 109 110 if rawget(_G, "T") then 111 -- testing clearing of dead elements from tables 112 collectgarbage("stop") -- stop GC 113 local a = {[{}] = 4, [3] = 0, alo = 1, 114 a1234567890123456789012345678901234567890 = 10} 115 116 local t = T.querytab(a) 117 118 for k,_ in pairs(a) do a[k] = undef end 119 collectgarbage() -- restore GC and collect dead fields in 'a' 120 for i=0,t-1 do 121 local k = querytab(a, i) 122 assert(k == nil or type(k) == 'number' or k == 'alo') 123 end 124 125 -- testing allocation errors during table insertions 126 local a = {} 127 local function additems () 128 a.x = true; a.y = true; a.z = true 129 a[1] = true 130 a[2] = true 131 end 132 for i = 1, math.huge do 133 T.alloccount(i) 134 local st, msg = pcall(additems) 135 T.alloccount() 136 local count = 0 137 for k, v in pairs(a) do 138 assert(a[k] == v) 139 count = count + 1 140 end 141 if st then assert(count == 5); break end 142 end 143 end 144 145 146 -- testing lexical environments 147 148 assert(_ENV == _G) 149 150 do 151 local dummy 152 local _ENV = (function (...) return ... end)(_G, dummy) -- { 153 154 do local _ENV = {assert=assert}; assert(true) end 155 local mt = {_G = _G} 156 local foo,x 157 A = false -- "declare" A 158 do local _ENV = mt 159 function foo (x) 160 A = x 161 do local _ENV = _G; A = 1000 end 162 return function (x) return A .. x end 163 end 164 end 165 assert(getenv(foo) == mt) 166 x = foo('hi'); assert(mt.A == 'hi' and A == 1000) 167 assert(x('*') == mt.A .. '*') 168 169 do local _ENV = {assert=assert, A=10}; 170 do local _ENV = {assert=assert, A=20}; 171 assert(A==20);x=A 172 end 173 assert(A==10 and x==20) 174 end 175 assert(x==20) 176 177 A = nil 178 179 180 do -- constants 181 local a<const>, b, c<const> = 10, 20, 30 182 b = a + c + b -- 'b' is not constant 183 assert(a == 10 and b == 60 and c == 30) 184 local function checkro (name, code) 185 local st, msg = load(code) 186 local gab = string.format("attempt to assign to const variable '%s'", name) 187 assert(not st and string.find(msg, gab)) 188 end 189 checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12") 190 checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11") 191 checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11") 192 checkro("foo", "local foo <const> = 10; function foo() end") 193 checkro("foo", "local foo <const> = {}; function foo() end") 194 195 checkro("z", [[ 196 local a, z <const>, b = 10; 197 function foo() a = 20; z = 32; end 198 ]]) 199 200 checkro("var1", [[ 201 local a, var1 <const> = 10; 202 function foo() a = 20; z = function () var1 = 12; end end 203 ]]) 204 end 205 206 207 print"testing to-be-closed variables" 208 209 local function stack(n) n = ((n == 0) or stack(n - 1)) end 210 211 local function func2close (f, x, y) 212 local obj = setmetatable({}, {__close = f}) 213 if x then 214 return x, obj, y 215 else 216 return obj 217 end 218 end 219 220 221 do 222 local a = {} 223 do 224 local b <close> = false -- not to be closed 225 local x <close> = setmetatable({"x"}, {__close = function (self) 226 a[#a + 1] = self[1] end}) 227 local w, y <close>, z = func2close(function (self, err) 228 assert(err == nil); a[#a + 1] = "y" 229 end, 10, 20) 230 local c <close> = nil -- not to be closed 231 a[#a + 1] = "in" 232 assert(w == 10 and z == 20) 233 end 234 a[#a + 1] = "out" 235 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out") 236 end 237 238 do 239 local X = false 240 241 local x, closescope = func2close(function (_, msg) 242 stack(10); 243 assert(msg == nil) 244 X = true 245 end, 100) 246 assert(x == 100); x = 101; -- 'x' is not read-only 247 248 -- closing functions do not corrupt returning values 249 local function foo (x) 250 local _ <close> = closescope 251 return x, X, 23 252 end 253 254 local a, b, c = foo(1.5) 255 assert(a == 1.5 and b == false and c == 23 and X == true) 256 257 X = false 258 foo = function (x) 259 local _<close> = func2close(function (_, msg) 260 -- without errors, enclosing function should be still active when 261 -- __close is called 262 assert(debug.getinfo(2).name == "foo") 263 assert(msg == nil) 264 end) 265 local _<close> = closescope 266 local y = 15 267 return y 268 end 269 270 assert(foo() == 15 and X == true) 271 272 X = false 273 foo = function () 274 local x <close> = closescope 275 return x 276 end 277 278 assert(foo() == closescope and X == true) 279 280 end 281 282 283 do -- testing presence of second argument 284 local function foo (howtoclose, obj, n) 285 local ca -- copy of 'a' visible inside its close metamethod 286 do 287 local a <close> = func2close(function (...) 288 local t = table.pack(...) 289 assert(select("#", ...) == n) 290 assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) 291 ca = 15 -- final value to be returned if howtoclose=="scope" 292 end) 293 ca = a 294 if howtoclose == "ret" then return obj -- 'a' closed by return 295 elseif howtoclose == "err" then error(obj) -- 'a' closed by error 296 end 297 end -- 'a' closed by end of scope 298 return ca -- ca now should be 15 299 end 300 -- with no errors, closing methods receive no extra argument 301 assert(foo("scope", nil, 1) == 15) -- close by end of scope 302 assert(foo("ret", 32, 1) == 32) -- close by return 303 -- with errors, they do 304 local st, msg = pcall(foo, "err", 23, 2) -- close by error 305 assert(not st and msg == 23) 306 end 307 308 309 -- testing to-be-closed x compile-time constants 310 -- (there were some bugs here in Lua 5.4-rc3, due to a confusion 311 -- between compile levels and stack levels of variables) 312 do 313 local flag = false 314 local x = setmetatable({}, 315 {__close = function() assert(flag == false); flag = true end}) 316 local y <const> = nil 317 local z <const> = nil 318 do 319 local a <close> = x 320 end 321 assert(flag) -- 'x' must be closed here 322 end 323 324 do 325 -- similar problem, but with implicit close in for loops 326 local flag = false 327 local x = setmetatable({}, 328 {__close = function () assert(flag == false); flag = true end}) 329 -- return an empty iterator, nil, nil, and 'x' to be closed 330 local function a () 331 return (function () return nil end), nil, nil, x 332 end 333 local v <const> = 1 334 local w <const> = 1 335 local x <const> = 1 336 local y <const> = 1 337 local z <const> = 1 338 for k in a() do 339 a = k 340 end -- ending the loop must close 'x' 341 assert(flag) -- 'x' must be closed here 342 end 343 344 345 346 do 347 -- calls cannot be tail in the scope of to-be-closed variables 348 local X, Y 349 local function foo () 350 local _ <close> = func2close(function () Y = 10 end) 351 assert(X == true and Y == nil) -- 'X' not closed yet 352 return 1,2,3 353 end 354 355 local function bar () 356 local _ <close> = func2close(function () X = false end) 357 X = true 358 do 359 return foo() -- not a tail call! 360 end 361 end 362 363 local a, b, c, d = bar() 364 assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil) 365 end 366 367 368 do 369 -- bug in 5.4.3: previous condition (calls cannot be tail in the 370 -- scope of to-be-closed variables) must be valid for tbc variables 371 -- created by 'for' loops. 372 373 local closed = false 374 375 local function foo () 376 return function () return true end, 0, 0, 377 func2close(function () closed = true end) 378 end 379 380 local function tail() return closed end 381 382 local function foo1 () 383 for k in foo() do return tail() end 384 end 385 386 assert(foo1() == false) 387 assert(closed == true) 388 end 389 390 391 do 392 -- bug in 5.4.4: 'break' may generate wrong 'close' instruction when 393 -- leaving a loop block. 394 395 local closed = false 396 397 local o1 = setmetatable({}, {__close=function() closed = true end}) 398 399 local function test() 400 for k, v in next, {}, nil, o1 do 401 local function f() return k end -- create an upvalue 402 break 403 end 404 assert(closed) 405 end 406 407 test() 408 end 409 410 411 do print("testing errors in __close") 412 413 -- original error is in __close 414 local function foo () 415 416 local x <close> = 417 func2close(function (self, msg) 418 assert(string.find(msg, "@y")) 419 error("@x") 420 end) 421 422 local x1 <close> = 423 func2close(function (self, msg) 424 assert(string.find(msg, "@y")) 425 end) 426 427 local gc <close> = func2close(function () collectgarbage() end) 428 429 local y <close> = 430 func2close(function (self, msg) 431 assert(string.find(msg, "@z")) -- error in 'z' 432 error("@y") 433 end) 434 435 local z <close> = 436 func2close(function (self, msg) 437 assert(msg == nil) 438 error("@z") 439 end) 440 441 return 200 442 end 443 444 local stat, msg = pcall(foo, false) 445 assert(string.find(msg, "@x")) 446 447 448 -- original error not in __close 449 local function foo () 450 451 local x <close> = 452 func2close(function (self, msg) 453 -- after error, 'foo' was discarded, so caller now 454 -- must be 'pcall' 455 assert(debug.getinfo(2).name == "pcall") 456 assert(string.find(msg, "@x1")) 457 end) 458 459 local x1 <close> = 460 func2close(function (self, msg) 461 assert(debug.getinfo(2).name == "pcall") 462 assert(string.find(msg, "@y")) 463 error("@x1") 464 end) 465 466 local gc <close> = func2close(function () collectgarbage() end) 467 468 local y <close> = 469 func2close(function (self, msg) 470 assert(debug.getinfo(2).name == "pcall") 471 assert(string.find(msg, "@z")) 472 error("@y") 473 end) 474 475 local first = true 476 local z <close> = 477 func2close(function (self, msg) 478 assert(debug.getinfo(2).name == "pcall") 479 -- 'z' close is called once 480 assert(first and msg == 4) 481 first = false 482 error("@z") 483 end) 484 485 error(4) -- original error 486 end 487 488 local stat, msg = pcall(foo, true) 489 assert(string.find(msg, "@x1")) 490 491 -- error leaving a block 492 local function foo (...) 493 do 494 local x1 <close> = 495 func2close(function (self, msg) 496 assert(string.find(msg, "@X")) 497 error("@Y") 498 end) 499 500 local x123 <close> = 501 func2close(function (_, msg) 502 assert(msg == nil) 503 error("@X") 504 end) 505 end 506 os.exit(false) -- should not run 507 end 508 509 local st, msg = xpcall(foo, debug.traceback) 510 assert(string.match(msg, "^[^ ]* @Y")) 511 512 -- error in toclose in vararg function 513 local function foo (...) 514 local x123 <close> = func2close(function () error("@x123") end) 515 end 516 517 local st, msg = xpcall(foo, debug.traceback) 518 assert(string.match(msg, "^[^ ]* @x123")) 519 assert(string.find(msg, "in metamethod 'close'")) 520 end 521 522 523 do -- errors due to non-closable values 524 local function foo () 525 local x <close> = {} 526 os.exit(false) -- should not run 527 end 528 local stat, msg = pcall(foo) 529 assert(not stat and 530 string.find(msg, "variable 'x' got a non%-closable value")) 531 532 local function foo () 533 local xyz <close> = setmetatable({}, {__close = print}) 534 getmetatable(xyz).__close = nil -- remove metamethod 535 end 536 local stat, msg = pcall(foo) 537 assert(not stat and string.find(msg, "metamethod 'close'")) 538 539 local function foo () 540 local a1 <close> = func2close(function (_, msg) 541 assert(string.find(msg, "number value")) 542 error(12) 543 end) 544 local a2 <close> = setmetatable({}, {__close = print}) 545 local a3 <close> = func2close(function (_, msg) 546 assert(msg == nil) 547 error(123) 548 end) 549 getmetatable(a2).__close = 4 -- invalidate metamethod 550 end 551 local stat, msg = pcall(foo) 552 assert(not stat and msg == 12) 553 end 554 555 556 do -- tbc inside close methods 557 local track = {} 558 local function foo () 559 local x <close> = func2close(function () 560 local xx <close> = func2close(function (_, msg) 561 assert(msg == nil) 562 track[#track + 1] = "xx" 563 end) 564 track[#track + 1] = "x" 565 end) 566 track[#track + 1] = "foo" 567 return 20, 30, 40 568 end 569 local a, b, c, d = foo() 570 assert(a == 20 and b == 30 and c == 40 and d == nil) 571 assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx") 572 573 -- again, with errors 574 local track = {} 575 local function foo () 576 local x0 <close> = func2close(function (_, msg) 577 assert(msg == 202) 578 track[#track + 1] = "x0" 579 end) 580 local x <close> = func2close(function () 581 local xx <close> = func2close(function (_, msg) 582 assert(msg == 101) 583 track[#track + 1] = "xx" 584 error(202) 585 end) 586 track[#track + 1] = "x" 587 error(101) 588 end) 589 track[#track + 1] = "foo" 590 return 20, 30, 40 591 end 592 local st, msg = pcall(foo) 593 assert(not st and msg == 202) 594 assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx" and 595 track[4] == "x0") 596 end 597 598 599 local function checktable (t1, t2) 600 assert(#t1 == #t2) 601 for i = 1, #t1 do 602 assert(t1[i] == t2[i]) 603 end 604 end 605 606 607 do -- test for tbc variable high in the stack 608 609 -- function to force a stack overflow 610 local function overflow (n) 611 overflow(n + 1) 612 end 613 614 -- error handler will create tbc variable handling a stack overflow, 615 -- high in the stack 616 local function errorh (m) 617 assert(string.find(m, "stack overflow")) 618 local x <close> = func2close(function (o) o[1] = 10 end) 619 return x 620 end 621 622 local flag 623 local st, obj 624 -- run test in a coroutine so as not to swell the main stack 625 local co = coroutine.wrap(function () 626 -- tbc variable down the stack 627 local y <close> = func2close(function (obj, msg) 628 assert(msg == nil) 629 obj[1] = 100 630 flag = obj 631 end) 632 tracegc.stop() 633 st, obj = xpcall(overflow, errorh, 0) 634 tracegc.start() 635 end) 636 co() 637 assert(not st and obj[1] == 10 and flag[1] == 100) 638 end 639 640 641 if rawget(_G, "T") then 642 643 do 644 -- bug in 5.4.3 645 -- 'lua_settop' may use a pointer to stack invalidated by 'luaF_close' 646 647 -- reduce stack size 648 collectgarbage(); collectgarbage(); collectgarbage() 649 650 -- force a stack reallocation 651 local function loop (n) 652 if n < 400 then loop(n + 1) end 653 end 654 655 -- close metamethod will reallocate the stack 656 local o = setmetatable({}, {__close = function () loop(0) end}) 657 658 local script = [[toclose 2; settop 1; return 1]] 659 660 assert(T.testC(script, o) == script) 661 662 end 663 664 665 -- memory error inside closing function 666 local function foo () 667 local y <close> = func2close(function () T.alloccount() end) 668 local x <close> = setmetatable({}, {__close = function () 669 T.alloccount(0); local x = {} -- force a memory error 670 end}) 671 error(1000) -- common error inside the function's body 672 end 673 674 stack(5) -- ensure a minimal number of CI structures 675 676 -- despite memory error, 'y' will be executed and 677 -- memory limit will be lifted 678 local _, msg = pcall(foo) 679 assert(msg == "not enough memory") 680 681 local closemsg 682 local close = func2close(function (self, msg) 683 T.alloccount() 684 closemsg = msg 685 end) 686 687 -- set a memory limit and return a closing object to remove the limit 688 local function enter (count) 689 stack(10) -- reserve some stack space 690 T.alloccount(count) 691 closemsg = nil 692 return close 693 end 694 695 local function test () 696 local x <close> = enter(0) -- set a memory limit 697 local y = {} -- raise a memory error 698 end 699 700 local _, msg = pcall(test) 701 assert(msg == "not enough memory" and closemsg == "not enough memory") 702 703 704 -- repeat test with extra closing upvalues 705 local function test () 706 local xxx <close> = func2close(function (self, msg) 707 assert(msg == "not enough memory"); 708 error(1000) -- raise another error 709 end) 710 local xx <close> = func2close(function (self, msg) 711 assert(msg == "not enough memory"); 712 end) 713 local x <close> = enter(0) -- set a memory limit 714 local y = {} -- raise a memory error 715 end 716 717 local _, msg = pcall(test) 718 assert(msg == 1000 and closemsg == "not enough memory") 719 720 do -- testing 'toclose' in C string buffer 721 collectgarbage() 722 local s = string.rep('a', 10000) -- large string 723 local m = T.totalmem() 724 collectgarbage("stop") 725 s = string.upper(s) -- allocate buffer + new string (10K each) 726 -- ensure buffer was deallocated 727 assert(T.totalmem() - m <= 11000) 728 collectgarbage("restart") 729 end 730 731 do -- now some tests for freeing buffer in case of errors 732 local lim = 10000 -- some size larger than the static buffer 733 local extra = 2000 -- some extra memory (for callinfo, etc.) 734 735 local s = string.rep("a", lim) 736 737 -- concat this table needs two buffer resizes (one for each 's') 738 local a = {s, s} 739 740 collectgarbage(); collectgarbage() 741 742 local m = T.totalmem() 743 collectgarbage("stop") 744 745 -- error in the first buffer allocation 746 T. totalmem(m + extra) 747 assert(not pcall(table.concat, a)) 748 -- first buffer was not even allocated 749 assert(T.totalmem() - m <= extra) 750 751 -- error in the second buffer allocation 752 T. totalmem(m + lim + extra) 753 assert(not pcall(table.concat, a)) 754 -- first buffer was released by 'toclose' 755 assert(T.totalmem() - m <= extra) 756 757 -- userdata, buffer, final string 758 T.totalmem(m + 2*lim + extra) 759 assert(#table.concat(a) == 2*lim) 760 761 T.totalmem(0) -- remove memory limit 762 collectgarbage("restart") 763 764 print'+' 765 end 766 767 768 do 769 -- '__close' vs. return hooks in C functions 770 local trace = {} 771 772 local function hook (event) 773 trace[#trace + 1] = event .. " " .. (debug.getinfo(2).name or "?") 774 end 775 776 -- create tbc variables to be used by C function 777 local x = func2close(function (_,msg) 778 trace[#trace + 1] = "x" 779 end) 780 781 local y = func2close(function (_,msg) 782 trace[#trace + 1] = "y" 783 end) 784 785 debug.sethook(hook, "r") 786 local t = {T.testC([[ 787 toclose 2 # x 788 pushnum 10 789 pushint 20 790 toclose 3 # y 791 return 2 792 ]], x, y)} 793 debug.sethook() 794 795 -- hooks ran before return hook from 'testC' 796 checktable(trace, 797 {"return sethook", "y", "return ?", "x", "return ?", "return testC"}) 798 -- results are correct 799 checktable(t, {10, 20}) 800 end 801 end 802 803 804 do -- '__close' vs. return hooks in Lua functions 805 local trace = {} 806 807 local function hook (event) 808 trace[#trace + 1] = event .. " " .. debug.getinfo(2).name 809 end 810 811 local function foo (...) 812 local x <close> = func2close(function (_,msg) 813 trace[#trace + 1] = "x" 814 end) 815 816 local y <close> = func2close(function (_,msg) 817 debug.sethook(hook, "r") 818 end) 819 820 return ... 821 end 822 823 local t = {foo(10,20,30)} 824 debug.sethook() 825 checktable(t, {10, 20, 30}) 826 checktable(trace, 827 {"return sethook", "return close", "x", "return close", "return foo"}) 828 end 829 830 831 print "to-be-closed variables in coroutines" 832 833 do 834 -- yielding inside closing metamethods 835 836 local trace = {} 837 local co = coroutine.wrap(function () 838 839 trace[#trace + 1] = "nowX" 840 841 -- will be closed after 'y' 842 local x <close> = func2close(function (_, msg) 843 assert(msg == nil) 844 trace[#trace + 1] = "x1" 845 coroutine.yield("x") 846 trace[#trace + 1] = "x2" 847 end) 848 849 return pcall(function () 850 do -- 'z' will be closed first 851 local z <close> = func2close(function (_, msg) 852 assert(msg == nil) 853 trace[#trace + 1] = "z1" 854 coroutine.yield("z") 855 trace[#trace + 1] = "z2" 856 end) 857 end 858 859 trace[#trace + 1] = "nowY" 860 861 -- will be closed after 'z' 862 local y <close> = func2close(function(_, msg) 863 assert(msg == nil) 864 trace[#trace + 1] = "y1" 865 coroutine.yield("y") 866 trace[#trace + 1] = "y2" 867 end) 868 869 return 10, 20, 30 870 end) 871 end) 872 873 assert(co() == "z") 874 assert(co() == "y") 875 assert(co() == "x") 876 checktable({co()}, {true, 10, 20, 30}) 877 checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"}) 878 879 end 880 881 882 do 883 -- yielding inside closing metamethods while returning 884 -- (bug in 5.4.3) 885 886 local extrares -- result from extra yield (if any) 887 888 local function check (body, extra, ...) 889 local t = table.pack(...) -- expected returns 890 local co = coroutine.wrap(body) 891 if extra then 892 extrares = co() -- runs until first (extra) yield 893 end 894 local res = table.pack(co()) -- runs until "regular" yield 895 -- regular yield will yield all values passed to the close function; 896 -- without errors, that is only the object being closed. 897 assert(res.n == 1 and type(res[1]) == "table") 898 local res2 = table.pack(co()) -- runs until end of function 899 assert(res2.n == t.n) 900 for i = 1, #t do 901 if t[i] == "x" then 902 assert(res2[i] == res[1]) -- value that was closed 903 else 904 assert(res2[i] == t[i]) 905 end 906 end 907 end 908 909 local function foo () 910 local x <close> = func2close(coroutine.yield) -- "regular" yield 911 local extra <close> = func2close(function (self) 912 assert(self == extrares) 913 coroutine.yield(100) -- first (extra) yield 914 end) 915 extrares = extra 916 return table.unpack{10, x, 30} 917 end 918 check(foo, true, 10, "x", 30) 919 assert(extrares == 100) 920 921 local function foo () 922 local x <close> = func2close(coroutine.yield) -- "regular" yield 923 return 924 end 925 check(foo, false) 926 927 local function foo () 928 local x <close> = func2close(coroutine.yield) -- "regular" yield 929 local y, z = 20, 30 930 return x 931 end 932 check(foo, false, "x") 933 934 local function foo () 935 local x <close> = func2close(coroutine.yield) -- "regular" yield 936 local extra <close> = func2close(coroutine.yield) -- extra yield 937 return table.unpack({}, 1, 100) -- 100 nils 938 end 939 check(foo, true, table.unpack({}, 1, 100)) 940 941 end 942 943 do 944 -- yielding inside closing metamethods after an error 945 946 local co = coroutine.wrap(function () 947 948 local function foo (err) 949 950 local z <close> = func2close(function(_, msg) 951 assert(msg == nil or msg == err + 20) 952 coroutine.yield("z") 953 return 100, 200 954 end) 955 956 local y <close> = func2close(function(_, msg) 957 -- still gets the original error (if any) 958 assert(msg == err or (msg == nil and err == 1)) 959 coroutine.yield("y") 960 if err then error(err + 20) end -- creates or changes the error 961 end) 962 963 local x <close> = func2close(function(_, msg) 964 assert(msg == err or (msg == nil and err == 1)) 965 coroutine.yield("x") 966 return 100, 200 967 end) 968 969 if err == 10 then error(err) else return 10, 20 end 970 end 971 972 coroutine.yield(pcall(foo, nil)) -- no error 973 coroutine.yield(pcall(foo, 1)) -- error in __close 974 return pcall(foo, 10) -- 'foo' will raise an error 975 end) 976 977 local a, b = co() -- first foo: no error 978 assert(a == "x" and b == nil) -- yields inside 'x'; Ok 979 a, b = co() 980 assert(a == "y" and b == nil) -- yields inside 'y'; Ok 981 a, b = co() 982 assert(a == "z" and b == nil) -- yields inside 'z'; Ok 983 local a, b, c = co() 984 assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)' 985 986 local a, b = co() -- second foo: error in __close 987 assert(a == "x" and b == nil) -- yields inside 'x'; Ok 988 a, b = co() 989 assert(a == "y" and b == nil) -- yields inside 'y'; Ok 990 a, b = co() 991 assert(a == "z" and b == nil) -- yields inside 'z'; Ok 992 local st, msg = co() -- reports the error in 'y' 993 assert(not st and msg == 21) 994 995 local a, b = co() -- third foo: error in function body 996 assert(a == "x" and b == nil) -- yields inside 'x'; Ok 997 a, b = co() 998 assert(a == "y" and b == nil) -- yields inside 'y'; Ok 999 a, b = co() 1000 assert(a == "z" and b == nil) -- yields inside 'z'; Ok 1001 local st, msg = co() -- gets final error 1002 assert(not st and msg == 10 + 20) 1003 1004 end 1005 1006 1007 do 1008 -- an error in a wrapped coroutine closes variables 1009 local x = false 1010 local y = false 1011 local co = coroutine.wrap(function () 1012 local xv <close> = func2close(function () x = true end) 1013 do 1014 local yv <close> = func2close(function () y = true end) 1015 coroutine.yield(100) -- yield doesn't close variable 1016 end 1017 coroutine.yield(200) -- yield doesn't close variable 1018 error(23) -- error does 1019 end) 1020 1021 local b = co() 1022 assert(b == 100 and not x and not y) 1023 b = co() 1024 assert(b == 200 and not x and y) 1025 local a, b = pcall(co) 1026 assert(not a and b == 23 and x and y) 1027 end 1028 1029 1030 do 1031 1032 -- error in a wrapped coroutine raising errors when closing a variable 1033 local x = 0 1034 local co = coroutine.wrap(function () 1035 local xx <close> = func2close(function (_, msg) 1036 x = x + 1; 1037 assert(string.find(msg, "@XXX")) 1038 error("@YYY") 1039 end) 1040 local xv <close> = func2close(function () x = x + 1; error("@XXX") end) 1041 coroutine.yield(100) 1042 error(200) 1043 end) 1044 assert(co() == 100); assert(x == 0) 1045 local st, msg = pcall(co); assert(x == 2) 1046 assert(not st and string.find(msg, "@YYY")) -- should get error raised 1047 1048 local x = 0 1049 local y = 0 1050 co = coroutine.wrap(function () 1051 local xx <close> = func2close(function (_, err) 1052 y = y + 1; 1053 assert(string.find(err, "XXX")) 1054 error("YYY") 1055 end) 1056 local xv <close> = func2close(function () 1057 x = x + 1; error("XXX") 1058 end) 1059 coroutine.yield(100) 1060 return 200 1061 end) 1062 assert(co() == 100); assert(x == 0) 1063 local st, msg = pcall(co) 1064 assert(x == 1 and y == 1) 1065 -- should get first error raised 1066 assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY")) 1067 1068 end 1069 1070 1071 -- a suspended coroutine should not close its variables when collected 1072 local co 1073 co = coroutine.wrap(function() 1074 -- should not run 1075 local x <close> = func2close(function () os.exit(false) end) 1076 co = nil 1077 coroutine.yield() 1078 end) 1079 co() -- start coroutine 1080 assert(co == nil) -- eventually it will be collected 1081 collectgarbage() 1082 1083 1084 if rawget(_G, "T") then 1085 print("to-be-closed variables x coroutines in C") 1086 do 1087 local token = 0 1088 local count = 0 1089 local f = T.makeCfunc[[ 1090 toclose 1 1091 toclose 2 1092 return . 1093 ]] 1094 1095 local obj = func2close(function (_, msg) 1096 count = count + 1 1097 token = coroutine.yield(count, token) 1098 end) 1099 1100 local co = coroutine.wrap(f) 1101 local ct, res = co(obj, obj, 10, 20, 30, 3) -- will return 10, 20, 30 1102 -- initial token value, after closing 2nd obj 1103 assert(ct == 1 and res == 0) 1104 -- run until yield when closing 1st obj 1105 ct, res = co(100) 1106 assert(ct == 2 and res == 100) 1107 res = {co(200)} -- run until end 1108 assert(res[1] == 10 and res[2] == 20 and res[3] == 30 and res[4] == nil) 1109 assert(token == 200) 1110 end 1111 1112 do 1113 local f = T.makeCfunc[[ 1114 toclose 1 1115 return . 1116 ]] 1117 1118 local obj = func2close(function () 1119 local temp 1120 local x <close> = func2close(function () 1121 coroutine.yield(temp) 1122 return 1,2,3 -- to be ignored 1123 end) 1124 temp = coroutine.yield("closing obj") 1125 return 1,2,3 -- to be ignored 1126 end) 1127 1128 local co = coroutine.wrap(f) 1129 local res = co(obj, 10, 30, 1) -- will return only 30 1130 assert(res == "closing obj") 1131 res = co("closing x") 1132 assert(res == "closing x") 1133 res = {co()} 1134 assert(res[1] == 30 and res[2] == nil) 1135 end 1136 1137 do 1138 -- still cannot yield inside 'closeslot' 1139 local f = T.makeCfunc[[ 1140 toclose 1 1141 closeslot 1 1142 ]] 1143 local obj = func2close(coroutine.yield) 1144 local co = coroutine.create(f) 1145 local st, msg = coroutine.resume(co, obj) 1146 assert(not st and string.find(msg, "attempt to yield across")) 1147 1148 -- nor outside a coroutine 1149 local f = T.makeCfunc[[ 1150 toclose 1 1151 ]] 1152 local st, msg = pcall(f, obj) 1153 assert(not st and string.find(msg, "attempt to yield from outside")) 1154 end 1155 end 1156 1157 1158 1159 -- to-be-closed variables in generic for loops 1160 do 1161 local numopen = 0 1162 local function open (x) 1163 numopen = numopen + 1 1164 return 1165 function () -- iteraction function 1166 x = x - 1 1167 if x > 0 then return x end 1168 end, 1169 nil, -- state 1170 nil, -- control variable 1171 func2close(function () numopen = numopen - 1 end) -- closing function 1172 end 1173 1174 local s = 0 1175 for i in open(10) do 1176 s = s + i 1177 end 1178 assert(s == 45 and numopen == 0) 1179 1180 local s = 0 1181 for i in open(10) do 1182 if i < 5 then break end 1183 s = s + i 1184 end 1185 assert(s == 35 and numopen == 0) 1186 1187 local s = 0 1188 for i in open(10) do 1189 for j in open(10) do 1190 if i + j < 5 then goto endloop end 1191 s = s + i 1192 end 1193 end 1194 ::endloop:: 1195 assert(s == 375 and numopen == 0) 1196 end 1197 1198 print('OK') 1199 1200 return 5,f 1201 1202 end -- } 1203