lua

A copy of the Lua development repository
Log | Files | Refs | README

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