From b56d4e570a60a8e84df8288c3122eb5bb5c20af6 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 14 Mar 2019 15:30:54 -0300 Subject: [PATCH] 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. --- lapi.c | 4 ++-- lauxlib.c | 34 ++++++++++++--------------- lbaselib.c | 2 +- lgc.c | 8 +++---- lstate.c | 4 ++-- lstate.h | 2 +- ltests.c | 60 ++++++++++++++++++------------------------------ lua.h | 4 ++-- manual/manual.of | 26 ++++++++++----------- testes/all.lua | 10 ++++---- testes/api.lua | 12 ++++------ testes/gc.lua | 11 +++++---- 12 files changed, 79 insertions(+), 98 deletions(-) diff --git a/lapi.c b/lapi.c index 66d75649..06396ad3 100644 --- a/lapi.c +++ b/lapi.c @@ -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); - luaE_warning(L, msg); + luaE_warning(L, msg, tocont); lua_unlock(L); } diff --git a/lauxlib.c b/lauxlib.c index abf923f8..e9c02d36 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -987,33 +987,29 @@ static int panic (lua_State *L) { /* -** checks whether 'message' ends with end-of-line -** (and therefore is the last part of a warning) +** Emit a warning. '*previoustocont' signals whether previous message +** was to be continued by the current one. */ -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; +static void warnf (void *ud, const char *message, int tocont) { + int *previoustocont = (int *)ud; + 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 */ + *previoustocont = tocont; } LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); if (L) { + int *previoustocont; /* space for warning state */ 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; } diff --git a/lbaselib.c b/lbaselib.c index 26683a1d..d4b619a5 100644 --- a/lbaselib.c +++ b/lbaselib.c @@ -45,7 +45,7 @@ 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); + lua_warning(L, msg, lua_toboolean(L, 2)); return 0; } diff --git a/lgc.c b/lgc.c index bf0460d5..c834319b 100644 --- a/lgc.c +++ b/lgc.c @@ -832,7 +832,7 @@ static void GCTM (lua_State *L) { lua_assert(!g->gcemergency); setgcovalue(L, &v, udata2finalize(g)); 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; lu_byte oldah = L->allowhook; int running = g->gcrunning; @@ -850,9 +850,9 @@ static void GCTM (lua_State *L) { const char *msg = (ttisstring(s2v(L->top - 1))) ? svalue(s2v(L->top - 1)) : "error object is not a string"; - luaE_warning(L, "error in __gc metamethod ("); - luaE_warning(L, msg); - luaE_warning(L, ")\n"); + luaE_warning(L, "error in __gc metamethod (", 1); + luaE_warning(L, msg, 1); + luaE_warning(L, ")", 0); } } } diff --git a/lstate.c b/lstate.c index 6c35ea1a..f5579a66 100644 --- a/lstate.c +++ b/lstate.c @@ -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; if (wf != NULL) - wf(&G(L)->ud_warn, msg); + wf(G(L)->ud_warn, msg, tocont); } diff --git a/lstate.h b/lstate.h index 11bf18fd..e35f8962 100644 --- a/lstate.h +++ b/lstate.h @@ -317,7 +317,7 @@ LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (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--) diff --git a/ltests.c b/ltests.c index 23375382..40de2292 100644 --- a/ltests.c +++ b/ltests.c @@ -63,11 +63,8 @@ static void pushobject (lua_State *L, const TValue *o) { } -static void badexit (const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - vfprintf(stderr, fmt, argp); - va_end(argp); +static void badexit (const char *fmt, const char *s) { + fprintf(stderr, fmt, s); /* avoid assertion failures when exiting */ l_memcontrol.numblocks = l_memcontrol.total = 0; 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 ** 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); -** messages containing '@' are stored in global '_WARN' (used to test +** - messages containing '@' are stored in global '_WARN' (used to test ** errors that generate warnings); -** other messages abort the tests (they represent real warning conditions; -** the standard tests should not generate these conditions unexpectedly). +** - other messages abort the tests (they represent real warning +** conditions; the standard tests should not generate these conditions +** unexpectedly). */ -static void warnf (void **pud, const char *msg) { - static char buff[200]; /* should be enough for tests... */ - static int cont = 0; /* message to be continued */ - if (cont) { /* continuation? */ - if (strlen(msg) >= sizeof(buff) - strlen(buff)) - badexit("warnf-buffer overflow"); - 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 */ +static void warnf (void *ud, const char *msg, int tocont) { + static char buff[200] = ""; /* should be enough for tests... */ + if (strlen(msg) >= sizeof(buff) - strlen(buff)) + badexit("%s", "warnf-buffer overflow"); + strcat(buff, msg); /* add new message to current warning */ + if (!tocont) { /* message finished? */ 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? */ - lua_State *L = cast(lua_State *, *pud); + lua_State *L = cast(lua_State *, ud); lua_unlock(L); lua_pushstring(L, buff); lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */ 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); - } + 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; printf("%s\n", msg); } + else if EQ("warningC") { + const char *msg = getstring; + lua_warning(L1, msg, 1); + } else if EQ("warning") { const char *msg = getstring; - lua_warning(L1, msg); + lua_warning(L1, msg, 0); } else if EQ("pushbool") { lua_pushboolean(L1, getnum); diff --git a/lua.h b/lua.h index b777624e..09611db5 100644 --- a/lua.h +++ b/lua.h @@ -128,7 +128,7 @@ 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); +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 */ 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); /* diff --git a/manual/manual.of b/manual/manual.of index b47fd865..63e78f95 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -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 @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. } @@ -4325,25 +4325,24 @@ Returns the version number of this core. } @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 first parameter is the address of a writable slot, -constant for a given Lua state and -initialized by @Lid{lua_setwarnf}. +The first parameter is an opaque pointer +set 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. +The third parameter is a boolean that +indicates whether the message is +to be continued by the message in the next call. } @APIEntry{ -void lua_warning (lua_State *L, const char *msg);| +void lua_warning (lua_State *L, const char *msg, int tocont);| @apii{0,0,-} 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. } @@ -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. -Note that messages not ending with an end-of-line -are assumed to be continued by the message in the next call. +A message in a call with @id{tocont} true should be +continued in another call to this function. +The default for @id{tocont} is false. } diff --git a/testes/all.lua b/testes/all.lua index 506afad2..8d727b6b 100644 --- a/testes/all.lua +++ b/testes/all.lua @@ -6,7 +6,7 @@ local version = "Lua 5.4" if _VERSION ~= version then 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 end @@ -190,16 +190,16 @@ assert(dofile('verybig.lua', true) == 10); collectgarbage() dofile('files.lua') if #msgs > 0 then - warn("#tests not performed:\n ") + warn("#tests not performed:", true) for i=1,#msgs do - warn(msgs[i]); warn("\n ") + warn("\n ", true); warn(msgs[i], true) end warn("\n") end print("(there should be two warnings now)") -warn("#This is "); warn("an expected"); warn(" warning\n") -warn("#This is"); warn(" another one\n") +warn("#This is ", true); warn("an expected", true); warn(" warning") +warn("#This is", true); warn(" another one") -- no test module should define 'debug' assert(debug == nil) diff --git a/testes/api.lua b/testes/api.lua index d034ea80..08672e8a 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -114,13 +114,11 @@ end -- testing warnings T.testC([[ - warning "#This shold be a" - warning " single " - warning "warning -" - warning "#This should be " - warning "another one -" + warningC "#This shold be a" + warningC " single " + warning "warning" + warningC "#This should be " + warning "another one" ]]) diff --git a/testes/gc.lua b/testes/gc.lua index 05bf564e..91e78a48 100644 --- a/testes/gc.lua +++ b/testes/gc.lua @@ -18,6 +18,8 @@ assert(collectgarbage("incremental") == "generational") assert(collectgarbage("incremental") == "incremental") +local function nop () end + local function gcinfo () return collectgarbage"count" * 1024 end @@ -388,7 +390,7 @@ if T then collectgarbage() for i = 1, 10 do assert(s[i]) end - getmetatable(u).__gc = false + getmetatable(u).__gc = nil end print '+' @@ -604,8 +606,8 @@ if T then collectgarbage("stop") local x = T.newuserdata(0) local y = T.newuserdata(0) - debug.setmetatable(y, {__gc = true}) -- bless the new udata before... - debug.setmetatable(x, {__gc = true}) -- ...the old one + debug.setmetatable(y, {__gc = nop}) -- bless the new udata before... + debug.setmetatable(x, {__gc = nop}) -- ...the old one assert(T.gccolor(y) == "white") T.checkmemory() collectgarbage("restart") @@ -631,6 +633,7 @@ if T then assert(T.totalmem("thread") == t + 1) end + -- create an object to be collected when state is closed do local setmetatable,assert,type,print,getmetatable = @@ -650,7 +653,7 @@ end -- create several objects to raise errors when collected while closing state 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 lastmsg local mt = {__gc = function (o)