diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-26 14:16:17 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-26 14:16:17 -0200 |
commit | 7696c6474fe51ed59fee324e78c1233af74febdd (patch) | |
tree | d5e674cf214a14df38ca39b02177f5cea75b581c | |
parent | 7e63d3da0240325db4011f5d2f2e8abfb5d60288 (diff) | |
download | lua-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.c | 4 | ||||
-rw-r--r-- | lauxlib.c | 12 | ||||
-rw-r--r-- | testes/locals.lua | 56 |
3 files changed, 49 insertions, 23 deletions
@@ -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 */ |
@@ -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 | */ |
530 | static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { | 530 | static 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) |
243 | end | 243 | end |
244 | 244 | ||
245 | |||
245 | do -- errors in __close | 246 | do -- 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) |
265 | end | 266 | end |
266 | 267 | ||
268 | |||
267 | if rawget(_G, "T") then | 269 | if 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 |