1
0
mirror of https://github.com/lua/lua.git synced 2025-01-28 06:03:00 +08:00

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.
This commit is contained in:
Roberto Ierusalimschy 2018-12-28 15:42:34 -02:00
parent ba7da13ec5
commit 437a5b07d4
10 changed files with 173 additions and 15 deletions

18
lapi.c
View File

@ -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);

View File

@ -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;
} }

View File

@ -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},

View File

@ -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 */

View File

@ -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;

View File

@ -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
lua.h
View File

@ -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
*/ */

View File

@ -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);| @APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);|
@apii{1,0,-} @apii{0,0,-}
Pops a value from the stack and sets it as Sets the @x{warning function} to be used by Lua to emit warnings
the new @id{n}-th user value associated to the @see{lua_WarnFunction}.
full userdata at the given index. The @id{ud} parameter initializes the slot @id{pud} passed to
Returns 0 if the userdata does not have that value. 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},

View File

@ -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, warn(string.format(
"\nExiting tests\n") "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)

View File

@ -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[[