aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-07 10:03:05 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-07 10:03:05 -0200
commitb8fed93215a23a3f443c5b0126f0de1725771b44 (patch)
tree1d7d29388b8a20fb7d0920d94b02a8040612314d
parent5e76a4fd313a8690d300085c4e8fcb9dca50c01a (diff)
downloadlua-b8fed93215a23a3f443c5b0126f0de1725771b44.tar.gz
lua-b8fed93215a23a3f443c5b0126f0de1725771b44.tar.bz2
lua-b8fed93215a23a3f443c5b0126f0de1725771b44.zip
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').
-rw-r--r--lapi.c2
-rw-r--r--lparser.c25
-rw-r--r--ltests.c4
-rw-r--r--lua.h2
-rw-r--r--testes/api.lua12
-rw-r--r--testes/files.lua6
-rw-r--r--testes/goto.lua2
-rw-r--r--testes/locals.lua42
8 files changed, 48 insertions, 47 deletions
diff --git a/lapi.c b/lapi.c
index 4fef43b7..91a6e389 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1207,7 +1207,7 @@ LUA_API int lua_next (lua_State *L, int idx) {
1207} 1207}
1208 1208
1209 1209
1210LUA_API void lua_tobeclosed (lua_State *L) { 1210LUA_API void lua_toclose (lua_State *L) {
1211 int nresults = L->ci->nresults; 1211 int nresults = L->ci->nresults;
1212 luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */ 1212 luaF_newtbcupval(L, L->top - 1); /* create new to-be-closed upvalue */
1213 if (!hastocloseCfunc(nresults)) /* function not marked yet? */ 1213 if (!hastocloseCfunc(nresults)) /* function not marked yet? */
diff --git a/lparser.c b/lparser.c
index a5b84aa1..e4f11cb6 100644
--- a/lparser.c
+++ b/lparser.c
@@ -1546,16 +1546,15 @@ static void localfunc (LexState *ls) {
1546} 1546}
1547 1547
1548 1548
1549static void commonlocalstat (LexState *ls, TString *firstvar) { 1549static void commonlocalstat (LexState *ls) {
1550 /* stat -> LOCAL NAME {',' NAME} ['=' explist] */ 1550 /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
1551 int nvars = 1; 1551 int nvars = 0;
1552 int nexps; 1552 int nexps;
1553 expdesc e; 1553 expdesc e;
1554 new_localvar(ls, firstvar); 1554 do {
1555 while (testnext(ls, ',')) {
1556 new_localvar(ls, str_checkname(ls)); 1555 new_localvar(ls, str_checkname(ls));
1557 nvars++; 1556 nvars++;
1558 } 1557 } while (testnext(ls, ','));
1559 if (testnext(ls, '=')) 1558 if (testnext(ls, '='))
1560 nexps = explist(ls, &e); 1559 nexps = explist(ls, &e);
1561 else { 1560 else {
@@ -1567,8 +1566,12 @@ static void commonlocalstat (LexState *ls, TString *firstvar) {
1567} 1566}
1568 1567
1569 1568
1570static void scopedlocalstat (LexState *ls) { 1569static void tocloselocalstat (LexState *ls) {
1571 FuncState *fs = ls->fs; 1570 FuncState *fs = ls->fs;
1571 TString *attr = str_checkname(ls);
1572 if (strcmp(getstr(attr), "toclose") != 0)
1573 luaK_semerror(ls,
1574 luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
1572 new_localvar(ls, str_checkname(ls)); 1575 new_localvar(ls, str_checkname(ls));
1573 checknext(ls, '='); 1576 checknext(ls, '=');
1574 exp1(ls, 0); 1577 exp1(ls, 0);
@@ -1580,13 +1583,11 @@ static void scopedlocalstat (LexState *ls) {
1580 1583
1581static void localstat (LexState *ls) { 1584static void localstat (LexState *ls) {
1582 /* stat -> LOCAL NAME {',' NAME} ['=' explist] 1585 /* stat -> LOCAL NAME {',' NAME} ['=' explist]
1583 | LOCAL SCOPED NAME '=' exp */ 1586 | LOCAL *toclose NAME '=' exp */
1584 TString *firstvar = str_checkname(ls); 1587 if (testnext(ls, '*'))
1585 if (ls->t.token == TK_NAME && 1588 tocloselocalstat(ls);
1586 eqshrstr(firstvar, luaS_newliteral(ls->L, "scoped")))
1587 scopedlocalstat(ls);
1588 else 1589 else
1589 commonlocalstat(ls, firstvar); 1590 commonlocalstat(ls);
1590} 1591}
1591 1592
1592 1593
diff --git a/ltests.c b/ltests.c
index aba44b87..192ae861 100644
--- a/ltests.c
+++ b/ltests.c
@@ -1550,8 +1550,8 @@ static struct X { int x; } x;
1550 int i = getindex; 1550 int i = getindex;
1551 return lua_yieldk(L1, nres, i, Cfunck); 1551 return lua_yieldk(L1, nres, i, Cfunck);
1552 } 1552 }
1553 else if EQ("tobeclosed") { 1553 else if EQ("toclose") {
1554 lua_tobeclosed(L); 1554 lua_toclose(L);
1555 } 1555 }
1556 else luaL_error(L, "unknown instruction %s", buff); 1556 else luaL_error(L, "unknown instruction %s", buff);
1557 } 1557 }
diff --git a/lua.h b/lua.h
index 16d685cc..fe468c8e 100644
--- a/lua.h
+++ b/lua.h
@@ -333,7 +333,7 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
333LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); 333LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
334LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); 334LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
335 335
336LUA_API void (lua_tobeclosed) (lua_State *L); 336LUA_API void (lua_toclose) (lua_State *L);
337 337
338 338
339/* 339/*
diff --git a/testes/api.lua b/testes/api.lua
index 988250f7..a6ddca8e 100644
--- a/testes/api.lua
+++ b/testes/api.lua
@@ -987,7 +987,7 @@ do
987 987
988 local a = T.testC([[ 988 local a = T.testC([[
989 call 0 1 # create resource 989 call 0 1 # create resource
990 tobeclosed # mark it to be closed 990 toclose # mark it to be closed
991 return 1 991 return 1
992 ]], newresource) 992 ]], newresource)
993 assert(a[1] == 11) 993 assert(a[1] == 11)
@@ -996,7 +996,7 @@ do
996 -- repeat the test, but calling function in a 'multret' context 996 -- repeat the test, but calling function in a 'multret' context
997 local a = {T.testC([[ 997 local a = {T.testC([[
998 call 0 1 # create resource 998 call 0 1 # create resource
999 tobeclosed # mark it to be closed 999 toclose # mark it to be closed
1000 return 2 1000 return 2
1001 ]], newresource)} 1001 ]], newresource)}
1002 assert(type(a[1]) == "string" and a[2][1] == 11) 1002 assert(type(a[1]) == "string" and a[2][1] == 11)
@@ -1005,7 +1005,7 @@ do
1005 -- error 1005 -- error
1006 local a, b = pcall(T.testC, [[ 1006 local a, b = pcall(T.testC, [[
1007 call 0 1 # create resource 1007 call 0 1 # create resource
1008 tobeclosed # mark it to be closed 1008 toclose # mark it to be closed
1009 error # resource is the error object 1009 error # resource is the error object
1010 ]], newresource) 1010 ]], newresource)
1011 assert(a == false and b[1] == 11) 1011 assert(a == false and b[1] == 11)
@@ -1019,10 +1019,10 @@ do
1019 local a = T.testC([[ 1019 local a = T.testC([[
1020 pushvalue 2 1020 pushvalue 2
1021 call 0 1 # create resource 1021 call 0 1 # create resource
1022 tobeclosed # mark it to be closed 1022 toclose # mark it to be closed
1023 pushvalue 2 1023 pushvalue 2
1024 call 0 1 # create another resource 1024 call 0 1 # create another resource
1025 tobeclosed # mark it to be closed 1025 toclose # mark it to be closed
1026 pushvalue 3 1026 pushvalue 3
1027 pushint 2 # there should be two open resources 1027 pushint 2 # there should be two open resources
1028 call 1 0 1028 call 1 0
@@ -1102,7 +1102,7 @@ end)
1102testamem("to-be-closed variables", function() 1102testamem("to-be-closed variables", function()
1103 local flag 1103 local flag
1104 do 1104 do
1105 local scoped x = function () flag = true end 1105 local *toclose x = function () flag = true end
1106 flag = false 1106 flag = false
1107 local x = {} 1107 local x = {}
1108 end 1108 end
diff --git a/testes/files.lua b/testes/files.lua
index a11c5e61..e68eb9b8 100644
--- a/testes/files.lua
+++ b/testes/files.lua
@@ -125,7 +125,7 @@ do
125 -- closing file by scope 125 -- closing file by scope
126 local F = nil 126 local F = nil
127 do 127 do
128 local scoped f = assert(io.open(file, "w")) 128 local *toclose f = assert(io.open(file, "w"))
129 F = f 129 F = f
130 end 130 end
131 assert(tostring(F) == "file (closed)") 131 assert(tostring(F) == "file (closed)")
@@ -135,7 +135,7 @@ assert(os.remove(file))
135 135
136do 136do
137 -- test writing/reading numbers 137 -- test writing/reading numbers
138 local scoped f = assert(io.open(file, "w")) 138 local *toclose f = assert(io.open(file, "w"))
139 f:write(maxint, '\n') 139 f:write(maxint, '\n')
140 f:write(string.format("0X%x\n", maxint)) 140 f:write(string.format("0X%x\n", maxint))
141 f:write("0xABCp-3", '\n') 141 f:write("0xABCp-3", '\n')
@@ -158,7 +158,7 @@ assert(os.remove(file))
158 158
159-- testing multiple arguments to io.read 159-- testing multiple arguments to io.read
160do 160do
161 local scoped f = assert(io.open(file, "w")) 161 local *toclose f = assert(io.open(file, "w"))
162 f:write[[ 162 f:write[[
163a line 163a line
164another line 164another line
diff --git a/testes/goto.lua b/testes/goto.lua
index 92f048fb..5d863a42 100644
--- a/testes/goto.lua
+++ b/testes/goto.lua
@@ -258,7 +258,7 @@ do
258 ::L2:: goto L3 258 ::L2:: goto L3
259 259
260 ::L1:: do 260 ::L1:: do
261 local scoped a = function () X = true end 261 local *toclose a = function () X = true end
262 assert(X == nil) 262 assert(X == nil)
263 if a then goto L2 end -- jumping back out of scope of 'a' 263 if a then goto L2 end -- jumping back out of scope of 'a'
264 end 264 end
diff --git a/testes/locals.lua b/testes/locals.lua
index 1e0f525b..869ac1ff 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -181,9 +181,9 @@ local function stack(n) n = ((n == 0) or stack(n - 1)) end
181do 181do
182 local a = {} 182 local a = {}
183 do 183 do
184 local scoped x = setmetatable({"x"}, {__close = function (self) 184 local *toclose x = setmetatable({"x"}, {__close = function (self)
185 a[#a + 1] = self[1] end}) 185 a[#a + 1] = self[1] end})
186 local scoped y = function (x) assert(x == nil); a[#a + 1] = "y" end 186 local *toclose y = function (x) assert(x == nil); a[#a + 1] = "y" end
187 a[#a + 1] = "in" 187 a[#a + 1] = "in"
188 end 188 end
189 a[#a + 1] = "out" 189 a[#a + 1] = "out"
@@ -197,7 +197,7 @@ do
197 197
198 -- closing functions do not corrupt returning values 198 -- closing functions do not corrupt returning values
199 local function foo (x) 199 local function foo (x)
200 local scoped _ = closescope 200 local *toclose _ = closescope
201 return x, X, 23 201 return x, X, 23
202 end 202 end
203 203
@@ -206,7 +206,7 @@ do
206 206
207 X = false 207 X = false
208 foo = function (x) 208 foo = function (x)
209 local scoped _ = closescope 209 local *toclose _ = closescope
210 local y = 15 210 local y = 15
211 return y 211 return y
212 end 212 end
@@ -215,7 +215,7 @@ do
215 215
216 X = false 216 X = false
217 foo = function () 217 foo = function ()
218 local scoped x = closescope 218 local *toclose x = closescope
219 return x 219 return x
220 end 220 end
221 221
@@ -228,13 +228,13 @@ do
228 -- to-be-closed variables must be closed in tail calls 228 -- to-be-closed variables must be closed in tail calls
229 local X, Y 229 local X, Y
230 local function foo () 230 local function foo ()
231 local scoped _ = function () Y = 10 end 231 local *toclose _ = function () Y = 10 end
232 assert(X == 20 and Y == nil) 232 assert(X == 20 and Y == nil)
233 return 1,2,3 233 return 1,2,3
234 end 234 end
235 235
236 local function bar () 236 local function bar ()
237 local scoped _ = function () X = 20 end 237 local *toclose _ = function () X = 20 end
238 return foo() 238 return foo()
239 end 239 end
240 240
@@ -245,11 +245,11 @@ end
245do -- errors in __close 245do -- errors in __close
246 local log = {} 246 local log = {}
247 local function foo (err) 247 local function foo (err)
248 local scoped x = function (msg) log[#log + 1] = msg; error(1) end 248 local *toclose x = function (msg) log[#log + 1] = msg; error(1) end
249 local scoped x1 = function (msg) log[#log + 1] = msg; end 249 local *toclose x1 = function (msg) log[#log + 1] = msg; end
250 local scoped gc = function () collectgarbage() end 250 local *toclose gc = function () collectgarbage() end
251 local scoped y = function (msg) log[#log + 1] = msg; error(2) end 251 local *toclose y = function (msg) log[#log + 1] = msg; error(2) end
252 local scoped z = function (msg) log[#log + 1] = msg or 10; error(3) end 252 local *toclose z = function (msg) log[#log + 1] = msg or 10; error(3) end
253 if err then error(4) end 253 if err then error(4) end
254 end 254 end
255 local stat, msg = pcall(foo, false) 255 local stat, msg = pcall(foo, false)
@@ -267,8 +267,8 @@ end
267if rawget(_G, "T") then 267if rawget(_G, "T") then
268 -- memory error inside closing function 268 -- memory error inside closing function
269 local function foo () 269 local function foo ()
270 local scoped y = function () T.alloccount() end 270 local *toclose y = function () T.alloccount() end
271 local scoped x = setmetatable({}, {__close = function () 271 local *toclose x = setmetatable({}, {__close = function ()
272 T.alloccount(0); local x = {} -- force a memory error 272 T.alloccount(0); local x = {} -- force a memory error
273 end}) 273 end})
274 error("a") -- common error inside the function's body 274 error("a") -- common error inside the function's body
@@ -294,7 +294,7 @@ if rawget(_G, "T") then
294 end 294 end
295 295
296 local function test () 296 local function test ()
297 local scoped x = enter(0) -- set a memory limit 297 local *toclose x = enter(0) -- set a memory limit
298 -- creation of previous upvalue will raise a memory error 298 -- creation of previous upvalue will raise a memory error
299 os.exit(false) -- should not run 299 os.exit(false) -- should not run
300 end 300 end
@@ -309,14 +309,14 @@ if rawget(_G, "T") then
309 309
310 -- repeat test with extra closing upvalues 310 -- repeat test with extra closing upvalues
311 local function test () 311 local function test ()
312 local scoped xxx = function (msg) 312 local *toclose xxx = function (msg)
313 assert(msg == "not enough memory"); 313 assert(msg == "not enough memory");
314 error(1000) -- raise another error 314 error(1000) -- raise another error
315 end 315 end
316 local scoped xx = function (msg) 316 local *toclose xx = function (msg)
317 assert(msg == "not enough memory"); 317 assert(msg == "not enough memory");
318 end 318 end
319 local scoped x = enter(0) -- set a memory limit 319 local *toclose x = enter(0) -- set a memory limit
320 -- creation of previous upvalue will raise a memory error 320 -- creation of previous upvalue will raise a memory error
321 os.exit(false) -- should not run 321 os.exit(false) -- should not run
322 end 322 end
@@ -333,9 +333,9 @@ do
333 local x = false 333 local x = false
334 local y = false 334 local y = false
335 local co = coroutine.create(function () 335 local co = coroutine.create(function ()
336 local scoped xv = function () x = true end 336 local *toclose xv = function () x = true end
337 do 337 do
338 local scoped yv = function () y = true end 338 local *toclose yv = function () y = true end
339 coroutine.yield(100) -- yield doesn't close variable 339 coroutine.yield(100) -- yield doesn't close variable
340 end 340 end
341 coroutine.yield(200) -- yield doesn't close variable 341 coroutine.yield(200) -- yield doesn't close variable
@@ -353,7 +353,7 @@ end
353-- a suspended coroutine should not close its variables when collected 353-- a suspended coroutine should not close its variables when collected
354local co 354local co
355co = coroutine.wrap(function() 355co = coroutine.wrap(function()
356 local scoped x = function () os.exit(false) end -- should not run 356 local *toclose x = function () os.exit(false) end -- should not run
357 co = nil 357 co = nil
358 coroutine.yield() 358 coroutine.yield()
359end) 359end)