Browse Source

No errors in 'luaO_pushvfstring'

Any call to 'va_start' must have a corresponding call to 'va_end';
so, functions called between them (luaO_pushvfstring in particular)
cannot raise errors.
Roberto Ierusalimschy 10 months ago
parent
commit
20d42ccaae
5 changed files with 45 additions and 24 deletions
  1. 2 0
      lapi.c
  2. 1 1
      lauxlib.c
  3. 2 1
      ldebug.c
  4. 24 13
      lobject.c
  5. 16 9
      manual/manual.of

+ 2 - 0
lapi.c

@@ -587,6 +587,8 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
   ret = luaO_pushvfstring(L, fmt, argp);
   va_end(argp);
   luaC_checkGC(L);
+  if (ret == NULL)  /* error? */
+    luaD_throw(L, LUA_ERRMEM);
   lua_unlock(L);
   return ret;
 }

+ 1 - 1
lauxlib.c

@@ -225,7 +225,7 @@ LUALIB_API void luaL_where (lua_State *L, int level) {
 /*
 ** Again, the use of 'lua_pushvfstring' ensures this function does
 ** not need reserved stack space when called. (At worst, it generates
-** an error with "stack overflow" instead of the given message.)
+** a memory error instead of the given message.)
 */
 LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
   va_list argp;

+ 2 - 1
ldebug.c

@@ -847,7 +847,8 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
   va_start(argp, fmt);
   msg = luaO_pushvfstring(L, fmt, argp);  /* format message */
   va_end(argp);
-  if (isLua(ci)) {  /* if Lua function, add source:line information */
+  if (msg != NULL && isLua(ci)) {  /* Lua function? (and no error) */
+    /* add source:line information */
     luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
     setobjs2s(L, L->top.p - 2, L->top.p - 1);  /* remove 'msg' */
     L->top.p--;

+ 24 - 13
lobject.c

@@ -480,7 +480,7 @@ void luaO_tostring (lua_State *L, TValue *obj) {
 #define BUFVFS		cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
 
 /*
-** Buffer used by 'luaO_pushvfstring'. 'err' signals any error while
+** Buffer used by 'luaO_pushvfstring'. 'err' signals an error while
 ** building result (memory error [1] or buffer overflow [2]).
 */
 typedef struct BuffFS {
@@ -512,9 +512,14 @@ static void pushbuff (lua_State *L, void *ud) {
     case 1:
       luaD_throw(L, LUA_ERRMEM);
       break;
-    case 2:
-      luaG_runerror(L, "buffer overflow");
-      break;
+    case 2:  /* length overflow: Add "..." at the end of result */
+      if (buff->buffsize - buff->blen < 3)
+        strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */
+      else {  /* there is enough space left for the "..." */
+        strcpy(buff->b + buff->blen, "...");
+        buff->blen += 3;
+      }
+      /* FALLTHROUGH */
     default: {  /* no errors */
       TString *ts = luaS_newlstr(L, buff->b, buff->blen);
       setsvalue2s(L, L->top.p, ts);
@@ -527,8 +532,10 @@ static void pushbuff (lua_State *L, void *ud) {
 static const char *clearbuff (BuffFS *buff) {
   lua_State *L = buff->L;
   const char *res;
-  pushbuff(L, buff);
-  res = getstr(tsvalue(s2v(L->top.p - 1)));
+  if (luaD_rawrunprotected(L, pushbuff, buff) != LUA_OK)  /* errors? */
+    res = NULL;  /* error message is on the top of the stack */
+  else
+    res = getstr(tsvalue(s2v(L->top.p - 1)));
   if (buff->b != buff->space)  /* using dynamic buffer? */
     luaM_freearray(L, buff->b, buff->buffsize);  /* free it */
   return res;
@@ -536,12 +543,14 @@ static const char *clearbuff (BuffFS *buff) {
 
 
 static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
+  size_t left = buff->buffsize - buff->blen;  /* space left in the buffer */
   if (buff->err)  /* do nothing else after an error */
     return;
-  if (slen > buff->buffsize - buff->blen) {
-    /* new string doesn't fit into current buffer */
+  if (slen > left) {  /* new string doesn't fit into current buffer? */
     if (slen > ((MAX_SIZE/2) - buff->blen)) {  /* overflow? */
-      buff->err = 2;
+      memcpy(buff->b + buff->blen, str, left);  /* copy what it can */
+      buff->blen = buff->buffsize;
+      buff->err = 2;  /* doesn't add anything else */
       return;
     }
     else {
@@ -552,13 +561,13 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
         : luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize,
                                                                char);
       if (newb == NULL) {  /* allocation error? */
-        buff->err = 1;
+        buff->err = 1;  /* signal a memory error */
         return;
       }
-      if (buff->b == buff->space)
+      if (buff->b == buff->space)  /* new buffer (not reallocated)? */
         memcpy(newb, buff->b, buff->blen);  /* copy previous content */
-      buff->b = newb;
-      buff->buffsize = newsize;
+      buff->b = newb;  /* set new (larger) buffer... */
+      buff->buffsize = newsize;  /* ...and its new size */
     }
   }
   memcpy(buff->b + buff->blen, str, slen);  /* copy new content */
@@ -651,6 +660,8 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
   va_start(argp, fmt);
   msg = luaO_pushvfstring(L, fmt, argp);
   va_end(argp);
+  if (msg == NULL)  /* error? */
+    luaD_throw(L, LUA_ERRMEM);
   return msg;
 }
 

+ 16 - 9
manual/manual.of

@@ -3974,7 +3974,7 @@ Lua will call @id{falloc} before raising the error.
 
 
 @APIEntry{const char *lua_pushfstring (lua_State *L, const char *fmt, ...);|
-@apii{0,1,v}
+@apii{0,1,m}
 
 Pushes onto the stack a formatted string
 and returns a pointer to this string @see{constchar}.
@@ -3997,9 +3997,6 @@ The conversion specifiers can only be
 @Char{%c} (inserts an @T{int} as a one-byte character), and
 @Char{%U} (inserts an @T{unsigned long} as a @x{UTF-8} byte sequence).
 
-This function may raise errors due to memory overflow
-or an invalid conversion specifier.
-
 }
 
 @APIEntry{void lua_pushglobaltable (lua_State *L);|
@@ -4104,10 +4101,14 @@ onto the stack.
 const char *lua_pushvfstring (lua_State *L,
                               const char *fmt,
                               va_list argp);|
-@apii{0,1,v}
+@apii{0,1,-}
 
-Equivalent to @Lid{lua_pushfstring}, except that it receives a @id{va_list}
-instead of a variable number of arguments.
+Equivalent to @Lid{lua_pushfstring},
+except that it receives a @id{va_list}
+instead of a variable number of arguments,
+and it does not raise errors.
+Instead, in case of errors it pushes the error message
+and returns @id{NULL}.
 
 }
 
@@ -5636,6 +5637,7 @@ It is defined as the following macro:
 }
 It @N{returns 0} (@Lid{LUA_OK}) if there are no errors,
 or 1 in case of errors.
+(Except for out-of-memory errors, which are raised.)
 
 }
 
@@ -5800,7 +5802,7 @@ The first line in the file is ignored if it starts with a @T{#}.
 
 The string @id{mode} works as in the function @Lid{lua_load}.
 
-This function returns the same results as @Lid{lua_load}
+This function returns the same results as @Lid{lua_load},
 or @Lid{LUA_ERRFILE} for file-related errors.
 
 As @Lid{lua_load}, this function only loads the chunk;
@@ -9260,7 +9262,7 @@ the script is compiled as a variadic function.
 In interactive mode,
 Lua repeatedly prompts and waits for a line.
 After reading a line,
-Lua first try to interpret the line as an expression.
+Lua first tries to interpret the line as an expression.
 If it succeeds, it prints its value.
 Otherwise, it interprets the line as a chunk.
 If you write an incomplete chunk,
@@ -9424,6 +9426,11 @@ instead, there is a new option @Lid{LUA_GCPARAM} to that end.
 Moreover, there were some changes in the parameters themselves.
 }
 
+@item{
+The function @Lid{lua_pushvfstring} now reports errors,
+instead of raising them.
+}
+
 }
 
 }