aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-26 14:16:17 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-26 14:16:17 -0200
commit7696c6474fe51ed59fee324e78c1233af74febdd (patch)
treed5e674cf214a14df38ca39b02177f5cea75b581c
parent7e63d3da0240325db4011f5d2f2e8abfb5d60288 (diff)
downloadlua-7696c6474fe51ed59fee324e78c1233af74febdd.tar.gz
lua-7696c6474fe51ed59fee324e78c1233af74febdd.tar.bz2
lua-7696c6474fe51ed59fee324e78c1233af74febdd.zip
Auxiliary buffer cannot close box with 'lua_remove'
To remove a to-be-closed variable from the stack in the C API a function must use 'lua_settop' or 'lua_pop'. Previous implementation of 'luaL_pushresult' was not closing the box. (This commit also added tests to check that box is being closed "as soon as possible".)
-rw-r--r--lapi.c4
-rw-r--r--lauxlib.c12
-rw-r--r--testes/locals.lua56
3 files changed, 49 insertions, 23 deletions
diff --git a/lapi.c b/lapi.c
index da866a66..147ed0ff 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1213,8 +1213,8 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
1213 lua_lock(L); 1213 lua_lock(L);
1214 o = index2stack(L, idx); 1214 o = index2stack(L, idx);
1215 nresults = L->ci->nresults; 1215 nresults = L->ci->nresults;
1216 api_check(L, L->openupval == NULL || uplevel(L->openupval) < o, 1216 api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
1217 "there is an already marked index below"); 1217 "marked index below or equal new one");
1218 luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ 1218 luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
1219 if (!hastocloseCfunc(nresults)) /* function not marked yet? */ 1219 if (!hastocloseCfunc(nresults)) /* function not marked yet? */
1220 L->ci->nresults = codeNresults(nresults); /* mark it */ 1220 L->ci->nresults = codeNresults(nresults); /* mark it */
diff --git a/lauxlib.c b/lauxlib.c
index 6069ed1b..fd4acbd1 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -524,8 +524,8 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
524 524
525/* 525/*
526** Returns a pointer to a free area with at least 'sz' bytes in buffer 526** Returns a pointer to a free area with at least 'sz' bytes in buffer
527** 'B'. 'boxidx' is the position in the stack where the buffer's box is 527** 'B'. 'boxidx' is the relative position in the stack where the
528** or should be. 528** buffer's box is or should be.
529*/ 529*/
530static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { 530static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
531 if (B->size - B->n >= sz) /* enough space? */ 531 if (B->size - B->n >= sz) /* enough space? */
@@ -538,8 +538,10 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
538 if (buffonstack(B)) /* buffer already has a box? */ 538 if (buffonstack(B)) /* buffer already has a box? */
539 newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ 539 newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
540 else { /* no box yet */ 540 else { /* no box yet */
541 lua_pushnil(L); /* reserve slot for final result */
541 newbox(L); /* create a new box */ 542 newbox(L); /* create a new box */
542 lua_insert(L, boxidx); /* move box to its intended position */ 543 /* move box (and slot) to its intended position */
544 lua_rotate(L, boxidx - 1, 2);
543 lua_toclose(L, boxidx); 545 lua_toclose(L, boxidx);
544 newbuff = (char *)resizebox(L, boxidx, newsize); 546 newbuff = (char *)resizebox(L, boxidx, newsize);
545 memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ 547 memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
@@ -576,8 +578,8 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
576 lua_State *L = B->L; 578 lua_State *L = B->L;
577 lua_pushlstring(L, B->b, B->n); 579 lua_pushlstring(L, B->b, B->n);
578 if (buffonstack(B)) { 580 if (buffonstack(B)) {
579 resizebox(L, -2, 0); /* delete old buffer */ 581 lua_copy(L, -1, -3); /* move string to reserved slot */
580 lua_remove(L, -2); /* remove its header from the stack */ 582 lua_pop(L, 2); /* pop string and box (closing the box) */
581 } 583 }
582} 584}
583 585
diff --git a/testes/locals.lua b/testes/locals.lua
index 8766b3d5..90a8b845 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -242,6 +242,7 @@ do
242 assert(a == 1 and b == 2 and c == 3 and X == 20 and Y == 10 and d == nil) 242 assert(a == 1 and b == 2 and c == 3 and X == 20 and Y == 10 and d == nil)
243end 243end
244 244
245
245do -- errors in __close 246do -- errors in __close
246 local log = {} 247 local log = {}
247 local function foo (err) 248 local function foo (err)
@@ -264,7 +265,9 @@ do -- errors in __close
264 and #log == 4) 265 and #log == 4)
265end 266end
266 267
268
267if rawget(_G, "T") then 269if rawget(_G, "T") then
270
268 -- memory error inside closing function 271 -- memory error inside closing function
269 local function foo () 272 local function foo ()
270 local *toclose y = function () T.alloccount() end 273 local *toclose y = function () T.alloccount() end
@@ -296,7 +299,7 @@ if rawget(_G, "T") then
296 local function test () 299 local function test ()
297 local *toclose x = enter(0) -- set a memory limit 300 local *toclose x = enter(0) -- set a memory limit
298 -- creation of previous upvalue will raise a memory error 301 -- creation of previous upvalue will raise a memory error
299 os.exit(false) -- should not run 302 assert(false) -- should not run
300 end 303 end
301 304
302 local _, msg = pcall(test) 305 local _, msg = pcall(test)
@@ -326,33 +329,54 @@ if rawget(_G, "T") then
326 329
327 330
328 do -- testing 'toclose' in C string buffer 331 do -- testing 'toclose' in C string buffer
329 local s = string.rep("a", 10000) 332 collectgarbage()
333 local s = string.rep('a', 10000) -- large string
334 local m = T.totalmem()
335 collectgarbage("stop")
336 s = string.upper(s) -- allocate buffer + new string (10K each)
337 -- ensure buffer was deallocated
338 assert(T.totalmem() - m <= 11000)
339 collectgarbage("restart")
340 end
341
342 do -- now some tests for freeing buffer in case of errors
343 local lim = 10000 -- some size larger than the static buffer
344 local extra = 2000 -- some extra memory (for callinfo, etc.)
345
346 local s = string.rep("a", lim)
347
348 -- concat this table needs two buffer resizes (one for each 's')
330 local a = {s, s} 349 local a = {s, s}
331 350
332 -- ensure proper initialization (stack space, metatable) 351 collectgarbage()
333 table.concat(a)
334 collectgarbage(); collectgarbage()
335 352
336 local m = T.totalmem() 353 m = T.totalmem()
354 collectgarbage("stop")
355
356 -- error in the first buffer allocation
357 T. totalmem(m + extra)
358 assert(not pcall(table.concat, a))
359 -- first buffer was not even allocated
360 assert(T.totalmem() - m <= extra)
337 361
338 -- error in the second buffer allocation 362 -- error in the second buffer allocation
339 T.alloccount(3) 363 T. totalmem(m + lim + extra)
340 assert(not pcall(table.concat, a)) 364 assert(not pcall(table.concat, a))
341 T.alloccount()
342 -- first buffer was released by 'toclose' 365 -- first buffer was released by 'toclose'
343 assert(T.totalmem() - m <= 5000) 366 assert(T.totalmem() - m <= extra)
344 367
345 -- error in creation of final string 368 -- error in creation of final string
346 T.alloccount(4) 369 T.totalmem(m + 2 * lim + extra)
347 assert(not pcall(table.concat, a)) 370 assert(not pcall(table.concat, a))
348 T.alloccount()
349 -- second buffer was released by 'toclose' 371 -- second buffer was released by 'toclose'
350 assert(T.totalmem() - m <= 5000) 372 assert(T.totalmem() - m <= extra)
351 373
352 -- userdata, upvalue, buffer, buffer, string 374 -- userdata, upvalue, buffer, buffer, final string
353 T.alloccount(5) 375 T.totalmem(m + 4*lim + extra)
354 assert(#table.concat(a) == 20000) 376 assert(#table.concat(a) == 2*lim)
355 T.alloccount() 377
378 T.totalmem(0) -- remove memory limit
379 collectgarbage("restart")
356 380
357 print'+' 381 print'+'
358 end 382 end