New syntax for to-be-closed variables
The new syntax is <local *toclose x = f()>. The mark '*' allows other
attributes to be added later without the need of new keywords; it
also allows better error messages. The API function was also renamed
('lua_tobeclosed' -> 'lua_toclose').
This commit is contained in:
2
lapi.c
2
lapi.c
@@ -1207,7 +1207,7 @@ LUA_API int lua_next (lua_State *L, int idx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LUA_API void lua_tobeclosed (lua_State *L) {
|
LUA_API void lua_toclose (lua_State *L) {
|
||||||
int nresults = L->ci->nresults;
|
int nresults = L->ci->nresults;
|
||||||
luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */
|
luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */
|
||||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||||
|
|||||||
25
lparser.c
25
lparser.c
@@ -1546,16 +1546,15 @@ static void localfunc (LexState *ls) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void commonlocalstat (LexState *ls, TString *firstvar) {
|
static void commonlocalstat (LexState *ls) {
|
||||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
|
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
|
||||||
int nvars = 1;
|
int nvars = 0;
|
||||||
int nexps;
|
int nexps;
|
||||||
expdesc e;
|
expdesc e;
|
||||||
new_localvar(ls, firstvar);
|
do {
|
||||||
while (testnext(ls, ',')) {
|
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
nvars++;
|
nvars++;
|
||||||
}
|
} while (testnext(ls, ','));
|
||||||
if (testnext(ls, '='))
|
if (testnext(ls, '='))
|
||||||
nexps = explist(ls, &e);
|
nexps = explist(ls, &e);
|
||||||
else {
|
else {
|
||||||
@@ -1567,8 +1566,12 @@ static void commonlocalstat (LexState *ls, TString *firstvar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void scopedlocalstat (LexState *ls) {
|
static void tocloselocalstat (LexState *ls) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
|
TString *attr = str_checkname(ls);
|
||||||
|
if (strcmp(getstr(attr), "toclose") != 0)
|
||||||
|
luaK_semerror(ls,
|
||||||
|
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
checknext(ls, '=');
|
checknext(ls, '=');
|
||||||
exp1(ls, 0);
|
exp1(ls, 0);
|
||||||
@@ -1580,13 +1583,11 @@ static void scopedlocalstat (LexState *ls) {
|
|||||||
|
|
||||||
static void localstat (LexState *ls) {
|
static void localstat (LexState *ls) {
|
||||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||||
| LOCAL SCOPED NAME '=' exp */
|
| LOCAL *toclose NAME '=' exp */
|
||||||
TString *firstvar = str_checkname(ls);
|
if (testnext(ls, '*'))
|
||||||
if (ls->t.token == TK_NAME &&
|
tocloselocalstat(ls);
|
||||||
eqshrstr(firstvar, luaS_newliteral(ls->L, "scoped")))
|
|
||||||
scopedlocalstat(ls);
|
|
||||||
else
|
else
|
||||||
commonlocalstat(ls, firstvar);
|
commonlocalstat(ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
ltests.c
4
ltests.c
@@ -1550,8 +1550,8 @@ static struct X { int x; } x;
|
|||||||
int i = getindex;
|
int i = getindex;
|
||||||
return lua_yieldk(L1, nres, i, Cfunck);
|
return lua_yieldk(L1, nres, i, Cfunck);
|
||||||
}
|
}
|
||||||
else if EQ("tobeclosed") {
|
else if EQ("toclose") {
|
||||||
lua_tobeclosed(L);
|
lua_toclose(L);
|
||||||
}
|
}
|
||||||
else luaL_error(L, "unknown instruction %s", buff);
|
else luaL_error(L, "unknown instruction %s", buff);
|
||||||
}
|
}
|
||||||
|
|||||||
2
lua.h
2
lua.h
@@ -333,7 +333,7 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
|||||||
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||||
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||||
|
|
||||||
LUA_API void (lua_tobeclosed) (lua_State *L);
|
LUA_API void (lua_toclose) (lua_State *L);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -987,7 +987,7 @@ do
|
|||||||
|
|
||||||
local a = T.testC([[
|
local a = T.testC([[
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
tobeclosed # mark it to be closed
|
toclose # mark it to be closed
|
||||||
return 1
|
return 1
|
||||||
]], newresource)
|
]], newresource)
|
||||||
assert(a[1] == 11)
|
assert(a[1] == 11)
|
||||||
@@ -996,7 +996,7 @@ do
|
|||||||
-- repeat the test, but calling function in a 'multret' context
|
-- repeat the test, but calling function in a 'multret' context
|
||||||
local a = {T.testC([[
|
local a = {T.testC([[
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
tobeclosed # mark it to be closed
|
toclose # mark it to be closed
|
||||||
return 2
|
return 2
|
||||||
]], newresource)}
|
]], newresource)}
|
||||||
assert(type(a[1]) == "string" and a[2][1] == 11)
|
assert(type(a[1]) == "string" and a[2][1] == 11)
|
||||||
@@ -1005,7 +1005,7 @@ do
|
|||||||
-- error
|
-- error
|
||||||
local a, b = pcall(T.testC, [[
|
local a, b = pcall(T.testC, [[
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
tobeclosed # mark it to be closed
|
toclose # mark it to be closed
|
||||||
error # resource is the error object
|
error # resource is the error object
|
||||||
]], newresource)
|
]], newresource)
|
||||||
assert(a == false and b[1] == 11)
|
assert(a == false and b[1] == 11)
|
||||||
@@ -1019,10 +1019,10 @@ do
|
|||||||
local a = T.testC([[
|
local a = T.testC([[
|
||||||
pushvalue 2
|
pushvalue 2
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource
|
||||||
tobeclosed # mark it to be closed
|
toclose # mark it to be closed
|
||||||
pushvalue 2
|
pushvalue 2
|
||||||
call 0 1 # create another resource
|
call 0 1 # create another resource
|
||||||
tobeclosed # mark it to be closed
|
toclose # mark it to be closed
|
||||||
pushvalue 3
|
pushvalue 3
|
||||||
pushint 2 # there should be two open resources
|
pushint 2 # there should be two open resources
|
||||||
call 1 0
|
call 1 0
|
||||||
@@ -1102,7 +1102,7 @@ end)
|
|||||||
testamem("to-be-closed variables", function()
|
testamem("to-be-closed variables", function()
|
||||||
local flag
|
local flag
|
||||||
do
|
do
|
||||||
local scoped x = function () flag = true end
|
local *toclose x = function () flag = true end
|
||||||
flag = false
|
flag = false
|
||||||
local x = {}
|
local x = {}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ do
|
|||||||
-- closing file by scope
|
-- closing file by scope
|
||||||
local F = nil
|
local F = nil
|
||||||
do
|
do
|
||||||
local scoped 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 scoped 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 scoped 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 scoped a = function () X = true end
|
local *toclose a = 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
|
||||||
|
|||||||
@@ -181,9 +181,9 @@ local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
|||||||
do
|
do
|
||||||
local a = {}
|
local a = {}
|
||||||
do
|
do
|
||||||
local scoped 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 scoped y = function (x) assert(x == nil); a[#a + 1] = "y" end
|
local *toclose y = function (x) assert(x == nil); a[#a + 1] = "y" end
|
||||||
a[#a + 1] = "in"
|
a[#a + 1] = "in"
|
||||||
end
|
end
|
||||||
a[#a + 1] = "out"
|
a[#a + 1] = "out"
|
||||||
@@ -197,7 +197,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 scoped _ = closescope
|
local *toclose _ = closescope
|
||||||
return x, X, 23
|
return x, X, 23
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ do
|
|||||||
|
|
||||||
X = false
|
X = false
|
||||||
foo = function (x)
|
foo = function (x)
|
||||||
local scoped _ = closescope
|
local *toclose _ = closescope
|
||||||
local y = 15
|
local y = 15
|
||||||
return y
|
return y
|
||||||
end
|
end
|
||||||
@@ -215,7 +215,7 @@ do
|
|||||||
|
|
||||||
X = false
|
X = false
|
||||||
foo = function ()
|
foo = function ()
|
||||||
local scoped x = closescope
|
local *toclose x = closescope
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -228,13 +228,13 @@ do
|
|||||||
-- to-be-closed variables must be closed in tail calls
|
-- to-be-closed variables must be closed in tail calls
|
||||||
local X, Y
|
local X, Y
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local scoped _ = function () Y = 10 end
|
local *toclose _ = function () Y = 10 end
|
||||||
assert(X == 20 and Y == nil)
|
assert(X == 20 and Y == nil)
|
||||||
return 1,2,3
|
return 1,2,3
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bar ()
|
local function bar ()
|
||||||
local scoped _ = function () X = 20 end
|
local *toclose _ = function () X = 20 end
|
||||||
return foo()
|
return foo()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -245,11 +245,11 @@ end
|
|||||||
do -- errors in __close
|
do -- errors in __close
|
||||||
local log = {}
|
local log = {}
|
||||||
local function foo (err)
|
local function foo (err)
|
||||||
local scoped x = function (msg) log[#log + 1] = msg; error(1) end
|
local *toclose x = function (msg) log[#log + 1] = msg; error(1) end
|
||||||
local scoped x1 = function (msg) log[#log + 1] = msg; end
|
local *toclose x1 = function (msg) log[#log + 1] = msg; end
|
||||||
local scoped gc = function () collectgarbage() end
|
local *toclose gc = function () collectgarbage() end
|
||||||
local scoped y = function (msg) log[#log + 1] = msg; error(2) end
|
local *toclose y = function (msg) log[#log + 1] = msg; error(2) end
|
||||||
local scoped z = function (msg) log[#log + 1] = msg or 10; error(3) end
|
local *toclose z = function (msg) log[#log + 1] = msg or 10; error(3) end
|
||||||
if err then error(4) end
|
if err then error(4) end
|
||||||
end
|
end
|
||||||
local stat, msg = pcall(foo, false)
|
local stat, msg = pcall(foo, false)
|
||||||
@@ -267,8 +267,8 @@ end
|
|||||||
if rawget(_G, "T") then
|
if rawget(_G, "T") then
|
||||||
-- memory error inside closing function
|
-- memory error inside closing function
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local scoped y = function () T.alloccount() end
|
local *toclose y = function () T.alloccount() end
|
||||||
local scoped 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
|
||||||
@@ -294,7 +294,7 @@ if rawget(_G, "T") then
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function test ()
|
local function test ()
|
||||||
local scoped 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
|
||||||
@@ -309,14 +309,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 scoped xxx = function (msg)
|
local *toclose xxx = function (msg)
|
||||||
assert(msg == "not enough memory");
|
assert(msg == "not enough memory");
|
||||||
error(1000) -- raise another error
|
error(1000) -- raise another error
|
||||||
end
|
end
|
||||||
local scoped xx = function (msg)
|
local *toclose xx = function (msg)
|
||||||
assert(msg == "not enough memory");
|
assert(msg == "not enough memory");
|
||||||
end
|
end
|
||||||
local scoped 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
|
||||||
@@ -333,9 +333,9 @@ do
|
|||||||
local x = false
|
local x = false
|
||||||
local y = false
|
local y = false
|
||||||
local co = coroutine.create(function ()
|
local co = coroutine.create(function ()
|
||||||
local scoped xv = function () x = true end
|
local *toclose xv = function () x = true end
|
||||||
do
|
do
|
||||||
local scoped yv = function () y = true end
|
local *toclose yv = 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
|
||||||
@@ -353,7 +353,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 scoped 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user