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 |
