Răsfoiți Sursa

Added a warning system to Lua

The warning system is just a way for Lua to emit warnings, messages
to the programmer that do not interfere with the running program.
Roberto Ierusalimschy 6 ani în urmă
părinte
comite
437a5b07d4
10 a modificat fișierele cu 173 adăugiri și 15 ștergeri
  1. 18 0
      lapi.c
  2. 27 1
      lauxlib.c
  3. 8 0
      lbaselib.c
  4. 2 0
      lstate.c
  5. 2 0
      lstate.h
  6. 32 1
      ltests.c
  7. 14 0
      lua.h
  8. 48 8
      manual/manual.of
  9. 8 5
      testes/all.lua
  10. 14 0
      testes/api.lua

+ 18 - 0
lapi.c

@@ -1267,6 +1267,24 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
 }
 }
 
 
 
 
+void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
+  lua_lock(L);
+  G(L)->ud_warn = ud;
+  G(L)->warnf = f;
+  lua_unlock(L);
+}
+
+
+void lua_warning (lua_State *L, const char *msg) {
+  lua_WarnFunction wf = G(L)->warnf;
+  lua_lock(L);
+  if (wf != NULL)
+    wf(&G(L)->ud_warn, msg);
+  lua_unlock(L);
+}
+
+
+
 LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
 LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
   Udata *u;
   Udata *u;
   lua_lock(L);
   lua_lock(L);

+ 27 - 1
lauxlib.c

@@ -986,9 +986,35 @@ static int panic (lua_State *L) {
 }
 }
 
 
 
 
+/*
+** checks whether 'message' ends with end-of-line
+** (and therefore is the last part of a warning)
+*/
+static int islast (const char *message) {
+  size_t len = strlen(message);
+  return (len > 0 && message[len - 1] == '\n');
+}
+
+
+/*
+** Emit a warning. If '*pud' is NULL, previous message was to be
+** continued by the current one.
+*/
+static void warnf (void **pud, const char *message) {
+  if (*pud == NULL)  /* previous message was not the last? */
+    lua_writestringerror("%s", message);
+  else  /* start a new warning */
+    lua_writestringerror("Lua warning: %s", message);
+  *pud = (islast(message)) ? pud : NULL;
+}
+
+
 LUALIB_API lua_State *luaL_newstate (void) {
 LUALIB_API lua_State *luaL_newstate (void) {
   lua_State *L = lua_newstate(l_alloc, NULL);
   lua_State *L = lua_newstate(l_alloc, NULL);
-  if (L) lua_atpanic(L, &panic);
+  if (L) {
+    lua_atpanic(L, &panic);
+    lua_setwarnf(L, warnf, L);
+  }
   return L;
   return L;
 }
 }
 
 

+ 8 - 0
lbaselib.c

@@ -43,6 +43,13 @@ static int luaB_print (lua_State *L) {
 }
 }
 
 
 
 
+static int luaB_warn (lua_State *L) {
+  const char *msg = luaL_checkstring(L, 1);
+  lua_warning(L, msg);
+  return 0;
+}
+
+
 #define SPACECHARS	" \f\n\r\t\v"
 #define SPACECHARS	" \f\n\r\t\v"
 
 
 static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
 static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
@@ -482,6 +489,7 @@ static const luaL_Reg base_funcs[] = {
   {"pairs", luaB_pairs},
   {"pairs", luaB_pairs},
   {"pcall", luaB_pcall},
   {"pcall", luaB_pcall},
   {"print", luaB_print},
   {"print", luaB_print},
+  {"warn", luaB_warn},
   {"rawequal", luaB_rawequal},
   {"rawequal", luaB_rawequal},
   {"rawlen", luaB_rawlen},
   {"rawlen", luaB_rawlen},
   {"rawget", luaB_rawget},
   {"rawget", luaB_rawget},

+ 2 - 0
lstate.c

@@ -365,6 +365,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   L->next = NULL;
   L->next = NULL;
   g->frealloc = f;
   g->frealloc = f;
   g->ud = ud;
   g->ud = ud;
+  g->warnf = NULL;
+  g->ud_warn = NULL;
   g->mainthread = L;
   g->mainthread = L;
   g->seed = luai_makeseed(L);
   g->seed = luai_makeseed(L);
   g->gcrunning = 0;  /* no GC while building state */
   g->gcrunning = 0;  /* no GC while building state */

+ 2 - 0
lstate.h

@@ -231,6 +231,8 @@ typedef struct global_State {
   TString *tmname[TM_N];  /* array with tag-method names */
   TString *tmname[TM_N];  /* array with tag-method names */
   struct Table *mt[LUA_NUMTAGS];  /* metatables for basic types */
   struct Table *mt[LUA_NUMTAGS];  /* metatables for basic types */
   TString *strcache[STRCACHE_N][STRCACHE_M];  /* cache for strings in API */
   TString *strcache[STRCACHE_N][STRCACHE_M];  /* cache for strings in API */
+  lua_WarnFunction warnf;  /* warning function */
+  void *ud_warn;         /* auxiliary data to 'warnf' */
 } global_State;
 } global_State;
 
 
 
 

+ 32 - 1
ltests.c

@@ -63,10 +63,36 @@ static void pushobject (lua_State *L, const TValue *o) {
 }
 }
 
 
 
 
+static void badexit (void) {
+  /* avoid assertion failures when exiting */
+  l_memcontrol.numblocks = l_memcontrol.total = 0;
+  exit(EXIT_FAILURE);
+}
+
+
 static int tpanic (lua_State *L) {
 static int tpanic (lua_State *L) {
   fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
   fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
                    lua_tostring(L, -1));
                    lua_tostring(L, -1));
-  return (exit(EXIT_FAILURE), 0);  /* do not return to Lua */
+  return (badexit(), 0);  /* do not return to Lua */
+}
+
+
+static int islast (const char *message) {
+  size_t len = strlen(message);
+  return (len > 0 && message[len - 1] == '\n');
+}
+
+
+static void warnf (void **pud, const char *msg) {
+  if (*pud == NULL)  /* continuation line? */
+    printf("%s", msg);  /* print it */
+  else if (msg[0] == '*')  /* expected warning? */
+    printf("Expected Lua warning: %s", msg + 1);  /* print without the star */
+  else {  /* a real warning; should not happen during tests */
+    fprintf(stderr, "Warning in test mode (%s), aborting...\n", msg);
+    badexit();
+  }
+  *pud = islast(msg) ? pud : NULL;
 }
 }
 
 
 
 
@@ -1405,6 +1431,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
       const char *msg = getstring;
       const char *msg = getstring;
       printf("%s\n", msg);
       printf("%s\n", msg);
     }
     }
+    else if EQ("warning") {
+      const char *msg = getstring;
+      lua_warning(L1, msg);
+    }
     else if EQ("pushbool") {
     else if EQ("pushbool") {
       lua_pushboolean(L1, getnum);
       lua_pushboolean(L1, getnum);
     }
     }
@@ -1743,6 +1773,7 @@ static void checkfinalmem (void) {
 int luaB_opentests (lua_State *L) {
 int luaB_opentests (lua_State *L) {
   void *ud;
   void *ud;
   lua_atpanic(L, &tpanic);
   lua_atpanic(L, &tpanic);
+  lua_setwarnf(L, &warnf, L);
   atexit(checkfinalmem);
   atexit(checkfinalmem);
   lua_assert(lua_getallocf(L, &ud) == debug_realloc);
   lua_assert(lua_getallocf(L, &ud) == debug_realloc);
   lua_assert(ud == cast_voidp(&l_memcontrol));
   lua_assert(ud == cast_voidp(&l_memcontrol));

+ 14 - 0
lua.h

@@ -126,6 +126,13 @@ typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
 typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
 typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
 
 
 
 
+/*
+** Type for warning functions
+*/
+typedef void (*lua_WarnFunction) (void **pud, const char *msg);
+
+
+
 
 
 /*
 /*
 ** generic extra include file
 ** generic extra include file
@@ -299,6 +306,13 @@ LUA_API int (lua_isyieldable) (lua_State *L);
 #define lua_yield(L,n)		lua_yieldk(L, (n), 0, NULL)
 #define lua_yield(L,n)		lua_yieldk(L, (n), 0, NULL)
 
 
 
 
+/*
+** Warning-related functions
+*/
+LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
+LUA_API void (lua_warning)  (lua_State *L, const char *msg);
+
+
 /*
 /*
 ** garbage-collection function and options
 ** garbage-collection function and options
 */
 */

+ 48 - 8
manual/manual.of

@@ -1795,7 +1795,7 @@ Functions with any detectable difference
 (different behavior, different definition) are always different.
 (different behavior, different definition) are always different.
 Functions created at different times but with no detectable differences
 Functions created at different times but with no detectable differences
 may be classified as equal or not
 may be classified as equal or not
-(depending on internal cashing details).
+(depending on internal caching details).
 
 
 You can change the way that Lua compares tables and userdata
 You can change the way that Lua compares tables and userdata
 by using the @idx{__eq} metamethod @see{metatable}.
 by using the @idx{__eq} metamethod @see{metatable}.
@@ -4033,6 +4033,16 @@ for the @Q{newindex} event @see{metatable}.
 
 
 }
 }
 
 
+@APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);|
+@apii{1,0,-}
+
+Pops a value from the stack and sets it as
+the new @id{n}-th user value associated to the
+full userdata at the given index.
+Returns 0 if the userdata does not have that value.
+
+}
+
 @APIEntry{void lua_setmetatable (lua_State *L, int index);|
 @APIEntry{void lua_setmetatable (lua_State *L, int index);|
 @apii{1,0,-}
 @apii{1,0,-}
 
 
@@ -4066,13 +4076,13 @@ If @id{index} @N{is 0}, then all stack elements are removed.
 
 
 }
 }
 
 
-@APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);|
-@apii{1,0,-}
+@APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);|
+@apii{0,0,-}
 
 
-Pops a value from the stack and sets it as
-the new @id{n}-th user value associated to the
-full userdata at the given index.
-Returns 0 if the userdata does not have that value.
+Sets the @x{warning function} to be used by Lua to emit warnings
+@see{lua_WarnFunction}.
+The @id{ud} parameter initializes the slot @id{pud} passed to
+the warning function.
 
 
 }
 }
 
 
@@ -4334,6 +4344,30 @@ Returns the version number of this core.
 
 
 }
 }
 
 
+@APIEntry{
+typedef void (*lua_WarnFunction) (void **pud, const char *msg);|
+
+The type of @x{warning function}s, called by Lua to emit warnings.
+The first parameter is the address of a writable slot,
+constant for a given Lua state and
+initialized by @Lid{lua_setwarnf}.
+The second parameter is the warning message.
+This function should assume that
+a message not ending with an end-of-line will be
+continued by the message in the next call.
+
+}
+
+@APIEntry{
+void lua_warning (lua_State *L, const char *msg);|
+@apii{0,0,-}
+
+Emits a warning with the given message.
+A message not ending with an end-of-line should be
+continued in another call to this function.
+
+}
+
 @APIEntry{
 @APIEntry{
 typedef int (*lua_Writer) (lua_State *L,
 typedef int (*lua_Writer) (lua_State *L,
                            const void* p,
                            const void* p,
@@ -4345,7 +4379,7 @@ Every time it produces another piece of chunk,
 @Lid{lua_dump} calls the writer,
 @Lid{lua_dump} calls the writer,
 passing along the buffer to be written (@id{p}),
 passing along the buffer to be written (@id{p}),
 its size (@id{sz}),
 its size (@id{sz}),
-and the @id{data} parameter supplied to @Lid{lua_dump}.
+and the @id{ud} parameter supplied to @Lid{lua_dump}.
 
 
 The writer returns an error code:
 The writer returns an error code:
 @N{0 means} no errors;
 @N{0 means} no errors;
@@ -6261,6 +6295,12 @@ The current value of this variable is @St{Lua 5.4}.
 
 
 }
 }
 
 
+@LibEntry{warn (message)|
+
+Emits a warning with the given message.
+
+}
+
 @LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
 @LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
 
 
 This function is similar to @Lid{pcall},
 This function is similar to @Lid{pcall},

+ 8 - 5
testes/all.lua

@@ -5,8 +5,8 @@
 
 
 local version = "Lua 5.4"
 local version = "Lua 5.4"
 if _VERSION ~= version then
 if _VERSION ~= version then
-  io.stderr:write("\nThis test suite is for ", version, ", not for ", _VERSION,
-    "\nExiting tests\n")
+  warn(string.format(
+   "This test suite is for %s, not for %s\nExiting tests\n", version, _VERSION))
   return
   return
 end
 end
 
 
@@ -190,11 +190,10 @@ assert(dofile('verybig.lua', true) == 10); collectgarbage()
 dofile('files.lua')
 dofile('files.lua')
 
 
 if #msgs > 0 then
 if #msgs > 0 then
-  print("\ntests not performed:")
+  warn("*tests not performed:\n  ")
   for i=1,#msgs do
   for i=1,#msgs do
-    print(msgs[i])
+    warn(msgs[i]); warn("\n  ")
   end
   end
-  print()
 end
 end
 
 
 -- no test module should define 'debug'
 -- no test module should define 'debug'
@@ -220,6 +219,10 @@ local _G, showmem, print, format, clock, time, difftime, assert, open =
 local fname = T and "time-debug.txt" or "time.txt"
 local fname = T and "time-debug.txt" or "time.txt"
 local lasttime
 local lasttime
 
 
+
+warn("*This is "); warn("an expected"); warn(" warning\n")
+warn("*This is"); warn(" another one\n")
+
 if not usertests then
 if not usertests then
   -- open file with time of last performed test
   -- open file with time of last performed test
   local f = io.open(fname)
   local f = io.open(fname)

+ 14 - 0
testes/api.lua

@@ -111,6 +111,20 @@ do   -- testing 'rotate'
   tcheck(t, {10, 20, 30, 40})
   tcheck(t, {10, 20, 30, 40})
 end
 end
 
 
+
+-- testing warnings
+T.testC([[
+  warning "*This "
+  warning "warning "
+  warning "should be in a"
+  warning " single line
+"
+  warning "*This should be "
+  warning "another warning
+"
+]])
+
+
 -- testing message handlers
 -- testing message handlers
 do
 do
   local f = T.makeCfunc[[
   local f = T.makeCfunc[[