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