mirror of https://github.com/lua/lua
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:
parent
5e76a4fd31
commit
b8fed93215
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;
|
||||
luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */
|
||||
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] */
|
||||
int nvars = 1;
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
new_localvar(ls, firstvar);
|
||||
while (testnext(ls, ',')) {
|
||||
do {
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
nvars++;
|
||||
}
|
||||
} while (testnext(ls, ','));
|
||||
if (testnext(ls, '='))
|
||||
nexps = explist(ls, &e);
|
||||
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;
|
||||
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));
|
||||
checknext(ls, '=');
|
||||
exp1(ls, 0);
|
||||
|
@ -1580,13 +1583,11 @@ static void scopedlocalstat (LexState *ls) {
|
|||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||
| LOCAL SCOPED NAME '=' exp */
|
||||
TString *firstvar = str_checkname(ls);
|
||||
if (ls->t.token == TK_NAME &&
|
||||
eqshrstr(firstvar, luaS_newliteral(ls->L, "scoped")))
|
||||
scopedlocalstat(ls);
|
||||
| LOCAL *toclose NAME '=' exp */
|
||||
if (testnext(ls, '*'))
|
||||
tocloselocalstat(ls);
|
||||
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;
|
||||
return lua_yieldk(L1, nres, i, Cfunck);
|
||||
}
|
||||
else if EQ("tobeclosed") {
|
||||
lua_tobeclosed(L);
|
||||
else if EQ("toclose") {
|
||||
lua_toclose(L);
|
||||
}
|
||||
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 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([[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
toclose # mark it to be closed
|
||||
return 1
|
||||
]], newresource)
|
||||
assert(a[1] == 11)
|
||||
|
@ -996,7 +996,7 @@ do
|
|||
-- repeat the test, but calling function in a 'multret' context
|
||||
local a = {T.testC([[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
toclose # mark it to be closed
|
||||
return 2
|
||||
]], newresource)}
|
||||
assert(type(a[1]) == "string" and a[2][1] == 11)
|
||||
|
@ -1005,7 +1005,7 @@ do
|
|||
-- error
|
||||
local a, b = pcall(T.testC, [[
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
toclose # mark it to be closed
|
||||
error # resource is the error object
|
||||
]], newresource)
|
||||
assert(a == false and b[1] == 11)
|
||||
|
@ -1019,10 +1019,10 @@ do
|
|||
local a = T.testC([[
|
||||
pushvalue 2
|
||||
call 0 1 # create resource
|
||||
tobeclosed # mark it to be closed
|
||||
toclose # mark it to be closed
|
||||
pushvalue 2
|
||||
call 0 1 # create another resource
|
||||
tobeclosed # mark it to be closed
|
||||
toclose # mark it to be closed
|
||||
pushvalue 3
|
||||
pushint 2 # there should be two open resources
|
||||
call 1 0
|
||||
|
@ -1102,7 +1102,7 @@ end)
|
|||
testamem("to-be-closed variables", function()
|
||||
local flag
|
||||
do
|
||||
local scoped x = function () flag = true end
|
||||
local *toclose x = function () flag = true end
|
||||
flag = false
|
||||
local x = {}
|
||||
end
|
||||
|
|
|
@ -125,7 +125,7 @@ do
|
|||
-- closing file by scope
|
||||
local F = nil
|
||||
do
|
||||
local scoped f = assert(io.open(file, "w"))
|
||||
local *toclose f = assert(io.open(file, "w"))
|
||||
F = f
|
||||
end
|
||||
assert(tostring(F) == "file (closed)")
|
||||
|
@ -135,7 +135,7 @@ assert(os.remove(file))
|
|||
|
||||
do
|
||||
-- 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(string.format("0X%x\n", maxint))
|
||||
f:write("0xABCp-3", '\n')
|
||||
|
@ -158,7 +158,7 @@ assert(os.remove(file))
|
|||
|
||||
-- testing multiple arguments to io.read
|
||||
do
|
||||
local scoped f = assert(io.open(file, "w"))
|
||||
local *toclose f = assert(io.open(file, "w"))
|
||||
f:write[[
|
||||
a line
|
||||
another line
|
||||
|
|
|
@ -258,7 +258,7 @@ do
|
|||
::L2:: goto L3
|
||||
|
||||
::L1:: do
|
||||
local scoped a = function () X = true end
|
||||
local *toclose a = function () X = true end
|
||||
assert(X == nil)
|
||||
if a then goto L2 end -- jumping back out of scope of 'a'
|
||||
end
|
||||
|
|
|
@ -181,9 +181,9 @@ local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
|||
do
|
||||
local a = {}
|
||||
do
|
||||
local scoped x = setmetatable({"x"}, {__close = function (self)
|
||||
local *toclose x = setmetatable({"x"}, {__close = function (self)
|
||||
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"
|
||||
end
|
||||
a[#a + 1] = "out"
|
||||
|
@ -197,7 +197,7 @@ do
|
|||
|
||||
-- closing functions do not corrupt returning values
|
||||
local function foo (x)
|
||||
local scoped _ = closescope
|
||||
local *toclose _ = closescope
|
||||
return x, X, 23
|
||||
end
|
||||
|
||||
|
@ -206,7 +206,7 @@ do
|
|||
|
||||
X = false
|
||||
foo = function (x)
|
||||
local scoped _ = closescope
|
||||
local *toclose _ = closescope
|
||||
local y = 15
|
||||
return y
|
||||
end
|
||||
|
@ -215,7 +215,7 @@ do
|
|||
|
||||
X = false
|
||||
foo = function ()
|
||||
local scoped x = closescope
|
||||
local *toclose x = closescope
|
||||
return x
|
||||
end
|
||||
|
||||
|
@ -228,13 +228,13 @@ do
|
|||
-- to-be-closed variables must be closed in tail calls
|
||||
local X, Y
|
||||
local function foo ()
|
||||
local scoped _ = function () Y = 10 end
|
||||
local *toclose _ = function () Y = 10 end
|
||||
assert(X == 20 and Y == nil)
|
||||
return 1,2,3
|
||||
end
|
||||
|
||||
local function bar ()
|
||||
local scoped _ = function () X = 20 end
|
||||
local *toclose _ = function () X = 20 end
|
||||
return foo()
|
||||
end
|
||||
|
||||
|
@ -245,11 +245,11 @@ end
|
|||
do -- errors in __close
|
||||
local log = {}
|
||||
local function foo (err)
|
||||
local scoped x = function (msg) log[#log + 1] = msg; error(1) end
|
||||
local scoped x1 = function (msg) log[#log + 1] = msg; end
|
||||
local scoped gc = function () collectgarbage() end
|
||||
local scoped 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 x = function (msg) log[#log + 1] = msg; error(1) end
|
||||
local *toclose x1 = function (msg) log[#log + 1] = msg; end
|
||||
local *toclose gc = function () collectgarbage() end
|
||||
local *toclose y = function (msg) log[#log + 1] = msg; error(2) end
|
||||
local *toclose z = function (msg) log[#log + 1] = msg or 10; error(3) end
|
||||
if err then error(4) end
|
||||
end
|
||||
local stat, msg = pcall(foo, false)
|
||||
|
@ -267,8 +267,8 @@ end
|
|||
if rawget(_G, "T") then
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
local scoped y = function () T.alloccount() end
|
||||
local scoped x = setmetatable({}, {__close = function ()
|
||||
local *toclose y = function () T.alloccount() end
|
||||
local *toclose x = setmetatable({}, {__close = function ()
|
||||
T.alloccount(0); local x = {} -- force a memory error
|
||||
end})
|
||||
error("a") -- common error inside the function's body
|
||||
|
@ -294,7 +294,7 @@ if rawget(_G, "T") then
|
|||
end
|
||||
|
||||
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
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
@ -309,14 +309,14 @@ if rawget(_G, "T") then
|
|||
|
||||
-- repeat test with extra closing upvalues
|
||||
local function test ()
|
||||
local scoped xxx = function (msg)
|
||||
local *toclose xxx = function (msg)
|
||||
assert(msg == "not enough memory");
|
||||
error(1000) -- raise another error
|
||||
end
|
||||
local scoped xx = function (msg)
|
||||
local *toclose xx = function (msg)
|
||||
assert(msg == "not enough memory");
|
||||
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
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
@ -333,9 +333,9 @@ do
|
|||
local x = false
|
||||
local y = false
|
||||
local co = coroutine.create(function ()
|
||||
local scoped xv = function () x = true end
|
||||
local *toclose xv = function () x = true end
|
||||
do
|
||||
local scoped yv = function () y = true end
|
||||
local *toclose yv = function () y = true end
|
||||
coroutine.yield(100) -- yield doesn't close variable
|
||||
end
|
||||
coroutine.yield(200) -- yield doesn't close variable
|
||||
|
@ -353,7 +353,7 @@ end
|
|||
-- a suspended coroutine should not close its variables when collected
|
||||
local co
|
||||
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
|
||||
coroutine.yield()
|
||||
end)
|
||||
|
|
Loading…
Reference in New Issue