mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
Flag for to-be-closed variables changed to '<toclose>'
The flag for to-be-closed variables was changed from '*toclose' to '<toclose>'. Several people found confusing the old syntax and the new one has a clear terminator, making it more flexible for future changes.
This commit is contained in:
parent
3f253f116e
commit
d881325c2f
@ -1621,6 +1621,7 @@ static void tocloselocalstat (LexState *ls) {
|
|||||||
if (strcmp(getstr(attr), "toclose") != 0)
|
if (strcmp(getstr(attr), "toclose") != 0)
|
||||||
luaK_semerror(ls,
|
luaK_semerror(ls,
|
||||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
||||||
|
testnext(ls, '>');
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
checknext(ls, '=');
|
checknext(ls, '=');
|
||||||
exp1(ls);
|
exp1(ls);
|
||||||
@ -1634,7 +1635,7 @@ static void tocloselocalstat (LexState *ls) {
|
|||||||
static void localstat (LexState *ls) {
|
static void localstat (LexState *ls) {
|
||||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||||
| LOCAL *toclose NAME '=' exp */
|
| LOCAL *toclose NAME '=' exp */
|
||||||
if (testnext(ls, '*'))
|
if (testnext(ls, '<'))
|
||||||
tocloselocalstat(ls);
|
tocloselocalstat(ls);
|
||||||
else
|
else
|
||||||
commonlocalstat(ls);
|
commonlocalstat(ls);
|
||||||
|
@ -1509,7 +1509,7 @@ A local variable can be declared as a @def{to-be-closed} variable,
|
|||||||
with the following syntax:
|
with the following syntax:
|
||||||
@Produc{
|
@Produc{
|
||||||
@producname{stat}@producbody{
|
@producname{stat}@producbody{
|
||||||
@Rw{local} @bnfter{*} @bnfter{toclose} Name @bnfter{=} exp
|
@Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp
|
||||||
}}
|
}}
|
||||||
A to-be-closed variable behaves like a normal local variable,
|
A to-be-closed variable behaves like a normal local variable,
|
||||||
except that its value is @emph{closed} whenever the variable
|
except that its value is @emph{closed} whenever the variable
|
||||||
@ -8949,7 +8949,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
|||||||
@OrNL @Rw{function} funcname funcbody
|
@OrNL @Rw{function} funcname funcbody
|
||||||
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
||||||
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
||||||
@OrNL @Rw{local} @bnfter{*} @bnfter{toclose} Name @bnfter{=} exp
|
@OrNL @Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp
|
||||||
}
|
}
|
||||||
|
|
||||||
@producname{retstat}@producbody{@Rw{return}
|
@producname{retstat}@producbody{@Rw{return}
|
||||||
|
@ -1137,7 +1137,8 @@ end)
|
|||||||
testamem("to-be-closed variables", function()
|
testamem("to-be-closed variables", function()
|
||||||
local flag
|
local flag
|
||||||
do
|
do
|
||||||
local *toclose x = setmetatable({}, {__close = function () flag = true end})
|
local <toclose> x =
|
||||||
|
setmetatable({}, {__close = function () flag = true end})
|
||||||
flag = false
|
flag = false
|
||||||
local x = {}
|
local x = {}
|
||||||
end
|
end
|
||||||
|
@ -151,7 +151,7 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
co = coroutine.create(function ()
|
co = coroutine.create(function ()
|
||||||
local *toclose x = func2close(function (self, err)
|
local <toclose> x = func2close(function (self, err)
|
||||||
assert(err == nil); X = false
|
assert(err == nil); X = false
|
||||||
end)
|
end)
|
||||||
X = true
|
X = true
|
||||||
@ -164,7 +164,7 @@ do
|
|||||||
|
|
||||||
-- error killing a coroutine
|
-- error killing a coroutine
|
||||||
co = coroutine.create(function()
|
co = coroutine.create(function()
|
||||||
local *toclose x = func2close(function (self, err)
|
local <toclose> x = func2close(function (self, err)
|
||||||
assert(err == nil); error(111)
|
assert(err == nil); error(111)
|
||||||
end)
|
end)
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
@ -348,7 +348,7 @@ do
|
|||||||
|
|
||||||
local X = false
|
local X = false
|
||||||
A = coroutine.wrap(function()
|
A = coroutine.wrap(function()
|
||||||
local *toclose _ = setmetatable({}, {__close = function () X = true end})
|
local <toclose> _ = setmetatable({}, {__close = function () X = true end})
|
||||||
return pcall(A, 1)
|
return pcall(A, 1)
|
||||||
end)
|
end)
|
||||||
st, res = A()
|
st, res = A()
|
||||||
|
@ -125,7 +125,7 @@ do
|
|||||||
-- closing file by scope
|
-- closing file by scope
|
||||||
local F = nil
|
local F = nil
|
||||||
do
|
do
|
||||||
local *toclose f = assert(io.open(file, "w"))
|
local <toclose> f = assert(io.open(file, "w"))
|
||||||
F = f
|
F = f
|
||||||
end
|
end
|
||||||
assert(tostring(F) == "file (closed)")
|
assert(tostring(F) == "file (closed)")
|
||||||
@ -135,7 +135,7 @@ assert(os.remove(file))
|
|||||||
|
|
||||||
do
|
do
|
||||||
-- test writing/reading numbers
|
-- test writing/reading numbers
|
||||||
local *toclose f = assert(io.open(file, "w"))
|
local <toclose> f = assert(io.open(file, "w"))
|
||||||
f:write(maxint, '\n')
|
f:write(maxint, '\n')
|
||||||
f:write(string.format("0X%x\n", maxint))
|
f:write(string.format("0X%x\n", maxint))
|
||||||
f:write("0xABCp-3", '\n')
|
f:write("0xABCp-3", '\n')
|
||||||
@ -158,7 +158,7 @@ assert(os.remove(file))
|
|||||||
|
|
||||||
-- testing multiple arguments to io.read
|
-- testing multiple arguments to io.read
|
||||||
do
|
do
|
||||||
local *toclose f = assert(io.open(file, "w"))
|
local <toclose> f = assert(io.open(file, "w"))
|
||||||
f:write[[
|
f:write[[
|
||||||
a line
|
a line
|
||||||
another line
|
another line
|
||||||
|
@ -258,7 +258,7 @@ do
|
|||||||
::L2:: goto L3
|
::L2:: goto L3
|
||||||
|
|
||||||
::L1:: do
|
::L1:: do
|
||||||
local *toclose a = setmetatable({}, {__close = function () X = true end})
|
local <toclose> a = setmetatable({}, {__close = function () X = true end})
|
||||||
assert(X == nil)
|
assert(X == nil)
|
||||||
if a then goto L2 end -- jumping back out of scope of 'a'
|
if a then goto L2 end -- jumping back out of scope of 'a'
|
||||||
end
|
end
|
||||||
|
@ -185,9 +185,9 @@ end
|
|||||||
do
|
do
|
||||||
local a = {}
|
local a = {}
|
||||||
do
|
do
|
||||||
local *toclose x = setmetatable({"x"}, {__close = function (self)
|
local <toclose> x = setmetatable({"x"}, {__close = function (self)
|
||||||
a[#a + 1] = self[1] end})
|
a[#a + 1] = self[1] end})
|
||||||
local *toclose y = func2close(function (self, err)
|
local <toclose> y = func2close(function (self, err)
|
||||||
assert(err == nil); a[#a + 1] = "y"
|
assert(err == nil); a[#a + 1] = "y"
|
||||||
end)
|
end)
|
||||||
a[#a + 1] = "in"
|
a[#a + 1] = "in"
|
||||||
@ -203,7 +203,7 @@ do
|
|||||||
|
|
||||||
-- closing functions do not corrupt returning values
|
-- closing functions do not corrupt returning values
|
||||||
local function foo (x)
|
local function foo (x)
|
||||||
local *toclose _ = closescope
|
local <toclose> _ = closescope
|
||||||
return x, X, 23
|
return x, X, 23
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ do
|
|||||||
|
|
||||||
X = false
|
X = false
|
||||||
foo = function (x)
|
foo = function (x)
|
||||||
local *toclose _ = closescope
|
local <toclose> _ = closescope
|
||||||
local y = 15
|
local y = 15
|
||||||
return y
|
return y
|
||||||
end
|
end
|
||||||
@ -221,7 +221,7 @@ do
|
|||||||
|
|
||||||
X = false
|
X = false
|
||||||
foo = function ()
|
foo = function ()
|
||||||
local *toclose x = closescope
|
local <toclose> x = closescope
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -234,13 +234,13 @@ do
|
|||||||
-- calls cannot be tail in the scope of to-be-closed variables
|
-- calls cannot be tail in the scope of to-be-closed variables
|
||||||
local X, Y
|
local X, Y
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local *toclose _ = func2close(function () Y = 10 end)
|
local <toclose> _ = func2close(function () Y = 10 end)
|
||||||
assert(X == true and Y == nil) -- 'X' not closed yet
|
assert(X == true and Y == nil) -- 'X' not closed yet
|
||||||
return 1,2,3
|
return 1,2,3
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bar ()
|
local function bar ()
|
||||||
local *toclose _ = func2close(function () X = false end)
|
local <toclose> _ = func2close(function () X = false end)
|
||||||
X = true
|
X = true
|
||||||
do
|
do
|
||||||
return foo() -- not a tail call!
|
return foo() -- not a tail call!
|
||||||
@ -255,14 +255,14 @@ end
|
|||||||
do -- errors in __close
|
do -- errors in __close
|
||||||
local log = {}
|
local log = {}
|
||||||
local function foo (err)
|
local function foo (err)
|
||||||
local *toclose x =
|
local <toclose> x =
|
||||||
func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
|
func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
|
||||||
local *toclose x1 =
|
local <toclose> x1 =
|
||||||
func2close(function (self, msg) log[#log + 1] = msg; end)
|
func2close(function (self, msg) log[#log + 1] = msg; end)
|
||||||
local *toclose gc = func2close(function () collectgarbage() end)
|
local <toclose> gc = func2close(function () collectgarbage() end)
|
||||||
local *toclose y =
|
local <toclose> y =
|
||||||
func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
|
func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
|
||||||
local *toclose z =
|
local <toclose> z =
|
||||||
func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end)
|
func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end)
|
||||||
if err then error(4) end
|
if err then error(4) end
|
||||||
end
|
end
|
||||||
@ -283,7 +283,7 @@ do
|
|||||||
|
|
||||||
-- errors due to non-closable values
|
-- errors due to non-closable values
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local *toclose x = 34
|
local <toclose> x = 34
|
||||||
end
|
end
|
||||||
local stat, msg = pcall(foo)
|
local stat, msg = pcall(foo)
|
||||||
assert(not stat and string.find(msg, "variable 'x'"))
|
assert(not stat and string.find(msg, "variable 'x'"))
|
||||||
@ -291,8 +291,8 @@ do
|
|||||||
|
|
||||||
-- with other errors, non-closable values are ignored
|
-- with other errors, non-closable values are ignored
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local *toclose x = 34
|
local <toclose> x = 34
|
||||||
local *toclose y = func2close(function () error(32) end)
|
local <toclose> y = func2close(function () error(32) end)
|
||||||
end
|
end
|
||||||
local stat, msg = pcall(foo)
|
local stat, msg = pcall(foo)
|
||||||
assert(not stat and msg == 32)
|
assert(not stat and msg == 32)
|
||||||
@ -304,8 +304,8 @@ if rawget(_G, "T") then
|
|||||||
|
|
||||||
-- memory error inside closing function
|
-- memory error inside closing function
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local *toclose y = func2close(function () T.alloccount() end)
|
local <toclose> y = func2close(function () T.alloccount() end)
|
||||||
local *toclose x = setmetatable({}, {__close = function ()
|
local <toclose> x = setmetatable({}, {__close = function ()
|
||||||
T.alloccount(0); local x = {} -- force a memory error
|
T.alloccount(0); local x = {} -- force a memory error
|
||||||
end})
|
end})
|
||||||
error("a") -- common error inside the function's body
|
error("a") -- common error inside the function's body
|
||||||
@ -331,7 +331,7 @@ if rawget(_G, "T") then
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function test ()
|
local function test ()
|
||||||
local *toclose x = enter(0) -- set a memory limit
|
local <toclose> x = enter(0) -- set a memory limit
|
||||||
-- creation of previous upvalue will raise a memory error
|
-- creation of previous upvalue will raise a memory error
|
||||||
assert(false) -- should not run
|
assert(false) -- should not run
|
||||||
end
|
end
|
||||||
@ -346,14 +346,14 @@ if rawget(_G, "T") then
|
|||||||
|
|
||||||
-- repeat test with extra closing upvalues
|
-- repeat test with extra closing upvalues
|
||||||
local function test ()
|
local function test ()
|
||||||
local *toclose xxx = func2close(function (self, msg)
|
local <toclose> xxx = func2close(function (self, msg)
|
||||||
assert(msg == "not enough memory");
|
assert(msg == "not enough memory");
|
||||||
error(1000) -- raise another error
|
error(1000) -- raise another error
|
||||||
end)
|
end)
|
||||||
local *toclose xx = func2close(function (self, msg)
|
local <toclose> xx = func2close(function (self, msg)
|
||||||
assert(msg == "not enough memory");
|
assert(msg == "not enough memory");
|
||||||
end)
|
end)
|
||||||
local *toclose x = enter(0) -- set a memory limit
|
local <toclose> x = enter(0) -- set a memory limit
|
||||||
-- creation of previous upvalue will raise a memory error
|
-- creation of previous upvalue will raise a memory error
|
||||||
os.exit(false) -- should not run
|
os.exit(false) -- should not run
|
||||||
end
|
end
|
||||||
@ -424,9 +424,9 @@ do
|
|||||||
local x = false
|
local x = false
|
||||||
local y = false
|
local y = false
|
||||||
local co = coroutine.wrap(function ()
|
local co = coroutine.wrap(function ()
|
||||||
local *toclose xv = func2close(function () x = true end)
|
local <toclose> xv = func2close(function () x = true end)
|
||||||
do
|
do
|
||||||
local *toclose yv = func2close(function () y = true end)
|
local <toclose> yv = func2close(function () y = true end)
|
||||||
coroutine.yield(100) -- yield doesn't close variable
|
coroutine.yield(100) -- yield doesn't close variable
|
||||||
end
|
end
|
||||||
coroutine.yield(200) -- yield doesn't close variable
|
coroutine.yield(200) -- yield doesn't close variable
|
||||||
@ -446,7 +446,7 @@ do
|
|||||||
-- error in a wrapped coroutine raising errors when closing a variable
|
-- error in a wrapped coroutine raising errors when closing a variable
|
||||||
local x = false
|
local x = false
|
||||||
local co = coroutine.wrap(function ()
|
local co = coroutine.wrap(function ()
|
||||||
local *toclose xv = func2close(function () error("XXX") end)
|
local <toclose> xv = func2close(function () error("XXX") end)
|
||||||
coroutine.yield(100)
|
coroutine.yield(100)
|
||||||
error(200)
|
error(200)
|
||||||
end)
|
end)
|
||||||
@ -461,7 +461,7 @@ end
|
|||||||
-- a suspended coroutine should not close its variables when collected
|
-- a suspended coroutine should not close its variables when collected
|
||||||
local co
|
local co
|
||||||
co = coroutine.wrap(function()
|
co = coroutine.wrap(function()
|
||||||
local *toclose x = function () os.exit(false) end -- should not run
|
local <toclose> x = function () os.exit(false) end -- should not run
|
||||||
co = nil
|
co = nil
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end)
|
end)
|
||||||
|
@ -320,11 +320,11 @@ NoRun("", "lua %s", prog) -- no message
|
|||||||
|
|
||||||
-- to-be-closed variables in main chunk
|
-- to-be-closed variables in main chunk
|
||||||
prepfile[[
|
prepfile[[
|
||||||
local *toclose x = function (err)
|
local <toclose> x = function (err)
|
||||||
assert(err == 120)
|
assert(err == 120)
|
||||||
print("Ok")
|
print("Ok")
|
||||||
end
|
end
|
||||||
local *toclose e1 = function () error(120) end
|
local <toclose> e1 = function () error(120) end
|
||||||
os.exit(true, true)
|
os.exit(true, true)
|
||||||
]]
|
]]
|
||||||
RUN('lua %s > %s', prog, out)
|
RUN('lua %s > %s', prog, out)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user