Browse Source

String buffer using to-be-closed variable

The string buffers in the C API now mark their boxes as to-be-closed
variables, to release their buffers in case of errors.
Roberto Ierusalimschy 6 years ago
parent
commit
8cb84210ab
2 changed files with 47 additions and 11 deletions
  1. 15 11
      lauxlib.c
  2. 32 0
      testes/locals.lua

+ 15 - 11
lauxlib.c

@@ -470,10 +470,8 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
   lua_Alloc allocf = lua_getallocf(L, &ud);
   lua_Alloc allocf = lua_getallocf(L, &ud);
   UBox *box = (UBox *)lua_touserdata(L, idx);
   UBox *box = (UBox *)lua_touserdata(L, idx);
   void *temp = allocf(ud, box->box, box->bsize, newsize);
   void *temp = allocf(ud, box->box, box->bsize, newsize);
-  if (temp == NULL && newsize > 0) {  /* allocation error? */
-    resizebox(L, idx, 0);  /* free buffer */
+  if (temp == NULL && newsize > 0)  /* allocation error? */
     luaL_error(L, "not enough memory for buffer allocation");
     luaL_error(L, "not enough memory for buffer allocation");
-  }
   box->box = temp;
   box->box = temp;
   box->bsize = newsize;
   box->bsize = newsize;
   return temp;
   return temp;
@@ -486,16 +484,20 @@ static int boxgc (lua_State *L) {
 }
 }
 
 
 
 
-static void *newbox (lua_State *L, size_t newsize) {
+static const luaL_Reg boxmt[] = {  /* box metamethods */
+  {"__gc", boxgc},
+  {"__close", boxgc},
+  {NULL, NULL}
+};
+
+
+static void newbox (lua_State *L) {
   UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
   UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
   box->box = NULL;
   box->box = NULL;
   box->bsize = 0;
   box->bsize = 0;
-  if (luaL_newmetatable(L, "_UBOX*")) {  /* creating metatable? */
-    lua_pushcfunction(L, boxgc);
-    lua_setfield(L, -2, "__gc");  /* metatable.__gc = boxgc */
-  }
+  if (luaL_newmetatable(L, "_UBOX*"))  /* creating metatable? */
+    luaL_setfuncs(L, boxmt, 0);  /* set its metamethods */
   lua_setmetatable(L, -2);
   lua_setmetatable(L, -2);
-  return resizebox(L, -1, newsize);
 }
 }
 
 
 
 
@@ -536,9 +538,11 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
     if (buffonstack(B))  /* buffer already has a box? */
     if (buffonstack(B))  /* buffer already has a box? */
       newbuff = (char *)resizebox(L, boxidx, newsize);  /* resize it */
       newbuff = (char *)resizebox(L, boxidx, newsize);  /* resize it */
     else {  /* no box yet */
     else {  /* no box yet */
-      newbuff = (char *)newbox(L, newsize);  /* create a new box */
-      memcpy(newbuff, B->b, B->n * sizeof(char));  /* copy original content */
+      newbox(L);  /* create a new box */
       lua_insert(L, boxidx);  /* move box to its intended position */
       lua_insert(L, boxidx);  /* move box to its intended position */
+      lua_toclose(L, boxidx);
+      newbuff = (char *)resizebox(L, boxidx, newsize);
+      memcpy(newbuff, B->b, B->n * sizeof(char));  /* copy original content */
     }
     }
     B->b = newbuff;
     B->b = newbuff;
     B->size = newsize;
     B->size = newsize;

+ 32 - 0
testes/locals.lua

@@ -324,6 +324,38 @@ if rawget(_G, "T") then
   local _, msg = pcall(test)
   local _, msg = pcall(test)
   assert(msg == 1000)
   assert(msg == 1000)
 
 
+
+  do    -- testing 'toclose' in C string buffer
+    local s = string.rep("a", 10000)
+    local a = {s, s}
+
+    -- ensure proper initialization (stack space, metatable)
+    table.concat(a)
+    collectgarbage(); collectgarbage()
+
+    local m = T.totalmem()
+
+    -- error in the second buffer allocation
+    T.alloccount(3)
+    assert(not pcall(table.concat, a))
+    T.alloccount()
+    -- first buffer was released by 'toclose'
+    assert(T.totalmem() - m <= 5000)
+
+    -- error in creation of final string
+    T.alloccount(4)
+    assert(not pcall(table.concat, a))
+    T.alloccount()
+    -- second buffer was released by 'toclose'
+    assert(T.totalmem() - m <= 5000)
+
+    -- userdata, upvalue, buffer, buffer, string
+    T.alloccount(5)
+    assert(#table.concat(a) == 20000)
+    T.alloccount()
+
+    print'+'
+  end
 end
 end