Changes in the warning system

- The warning functions get an extra parameter that tells whether
message is to be continued (instead of using end-of-lines as a signal).

- The user data for the warning function is a regular value, instead
of a writable slot inside the Lua state.
This commit is contained in:
Roberto Ierusalimschy 2019-03-14 15:30:54 -03:00
parent 9eca305e75
commit b56d4e570a
12 changed files with 79 additions and 98 deletions

4
lapi.c
View File

@ -1286,9 +1286,9 @@ void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
} }
void lua_warning (lua_State *L, const char *msg) { void lua_warning (lua_State *L, const char *msg, int tocont) {
lua_lock(L); lua_lock(L);
luaE_warning(L, msg); luaE_warning(L, msg, tocont);
lua_unlock(L); lua_unlock(L);
} }

View File

@ -987,33 +987,29 @@ static int panic (lua_State *L) {
/* /*
** checks whether 'message' ends with end-of-line ** Emit a warning. '*previoustocont' signals whether previous message
** (and therefore is the last part of a warning) ** was to be continued by the current one.
*/ */
static int islast (const char *message) { static void warnf (void *ud, const char *message, int tocont) {
size_t len = strlen(message); int *previoustocont = (int *)ud;
return (len > 0 && message[len - 1] == '\n'); if (!*previoustocont) /* previous message was the last? */
} lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
lua_writestringerror("%s", message); /* write message */
if (!tocont) /* is this the last part? */
/* lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
** Emit a warning. If '*pud' is NULL, previous message was to be *previoustocont = tocont;
** 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) { if (L) {
int *previoustocont; /* space for warning state */
lua_atpanic(L, &panic); lua_atpanic(L, &panic);
lua_setwarnf(L, warnf, L); previoustocont = (int *)lua_newuserdatauv(L, sizeof(int), 0);
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*previoustocont = 0; /* next message starts a new warning */
lua_setwarnf(L, warnf, previoustocont);
} }
return L; return L;
} }

View File

@ -45,7 +45,7 @@ static int luaB_print (lua_State *L) {
static int luaB_warn (lua_State *L) { static int luaB_warn (lua_State *L) {
const char *msg = luaL_checkstring(L, 1); const char *msg = luaL_checkstring(L, 1);
lua_warning(L, msg); lua_warning(L, msg, lua_toboolean(L, 2));
return 0; return 0;
} }

8
lgc.c
View File

@ -832,7 +832,7 @@ static void GCTM (lua_State *L) {
lua_assert(!g->gcemergency); lua_assert(!g->gcemergency);
setgcovalue(L, &v, udata2finalize(g)); setgcovalue(L, &v, udata2finalize(g));
tm = luaT_gettmbyobj(L, &v, TM_GC); tm = luaT_gettmbyobj(L, &v, TM_GC);
if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */ if (ttisfunction(tm)) { /* is the finalizer a function? */
int status; int status;
lu_byte oldah = L->allowhook; lu_byte oldah = L->allowhook;
int running = g->gcrunning; int running = g->gcrunning;
@ -850,9 +850,9 @@ static void GCTM (lua_State *L) {
const char *msg = (ttisstring(s2v(L->top - 1))) const char *msg = (ttisstring(s2v(L->top - 1)))
? svalue(s2v(L->top - 1)) ? svalue(s2v(L->top - 1))
: "error object is not a string"; : "error object is not a string";
luaE_warning(L, "error in __gc metamethod ("); luaE_warning(L, "error in __gc metamethod (", 1);
luaE_warning(L, msg); luaE_warning(L, msg, 1);
luaE_warning(L, ")\n"); luaE_warning(L, ")", 0);
} }
} }
} }

View File

@ -411,10 +411,10 @@ LUA_API void lua_close (lua_State *L) {
} }
void luaE_warning (lua_State *L, const char *msg) { void luaE_warning (lua_State *L, const char *msg, int tocont) {
lua_WarnFunction wf = G(L)->warnf; lua_WarnFunction wf = G(L)->warnf;
if (wf != NULL) if (wf != NULL)
wf(&G(L)->ud_warn, msg); wf(G(L)->ud_warn, msg, tocont);
} }

View File

@ -317,7 +317,7 @@ LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_enterCcall (lua_State *L); LUAI_FUNC void luaE_enterCcall (lua_State *L);
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
#define luaE_exitCcall(L) ((L)->nCcalls--) #define luaE_exitCcall(L) ((L)->nCcalls--)

View File

@ -63,11 +63,8 @@ static void pushobject (lua_State *L, const TValue *o) {
} }
static void badexit (const char *fmt, ...) { static void badexit (const char *fmt, const char *s) {
va_list argp; fprintf(stderr, fmt, s);
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
/* avoid assertion failures when exiting */ /* avoid assertion failures when exiting */
l_memcontrol.numblocks = l_memcontrol.total = 0; l_memcontrol.numblocks = l_memcontrol.total = 0;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -81,52 +78,35 @@ static int tpanic (lua_State *L) {
} }
static int islast (const char *message) {
size_t len = strlen(message);
return (len > 0 && message[len - 1] == '\n');
}
/* /*
** Warning function for tests. Fist, it concatenates all parts of ** Warning function for tests. Fist, it concatenates all parts of
** a warning in buffer 'buff'. Then: ** a warning in buffer 'buff'. Then:
** messages starting with '#' are shown on standard output (used to ** - messages starting with '#' are shown on standard output (used to
** test explicit warnings); ** test explicit warnings);
** messages containing '@' are stored in global '_WARN' (used to test ** - messages containing '@' are stored in global '_WARN' (used to test
** errors that generate warnings); ** errors that generate warnings);
** other messages abort the tests (they represent real warning conditions; ** - other messages abort the tests (they represent real warning
** the standard tests should not generate these conditions unexpectedly). ** conditions; the standard tests should not generate these conditions
** unexpectedly).
*/ */
static void warnf (void **pud, const char *msg) { static void warnf (void *ud, const char *msg, int tocont) {
static char buff[200]; /* should be enough for tests... */ static char buff[200] = ""; /* should be enough for tests... */
static int cont = 0; /* message to be continued */ if (strlen(msg) >= sizeof(buff) - strlen(buff))
if (cont) { /* continuation? */ badexit("%s", "warnf-buffer overflow");
if (strlen(msg) >= sizeof(buff) - strlen(buff)) strcat(buff, msg); /* add new message to current warning */
badexit("warnf-buffer overflow"); if (!tocont) { /* message finished? */
strcat(buff, msg); /* add new message to current warning */
}
else { /* new warning */
if (strlen(msg) >= sizeof(buff))
badexit("warnf-buffer overflow");
strcpy(buff, msg); /* start a new warning */
}
if (!islast(msg)) /* message not finished yet? */
cont = 1; /* wait for more */
else { /* handle message */
cont = 0; /* prepare for next message */
if (buff[0] == '#') /* expected warning? */ if (buff[0] == '#') /* expected warning? */
printf("Expected Lua warning: %s", buff); /* print it */ printf("Expected Lua warning: %s\n", buff); /* print it */
else if (strchr(buff, '@') != NULL) { /* warning for test purposes? */ else if (strchr(buff, '@') != NULL) { /* warning for test purposes? */
lua_State *L = cast(lua_State *, *pud); lua_State *L = cast(lua_State *, ud);
lua_unlock(L); lua_unlock(L);
lua_pushstring(L, buff); lua_pushstring(L, buff);
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */ lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
lua_lock(L); lua_lock(L);
return;
} }
else { /* a real warning; should not happen during tests */ else /* a real warning; should not happen during tests */
badexit("Unexpected warning in test mode: %s\naborting...\n", buff); badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
} buff[0] = '\0'; /* prepare buffer for next warning */
} }
} }
@ -1466,9 +1446,13 @@ 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("warningC") {
const char *msg = getstring;
lua_warning(L1, msg, 1);
}
else if EQ("warning") { else if EQ("warning") {
const char *msg = getstring; const char *msg = getstring;
lua_warning(L1, msg); lua_warning(L1, msg, 0);
} }
else if EQ("pushbool") { else if EQ("pushbool") {
lua_pushboolean(L1, getnum); lua_pushboolean(L1, getnum);

4
lua.h
View File

@ -128,7 +128,7 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/* /*
** Type for warning functions ** Type for warning functions
*/ */
typedef void (*lua_WarnFunction) (void **pud, const char *msg); typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
@ -309,7 +309,7 @@ LUA_API int (lua_isyieldable) (lua_State *L);
** Warning-related functions ** Warning-related functions
*/ */
LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
LUA_API void (lua_warning) (lua_State *L, const char *msg); LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
/* /*

View File

@ -4061,7 +4061,7 @@ If @id{index} @N{is 0}, then all stack elements are removed.
Sets the @x{warning function} to be used by Lua to emit warnings Sets the @x{warning function} to be used by Lua to emit warnings
@see{lua_WarnFunction}. @see{lua_WarnFunction}.
The @id{ud} parameter initializes the slot @id{pud} passed to The @id{ud} parameter sets the value @id{ud} passed to
the warning function. the warning function.
} }
@ -4325,25 +4325,24 @@ Returns the version number of this core.
} }
@APIEntry{ @APIEntry{
typedef void (*lua_WarnFunction) (void **pud, const char *msg);| typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);|
The type of @x{warning function}s, called by Lua to emit warnings. The type of @x{warning function}s, called by Lua to emit warnings.
The first parameter is the address of a writable slot, The first parameter is an opaque pointer
constant for a given Lua state and set by @Lid{lua_setwarnf}.
initialized by @Lid{lua_setwarnf}.
The second parameter is the warning message. The second parameter is the warning message.
This function should assume that The third parameter is a boolean that
a message not ending with an end-of-line will be indicates whether the message is
continued by the message in the next call. to be continued by the message in the next call.
} }
@APIEntry{ @APIEntry{
void lua_warning (lua_State *L, const char *msg);| void lua_warning (lua_State *L, const char *msg, int tocont);|
@apii{0,0,-} @apii{0,0,-}
Emits a warning with the given message. Emits a warning with the given message.
A message not ending with an end-of-line should be A message in a call with @id{tocont} true should be
continued in another call to this function. continued in another call to this function.
} }
@ -6275,11 +6274,12 @@ The current value of this variable is @St{Lua 5.4}.
} }
@LibEntry{warn (message)| @LibEntry{warn (message [, tocont])|
Emits a warning with the given message. Emits a warning with the given message.
Note that messages not ending with an end-of-line A message in a call with @id{tocont} true should be
are assumed to be continued by the message in the next call. continued in another call to this function.
The default for @id{tocont} is false.
} }

View File

@ -6,7 +6,7 @@
local version = "Lua 5.4" local version = "Lua 5.4"
if _VERSION ~= version then if _VERSION ~= version then
warn(string.format( warn(string.format(
"This test suite is for %s, not for %s\nExiting tests\n", version, _VERSION)) "This test suite is for %s, not for %s\nExiting tests", version, _VERSION))
return return
end end
@ -190,16 +190,16 @@ assert(dofile('verybig.lua', true) == 10); collectgarbage()
dofile('files.lua') dofile('files.lua')
if #msgs > 0 then if #msgs > 0 then
warn("#tests not performed:\n ") warn("#tests not performed:", true)
for i=1,#msgs do for i=1,#msgs do
warn(msgs[i]); warn("\n ") warn("\n ", true); warn(msgs[i], true)
end end
warn("\n") warn("\n")
end end
print("(there should be two warnings now)") print("(there should be two warnings now)")
warn("#This is "); warn("an expected"); warn(" warning\n") warn("#This is ", true); warn("an expected", true); warn(" warning")
warn("#This is"); warn(" another one\n") warn("#This is", true); warn(" another one")
-- no test module should define 'debug' -- no test module should define 'debug'
assert(debug == nil) assert(debug == nil)

View File

@ -114,13 +114,11 @@ end
-- testing warnings -- testing warnings
T.testC([[ T.testC([[
warning "#This shold be a" warningC "#This shold be a"
warning " single " warningC " single "
warning "warning warning "warning"
" warningC "#This should be "
warning "#This should be " warning "another one"
warning "another one
"
]]) ]])

View File

@ -18,6 +18,8 @@ assert(collectgarbage("incremental") == "generational")
assert(collectgarbage("incremental") == "incremental") assert(collectgarbage("incremental") == "incremental")
local function nop () end
local function gcinfo () local function gcinfo ()
return collectgarbage"count" * 1024 return collectgarbage"count" * 1024
end end
@ -388,7 +390,7 @@ if T then
collectgarbage() collectgarbage()
for i = 1, 10 do assert(s[i]) end for i = 1, 10 do assert(s[i]) end
getmetatable(u).__gc = false getmetatable(u).__gc = nil
end end
print '+' print '+'
@ -604,8 +606,8 @@ if T then
collectgarbage("stop") collectgarbage("stop")
local x = T.newuserdata(0) local x = T.newuserdata(0)
local y = T.newuserdata(0) local y = T.newuserdata(0)
debug.setmetatable(y, {__gc = true}) -- bless the new udata before... debug.setmetatable(y, {__gc = nop}) -- bless the new udata before...
debug.setmetatable(x, {__gc = true}) -- ...the old one debug.setmetatable(x, {__gc = nop}) -- ...the old one
assert(T.gccolor(y) == "white") assert(T.gccolor(y) == "white")
T.checkmemory() T.checkmemory()
collectgarbage("restart") collectgarbage("restart")
@ -631,6 +633,7 @@ if T then
assert(T.totalmem("thread") == t + 1) assert(T.totalmem("thread") == t + 1)
end end
-- create an object to be collected when state is closed -- create an object to be collected when state is closed
do do
local setmetatable,assert,type,print,getmetatable = local setmetatable,assert,type,print,getmetatable =
@ -650,7 +653,7 @@ end
-- create several objects to raise errors when collected while closing state -- create several objects to raise errors when collected while closing state
if T then if T then
local error, assert, warn, find = error, assert, warn, string.find local error, assert, find = error, assert, string.find
local n = 0 local n = 0
local lastmsg local lastmsg
local mt = {__gc = function (o) local mt = {__gc = function (o)