diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-28 15:42:34 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-28 15:42:34 -0200 |
commit | 437a5b07d415e1a74160ddfd804017171d6cc5cb (patch) | |
tree | 861f9a56ae175eaed91c163409c33ab85bee7ff9 | |
parent | ba7da13ec5938f978c37d63aa40a3e340b301f79 (diff) | |
download | lua-437a5b07d415e1a74160ddfd804017171d6cc5cb.tar.gz lua-437a5b07d415e1a74160ddfd804017171d6cc5cb.tar.bz2 lua-437a5b07d415e1a74160ddfd804017171d6cc5cb.zip |
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.
-rw-r--r-- | lapi.c | 18 | ||||
-rw-r--r-- | lauxlib.c | 28 | ||||
-rw-r--r-- | lbaselib.c | 8 | ||||
-rw-r--r-- | lstate.c | 2 | ||||
-rw-r--r-- | lstate.h | 2 | ||||
-rw-r--r-- | ltests.c | 33 | ||||
-rw-r--r-- | lua.h | 14 | ||||
-rw-r--r-- | manual/manual.of | 56 | ||||
-rw-r--r-- | testes/all.lua | 13 | ||||
-rw-r--r-- | testes/api.lua | 14 |
10 files changed, 173 insertions, 15 deletions
@@ -1267,6 +1267,24 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { | |||
1267 | } | 1267 | } |
1268 | 1268 | ||
1269 | 1269 | ||
1270 | void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { | ||
1271 | lua_lock(L); | ||
1272 | G(L)->ud_warn = ud; | ||
1273 | G(L)->warnf = f; | ||
1274 | lua_unlock(L); | ||
1275 | } | ||
1276 | |||
1277 | |||
1278 | void lua_warning (lua_State *L, const char *msg) { | ||
1279 | lua_WarnFunction wf = G(L)->warnf; | ||
1280 | lua_lock(L); | ||
1281 | if (wf != NULL) | ||
1282 | wf(&G(L)->ud_warn, msg); | ||
1283 | lua_unlock(L); | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | |||
1270 | LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { | 1288 | LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { |
1271 | Udata *u; | 1289 | Udata *u; |
1272 | lua_lock(L); | 1290 | lua_lock(L); |
@@ -986,9 +986,35 @@ static int panic (lua_State *L) { | |||
986 | } | 986 | } |
987 | 987 | ||
988 | 988 | ||
989 | /* | ||
990 | ** checks whether 'message' ends with end-of-line | ||
991 | ** (and therefore is the last part of a warning) | ||
992 | */ | ||
993 | static int islast (const char *message) { | ||
994 | size_t len = strlen(message); | ||
995 | return (len > 0 && message[len - 1] == '\n'); | ||
996 | } | ||
997 | |||
998 | |||
999 | /* | ||
1000 | ** Emit a warning. If '*pud' is NULL, previous message was to be | ||
1001 | ** continued by the current one. | ||
1002 | */ | ||
1003 | static void warnf (void **pud, const char *message) { | ||
1004 | if (*pud == NULL) /* previous message was not the last? */ | ||
1005 | lua_writestringerror("%s", message); | ||
1006 | else /* start a new warning */ | ||
1007 | lua_writestringerror("Lua warning: %s", message); | ||
1008 | *pud = (islast(message)) ? pud : NULL; | ||
1009 | } | ||
1010 | |||
1011 | |||
989 | LUALIB_API lua_State *luaL_newstate (void) { | 1012 | LUALIB_API lua_State *luaL_newstate (void) { |
990 | lua_State *L = lua_newstate(l_alloc, NULL); | 1013 | lua_State *L = lua_newstate(l_alloc, NULL); |
991 | if (L) lua_atpanic(L, &panic); | 1014 | if (L) { |
1015 | lua_atpanic(L, &panic); | ||
1016 | lua_setwarnf(L, warnf, L); | ||
1017 | } | ||
992 | return L; | 1018 | return L; |
993 | } | 1019 | } |
994 | 1020 | ||
@@ -43,6 +43,13 @@ static int luaB_print (lua_State *L) { | |||
43 | } | 43 | } |
44 | 44 | ||
45 | 45 | ||
46 | static int luaB_warn (lua_State *L) { | ||
47 | const char *msg = luaL_checkstring(L, 1); | ||
48 | lua_warning(L, msg); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | |||
46 | #define SPACECHARS " \f\n\r\t\v" | 53 | #define SPACECHARS " \f\n\r\t\v" |
47 | 54 | ||
48 | static const char *b_str2int (const char *s, int base, lua_Integer *pn) { | 55 | static const char *b_str2int (const char *s, int base, lua_Integer *pn) { |
@@ -482,6 +489,7 @@ static const luaL_Reg base_funcs[] = { | |||
482 | {"pairs", luaB_pairs}, | 489 | {"pairs", luaB_pairs}, |
483 | {"pcall", luaB_pcall}, | 490 | {"pcall", luaB_pcall}, |
484 | {"print", luaB_print}, | 491 | {"print", luaB_print}, |
492 | {"warn", luaB_warn}, | ||
485 | {"rawequal", luaB_rawequal}, | 493 | {"rawequal", luaB_rawequal}, |
486 | {"rawlen", luaB_rawlen}, | 494 | {"rawlen", luaB_rawlen}, |
487 | {"rawget", luaB_rawget}, | 495 | {"rawget", luaB_rawget}, |
@@ -365,6 +365,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
365 | L->next = NULL; | 365 | L->next = NULL; |
366 | g->frealloc = f; | 366 | g->frealloc = f; |
367 | g->ud = ud; | 367 | g->ud = ud; |
368 | g->warnf = NULL; | ||
369 | g->ud_warn = NULL; | ||
368 | g->mainthread = L; | 370 | g->mainthread = L; |
369 | g->seed = luai_makeseed(L); | 371 | g->seed = luai_makeseed(L); |
370 | g->gcrunning = 0; /* no GC while building state */ | 372 | g->gcrunning = 0; /* no GC while building state */ |
@@ -231,6 +231,8 @@ typedef struct global_State { | |||
231 | TString *tmname[TM_N]; /* array with tag-method names */ | 231 | TString *tmname[TM_N]; /* array with tag-method names */ |
232 | struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ | 232 | struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ |
233 | TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ | 233 | TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ |
234 | lua_WarnFunction warnf; /* warning function */ | ||
235 | void *ud_warn; /* auxiliary data to 'warnf' */ | ||
234 | } global_State; | 236 | } global_State; |
235 | 237 | ||
236 | 238 | ||
@@ -63,10 +63,36 @@ static void pushobject (lua_State *L, const TValue *o) { | |||
63 | } | 63 | } |
64 | 64 | ||
65 | 65 | ||
66 | static void badexit (void) { | ||
67 | /* avoid assertion failures when exiting */ | ||
68 | l_memcontrol.numblocks = l_memcontrol.total = 0; | ||
69 | exit(EXIT_FAILURE); | ||
70 | } | ||
71 | |||
72 | |||
66 | static int tpanic (lua_State *L) { | 73 | static int tpanic (lua_State *L) { |
67 | fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", | 74 | fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", |
68 | lua_tostring(L, -1)); | 75 | lua_tostring(L, -1)); |
69 | return (exit(EXIT_FAILURE), 0); /* do not return to Lua */ | 76 | return (badexit(), 0); /* do not return to Lua */ |
77 | } | ||
78 | |||
79 | |||
80 | static int islast (const char *message) { | ||
81 | size_t len = strlen(message); | ||
82 | return (len > 0 && message[len - 1] == '\n'); | ||
83 | } | ||
84 | |||
85 | |||
86 | static void warnf (void **pud, const char *msg) { | ||
87 | if (*pud == NULL) /* continuation line? */ | ||
88 | printf("%s", msg); /* print it */ | ||
89 | else if (msg[0] == '*') /* expected warning? */ | ||
90 | printf("Expected Lua warning: %s", msg + 1); /* print without the star */ | ||
91 | else { /* a real warning; should not happen during tests */ | ||
92 | fprintf(stderr, "Warning in test mode (%s), aborting...\n", msg); | ||
93 | badexit(); | ||
94 | } | ||
95 | *pud = islast(msg) ? pud : NULL; | ||
70 | } | 96 | } |
71 | 97 | ||
72 | 98 | ||
@@ -1405,6 +1431,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { | |||
1405 | const char *msg = getstring; | 1431 | const char *msg = getstring; |
1406 | printf("%s\n", msg); | 1432 | printf("%s\n", msg); |
1407 | } | 1433 | } |
1434 | else if EQ("warning") { | ||
1435 | const char *msg = getstring; | ||
1436 | lua_warning(L1, msg); | ||
1437 | } | ||
1408 | else if EQ("pushbool") { | 1438 | else if EQ("pushbool") { |
1409 | lua_pushboolean(L1, getnum); | 1439 | lua_pushboolean(L1, getnum); |
1410 | } | 1440 | } |
@@ -1743,6 +1773,7 @@ static void checkfinalmem (void) { | |||
1743 | int luaB_opentests (lua_State *L) { | 1773 | int luaB_opentests (lua_State *L) { |
1744 | void *ud; | 1774 | void *ud; |
1745 | lua_atpanic(L, &tpanic); | 1775 | lua_atpanic(L, &tpanic); |
1776 | lua_setwarnf(L, &warnf, L); | ||
1746 | atexit(checkfinalmem); | 1777 | atexit(checkfinalmem); |
1747 | lua_assert(lua_getallocf(L, &ud) == debug_realloc); | 1778 | lua_assert(lua_getallocf(L, &ud) == debug_realloc); |
1748 | lua_assert(ud == cast_voidp(&l_memcontrol)); | 1779 | lua_assert(ud == cast_voidp(&l_memcontrol)); |
@@ -126,6 +126,13 @@ typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); | |||
126 | typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); | 126 | typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); |
127 | 127 | ||
128 | 128 | ||
129 | /* | ||
130 | ** Type for warning functions | ||
131 | */ | ||
132 | typedef void (*lua_WarnFunction) (void **pud, const char *msg); | ||
133 | |||
134 | |||
135 | |||
129 | 136 | ||
130 | /* | 137 | /* |
131 | ** generic extra include file | 138 | ** generic extra include file |
@@ -300,6 +307,13 @@ LUA_API int (lua_isyieldable) (lua_State *L); | |||
300 | 307 | ||
301 | 308 | ||
302 | /* | 309 | /* |
310 | ** Warning-related functions | ||
311 | */ | ||
312 | LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); | ||
313 | LUA_API void (lua_warning) (lua_State *L, const char *msg); | ||
314 | |||
315 | |||
316 | /* | ||
303 | ** garbage-collection function and options | 317 | ** garbage-collection function and options |
304 | */ | 318 | */ |
305 | 319 | ||
diff --git a/manual/manual.of b/manual/manual.of index 044bd09c..196ea1ef 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -1795,7 +1795,7 @@ Functions with any detectable difference | |||
1795 | (different behavior, different definition) are always different. | 1795 | (different behavior, different definition) are always different. |
1796 | Functions created at different times but with no detectable differences | 1796 | Functions created at different times but with no detectable differences |
1797 | may be classified as equal or not | 1797 | may be classified as equal or not |
1798 | (depending on internal cashing details). | 1798 | (depending on internal caching details). |
1799 | 1799 | ||
1800 | You can change the way that Lua compares tables and userdata | 1800 | You can change the way that Lua compares tables and userdata |
1801 | by using the @idx{__eq} metamethod @see{metatable}. | 1801 | by using the @idx{__eq} metamethod @see{metatable}. |
@@ -4033,6 +4033,16 @@ for the @Q{newindex} event @see{metatable}. | |||
4033 | 4033 | ||
4034 | } | 4034 | } |
4035 | 4035 | ||
4036 | @APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);| | ||
4037 | @apii{1,0,-} | ||
4038 | |||
4039 | Pops a value from the stack and sets it as | ||
4040 | the new @id{n}-th user value associated to the | ||
4041 | full userdata at the given index. | ||
4042 | Returns 0 if the userdata does not have that value. | ||
4043 | |||
4044 | } | ||
4045 | |||
4036 | @APIEntry{void lua_setmetatable (lua_State *L, int index);| | 4046 | @APIEntry{void lua_setmetatable (lua_State *L, int index);| |
4037 | @apii{1,0,-} | 4047 | @apii{1,0,-} |
4038 | 4048 | ||
@@ -4066,13 +4076,13 @@ If @id{index} @N{is 0}, then all stack elements are removed. | |||
4066 | 4076 | ||
4067 | } | 4077 | } |
4068 | 4078 | ||
4069 | @APIEntry{int lua_setiuservalue (lua_State *L, int index, int n);| | 4079 | @APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);| |
4070 | @apii{1,0,-} | 4080 | @apii{0,0,-} |
4071 | 4081 | ||
4072 | Pops a value from the stack and sets it as | 4082 | Sets the @x{warning function} to be used by Lua to emit warnings |
4073 | the new @id{n}-th user value associated to the | 4083 | @see{lua_WarnFunction}. |
4074 | full userdata at the given index. | 4084 | The @id{ud} parameter initializes the slot @id{pud} passed to |
4075 | Returns 0 if the userdata does not have that value. | 4085 | the warning function. |
4076 | 4086 | ||
4077 | } | 4087 | } |
4078 | 4088 | ||
@@ -4335,6 +4345,30 @@ Returns the version number of this core. | |||
4335 | } | 4345 | } |
4336 | 4346 | ||
4337 | @APIEntry{ | 4347 | @APIEntry{ |
4348 | typedef void (*lua_WarnFunction) (void **pud, const char *msg);| | ||
4349 | |||
4350 | The type of @x{warning function}s, called by Lua to emit warnings. | ||
4351 | The first parameter is the address of a writable slot, | ||
4352 | constant for a given Lua state and | ||
4353 | initialized by @Lid{lua_setwarnf}. | ||
4354 | The second parameter is the warning message. | ||
4355 | This function should assume that | ||
4356 | a message not ending with an end-of-line will be | ||
4357 | continued by the message in the next call. | ||
4358 | |||
4359 | } | ||
4360 | |||
4361 | @APIEntry{ | ||
4362 | void lua_warning (lua_State *L, const char *msg);| | ||
4363 | @apii{0,0,-} | ||
4364 | |||
4365 | Emits a warning with the given message. | ||
4366 | A message not ending with an end-of-line should be | ||
4367 | continued in another call to this function. | ||
4368 | |||
4369 | } | ||
4370 | |||
4371 | @APIEntry{ | ||
4338 | typedef int (*lua_Writer) (lua_State *L, | 4372 | typedef int (*lua_Writer) (lua_State *L, |
4339 | const void* p, | 4373 | const void* p, |
4340 | size_t sz, | 4374 | size_t sz, |
@@ -4345,7 +4379,7 @@ Every time it produces another piece of chunk, | |||
4345 | @Lid{lua_dump} calls the writer, | 4379 | @Lid{lua_dump} calls the writer, |
4346 | passing along the buffer to be written (@id{p}), | 4380 | passing along the buffer to be written (@id{p}), |
4347 | its size (@id{sz}), | 4381 | its size (@id{sz}), |
4348 | and the @id{data} parameter supplied to @Lid{lua_dump}. | 4382 | and the @id{ud} parameter supplied to @Lid{lua_dump}. |
4349 | 4383 | ||
4350 | The writer returns an error code: | 4384 | The writer returns an error code: |
4351 | @N{0 means} no errors; | 4385 | @N{0 means} no errors; |
@@ -6261,6 +6295,12 @@ The current value of this variable is @St{Lua 5.4}. | |||
6261 | 6295 | ||
6262 | } | 6296 | } |
6263 | 6297 | ||
6298 | @LibEntry{warn (message)| | ||
6299 | |||
6300 | Emits a warning with the given message. | ||
6301 | |||
6302 | } | ||
6303 | |||
6264 | @LibEntry{xpcall (f, msgh [, arg1, @Cdots])| | 6304 | @LibEntry{xpcall (f, msgh [, arg1, @Cdots])| |
6265 | 6305 | ||
6266 | This function is similar to @Lid{pcall}, | 6306 | This function is similar to @Lid{pcall}, |
diff --git a/testes/all.lua b/testes/all.lua index 84ba80a6..bde4195e 100644 --- a/testes/all.lua +++ b/testes/all.lua | |||
@@ -5,8 +5,8 @@ | |||
5 | 5 | ||
6 | local version = "Lua 5.4" | 6 | local version = "Lua 5.4" |
7 | if _VERSION ~= version then | 7 | if _VERSION ~= version then |
8 | io.stderr:write("\nThis test suite is for ", version, ", not for ", _VERSION, | 8 | warn(string.format( |
9 | "\nExiting tests\n") | 9 | "This test suite is for %s, not for %s\nExiting tests\n", version, _VERSION)) |
10 | return | 10 | return |
11 | end | 11 | end |
12 | 12 | ||
@@ -190,11 +190,10 @@ assert(dofile('verybig.lua', true) == 10); collectgarbage() | |||
190 | dofile('files.lua') | 190 | dofile('files.lua') |
191 | 191 | ||
192 | if #msgs > 0 then | 192 | if #msgs > 0 then |
193 | print("\ntests not performed:") | 193 | warn("*tests not performed:\n ") |
194 | for i=1,#msgs do | 194 | for i=1,#msgs do |
195 | print(msgs[i]) | 195 | warn(msgs[i]); warn("\n ") |
196 | end | 196 | end |
197 | print() | ||
198 | end | 197 | end |
199 | 198 | ||
200 | -- no test module should define 'debug' | 199 | -- no test module should define 'debug' |
@@ -220,6 +219,10 @@ local _G, showmem, print, format, clock, time, difftime, assert, open = | |||
220 | local fname = T and "time-debug.txt" or "time.txt" | 219 | local fname = T and "time-debug.txt" or "time.txt" |
221 | local lasttime | 220 | local lasttime |
222 | 221 | ||
222 | |||
223 | warn("*This is "); warn("an expected"); warn(" warning\n") | ||
224 | warn("*This is"); warn(" another one\n") | ||
225 | |||
223 | if not usertests then | 226 | if not usertests then |
224 | -- open file with time of last performed test | 227 | -- open file with time of last performed test |
225 | local f = io.open(fname) | 228 | local f = io.open(fname) |
diff --git a/testes/api.lua b/testes/api.lua index 6f35e132..b4d63866 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
@@ -111,6 +111,20 @@ do -- testing 'rotate' | |||
111 | tcheck(t, {10, 20, 30, 40}) | 111 | tcheck(t, {10, 20, 30, 40}) |
112 | end | 112 | end |
113 | 113 | ||
114 | |||
115 | -- testing warnings | ||
116 | T.testC([[ | ||
117 | warning "*This " | ||
118 | warning "warning " | ||
119 | warning "should be in a" | ||
120 | warning " single line | ||
121 | " | ||
122 | warning "*This should be " | ||
123 | warning "another warning | ||
124 | " | ||
125 | ]]) | ||
126 | |||
127 | |||
114 | -- testing message handlers | 128 | -- testing message handlers |
115 | do | 129 | do |
116 | local f = T.makeCfunc[[ | 130 | local f = T.makeCfunc[[ |