123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- -- $Id: testes/locals.lua $
- -- See Copyright Notice in file all.lua
- print('testing local variables and environments')
- local debug = require"debug"
- -- bug in 5.1:
- local function f(x) x = nil; return x end
- assert(f(10) == nil)
- local function f() local x; return x end
- assert(f(10) == nil)
- local function f(x) x = nil; local y; return x, y end
- assert(f(10) == nil and select(2, f(20)) == nil)
- do
- local i = 10
- do local i = 100; assert(i==100) end
- do local i = 1000; assert(i==1000) end
- assert(i == 10)
- if i ~= 10 then
- local i = 20
- else
- local i = 30
- assert(i == 30)
- end
- end
- f = nil
- local f
- x = 1
- a = nil
- load('local a = {}')()
- assert(a == nil)
- function f (a)
- local _1, _2, _3, _4, _5
- local _6, _7, _8, _9, _10
- local x = 3
- local b = a
- local c,d = a,b
- if (d == b) then
- local x = 'q'
- x = b
- assert(x == 2)
- else
- assert(nil)
- end
- assert(x == 3)
- local f = 10
- end
- local b=10
- local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3
- assert(x == 1)
- f(2)
- assert(type(f) == 'function')
- local function getenv (f)
- local a,b = debug.getupvalue(f, 1)
- assert(a == '_ENV')
- return b
- end
- -- test for global table of loaded chunks
- assert(getenv(load"a=3") == _G)
- local c = {}; local f = load("a = 3", nil, nil, c)
- assert(getenv(f) == c)
- assert(c.a == nil)
- f()
- assert(c.a == 3)
- -- old test for limits for special instructions (now just a generic test)
- do
- local i = 2
- local p = 4 -- p == 2^i
- repeat
- for j=-3,3 do
- assert(load(string.format([[local a=%s;
- a=a+%s;
- assert(a ==2^%s)]], j, p-j, i), '')) ()
- assert(load(string.format([[local a=%s;
- a=a-%s;
- assert(a==-2^%s)]], -j, p-j, i), '')) ()
- assert(load(string.format([[local a,b=0,%s;
- a=b-%s;
- assert(a==-2^%s)]], -j, p-j, i), '')) ()
- end
- p = 2 * p; i = i + 1
- until p <= 0
- end
- print'+'
- if rawget(_G, "T") then
- -- testing clearing of dead elements from tables
- collectgarbage("stop") -- stop GC
- local a = {[{}] = 4, [3] = 0, alo = 1,
- a1234567890123456789012345678901234567890 = 10}
- local t = T.querytab(a)
- for k,_ in pairs(a) do a[k] = undef end
- collectgarbage() -- restore GC and collect dead fiels in `a'
- for i=0,t-1 do
- local k = querytab(a, i)
- assert(k == nil or type(k) == 'number' or k == 'alo')
- end
- -- testing allocation errors during table insertions
- local a = {}
- local function additems ()
- a.x = true; a.y = true; a.z = true
- a[1] = true
- a[2] = true
- end
- for i = 1, math.huge do
- T.alloccount(i)
- local st, msg = pcall(additems)
- T.alloccount()
- local count = 0
- for k, v in pairs(a) do
- assert(a[k] == v)
- count = count + 1
- end
- if st then assert(count == 5); break end
- end
- end
- -- testing lexical environments
- assert(_ENV == _G)
- do
- local dummy
- local _ENV = (function (...) return ... end)(_G, dummy) -- {
- do local _ENV = {assert=assert}; assert(true) end
- mt = {_G = _G}
- local foo,x
- A = false -- "declare" A
- do local _ENV = mt
- function foo (x)
- A = x
- do local _ENV = _G; A = 1000 end
- return function (x) return A .. x end
- end
- end
- assert(getenv(foo) == mt)
- x = foo('hi'); assert(mt.A == 'hi' and A == 1000)
- assert(x('*') == mt.A .. '*')
- do local _ENV = {assert=assert, A=10};
- do local _ENV = {assert=assert, A=20};
- assert(A==20);x=A
- end
- assert(A==10 and x==20)
- end
- assert(x==20)
- print"testing to-be-closed variables"
- local function stack(n) n = ((n == 0) or stack(n - 1)) end
- local function func2close (f)
- return setmetatable({}, {__close = f})
- end
- do
- local a = {}
- do
- local <toclose> x = setmetatable({"x"}, {__close = function (self)
- a[#a + 1] = self[1] end})
- local <toclose> y = func2close(function (self, err)
- assert(err == nil); a[#a + 1] = "y"
- end)
- a[#a + 1] = "in"
- end
- a[#a + 1] = "out"
- assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
- end
- do
- local X = false
- local closescope = func2close(function () stack(10); X = true end)
- -- closing functions do not corrupt returning values
- local function foo (x)
- local <toclose> _ = closescope
- return x, X, 23
- end
- local a, b, c = foo(1.5)
- assert(a == 1.5 and b == false and c == 23 and X == true)
- X = false
- foo = function (x)
- local <toclose> _ = closescope
- local y = 15
- return y
- end
- assert(foo() == 15 and X == true)
- X = false
- foo = function ()
- local <toclose> x = closescope
- return x
- end
- assert(foo() == closescope and X == true)
- end
- do
- -- calls cannot be tail in the scope of to-be-closed variables
- local X, Y
- local function foo ()
- local <toclose> _ = func2close(function () Y = 10 end)
- assert(X == true and Y == nil) -- 'X' not closed yet
- return 1,2,3
- end
- local function bar ()
- local <toclose> _ = func2close(function () X = false end)
- X = true
- do
- return foo() -- not a tail call!
- end
- end
- local a, b, c, d = bar()
- assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil)
- end
- do -- errors in __close
- local log = {}
- local function foo (err)
- local <toclose> x =
- func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
- local <toclose> x1 =
- func2close(function (self, msg) log[#log + 1] = msg; end)
- local <toclose> gc = func2close(function () collectgarbage() end)
- local <toclose> y =
- func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
- local <toclose> z =
- func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end)
- if err then error(4) end
- end
- local stat, msg = pcall(foo, false)
- assert(msg == 3)
- assert(log[1] == 10 and log[2] == 3 and log[3] == 3 and log[4] == 3
- and #log == 4)
- log = {}
- local stat, msg = pcall(foo, true)
- assert(msg == 4)
- assert(log[1] == 4 and log[2] == 4 and log[3] == 4 and log[4] == 4
- and #log == 4)
- -- error in toclose in vararg function
- function foo (...)
- local <toclose> x123 = 10
- end
- local st, msg = pcall(foo)
- assert(string.find(msg, "'x123'"))
- end
- do
- -- errors due to non-closable values
- local function foo ()
- local <toclose> x = 34
- end
- local stat, msg = pcall(foo)
- assert(not stat and string.find(msg, "variable 'x'"))
- -- with other errors, non-closable values are ignored
- local function foo ()
- local <toclose> x = 34
- local <toclose> y = func2close(function () error(32) end)
- end
- local stat, msg = pcall(foo)
- assert(not stat and msg == 32)
- end
- if rawget(_G, "T") then
- -- memory error inside closing function
- local function foo ()
- local <toclose> y = func2close(function () T.alloccount() end)
- local <toclose> x = setmetatable({}, {__close = function ()
- T.alloccount(0); local x = {} -- force a memory error
- end})
- error(1000) -- common error inside the function's body
- end
- stack(5) -- ensure a minimal number of CI structures
- -- despite memory error, 'y' will be executed and
- -- memory limit will be lifted
- local _, msg = pcall(foo)
- assert(msg == 1000)
- local close = func2close(function (self, msg)
- T.alloccount()
- assert(msg == "not enough memory")
- end)
- -- set a memory limit and return a closing object to remove the limit
- local function enter (count)
- stack(10) -- reserve some stack space
- T.alloccount(count)
- return close
- end
- local function test ()
- local <toclose> x = enter(0) -- set a memory limit
- -- creation of previous upvalue will raise a memory error
- assert(false) -- should not run
- end
- local _, msg = pcall(test)
- assert(msg == "not enough memory")
- -- now use metamethod for closing
- close = setmetatable({}, {__close = function ()
- T.alloccount()
- end})
- -- repeat test with extra closing upvalues
- local function test ()
- local <toclose> xxx = func2close(function (self, msg)
- assert(msg == "not enough memory");
- error(1000) -- raise another error
- end)
- local <toclose> xx = func2close(function (self, msg)
- assert(msg == "not enough memory");
- end)
- local <toclose> x = enter(0) -- set a memory limit
- -- creation of previous upvalue will raise a memory error
- os.exit(false) -- should not run
- end
- local _, msg = pcall(test)
- assert(msg == "not enough memory") -- reported error is the first one
- do -- testing 'toclose' in C string buffer
- collectgarbage()
- local s = string.rep('a', 10000) -- large string
- local m = T.totalmem()
- collectgarbage("stop")
- s = string.upper(s) -- allocate buffer + new string (10K each)
- -- ensure buffer was deallocated
- assert(T.totalmem() - m <= 11000)
- collectgarbage("restart")
- end
- do -- now some tests for freeing buffer in case of errors
- local lim = 10000 -- some size larger than the static buffer
- local extra = 2000 -- some extra memory (for callinfo, etc.)
- local s = string.rep("a", lim)
- -- concat this table needs two buffer resizes (one for each 's')
- local a = {s, s}
- collectgarbage()
- m = T.totalmem()
- collectgarbage("stop")
- -- error in the first buffer allocation
- T. totalmem(m + extra)
- assert(not pcall(table.concat, a))
- -- first buffer was not even allocated
- assert(T.totalmem() - m <= extra)
- -- error in the second buffer allocation
- T. totalmem(m + lim + extra)
- assert(not pcall(table.concat, a))
- -- first buffer was released by 'toclose'
- assert(T.totalmem() - m <= extra)
- -- error in creation of final string
- T.totalmem(m + 2 * lim + extra)
- assert(not pcall(table.concat, a))
- -- second buffer was released by 'toclose'
- assert(T.totalmem() - m <= extra)
- -- userdata, upvalue, buffer, buffer, final string
- T.totalmem(m + 4*lim + extra)
- assert(#table.concat(a) == 2*lim)
- T.totalmem(0) -- remove memory limit
- collectgarbage("restart")
- print'+'
- end
- end
- print "to-be-closed variables in coroutines"
- do
- -- an error in a wrapped coroutine closes variables
- local x = false
- local y = false
- local co = coroutine.wrap(function ()
- local <toclose> xv = func2close(function () x = true end)
- do
- local <toclose> yv = func2close(function () y = true end)
- coroutine.yield(100) -- yield doesn't close variable
- end
- coroutine.yield(200) -- yield doesn't close variable
- error(23) -- error does
- end)
- local b = co()
- assert(b == 100 and not x and not y)
- b = co()
- assert(b == 200 and not x and y)
- local a, b = pcall(co)
- assert(not a and b == 23 and x and y)
- end
- do
- -- error in a wrapped coroutine raising errors when closing a variable
- local x = 0
- local co = coroutine.wrap(function ()
- local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
- local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
- coroutine.yield(100)
- error(200)
- end)
- assert(co() == 100); assert(x == 0)
- local st, msg = pcall(co); assert(x == 2)
- assert(not st and msg == 200) -- should get first error raised
- x = 0
- co = coroutine.wrap(function ()
- local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
- local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
- coroutine.yield(100)
- return 200
- end)
- assert(co() == 100); assert(x == 0)
- local st, msg = pcall(co); assert(x == 2)
- -- should get first error raised
- assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
- end
- -- a suspended coroutine should not close its variables when collected
- local co
- co = coroutine.wrap(function()
- local <toclose> x = function () os.exit(false) end -- should not run
- co = nil
- coroutine.yield()
- end)
- co() -- start coroutine
- assert(co == nil) -- eventually it will be collected
- collectgarbage()
- -- to-be-closed variables in generic for loops
- do
- local numopen = 0
- local function open (x)
- numopen = numopen + 1
- return
- function () -- iteraction function
- x = x - 1
- if x > 0 then return x end
- end,
- nil, -- state
- nil, -- control variable
- func2close(function () numopen = numopen - 1 end) -- closing function
- end
- local s = 0
- for i in open(10) do
- s = s + i
- end
- assert(s == 45 and numopen == 0)
- local s = 0
- for i in open(10) do
- if i < 5 then break end
- s = s + i
- end
- assert(s == 35 and numopen == 0)
- -- repeat test with '__open' metamethod instead of a function
- local function open (x)
- numopen = numopen + 1
- local state = setmetatable({x},
- {__close = function () numopen = numopen - 1 end})
- return
- function (t) -- iteraction function
- t[1] = t[1] - 1
- if t[1] > 0 then return t[1] end
- end,
- state,
- nil,
- state -- to-be-closed
- end
- local s = 0
- for i in open(10) do
- if (i < 5) then break end
- s = s + i
- end
- assert(s == 35 and numopen == 0)
- end
- print('OK')
- return 5,f
- end -- }
|