From 0dbf01bf1d6b1504103f6759e71e422c982145df Mon Sep 17 00:00:00 2001 From: Michael Lenaghan Date: Sat, 15 Jun 2024 20:13:08 -0400 Subject: [PATCH] Bring Lua to 5.4.6. (#1214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This essentially re-does the work of #875 on top of master. This is what I did to check that Cosmo's Lua extensions still worked: ``` $ build/bootstrap/make MODE=aarch64 o/aarch64/third_party/lua/lua $ ape o/aarch64/third_party/lua/lua >: 10 10 >: 010 8 >: 0b10 2 >: string.byte("\e") 27 >: "Hello, %s" % {"world"} Hello, world >: "*" * 3 *** ``` `luaL_traceback2` was used to show the stack trace with parameter values; it's used in `LuaCallWithTrace`, which is used in Redbean to run Lua code. You should be able to see the extended stack trace by running something like this: `redbean -e "function a(b)c()end a(2)"` (with "params" indicating the extended stack trace): ``` stack traceback: [string "function a(b)c()end a(2)"]:1: in function 'a', params: b = 2; [string "function a(b)c()end a(2)"]:1: in main chunk ``` @pkulchenko confirmed that I get the expected result with the updated code. This is what I did to check that Lua itself still worked: ``` $ cd third_party/lua/test/ $ ape ../../../o/aarch64/third_party/lua/lua all.lua ``` There's one test failure, in `files.lua`: ``` ***** FILE 'files.lua'***** testing i/o ../../../o/aarch64/third_party/lua/lua: files.lua:84: assertion failed! stack traceback: [C]: in function 'assert' files.lua:84: in main chunk (...tail calls...) all.lua:195: in main chunk [C]: in ? .>>> closing state <<< ``` That isn't a result of these changes; the same test is failing in master. The failure is here: ```lua if not _port then -- invalid seek local status, msg, code = io.stdin:seek("set", 1000) assert(not status and type(msg) == "string" and type(code) == "number") end ``` The test expects a seek to offset 1,000 on stdin to fail — but it doesn't. `status` ends up being the new offset rather than `nil`. If I comment out that one test, the remaining tests succeed. --- third_party/lua/README.cosmo | 136 +++++++++++- third_party/lua/lapi.c | 292 ++++++++++++------------ third_party/lua/lapi.h | 13 +- third_party/lua/lauxlib.c | 120 +++++----- third_party/lua/lauxlib.h | 13 +- third_party/lua/lbaselib.c | 28 ++- third_party/lua/lcode.c | 171 +++++++++----- third_party/lua/lcorolib.c | 11 +- third_party/lua/lctype.h | 3 + third_party/lua/ldblib.c | 4 +- third_party/lua/ldebug.c | 186 ++++++++++------ third_party/lua/ldebug.h | 4 +- third_party/lua/ldo.c | 333 ++++++++++++++++------------ third_party/lua/ldo.h | 18 +- third_party/lua/ldump.c | 10 +- third_party/lua/lfunc.c | 57 ++--- third_party/lua/lfunc.h | 6 +- third_party/lua/lgc.c | 124 ++++++----- third_party/lua/lgc.h | 29 ++- third_party/lua/linit.c | 4 +- third_party/lua/liolib.c | 3 +- third_party/lua/ljumptab.inc | 6 - third_party/lua/llex.c | 11 +- third_party/lua/llex.h | 6 - third_party/lua/llimits.h | 37 +++- third_party/lua/lmathlib.c | 17 +- third_party/lua/lmem.c | 74 ++++--- third_party/lua/lnotice.c | 4 +- third_party/lua/loadlib.c | 20 +- third_party/lua/lobject.c | 61 +++-- third_party/lua/lobject.h | 28 ++- third_party/lua/lopcodes.c | 3 +- third_party/lua/lopcodes.h | 23 +- third_party/lua/lopnames.inc | 9 +- third_party/lua/loslib.c | 40 ++-- third_party/lua/lparser.c | 56 +++-- third_party/lua/lprefix.h | 2 + third_party/lua/lrepl.c | 2 +- third_party/lua/lstate.c | 79 ++++--- third_party/lua/lstate.h | 29 +-- third_party/lua/lstring.c | 3 +- third_party/lua/lstrlib.c | 136 ++++++++---- third_party/lua/ltable.c | 60 ++--- third_party/lua/ltable.h | 1 - third_party/lua/ltablib.c | 10 +- third_party/lua/ltests.c | 46 ++-- third_party/lua/ltests.h | 8 + third_party/lua/ltm.c | 41 ++-- third_party/lua/ltm.h | 13 +- third_party/lua/lua.h | 30 ++- third_party/lua/lua.main.c | 67 ++++-- third_party/lua/luac.main.c | 15 +- third_party/lua/luaconf.h | 21 +- third_party/lua/lualib.h | 1 + third_party/lua/lundump.c | 42 +++- third_party/lua/lundump.h | 1 + third_party/lua/lutf8lib.c | 70 ++++-- third_party/lua/lvm.c | 234 ++++++++++++------- third_party/lua/lvm.h | 6 + third_party/lua/lzio.c | 3 +- third_party/lua/lzio.h | 6 - third_party/lua/test/all.lua | 1 + third_party/lua/test/api.lua | 117 +++++----- third_party/lua/test/attrib.lua | 34 ++- third_party/lua/test/big.lua | 2 +- third_party/lua/test/bitwise.lua | 17 ++ third_party/lua/test/calls.lua | 68 ++++-- third_party/lua/test/closure.lua | 6 +- third_party/lua/test/code.lua | 20 +- third_party/lua/test/constructs.lua | 61 +++-- third_party/lua/test/coroutine.lua | 117 +++++++--- third_party/lua/test/cstack.lua | 40 ++++ third_party/lua/test/db.lua | 72 +++++- third_party/lua/test/errors.lua | 111 ++++++---- third_party/lua/test/events.lua | 3 + third_party/lua/test/files.lua | 18 +- third_party/lua/test/gc.lua | 34 +-- third_party/lua/test/libs/makefile | 10 +- third_party/lua/test/literals.lua | 57 +++-- third_party/lua/test/locals.lua | 134 ++++++++++- third_party/lua/test/main.lua | 72 +++++- third_party/lua/test/math.lua | 7 +- third_party/lua/test/nextvar.lua | 78 ++++++- third_party/lua/test/pm.lua | 30 +-- third_party/lua/test/sort.lua | 23 +- third_party/lua/test/strings.lua | 47 +++- third_party/lua/test/tpack.lua | 2 +- third_party/lua/test/utf8.lua | 20 +- third_party/lua/test/vararg.lua | 18 +- third_party/lua/test/verybig.lua | 12 +- 90 files changed, 2741 insertions(+), 1376 deletions(-) diff --git a/third_party/lua/README.cosmo b/third_party/lua/README.cosmo index 6f02aec3c..a460911b9 100644 --- a/third_party/lua/README.cosmo +++ b/third_party/lua/README.cosmo @@ -9,14 +9,14 @@ PROVENANCE https://github.com/lua/lua/ - commit e7803f7dbcdc966ab1f9db143424ee811ab1a398 + commit 6443185167c77adcc8552a3fee7edab7895db1a9 Author: Roberto Ierusalimschy - Date: Wed Mar 3 09:44:20 2021 -0300 + Date: May 2, 2023 at 3:44 PM EDT - New release number (5.4.3) + New release number (5.4.6) luac.c needed to be sourced from: - https://www.lua.org/ftp/lua-5.4.3.tar.gz + https://www.lua.org/ftp/lua-5.4.6.tar.gz LOCAL MODIFICATIONS @@ -31,10 +31,130 @@ LOCAL MODIFICATIONS character. It may be used for teletypewriter control like having bold text, which can be encoded elegantly as `\e[1mHELLO\e[0m`. + Added Python-like printf modulus operator for strings, e.g.: + `"Hello, %s!" % {"world"}`. + + Added Python-like printf multiply operator for strings, e.g.: + `"Hello, world! " * 3`. + + Added `unix` module that exposes the low-level System Five system + call interface, e.g.: `require("unix"); print(unix.getcwd())`. + Added luaL_traceback2() for function parameters in traceback. - Added Python-like printf modulus operator for strings. - - Added Python-like printf multiply operator for strings. - Fixed a buffer overflow in os.tmpname + +NOTES + + If you'd like to update Cosmo's Lua to the latest version, here + are some things that might be helpful to know. + + Cosmo's Lua adds ~20 or so files (e.g., `luacallwithtrace.c`, + `luaencodejsondata.c`, `luaencodeluadata.c`, etc.) to the + directory. In other words, a bunch of Cosmo's files don't + have any Lua counterpart. + + Some of those files (e.g., `lunix.c`, `lunix.h`) implement + functionality that's available within the `lua` that Cosmo builds; + most, though, implement functionality that's (currently?) only + available within `redbean`. In other words, not everything can be + tested using `lua`; some things need to be tested using `redbean`. + + Some of Lua's files were renamed. For example, `lua.c` was renamed + to `lua.main.c`, and `luac.c` (which is only available in Lua's + distributions, and not in Lua's Github repo) was renamed to + `luac.main.c`. `ljumptab.h` was renamed to `ljumptab.inc`, and + `lopnames.h` was renamed to `lopnames.inc`. In other words, you'll + need to take some kind of action in order to properly diff all of + Lua's files. + + Lua's `.h` files had the comment headers that look like this + removed: + + /* + ** $Id: lua.h $ + ** Lua - A Scripting Language + ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) + ** See Copyright Notice at the end of this file + */ + + Lua's `.c` files *replaced* those comment headers with a Cosmo + emacs/vim header followed by a Lua copyright declaration. + + The `.c` files also added a `__static_yoink("lua_notice");` + right after the `#include`s. + + Some of Lua's tests have been modified to accommodate Cosmo's + changes. (And some of Lua's tests have been commented out + due to Cosmo's changes.) + + Five of Lua's test files intentionally contain ISO-8859-1 (rather + than UTF-8) characters: + + * test/db.lua + * test/files.lua + * test/pm.lua + * test/sort.lua + * test/strings.lua + + If you edit those files as if they were UTF-8-encoded you'll + corrupt the ISO-8859-1 characters and cause certain tests to fail. + (Some of the tests count bytes, so you can't just fix the problem + by converting the files — you also have to change various expected + results.) + + The modifications listed way up above are really only the + *user-visible* modifications. There are many that aren't + user-visible. For example, `_longjmp` was replaced with + `gclongjmp`, and `abort` was replaced, ultimately, with a + call to `_Exit`. + + To update Cosmo's Lua, you'll need to diff the latest Lua against + the previous Lua, and Cosmo's Lua against the latest Lua. As you + do that, you'll be trying to figure out both what Lua changed + *and* what Cosmo changed; you'll be trying to add Lua's changes + without accidentally removing Cosmo's changes. + + It's tricky! + + We've started to try to make that process a bit easier by tagging + Cosmo's changes with `[jart]`. For example, one side of the diff + might (now) show: + + #define LUAI_THROW(L,c) _longjmp((c)->b, 1) + + while the other side might (now) show: + + #define LUAI_THROW(L,c) gclongjmp((c)->b, 1) // [jart] + + The presence of the `[jart]` tag makes it easy to see that the + Cosmo change was intentional. + + Be aware that not all changes have been tagged! + + There are *other* things we've done that are *also* meant to make + diffing easier — though the intention is less obvious. + + For example, Cosmo moved the `enum` of opcodes from `ltm.h` to + `tms.h`. Originally nothing at all was left behind in `ltm.h`. + Because of that, you'd see an `enum` in Lua that seemed to be + missing in Cosmo's Lua — as though the `enum` had recently been + added to Lua, and now needed to be added to Cosmo! To make the + intention of Cosmo's change more obvious, we added a tombstone + of sorts to `ltm.h`: + + /* + * WARNING: if you change the order of this enumeration, + * grep "ORDER TM" and "ORDER OP" + */ + // [jart] moved to tms.h + + The comment just above the tag comes from Lua; it's the comment + above Lua's original `enum`. *The presence of the comment in both + Lua and Cosmo helps diff tools "resync" at that point.* That in + turn makes it easier to see Cosmo's change, and to see that it was + intentional. + + The more things like that we do — the easier we can make it to + quickly and correctly read diffs — the easier we'll make it to + keep Cosmo's Lua in sync with the latest Lua. diff --git a/third_party/lua/lapi.c b/third_party/lua/lapi.c index ae8988f2c..884e56a44 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lapi_c #define LUA_CORE + #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -66,45 +67,57 @@ const char lua_ident[] = #define isupvalue(i) ((i) < LUA_REGISTRYINDEX) +/* +** Convert an acceptable index to a pointer to its respective value. +** Non-valid indices return the special nil value 'G(L)->nilvalue'. +*/ static TValue *index2value (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { - StkId o = ci->func + idx; - api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); - if (o >= L->top) return &G(L)->nilvalue; + StkId o = ci->func.p + idx; + api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index"); + if (o >= L->top.p) return &G(L)->nilvalue; else return s2v(o); } else if (!ispseudo(idx)) { /* negative index */ - api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); - return s2v(L->top + idx); + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + return s2v(L->top.p + idx); } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); - if (ttislcf(s2v(ci->func))) /* light C function? */ - return &G(L)->nilvalue; /* it has no upvalues */ - else { - CClosure *func = clCvalue(s2v(ci->func)); + if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */ + CClosure *func = clCvalue(s2v(ci->func.p)); return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue; } + else { /* light C function or Lua function (through a hook)?) */ + api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function"); + return &G(L)->nilvalue; /* no upvalues */ + } } } -static StkId index2stack (lua_State *L, int idx) { + +/* +** Convert a valid actual index (not a pseudo-index) to its address. +*/ +l_sinline StkId index2stack (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { - StkId o = ci->func + idx; - api_check(L, o < L->top, "unacceptable index"); + StkId o = ci->func.p + idx; + api_check(L, o < L->top.p, "invalid index"); return o; } else { /* non-positive index */ - api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); api_check(L, !ispseudo(idx), "invalid index"); - return L->top + idx; + return L->top.p + idx; } } @@ -115,17 +128,12 @@ LUA_API int lua_checkstack (lua_State *L, int n) { lua_lock(L); ci = L->ci; api_check(L, n >= 0, "negative 'n'"); - if (L->stack_last - L->top > n) /* stack large enough? */ + if (L->stack_last.p - L->top.p > n) /* stack large enough? */ res = 1; /* yes; check is OK */ - else { /* no; need to grow stack */ - int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; - if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ - res = 0; /* no */ - else /* try to grow stack */ + else /* need to grow stack */ res = luaD_growstack(L, n, 0); - } - if (res && ci->top < L->top + n) - ci->top = L->top + n; /* adjust frame top */ + if (res && ci->top.p < L->top.p + n) + ci->top.p = L->top.p + n; /* adjust frame top */ lua_unlock(L); return res; } @@ -142,11 +150,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); - api_check(from, to->ci->top - to->top >= n, "stack overflow"); - from->top -= n; + api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); + from->top.p -= n; for (i = 0; i < n; i++) { - setobjs2s(to, to->top, from->top + i); - to->top++; /* stack already checked by previous 'api_check' */ + setobjs2s(to, to->top.p, from->top.p + i); + to->top.p++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } @@ -199,7 +207,7 @@ LUA_API lua_Number lua_version (lua_State *L) { LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx - : cast_int(L->top - L->ci->func) + idx; + : cast_int(L->top.p - L->ci->func.p) + idx; } /** @@ -210,7 +218,7 @@ LUA_API int lua_absindex (lua_State *L, int idx) { * particular, 0 means an empty stack. */ LUA_API int lua_gettop (lua_State *L) { - return cast_int(L->top - (L->ci->func + 1)); + return cast_int(L->top.p - (L->ci->func.p + 1)); } /** @@ -229,24 +237,24 @@ LUA_API void lua_settop (lua_State *L, int idx) { ptrdiff_t diff; /* difference for new top */ lua_lock(L); ci = L->ci; - func = ci->func; + func = ci->func.p; if (idx >= 0) { - api_check(L, idx <= ci->top - (func + 1), "new top too large"); - diff = ((func + 1) + idx) - L->top; + api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top.p; for (; diff > 0; diff--) - setnilvalue(s2v(L->top++)); /* clear new slots */ + setnilvalue(s2v(L->top.p++)); /* clear new slots */ } else { - api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); + api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } - api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot"); - newtop = L->top + diff; - if (diff < 0 && L->tbclist >= newtop) { + api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); + newtop = L->top.p + diff; + if (diff < 0 && L->tbclist.p >= newtop) { lua_assert(hastocloseCfunc(ci->nresults)); - luaF_close(L, newtop, CLOSEKTOP, 0); + newtop = luaF_close(L, newtop, CLOSEKTOP, 0); } - L->top = newtop; /* correct top only after closing any upvalue */ + L->top.p = newtop; /* correct top only after closing any upvalue */ lua_unlock(L); } @@ -255,10 +263,9 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { StkId level; lua_lock(L); level = index2stack(L, idx); - api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level, + api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, "no variable to close at given level"); - luaF_close(L, level, CLOSEKTOP, 0); - level = index2stack(L, idx); /* stack may be moved */ + level = luaF_close(L, level, CLOSEKTOP, 0); setnilvalue(s2v(level)); lua_unlock(L); } @@ -270,7 +277,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { ** Note that we move(copy) only the value inside the stack. ** (We do not move additional fields that may exist.) */ -static void reverse (lua_State *L, StkId from, StkId to) { +l_sinline void reverse (lua_State *L, StkId from, StkId to) { for (; from < to; from++, to--) { TValue temp; setobj(L, &temp, s2v(from)); @@ -287,7 +294,7 @@ static void reverse (lua_State *L, StkId from, StkId to) { LUA_API void lua_rotate (lua_State *L, int idx, int n) { StkId p, t, m; lua_lock(L); - t = L->top - 1; /* end of stack segment being rotated */ + t = L->top.p - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ @@ -306,7 +313,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { api_check(L, isvalid(L, to), "invalid index"); setobj(L, to, fr); if (isupvalue(toidx)) /* function upvalue? */ - luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); + luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ lua_unlock(L); @@ -315,7 +322,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); - setobj2s(L, L->top, index2value(L, idx)); + setobj2s(L, L->top.p, index2value(L, idx)); api_incr_top(L); lua_unlock(L); } @@ -384,12 +391,12 @@ LUA_API void lua_arith (lua_State *L, int op) { api_checknelems(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); - setobjs2s(L, L->top, L->top - 1); + setobjs2s(L, L->top.p, L->top.p - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ - luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); - L->top--; /* remove second operand */ + luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); + L->top.p--; /* remove second operand */ lua_unlock(L); } @@ -415,7 +422,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { - size_t sz = luaO_str2num(s, s2v(L->top)); + size_t sz = luaO_str2num(s, s2v(L->top.p)); if (sz != 0) api_incr_top(L); return sz; @@ -490,7 +497,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { } -static void *touserdata (const TValue *o) { +l_sinline void *touserdata (const TValue *o) { switch (ttype(o)) { case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); @@ -572,7 +579,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) { */ LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -585,7 +592,7 @@ LUA_API void lua_pushnil (lua_State *L) { */ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); - setfltvalue(s2v(L->top), n); + setfltvalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } @@ -600,7 +607,7 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { */ LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); - setivalue(s2v(L->top), n); + setivalue(s2v(L->top.p), n); api_incr_top(L); lua_unlock(L); } @@ -615,7 +622,7 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); - setsvalue2s(L, L->top, ts); + setsvalue2s(L, L->top.p, ts); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); @@ -637,11 +644,11 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); else { TString *ts; ts = luaS_new(L, s); - setsvalue2s(L, L->top, ts); + setsvalue2s(L, L->top.p, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); @@ -729,7 +736,7 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { - setfvalue(s2v(L->top), fn); + setfvalue(s2v(L->top.p), fn); api_incr_top(L); } else { @@ -738,13 +745,13 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; - L->top -= n; + L->top.p -= n; while (n--) { - setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); + setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); /* does not need barrier because closure is white */ lua_assert(iswhite(cl)); } - setclCvalue(L, s2v(L->top), cl); + setclCvalue(L, s2v(L->top.p), cl); api_incr_top(L); luaC_checkGC(L); } @@ -761,9 +768,9 @@ LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); /* a.k.a. L->top->val.tt_ = b ? LUA_VTRUE : LUA_VFALSE; */ if (b) - setbtvalue(s2v(L->top)); + setbtvalue(s2v(L->top.p)); else - setbfvalue(s2v(L->top)); + setbfvalue(s2v(L->top.p)); api_incr_top(L); lua_unlock(L); } @@ -781,7 +788,7 @@ LUA_API void lua_pushboolean (lua_State *L, int b) { */ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); - setpvalue(s2v(L->top), p); + setpvalue(s2v(L->top.p), p); api_incr_top(L); lua_unlock(L); } @@ -795,7 +802,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { */ LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); - setthvalue(L, s2v(L->top), L); + setthvalue(L, s2v(L->top.p), L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); @@ -808,20 +815,20 @@ LUA_API int lua_pushthread (lua_State *L) { */ -static int auxgetstr (lua_State *L, const TValue *t, const char *k) { +l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - setobj2s(L, L->top, slot); + setobj2s(L, L->top.p, slot); api_incr_top(L); } else { - setsvalue2s(L, L->top, str); + setsvalue2s(L, L->top.p, str); api_incr_top(L); - luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); } lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -848,13 +855,13 @@ LUA_API int lua_gettable (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { - setobj2s(L, L->top - 1, slot); + if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { + setobj2s(L, L->top.p - 1, slot); } else - luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -870,27 +877,27 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { lua_lock(L); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { - setobj2s(L, L->top, slot); + setobj2s(L, L->top.p, slot); } else { TValue aux; setivalue(&aux, n); - luaV_finishget(L, t, &aux, L->top, slot); + luaV_finishget(L, t, &aux, L->top.p, slot); } api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } -static int finishrawget (lua_State *L, const TValue *val) { +l_sinline int finishrawget (lua_State *L, const TValue *val) { if (isempty(val)) /* avoid copying empty items to the stack */ - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); else - setobj2s(L, L->top, val); + setobj2s(L, L->top.p, val); api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top - 1)); + return ttype(s2v(L->top.p - 1)); } @@ -913,8 +920,8 @@ LUA_API int lua_rawget (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - val = luaH_get(t, s2v(L->top - 1)); - L->top--; /* remove key */ + val = luaH_get(t, s2v(L->top.p - 1)); + L->top.p--; /* remove key */ return finishrawget(L, val); } @@ -961,7 +968,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); t = luaH_new(L); - sethvalue2s(L, L->top, t); + sethvalue2s(L, L->top.p, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); @@ -997,7 +1004,7 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { break; } if (mt != NULL) { - sethvalue2s(L, L->top, mt); + sethvalue2s(L, L->top.p, mt); api_incr_top(L); res = 1; } @@ -1024,12 +1031,12 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (n <= 0 || n > uvalue(o)->nuvalue) { - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); t = LUA_TNONE; } else { - setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); - t = ttype(s2v(L->top)); + setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top.p)); } api_incr_top(L); lua_unlock(L); @@ -1049,14 +1056,14 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { TString *str = luaS_new(L, k); api_checknelems(L, 1); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); - L->top--; /* pop value */ + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + L->top.p--; /* pop value */ } else { - setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ + setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); - luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); - L->top -= 2; /* pop value and key */ + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + L->top.p -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ } @@ -1076,12 +1083,12 @@ LUA_API void lua_settable (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 2); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); } else - luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); - L->top -= 2; /* pop index and value */ + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + L->top.p -= 2; /* pop index and value */ lua_unlock(L); } @@ -1108,14 +1115,14 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { api_checknelems(L, 1); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { - luaV_finishfastset(L, t, slot, s2v(L->top - 1)); + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); } else { TValue aux; setivalue(&aux, n); - luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); + luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); } - L->top--; /* pop value */ + L->top.p--; /* pop value */ lua_unlock(L); } @@ -1125,10 +1132,10 @@ static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { lua_lock(L); api_checknelems(L, n); t = gettable(L, idx); - luaH_set(L, t, key, s2v(L->top - 1)); + luaH_set(L, t, key, s2v(L->top.p - 1)); invalidateTMcache(t); - luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); - L->top -= n; + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p -= n; lua_unlock(L); } @@ -1140,7 +1147,7 @@ static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { * metamethods). */ LUA_API void lua_rawset (lua_State *L, int idx) { - aux_rawset(L, idx, s2v(L->top - 2), 2); + aux_rawset(L, idx, s2v(L->top.p - 2), 2); } @@ -1175,9 +1182,9 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - luaH_setint(L, t, n, s2v(L->top - 1)); - luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); - L->top--; + luaH_setint(L, t, n, s2v(L->top.p - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p--; lua_unlock(L); } @@ -1197,11 +1204,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { lua_lock(L); api_checknelems(L, 1); obj = index2value(L, objindex); - if (ttisnil(s2v(L->top - 1))) + if (ttisnil(s2v(L->top.p - 1))) mt = NULL; else { - api_check(L, ttistable(s2v(L->top - 1)), "table expected"); - mt = hvalue(s2v(L->top - 1)); + api_check(L, ttistable(s2v(L->top.p - 1)), "table expected"); + mt = hvalue(s2v(L->top.p - 1)); } switch (ttype(obj)) { case LUA_TTABLE: { @@ -1225,7 +1232,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { break; } } - L->top--; + L->top.p--; lua_unlock(L); return 1; } @@ -1248,11 +1255,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ else { - setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); - luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1)); res = 1; } - L->top--; + L->top.p--; lua_unlock(L); return res; } @@ -1264,7 +1271,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { #define checkresults(L,na,nr) \ - api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ + api_check(L, (nr) == LUA_MULTRET \ + || (L->ci->top.p - L->top.p >= (nr) - (na)), \ "results from function overflow current stack size") @@ -1277,7 +1285,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); - func = L->top - (nargs+1); + func = L->top.p - (nargs+1); if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ @@ -1325,7 +1333,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); func = savestack(L, o); } - c.func = L->top - (nargs+1); /* function to be called */ + c.func = L->top.p - (nargs+1); /* function to be called */ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); @@ -1360,12 +1368,12 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ - LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ + LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ const TValue *gt = getGtable(L); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ - setobj(L, f->upvals[0]->v, gt); + setobj(L, f->upvals[0]->v.p, gt); luaC_barrier(L, f->upvals[0], gt); } } @@ -1379,7 +1387,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { TValue *o; lua_lock(L); api_checknelems(L, 1); - o = s2v(L->top - 1); + o = s2v(L->top.p - 1); if (isLfunction(o)) status = luaU_dump(L, getproto(o), writer, data, strip); else @@ -1400,18 +1408,19 @@ LUA_API int lua_status (lua_State *L) { LUA_API int lua_gc (lua_State *L, int what, ...) { va_list argp; int res = 0; - global_State *g; + global_State *g = G(L); + if (g->gcstp & GCSTPGC) /* internal stop? */ + return -1; /* all options are invalid when stopped */ lua_lock(L); - g = G(L); va_start(argp, what); switch (what) { case LUA_GCSTOP: { - g->gcrunning = 0; + g->gcstp = GCSTPUSR; /* stopped by the user */ break; } case LUA_GCRESTART: { luaE_setdebt(g, 0); - g->gcrunning = 1; + g->gcstp = 0; /* (GCSTPGC must be already zero here) */ break; } case LUA_GCCOLLECT: { @@ -1430,8 +1439,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { case LUA_GCSTEP: { int data = va_arg(argp, int); l_mem debt = 1; /* =1 to signal that it did an actual step */ - lu_byte oldrunning = g->gcrunning; - g->gcrunning = 1; /* allow GC to run */ + lu_byte oldstp = g->gcstp; + g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ if (data == 0) { luaE_setdebt(g, 0); /* do a basic step */ luaC_step(L); @@ -1441,7 +1450,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { luaE_setdebt(g, debt); luaC_checkGC(L); } - g->gcrunning = oldrunning; /* restore previous state */ + g->gcstp = oldstp; /* restore previous state */ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ break; @@ -1459,7 +1468,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { break; } case LUA_GCISRUNNING: { - res = g->gcrunning; + res = gcrunning(g); break; } case LUA_GCGEN: { @@ -1511,7 +1520,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { LUA_API int lua_error (lua_State *L) { TValue *errobj; lua_lock(L); - errobj = s2v(L->top - 1); + errobj = s2v(L->top.p - 1); api_checknelems(L, 1); /* error object is the memory error message? */ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) @@ -1519,7 +1528,7 @@ LUA_API int lua_error (lua_State *L) { else luaG_errormsg(L); /* raise a regular error */ /* code unreachable; will unlock when control actually leaves the kernel */ - __builtin_unreachable(); + __builtin_unreachable(); // [jart] } @@ -1558,12 +1567,12 @@ LUA_API int lua_next (lua_State *L, int idx) { lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - more = luaH_next(L, t, L->top - 1); + more = luaH_next(L, t, L->top.p - 1); if (more) { api_incr_top(L); } else /* no more elements */ - L->top -= 1; /* remove key */ + L->top.p -= 1; /* remove key */ lua_unlock(L); return more; } @@ -1596,7 +1605,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { lua_lock(L); o = index2stack(L, idx); nresults = L->ci->nresults; - api_check(L, L->tbclist < o, "given index below or equal a marked one"); + api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ if (!hastocloseCfunc(nresults)) /* function not marked yet? */ L->ci->nresults = codeNresults(nresults); /* mark it */ @@ -1619,7 +1628,7 @@ LUA_API void lua_concat (lua_State *L, int n) { if (n > 0) luaV_concat(L, n); else { /* nothing to concatenate */ - setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */ + setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ api_incr_top(L); } luaC_checkGC(L); @@ -1638,7 +1647,7 @@ LUA_API void lua_len (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); - luaV_objlen(L, L->top, t); + luaV_objlen(L, L->top.p, t); api_incr_top(L); lua_unlock(L); } @@ -1725,7 +1734,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { lua_lock(L); api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); u = luaS_newudata(L, size, nuvalue); - setuvalue(L, s2v(L->top), u); + setuvalue(L, s2v(L->top.p), u); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); @@ -1733,6 +1742,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { } + static const char *aux_upvalue (TValue *fi, int n, TValue **val, GCObject **owner) { switch (ttypetag(fi)) { @@ -1750,7 +1760,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val, Proto *p = f->p; if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) return NULL; /* 'n' not in [1, p->sizeupvalues] */ - *val = f->upvals[n-1]->v; + *val = f->upvals[n-1]->v.p; if (owner) *owner = obj2gco(f->upvals[n - 1]); name = p->upvalues[n-1].name; return (name == NULL) ? "(no name)" : getstr(name); @@ -1776,7 +1786,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { lua_lock(L); name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); if (name) { - setobj2s(L, L->top, val); + setobj2s(L, L->top.p, val); api_incr_top(L); } lua_unlock(L); @@ -1806,8 +1816,8 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner); if (name) { - L->top--; - setobj(L, val, s2v(L->top)); + L->top.p--; + setobj(L, val, s2v(L->top.p)); luaC_barrier(L, owner, val); } lua_unlock(L); diff --git a/third_party/lua/lapi.h b/third_party/lua/lapi.h index ee8f01a2b..cf9e69085 100644 --- a/third_party/lua/lapi.h +++ b/third_party/lua/lapi.h @@ -5,22 +5,25 @@ #include "third_party/lua/lstate.h" -/* Increments 'L->top', checking for stack overflows */ -#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ +/* Increments 'L->top.p', checking for stack overflows */ +#define api_incr_top(L) {L->top.p++; \ + api_check(L, L->top.p <= L->ci->top.p, \ "stack overflow");} /* ** If a call returns too many multiple returns, the callee may not have ** stack space to accommodate all results. In this case, this macro -** increases its stack space ('L->ci->top'). +** increases its stack space ('L->ci->top.p'). */ #define adjustresults(L,nres) \ - { if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } + { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ + L->ci->top.p = L->top.p; } /* Ensure the stack has at least 'n' elements */ -#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ +#define api_checknelems(L,n) \ + api_check(L, (n) < (L->top.p - L->ci->func.p), \ "not enough elements in the stack") diff --git a/third_party/lua/lauxlib.c b/third_party/lua/lauxlib.c index 1fa74d1f2..4f9ce3a79 100644 --- a/third_party/lua/lauxlib.c +++ b/third_party/lua/lauxlib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lauxlib_c #define LUA_LIB + #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/mem/mem.h" @@ -38,34 +39,34 @@ __static_yoink("lua_notice"); /** - * @fileoverview The Auxiliary Library - * - * The auxiliary library provides several convenient functions to interface C - * with Lua. While the basic API provides the primitive functions for all - * interactions between C and Lua, the auxiliary library provides - * higher-level functions for some common tasks. - * - * All functions and types from the auxiliary library are defined in header - * file lauxlib.h and have a prefix luaL_. - * - * All functions in the auxiliary library are built on top of the basic API, - * and so they provide nothing that cannot be done with that API. - * Nevertheless, the use of the auxiliary library ensures more consistency to - * your code. - * - * Several functions in the auxiliary library use internally some extra stack - * slots. When a function in the auxiliary library uses less than five slots, - * it does not check the stack size; it simply assumes that there are enough - * slots. - * - * Several functions in the auxiliary library are used to check C function - * arguments. Because the error message is formatted for arguments (e.g., - * "bad argument #1"), you should not use these functions for other stack - * values. - * - * Functions called luaL_check* always raise an error if the check is not - * satisfied. - */ + * @fileoverview The Auxiliary Library + * + * The auxiliary library provides several convenient functions to interface C + * with Lua. While the basic API provides the primitive functions for all + * interactions between C and Lua, the auxiliary library provides + * higher-level functions for some common tasks. + * + * All functions and types from the auxiliary library are defined in header + * file lauxlib.h and have a prefix luaL_. + * + * All functions in the auxiliary library are built on top of the basic API, + * and so they provide nothing that cannot be done with that API. + * Nevertheless, the use of the auxiliary library ensures more consistency to + * your code. + * + * Several functions in the auxiliary library use internally some extra stack + * slots. When a function in the auxiliary library uses less than five slots, + * it does not check the stack size; it simply assumes that there are enough + * slots. + * + * Several functions in the auxiliary library are used to check C function + * arguments. Because the error message is formatted for arguments (e.g., + * "bad argument #1"), you should not use these functions for other stack + * values. + * + * Functions called luaL_check* always raise an error if the check is not + * satisfied. + */ #if !defined(MAX_SIZET) @@ -191,21 +192,21 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, while (lua_getstack(L1, level++, &ar)) { if (limit2show-- == 0) { /* too many levels? */ int n = last - level - LEVELS2 + 1; /* number of levels to skip */ - lua_pushfstring(L, "\n...(skipping %d levels)", n); + lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); luaL_addvalue(&b); /* add warning about skip */ level += n; /* and skip to last levels */ } else { lua_getinfo(L1, "Slnt", &ar); if (ar.currentline <= 0) - lua_pushfstring(L, "\n%s: in ", ar.short_src); + lua_pushfstring(L, "\n\t%s: in ", ar.short_src); else - lua_pushfstring(L, "\n%s:%d: in ", ar.short_src, ar.currentline); + lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); luaL_addvalue(&b); pushfuncname(L, &ar); luaL_addvalue(&b); if (ar.istailcall) - luaL_addstring(&b, "\n(...tail calls...)"); + luaL_addstring(&b, "\n\t(...tail calls...)"); } } luaL_pushresult(&b); @@ -764,13 +765,14 @@ static void newbox (lua_State *L) { /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' -** bytes. +** bytes. (The test for "not big enough" also gets the case when the +** computation of 'newsize' overflows.) */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { - size_t newsize = B->size * 2; /* double buffer size */ + size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ return luaL_error(B->L, "buffer too large"); - if (newsize < B->n + sz) /* double is not big enough? */ + if (newsize < B->n + sz) /* not big enough? */ newsize = B->n + sz; return newsize; } @@ -872,7 +874,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { ** box (if existent) is not on the top of the stack. So, instead of ** calling 'luaL_addlstring', it replicates the code using -2 as the ** last argument to 'prepbuffsize', signaling that the box is (or will -** be) bellow the string being added to the buffer. (Box creation can +** be) below the string being added to the buffer. (Box creation can ** trigger an emergency GC, so we should not remove the string from the ** stack before we have the space guaranteed.) */ @@ -1020,17 +1022,18 @@ static int errfile (lua_State *L, const char *what, int fnameindex) { } -static int skipBOM (LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ - int c; - lf->n = 0; - do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char *)p++) return c; - lf->buff[lf->n++] = c; /* to be read by the parser */ - } while (*p != '\0'); - lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ +/* +** Skip an optional BOM at the start of a stream. If there is an +** incomplete BOM (the first character is correct but the rest is +** not), returns the first character anyway to force an error +** (as no chunk can start with 0xEF). +*/ +static int skipBOM (FILE *f) { + int c = getc(f); /* read first character */ + if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */ + return getc(f); /* ignore BOM and return next char */ + else /* no (valid) BOM */ + return c; /* return first character */ } @@ -1041,13 +1044,13 @@ static int skipBOM (LoadF *lf) { ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ -static int skipcomment (LoadF *lf, int *cp) { - int c = *cp = skipBOM(lf); +static int skipcomment (FILE *f, int *cp) { + int c = *cp = skipBOM(f); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ - c = getc(lf->f); + c = getc(f); } while (c != EOF && c != '\n'); - *cp = getc(lf->f); /* skip end-of-line, if present */ + *cp = getc(f); /* next character after comment, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ @@ -1084,12 +1087,16 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } - if (skipcomment(&lf, &c)) /* read initial portion */ - lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.n = 0; + if (skipcomment(lf.f, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */ + if (c == LUA_SIGNATURE[0]) { /* binary file? */ + lf.n = 0; /* remove possible newline */ + if (filename) { /* "real" file? */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); - skipcomment(&lf, &c); /* re-read initial portion */ + skipcomment(lf.f, &c); /* re-read initial portion */ + } } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ @@ -1236,6 +1243,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { * the result of the call as its result. */ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + idx = lua_absindex(L,idx); if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ if (!lua_isstring(L, -1)) luaL_error(L, "'__tostring' must return a string"); diff --git a/third_party/lua/lauxlib.h b/third_party/lua/lauxlib.h index 194cf3e62..c828e8f66 100644 --- a/third_party/lua/lauxlib.h +++ b/third_party/lua/lauxlib.h @@ -1,10 +1,12 @@ #ifndef lauxlib_h #define lauxlib_h + #include "libc/assert.h" #include "libc/stdio/stdio.h" #include "third_party/lua/lua.h" #include "third_party/lua/luaconf.h" + /* global table */ #define LUA_GNAME "_G" @@ -91,7 +93,7 @@ LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); -LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, +LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, const char *p, const char *r); LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r); @@ -146,6 +148,14 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, #define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) +/* +** Perform arithmetic operations on lua_Integer values with wrap-around +** semantics, as the Lua core does. +*/ +#define luaL_intop(op,v1,v2) \ + ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) + + /* push the value used to represent failure/error */ #define luaL_pushfail(L) lua_pushnil(L) @@ -158,6 +168,7 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, #if defined LUAI_ASSERT #define lua_assert(c) assert(c) #else + // [jart] #define lua_assert(c) unassert(c) #endif diff --git a/third_party/lua/lbaselib.c b/third_party/lua/lbaselib.c index 5e936c406..10a0e75f1 100644 --- a/third_party/lua/lbaselib.c +++ b/third_party/lua/lbaselib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lbaselib_c #define LUA_LIB + #include "libc/str/str.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" @@ -196,12 +197,20 @@ static int luaB_rawset (lua_State *L) { static int pushmode (lua_State *L, int oldmode) { + if (oldmode == -1) + luaL_pushfail(L); /* invalid call to 'lua_gc' */ + else lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational"); return 1; } +/* +** check whether call to 'lua_gc' was valid (not inside a finalizer) +*/ +#define checkvalres(res) { if (res == -1) break; } + static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", "count", "step", "setpause", "setstepmul", @@ -214,12 +223,14 @@ static int luaB_collectgarbage (lua_State *L) { case LUA_GCCOUNT: { int k = lua_gc(L, o); int b = lua_gc(L, LUA_GCCOUNTB); + checkvalres(k); lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); return 1; } case LUA_GCSTEP: { int step = (int)luaL_optinteger(L, 2, 0); int res = lua_gc(L, o, step); + checkvalres(res); lua_pushboolean(L, res); return 1; } @@ -227,11 +238,13 @@ static int luaB_collectgarbage (lua_State *L) { case LUA_GCSETSTEPMUL: { int p = (int)luaL_optinteger(L, 2, 0); int previous = lua_gc(L, o, p); + checkvalres(previous); lua_pushinteger(L, previous); return 1; } case LUA_GCISRUNNING: { int res = lua_gc(L, o); + checkvalres(res); lua_pushboolean(L, res); return 1; } @@ -248,10 +261,13 @@ static int luaB_collectgarbage (lua_State *L) { } default: { int res = lua_gc(L, o); + checkvalres(res); lua_pushinteger(L, res); return 1; } } + luaL_pushfail(L); /* invalid call (inside a finalizer) */ + return 1; } @@ -275,6 +291,11 @@ static int luaB_next (lua_State *L) { } +static int pairscont (lua_State *L, int status, lua_KContext k) { + (void)L; (void)status; (void)k; /* unused */ + return 3; +} + static int luaB_pairs (lua_State *L) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ @@ -284,7 +305,7 @@ static int luaB_pairs (lua_State *L) { } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ - lua_call(L, 1, 3); /* get 3 values from metamethod */ + lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ } return 3; } @@ -294,7 +315,8 @@ static int luaB_pairs (lua_State *L) { ** Traversal function for 'ipairs' */ static int ipairsaux (lua_State *L) { - lua_Integer i = luaL_checkinteger(L, 2) + 1; + lua_Integer i = luaL_checkinteger(L, 2); + i = luaL_intop(+, i, 1); lua_pushinteger(L, i); return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } diff --git a/third_party/lua/lcode.c b/third_party/lua/lcode.c index c6c9c0a2b..7f7f00d8c 100644 --- a/third_party/lua/lcode.c +++ b/third_party/lua/lcode.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lcode_c #define LUA_CORE + #include "libc/fmt/conv.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -595,24 +596,41 @@ static int stringK (FuncState *fs, TString *s) { /* ** Add an integer to list of constants and return its index. -** Integers use userdata as keys to avoid collision with floats with -** same value; conversion to 'void*' is used only for hashing, so there -** are no "precision" problems. */ static int luaK_intK (FuncState *fs, lua_Integer n) { - TValue k, o; - setpvalue(&k, cast_voidp(cast_sizet(n))); + TValue o; setivalue(&o, n); - return addk(fs, &k, &o); + return addk(fs, &o, &o); /* use integer itself as key */ } /* -** Add a float to list of constants and return its index. +** Add a float to list of constants and return its index. Floats +** with integral values need a different key, to avoid collision +** with actual integers. To that, we add to the number its smaller +** power-of-two fraction that is still significant in its scale. +** For doubles, that would be 1/2^52. +** (This method is not bulletproof: there may be another float +** with that value, and for floats larger than 2^53 the result is +** still an integer. At worst, this only wastes an entry with +** a duplicate.) */ static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; + lua_Integer ik; setfltvalue(&o, r); + if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */ return addk(fs, &o, &o); /* use number itself as key */ + else { /* must build an alternative key */ + const int nbm = l_floatatt(MANT_DIG); + const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); + const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */ + TValue kv; + setfltvalue(&kv, k); + /* result is not an integral value, unless value is too large */ + lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) || + l_mathop(fabs)(r) >= l_mathop(1e6)); + return addk(fs, &kv, &o); + } } @@ -1348,6 +1366,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1, } +/* +** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) { + lua_assert(baser <= opr && + ((baser == OPR_ADD && opr <= OPR_SHR) || + (baser == OPR_LT && opr <= OPR_LE))); + return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base)); +} + + +/* +** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode unopr2op (UnOpr opr) { + return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) + + cast_int(OP_UNM)); +} + + +/* +** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM) +*/ +l_sinline TMS binopr2TM (BinOpr opr) { + lua_assert(OPR_ADD <= opr && opr <= OPR_SHR); + return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD)); +} + + /* ** Emit code for unary expressions that "produce values" ** (everything but 'not'). @@ -1386,12 +1433,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, ** Emit code for binary expressions that "produce values" over ** two registers. */ -static void codebinexpval (FuncState *fs, OpCode op, +static void codebinexpval (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { - int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADD); + int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */ + /* 'e1' must be already in a register or it is a constant */ + lua_assert((VNIL <= e1->k && e1->k <= VKSTR) || + e1->k == VNONRELOC || e1->k == VRELOC); lua_assert(OP_ADD <= op && op <= OP_SHR); - finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, - cast(TMS, (op - OP_ADD) + TM_ADD)); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr)); } @@ -1407,6 +1457,18 @@ static void codebini (FuncState *fs, OpCode op, } +/* +** Code binary operators with K operand. +*/ +static void codebinK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = binopr2TM(opr); + int v2 = e2->u.info; /* K index */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); +} + + /* Try to code a binary operator negating its second operand. ** For the metamethod, 2nd operand must keep its original value. */ @@ -1434,24 +1496,27 @@ static void swapexps (expdesc *e1, expdesc *e2) { } +/* +** Code binary operators with no constant operand. +*/ +static void codebinNoK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, opr, e1, e2, line); /* use standard operators */ +} + + /* ** Code arithmetic operators ('+', '-', ...). If second operand is a ** constant in the proper range, use variant opcodes with K operands. */ static void codearith (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int flip, int line) { - TMS event = cast(TMS, opr + TM_ADD); - if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ - int v2 = e2->u.info; /* K index */ - OpCode op = cast(OpCode, opr + OP_ADDK); - finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); - } - else { /* 'e2' is neither an immediate nor a K operand */ - OpCode op = cast(OpCode, opr + OP_ADD); - if (flip) - swapexps(e1, e2); /* back to original order */ - codebinexpval(fs, op, e1, e2, line); /* use standard operators */ - } + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* 'e2' is neither an immediate nor a K operand */ + codebinNoK(fs, opr, e1, e2, flip, line); } @@ -1468,35 +1533,27 @@ static void codecommutative (FuncState *fs, BinOpr op, flip = 1; } if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ - codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD); + codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD); else codearith(fs, op, e1, e2, flip, line); } /* -** Code bitwise operations; they are all associative, so the function +** Code bitwise operations; they are all commutative, so the function ** tries to put an integer constant as the 2nd operand (a K operand). */ static void codebitwise (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { int flip = 0; - int v2; - OpCode op; - if (e1->k == VKINT && luaK_exp2RK(fs, e1)) { + if (e1->k == VKINT) { swapexps(e1, e2); /* 'e2' will be the constant operand */ flip = 1; } - else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */ - op = cast(OpCode, opr + OP_ADD); - codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */ - return; - } - v2 = e2->u.info; /* index in K array */ - op = cast(OpCode, opr + OP_ADDK); - lua_assert(ttisinteger(&fs->f->k[v2])); - finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, - cast(TMS, opr + TM_ADD)); + if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* no constants */ + codebinNoK(fs, opr, e1, e2, flip, line); } @@ -1504,25 +1561,27 @@ static void codebitwise (FuncState *fs, BinOpr opr, ** Emit code for order comparisons. When using an immediate operand, ** 'isfloat' tells whether the original value was a float. */ -static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { +static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { int r1, r2; int im; int isfloat = 0; + OpCode op; if (isSCnumber(e2, &im, &isfloat)) { /* use immediate operand */ r1 = luaK_exp2anyreg(fs, e1); r2 = im; - op = cast(OpCode, (op - OP_LT) + OP_LTI); + op = binopr2op(opr, OPR_LT, OP_LTI); } else if (isSCnumber(e1, &im, &isfloat)) { /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ r1 = luaK_exp2anyreg(fs, e2); r2 = im; - op = (op == OP_LT) ? OP_GTI : OP_GEI; + op = binopr2op(opr, OPR_LT, OP_GTI); } else { /* regular case, compare two registers */ r1 = luaK_exp2anyreg(fs, e1); r2 = luaK_exp2anyreg(fs, e2); + op = binopr2op(opr, OPR_LT, OP_LT); } freeexps(fs, e1, e2); e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); @@ -1548,7 +1607,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { op = OP_EQI; r2 = im; /* immediate operand */ } - else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */ + else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */ op = OP_EQK; r2 = e2->u.info; /* constant index */ } @@ -1565,16 +1624,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { /* ** Apply prefix operation 'op' to expression 'e'. */ -void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { +void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) { static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; luaK_dischargevars(fs, e); - switch (op) { + switch (opr) { case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ - if (constfolding(fs, op + LUA_OPUNM, e, &ef)) + if (constfolding(fs, opr + LUA_OPUNM, e, &ef)) break; /* else */ /* FALLTHROUGH */ case OPR_LEN: - codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); + codeunexpval(fs, unopr2op(opr), e, line); break; case OPR_NOT: codenot(fs, e); break; default: lua_assert(0); @@ -1608,7 +1667,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { case OPR_SHL: case OPR_SHR: { if (!tonumeral(v, NULL)) luaK_exp2anyreg(fs, v); - /* else keep numeral, which may be folded with 2nd operand */ + /* else keep numeral, which may be folded or used as an immediate + operand */ break; } case OPR_EQ: case OPR_NE: { @@ -1703,30 +1763,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr, /* coded as (r1 >> -I) */; } else /* regular case (two registers) */ - codebinexpval(fs, OP_SHL, e1, e2, line); + codebinexpval(fs, opr, e1, e2, line); break; } case OPR_SHR: { if (isSCint(e2)) codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ else /* regular case (two registers) */ - codebinexpval(fs, OP_SHR, e1, e2, line); + codebinexpval(fs, opr, e1, e2, line); break; } case OPR_EQ: case OPR_NE: { codeeq(fs, opr, e1, e2); break; } - case OPR_LT: case OPR_LE: { - OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); - codeorder(fs, op, e1, e2); - break; - } case OPR_GT: case OPR_GE: { /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ - OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); swapexps(e1, e2); - codeorder(fs, op, e1, e2); + opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT); + } /* FALLTHROUGH */ + case OPR_LT: case OPR_LE: { + codeorder(fs, opr, e1, e2); break; } default: lua_assert(0); diff --git a/third_party/lua/lcorolib.c b/third_party/lua/lcorolib.c index be6f75f5d..25236fd68 100644 --- a/third_party/lua/lcorolib.c +++ b/third_party/lua/lcorolib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lcorolib_c #define LUA_LIB + #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" @@ -92,9 +93,9 @@ static int luaB_auxwrap (lua_State *L) { if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ - stat = lua_resetthread(co); /* close its tbc variables */ + stat = lua_closethread(co, L); /* close its tbc variables */ lua_assert(stat != LUA_OK); - lua_xmove(co, L, 1); /* copy error message */ + lua_xmove(co, L, 1); /* move error message to the caller */ } if (stat != LUA_ERRMEM && /* not a memory error and ... */ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ @@ -188,14 +189,14 @@ static int luaB_close (lua_State *L) { int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { - status = lua_resetthread(co); + status = lua_closethread(co, L); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; } else { lua_pushboolean(L, 0); - lua_xmove(co, L, 1); /* copy error message */ + lua_xmove(co, L, 1); /* move error message */ return 2; } } diff --git a/third_party/lua/lctype.h b/third_party/lua/lctype.h index 1204fe0d3..563e31475 100644 --- a/third_party/lua/lctype.h +++ b/third_party/lua/lctype.h @@ -1,7 +1,9 @@ #ifndef lctype_h #define lctype_h + #include "third_party/lua/lua.h" + /* ** In ASCII, this 'ltolower' is correct for alphabetic characters and ** for '.'. That is enough for Lua needs. ('check_exp' ensures that @@ -12,6 +14,7 @@ check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ (c) | ('A' ^ 'a')) +// [jart] #define lisdigit(C) \ ({ \ unsigned char c_ = (C); \ diff --git a/third_party/lua/ldblib.c b/third_party/lua/ldblib.c index 74f6ed32b..08a5e89cb 100644 --- a/third_party/lua/ldblib.c +++ b/third_party/lua/ldblib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ldblib_c #define LUA_LIB + #include "libc/str/str.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" @@ -495,3 +496,4 @@ LUAMOD_API int luaopen_debug (lua_State *L) { luaL_newlib(L, dblib); return 1; } + diff --git a/third_party/lua/ldebug.c b/third_party/lua/ldebug.c index 8e103095f..8499a3f74 100644 --- a/third_party/lua/ldebug.c +++ b/third_party/lua/ldebug.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ldebug_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/lapi.h" #include "third_party/lua/lcode.h" @@ -48,7 +49,7 @@ __static_yoink("lua_notice"); #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) -static const char *funcnamefromcode (lua_State *L, CallInfo *ci, +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name); @@ -78,7 +79,7 @@ static int getbaseline (const Proto *f, int pc, int *basepc) { } else { int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ - /* estimate must be a lower bond of the correct base */ + /* estimate must be a lower bound of the correct base */ lua_assert(i < 0 || (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) @@ -196,10 +197,10 @@ static const char *upvalname (const Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { - if (clLvalue(s2v(ci->func))->p->is_vararg) { + if (clLvalue(s2v(ci->func.p))->p->is_vararg) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ - *pos = ci->func - nextra - (n + 1); + *pos = ci->func.p - nextra - (n + 1); return "(vararg)"; /* generic name for any vararg */ } } @@ -208,7 +209,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) { const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { - StkId base = ci->func + 1; + StkId base = ci->func.p + 1; const char *name = NULL; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ @@ -217,7 +218,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); } if (name == NULL) { /* no 'standard' name? */ - StkId limit = (ci == L->ci) ? L->top : ci->next->func; + StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p; if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ /* generic name for any valid slot */ name = isLua(ci) ? "(temporary)" : "(C temporary)"; @@ -235,16 +236,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); if (ar == NULL) { /* information about non-active function? */ - if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ + if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ - name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); + name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0); } else { /* active function; get information through 'ar' */ StkId pos = NULL; /* to avoid warnings */ name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobjs2s(L, L->top, pos); + setobjs2s(L, L->top.p, pos); api_incr_top(L); } } @@ -259,8 +260,8 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { - setobjs2s(L, pos, L->top - 1); - L->top--; /* pop value */ + setobjs2s(L, pos, L->top.p - 1); + L->top.p--; /* pop value */ } lua_unlock(L); return name; @@ -303,7 +304,7 @@ static int nextline (const Proto *p, int currentline, int pc) { static void collectvalidlines (lua_State *L, Closure *f) { if (noLuaClosure(f)) { - setnilvalue(s2v(L->top)); + setnilvalue(s2v(L->top.p)); api_incr_top(L); } else { @@ -312,10 +313,17 @@ static void collectvalidlines (lua_State *L, Closure *f) { const Proto *p = f->l.p; int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ - sethvalue2s(L, L->top, t); /* push it on stack */ + sethvalue2s(L, L->top.p, t); /* push it on stack */ api_incr_top(L); setbtvalue(&v); /* boolean 'true' to be the value of all indices */ - for (i = 0; i < p->sizelineinfo; i++) { /* for all instructions */ + if (!p->is_vararg) /* regular function? */ + i = 0; /* consider all instructions */ + else { /* vararg function */ + lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); + currentline = nextline(p, currentline, 0); + i = 1; /* skip first instruction (OP_VARARGPREP) */ + } + for (; i < p->sizelineinfo; i++) { /* for each instruction */ currentline = nextline(p, currentline, i); /* get its line */ luaH_setint(L, t, currentline, &v); /* table[line] = true */ } @@ -324,15 +332,9 @@ static void collectvalidlines (lua_State *L, Closure *f) { static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { - if (ci == NULL) /* no 'ci'? */ - return NULL; /* no info */ - else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */ - *name = "__gc"; - return "metamethod"; /* report it as such */ - } - /* calling function is a known Lua function? */ - else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) - return funcnamefromcode(L, ci->previous, name); + /* calling function is a known function? */ + if (ci != NULL && !(ci->callstatus & CIST_TAIL)) + return funcnamefromcall(L, ci->previous, name); else return NULL; /* no way to find a name */ } @@ -401,20 +403,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { lua_lock(L); if (*what == '>') { ci = NULL; - func = s2v(L->top - 1); + func = s2v(L->top.p - 1); api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ - L->top--; /* pop function */ + L->top.p--; /* pop function */ } else { ci = ar->i_ci; - func = s2v(ci->func); + func = s2v(ci->func.p); lua_assert(ttisfunction(func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { - setobj2s(L, L->top, func); + setobj2s(L, L->top.p, func); api_incr_top(L); } if (strchr(what, 'L')) @@ -604,16 +606,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg, ** Returns what the name is (e.g., "for iterator", "method", ** "metamethod") and sets '*name' to point to the name. */ -static const char *funcnamefromcode (lua_State *L, CallInfo *ci, - const char **name) { +static const char *funcnamefromcode (lua_State *L, const Proto *p, + int pc, const char **name) { TMS tm = (TMS)0; /* (initial value avoids warnings) */ - const Proto *p = ci_func(ci)->p; /* calling function */ - int pc = currentpc(ci); /* calling instruction index */ Instruction i = p->code[pc]; /* calling instruction */ - if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ - *name = "?"; - return "hook"; - } switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: @@ -650,23 +646,44 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci, return "metamethod"; } + +/* +** Try to find a name for a function based on how it was called. +*/ +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, + const char **name) { + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } + else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */ + *name = "__gc"; + return "metamethod"; /* report it as such */ + } + else if (isLua(ci)) + return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name); + else + return NULL; +} + /* }====================================================== */ /* -** Check whether pointer 'o' points to some value in the stack -** frame of the current function. Because 'o' may not point to a -** value in this stack, we cannot compare it with the region -** boundaries (undefined behaviour in ISO C). +** Check whether pointer 'o' points to some value in the stack frame of +** the current function and, if so, returns its index. Because 'o' may +** not point to a value in this stack, we cannot compare it with the +** region boundaries (undefined behavior in ISO C). */ -static int isinstack (CallInfo *ci, const TValue *o) { - StkId pos; - for (pos = ci->func + 1; pos < ci->top; pos++) { - if (o == s2v(pos)) - return 1; +static int instack (CallInfo *ci, const TValue *o) { + int pos; + StkId base = ci->func.p + 1; + for (pos = 0; base + pos < ci->top.p; pos++) { + if (o == s2v(base + pos)) + return pos; } - return 0; /* not found */ + return -1; /* not found */ } @@ -680,7 +697,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { - if (c->upvals[i]->v == o) { + if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); return "upvalue"; } @@ -689,36 +706,64 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, } +static const char *formatvarinfo (lua_State *L, const char *kind, + const char *name) { + if (kind == NULL) + return ""; /* no information */ + else + return luaO_pushfstring(L, " (%s '%s')", kind, name); +} + +/* +** Build a string with a "description" for the value 'o', such as +** "variable 'x'" or "upvalue 'y'". +*/ static const char *varinfo (lua_State *L, const TValue *o) { - const char *name = NULL; /* to avoid warnings */ CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ - if (!kind && isinstack(ci, o)) /* no? try a register */ - kind = getobjname(ci_func(ci)->p, currentpc(ci), - cast_int(cast(StkId, o) - (ci->func + 1)), &name); + if (!kind) { /* not an upvalue? */ + int reg = instack(ci, o); /* try a register */ + if (reg >= 0) /* is 'o' a register? */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name); + } } - return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : ""; + return formatvarinfo(L, kind, name); } -l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { +/* +** Raise a type error +*/ +static l_noret typeerror (lua_State *L, const TValue *o, const char *op, + const char *extra) { const char *t = luaT_objtypename(L, o); - luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra); } +/* +** Raise a type error with "standard" information about the faulty +** object 'o' (using 'varinfo'). +*/ +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + typeerror(L, o, op, varinfo(L, o)); +} + + +/* +** Raise an error for calling a non-callable object. Try to find a name +** for the object based on how it was called ('funcnamefromcall'); if it +** cannot get a name there, try 'varinfo'. +*/ l_noret luaG_callerror (lua_State *L, const TValue *o) { CallInfo *ci = L->ci; const char *name = NULL; /* to avoid warnings */ - const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL; - if (what != NULL) { - const char *t = luaT_objtypename(L, o); - luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t); - } - else - luaG_typeerror(L, o, "call"); + const char *kind = funcnamefromcall(L, ci, &name); + const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o); + typeerror(L, o, "call", extra); } @@ -780,10 +825,10 @@ l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); lua_assert(ttisfunction(s2v(errfunc))); - setobjs2s(L, L->top, L->top - 1); /* move argument */ - setobjs2s(L, L->top - 1, errfunc); /* push function */ - L->top++; /* assume EXTRA_STACK */ - luaD_callnoyield(L, L->top - 2, 1); /* call it */ + setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */ + setobjs2s(L, L->top.p - 1, errfunc); /* push function */ + L->top.p++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } @@ -797,8 +842,11 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); - if (isLua(ci)) /* if Lua function, add source:line information */ + if (isLua(ci)) { /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ + L->top.p--; + } luaG_errormsg(L); } @@ -815,7 +863,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) { if (p->lineinfo == NULL) /* no debug information? */ return 0; if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ - int delta = 0; /* line diference */ + int delta = 0; /* line difference */ int pc = oldpc; for (;;) { int lineinfo = p->lineinfo[++pc]; @@ -842,7 +890,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) { ** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' ** at most causes an extra call to a line hook.) ** This function is not "Protected" when called, so it should correct -** 'L->top' before calling anything that can run the GC. +** 'L->top.p' before calling anything that can run the GC. */ int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; @@ -865,7 +913,7 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) { return 1; /* do not call hook again (VM yielded, so it did not move) */ } if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ - L->top = ci->top; /* correct top */ + L->top.p = ci->top.p; /* correct top */ if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ if (mask & LUA_MASKLINE) { diff --git a/third_party/lua/ldebug.h b/third_party/lua/ldebug.h index 2e5d59b1b..8cb77c579 100644 --- a/third_party/lua/ldebug.h +++ b/third_party/lua/ldebug.h @@ -1,15 +1,15 @@ #ifndef ldebug_h #define ldebug_h -#include "third_party/lua/lstate.h" +#include "third_party/lua/lstate.h" #define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) /* Active Lua function (given call info) */ -#define ci_func(ci) (clLvalue(s2v((ci)->func))) +#define ci_func(ci) (clLvalue(s2v((ci)->func.p))) #define resethookcount(L) (L->hookcount = L->basehookcount) diff --git a/third_party/lua/ldo.c b/third_party/lua/ldo.c index abfa70719..9482e96a4 100644 --- a/third_party/lua/ldo.c +++ b/third_party/lua/ldo.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,7 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ldo_c #define LUA_CORE -#include "third_party/lua/ldo.h" + #include "libc/intrin/weaken.h" #include "libc/log/log.h" #include "libc/mem/gc.h" @@ -82,14 +82,14 @@ __static_yoink("lua_notice"); #elif defined(LUA_USE_POSIX) /* }{ */ /* in POSIX, try _longjmp/_setjmp (more efficient) */ -#define LUAI_THROW(L,c) gclongjmp((c)->b, 1) +#define LUAI_THROW(L,c) gclongjmp((c)->b, 1) // [jart] #define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #else /* }{ */ /* ISO C handling with long jumps */ -#define LUAI_THROW(L,c) gclongjmp((c)->b, 1) +#define LUAI_THROW(L,c) gclongjmp((c)->b, 1) // [jart] #define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf @@ -123,11 +123,11 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { } default: { lua_assert(errorstatus(errcode)); /* real error */ - setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ break; } } - L->top = oldtop + 1; + L->top.p = oldtop + 1; } @@ -140,7 +140,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { global_State *g = G(L); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ if (g->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ + setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ @@ -148,7 +148,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { lua_unlock(L); g->panic(L); /* call panic function (last chance to jump out) */ } - if (_weaken(__die)) _weaken(__die)(); + if (_weaken(__die)) _weaken(__die)(); // [jart] _Exit(41); } } @@ -177,16 +177,38 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { ** Stack reallocation ** =================================================================== */ -static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { + + +/* +** Change all pointers to the stack into offsets. +*/ +static void relstack (lua_State *L) { CallInfo *ci; UpVal *up; - L->top = (L->top - oldstack) + newstack; - L->tbclist = (L->tbclist - oldstack) + newstack; + L->top.offset = savestack(L, L->top.p); + L->tbclist.offset = savestack(L, L->tbclist.p); for (up = L->openupval; up != NULL; up = up->u.open.next) - up->v = s2v((uplevel(up) - oldstack) + newstack); + up->v.offset = savestack(L, uplevel(up)); for (ci = L->ci; ci != NULL; ci = ci->previous) { - ci->top = (ci->top - oldstack) + newstack; - ci->func = (ci->func - oldstack) + newstack; + ci->top.offset = savestack(L, ci->top.p); + ci->func.offset = savestack(L, ci->func.p); + } +} + + +/* +** Change back all offsets into pointers. +*/ +static void correctstack (lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.p = restorestack(L, L->top.offset); + L->tbclist.p = restorestack(L, L->tbclist.offset); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(restorestack(L, up->v.offset)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = restorestack(L, ci->top.offset); + ci->func.p = restorestack(L, ci->func.offset); if (isLua(ci)) ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } @@ -196,44 +218,45 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { /* some space for error handling */ #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) - /* -** Reallocate the stack to a new size, correcting all pointers into -** it. (There are pointers to a stack from its upvalues, from its list -** of call infos, plus a few individual pointers.) The reallocation is -** done in two steps (allocation + free) because the correction must be -** done while both addresses (the old stack and the new one) are valid. -** (In ISO C, any pointer use after the pointer has been deallocated is -** undefined behavior.) +** Reallocate the stack to a new size, correcting all pointers into it. +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before the reallocation, all pointers are +** changed to offsets, and after the reallocation they are changed back +** to pointers. As during the reallocation the pointers are invalid, the +** reallocation cannot run emergency collections. +** ** In case of allocation error, raise an error or return false according ** to 'raiseerror'. */ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int oldsize = stacksize(L); int i; - StkId newstack = luaM_reallocvector(L, NULL, 0, - newsize + EXTRA_STACK, StackValue); + StkId newstack; + int oldgcstop = G(L)->gcstopem; lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + relstack(L); /* change pointers to offsets */ + G(L)->gcstopem = 1; /* stop emergency collection */ + newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, + newsize + EXTRA_STACK, StackValue); + G(L)->gcstopem = oldgcstop; /* restore emergency collection */ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ + correctstack(L); /* change offsets back to pointers */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } - /* number of elements to be copied to the new stack */ - i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK; - memcpy(newstack, L->stack, i * sizeof(StackValue)); - for (; i < newsize + EXTRA_STACK; i++) + L->stack.p = newstack; + correctstack(L); /* change offsets back to pointers */ + L->stack_last.p = L->stack.p + newsize; + for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) setnilvalue(s2v(newstack + i)); /* erase new segment */ - correctstack(L, L->stack, newstack); - luaM_freearray(L, L->stack, oldsize + EXTRA_STACK); - L->stack = newstack; - L->stack_last = L->stack + newsize; return 1; } /* -** Try to grow the stack by at least 'n' elements. when 'raiseerror' +** Try to grow the stack by at least 'n' elements. When 'raiseerror' ** is true, raises any error; otherwise, return 0 in case of errors. */ int luaD_growstack (lua_State *L, int n, int raiseerror) { @@ -247,35 +270,38 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { luaD_throw(L, LUA_ERRERR); /* error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } - else { + else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ int newsize = 2 * size; /* tentative new size */ - int needed = cast_int(L->top - L->stack) + n; + int needed = cast_int(L->top.p - L->stack.p) + n; if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ newsize = LUAI_MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; if (l_likely(newsize <= LUAI_MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); - else { /* stack overflow */ + } + /* else stack overflow */ /* add extra size to be able to handle the error message */ luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); if (raiseerror) luaG_runerror(L, "stack overflow"); return 0; } - } -} +/* +** Compute how much of the stack is being used, by computing the +** maximum top of all call frames in the stack and the current top. +*/ static int stackinuse (lua_State *L) { CallInfo *ci; int res; - StkId lim = L->top; + StkId lim = L->top.p; for (ci = L->ci; ci != NULL; ci = ci->previous) { - if (lim < ci->top) lim = ci->top; + if (lim < ci->top.p) lim = ci->top.p; } - lua_assert(lim <= L->stack_last); - res = cast_int(lim - L->stack) + 1; /* part of stack in use */ + lua_assert(lim <= L->stack_last.p + EXTRA_STACK); + res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */ if (res < LUA_MINSTACK) res = LUA_MINSTACK; /* ensure a minimum size */ return res; @@ -293,17 +319,13 @@ static int stackinuse (lua_State *L) { */ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); - int nsize = inuse * 2; /* proposed new size */ - int max = inuse * 3; /* maximum "reasonable" size */ - if (max > LUAI_MAXSTACK) { - max = LUAI_MAXSTACK; /* respect stack limit */ - if (nsize > LUAI_MAXSTACK) - nsize = LUAI_MAXSTACK; - } + int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; /* if thread is currently not handling a stack overflow and its size is larger than maximum "reasonable" size, shrink it */ - if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) + if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; luaD_reallocstack(L, nsize, 0); /* ok if that fails */ + } else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ luaE_shrinkCI(L); /* shrink CI list */ @@ -312,7 +334,7 @@ void luaD_shrinkstack (lua_State *L) { void luaD_inctop (lua_State *L) { luaD_checkstack(L, 1); - L->top++; + L->top.p++; } /* }================================================================== */ @@ -329,8 +351,8 @@ void luaD_hook (lua_State *L, int event, int line, if (hook && L->allowhook) { /* make sure there is a hook */ int mask = CIST_HOOKED; CallInfo *ci = L->ci; - ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */ - ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */ + ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ + ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ lua_Debug ar; ar.event = event; ar.currentline = line; @@ -340,11 +362,11 @@ void luaD_hook (lua_State *L, int event, int line, ci->u2.transferinfo.ftransfer = ftransfer; ci->u2.transferinfo.ntransfer = ntransfer; } - if (isLua(ci) && L->top < ci->top) - L->top = ci->top; /* protect entire activation register */ + if (isLua(ci) && L->top.p < ci->top.p) + L->top.p = ci->top.p; /* protect entire activation register */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ - if (ci->top < L->top + LUA_MINSTACK) - ci->top = L->top + LUA_MINSTACK; + if (ci->top.p < L->top.p + LUA_MINSTACK) + ci->top.p = L->top.p + LUA_MINSTACK; L->allowhook = 0; /* cannot call hooks inside a hook */ ci->callstatus |= mask; lua_unlock(L); @@ -352,8 +374,8 @@ void luaD_hook (lua_State *L, int event, int line, lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; - ci->top = restorestack(L, ci_top); - L->top = restorestack(L, top); + ci->top.p = restorestack(L, ci_top); + L->top.p = restorestack(L, top); ci->callstatus &= ~mask; } } @@ -384,7 +406,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { */ static void rethook (lua_State *L, CallInfo *ci, int nres) { if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ - StkId firstres = L->top - nres; /* index of first result */ + StkId firstres = L->top.p - nres; /* index of first result */ int delta = 0; /* correction for vararg functions */ int ftransfer; if (isLua(ci)) { @@ -392,10 +414,10 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { if (p->is_vararg) delta = ci->u.l.nextraargs + p->numparams + 1; } - ci->func += delta; /* if vararg, back to virtual 'func' */ - ftransfer = cast(unsigned short, firstres - ci->func); + ci->func.p += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast(unsigned short, firstres - ci->func.p); luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ - ci->func -= delta; + ci->func.p -= delta; } if (isLua(ci = ci->previous)) L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ @@ -407,15 +429,18 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { ** stack, below original 'func', so that 'luaD_precall' can call it. Raise ** an error if there is no '__call' metafield. */ -void luaD_tryfuncTM (lua_State *L, StkId func) { - const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); +StkId luaD_tryfuncTM (lua_State *L, StkId func) { + const TValue *tm; StkId p; + checkstackGCp(L, 1, func); /* space for metamethod */ + tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ if (l_unlikely(ttisnil(tm))) luaG_callerror(L, s2v(func)); /* nothing to call */ - for (p = L->top; p > func; p--) /* open space for metamethod */ + for (p = L->top.p; p > func; p--) /* open space for metamethod */ setobjs2s(L, p, p-1); - L->top++; /* stack space pre-allocated by the caller */ + L->top.p++; /* stack space pre-allocated by the caller */ setobj2s(L, func, tm); /* metamethod is the new function to be called */ + return func; } @@ -425,33 +450,34 @@ void luaD_tryfuncTM (lua_State *L, StkId func) { ** expressions, multiple results for tail calls/single parameters) ** separated. */ -static void moveresults (lua_State *L, StkId res, int nres, int wanted) { +l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { StkId firstresult; int i; switch (wanted) { /* handle typical cases separately */ case 0: /* no values needed */ - L->top = res; + L->top.p = res; return; case 1: /* one value needed */ if (nres == 0) /* no results? */ setnilvalue(s2v(res)); /* adjust with nil */ else /* at least one result */ - setobjs2s(L, res, L->top - nres); /* move it to proper place */ - L->top = res + 1; + setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ + L->top.p = res + 1; return; case LUA_MULTRET: wanted = nres; /* we want all results */ break; default: /* two/more results and/or to-be-closed variables */ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ - ptrdiff_t savedres = savestack(L, res); L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ L->ci->u2.nres = nres; - luaF_close(L, res, CLOSEKTOP, 1); + res = luaF_close(L, res, CLOSEKTOP, 1); L->ci->callstatus &= ~CIST_CLSRET; - if (L->hookmask) /* if needed, call hook after '__close's */ + if (L->hookmask) { /* if needed, call hook after '__close's */ + ptrdiff_t savedres = savestack(L, res); rethook(L, L->ci, nres); - res = restorestack(L, savedres); /* close and hook can move stack */ + res = restorestack(L, savedres); /* hook can move stack */ + } wanted = decodeNresults(wanted); if (wanted == LUA_MULTRET) wanted = nres; /* we want all results */ @@ -459,14 +485,14 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) { break; } /* generic case */ - firstresult = L->top - nres; /* index of first result */ + firstresult = L->top.p - nres; /* index of first result */ if (nres > wanted) /* extra results? */ nres = wanted; /* don't need them */ for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstresult + i); for (; i < wanted; i++) /* complete wanted number of results */ setnilvalue(s2v(res + i)); - L->top = res + wanted; /* top points after the last result */ + L->top.p = res + wanted; /* top points after the last result */ } @@ -481,7 +507,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) rethook(L, ci, nres); /* move results to proper place */ - moveresults(L, ci->func, nres, wanted); + moveresults(L, ci->func.p, nres, wanted); /* function cannot be in any of these cases when returning */ lua_assert(!(ci->callstatus & (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); @@ -493,27 +519,81 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) +l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, + int mask, StkId top) { + CallInfo *ci = L->ci = next_ci(L); /* new frame */ + ci->func.p = func; + ci->nresults = nret; + ci->callstatus = mask; + ci->top.p = top; + return ci; +} + + +/* +** precall for C functions +*/ +l_sinline int precallC (lua_State *L, StkId func, int nresults, + lua_CFunction f) { + int n; /* number of returns */ + CallInfo *ci; + checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, + L->top.p + LUA_MINSTACK); + lua_assert(ci->top.p <= L->stack_last.p); + if (l_unlikely(L->hookmask & LUA_MASKCALL)) { + int narg = cast_int(L->top.p - func) - 1; + luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); + } + lua_unlock(L); + n = (*f)(L); /* do the actual call */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, ci, n); + return n; +} + + /* ** Prepare a function for a tail call, building its call info on top ** of the current call info. 'narg1' is the number of arguments plus 1 -** (so that it includes the function itself). +** (so that it includes the function itself). Return the number of +** results, if it was a C function, or -1 for a Lua function. */ -void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { +int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta) { + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); + case LUA_VLCF: /* light C function */ + return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); + case LUA_VLCL: { /* Lua function */ Proto *p = clLvalue(s2v(func))->p; int fsize = p->maxstacksize; /* frame size */ int nfixparams = p->numparams; int i; + checkstackGCp(L, fsize - delta, func); + ci->func.p -= delta; /* restore 'func' (if vararg) */ for (i = 0; i < narg1; i++) /* move down function and arguments */ - setobjs2s(L, ci->func + i, func + i); - checkstackGC(L, fsize); - func = ci->func; /* moved-down function */ + setobjs2s(L, ci->func.p + i, func + i); + func = ci->func.p; /* moved-down function */ for (; narg1 <= nfixparams; narg1++) setnilvalue(s2v(func + narg1)); /* complete missing arguments */ - ci->top = func + 1 + fsize; /* top for new function */ - lua_assert(ci->top <= L->stack_last); + ci->top.p = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top.p <= L->stack_last.p); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus |= CIST_TAIL; - L->top = func + narg1; /* set top */ + L->top.p = func + narg1; /* set top */ + return -1; + } + default: { /* not a function */ + func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ + narg1++; + goto retry; /* try again */ + } + } } @@ -526,56 +606,31 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { ** original function position. */ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { - lua_CFunction f; retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ - f = clCvalue(s2v(func))->f; - goto Cfunc; - case LUA_VLCF: /* light C function */ - f = fvalue(s2v(func)); - Cfunc: { - int n; /* number of returns */ - CallInfo *ci; - checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ - L->ci = ci = next_ci(L); - ci->nresults = nresults; - ci->callstatus = CIST_C; - ci->top = L->top + LUA_MINSTACK; - ci->func = func; - lua_assert(ci->top <= L->stack_last); - if (l_unlikely(L->hookmask & LUA_MASKCALL)) { - int narg = cast_int(L->top - func) - 1; - luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); - } - lua_unlock(L); - n = (*f)(L); /* do the actual call */ - lua_lock(L); - api_checknelems(L, n); - luaD_poscall(L, ci, n); + precallC(L, func, nresults, clCvalue(s2v(func))->f); + return NULL; + case LUA_VLCF: /* light C function */ + precallC(L, func, nresults, fvalue(s2v(func))); return NULL; - } case LUA_VLCL: { /* Lua function */ CallInfo *ci; Proto *p = clLvalue(s2v(func))->p; - int narg = cast_int(L->top - func) - 1; /* number of real arguments */ + int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ int nfixparams = p->numparams; int fsize = p->maxstacksize; /* frame size */ checkstackGCp(L, fsize, func); - L->ci = ci = next_ci(L); - ci->nresults = nresults; + L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); ci->u.l.savedpc = p->code; /* starting point */ - ci->top = func + 1 + fsize; - ci->func = func; - L->ci = ci; for (; narg < nfixparams; narg++) - setnilvalue(s2v(L->top++)); /* complete missing arguments */ - lua_assert(ci->top <= L->stack_last); + setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + lua_assert(ci->top.p <= L->stack_last.p); return ci; } default: { /* not a function */ - checkstackGCp(L, 1, func); /* space for metamethod */ - luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_precall(L, func, nresults); */ goto retry; /* try again with metamethod */ } } @@ -586,12 +641,17 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { ** Call a function (C or Lua) through C. 'inc' can be 1 (increment ** number of recursive invocations in the C stack) or nyci (the same ** plus increment number of non-yieldable calls). +** This function can be called with some use of EXTRA_STACK, so it should +** check the stack before doing anything else. 'luaD_precall' already +** does that. */ -static void ccall (lua_State *L, StkId func, int nResults, int inc) { +l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) { CallInfo *ci; L->nCcalls += inc; - if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) { + checkstackp(L, 0, func); /* free any use of EXTRA_STACK */ luaE_checkcstack(L); + } if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ luaV_execute(L, ci); /* call it */ @@ -639,8 +699,7 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { else { /* error */ StkId func = restorestack(L, ci->u2.funcidx); L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ - luaF_close(L, func, status, 1); /* can yield or raise an error */ - func = restorestack(L, ci->u2.funcidx); /* stack may be moved */ + func = luaF_close(L, func, status, 1); /* can yield or raise an error */ luaD_seterrorobj(L, status, func); luaD_shrinkstack(L); /* restore stack size in case of overflow */ setcistrecst(ci, LUA_OK); /* clear original status */ @@ -728,8 +787,8 @@ static CallInfo *findpcall (lua_State *L) { ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { - L->top -= narg; /* remove args from the stack */ - setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ + L->top.p -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ api_incr_top(L); lua_unlock(L); return LUA_ERRRUN; @@ -745,16 +804,15 @@ static int resume_error (lua_State *L, const char *msg, int narg) { */ static void resume (lua_State *L, void *ud) { int n = *(cast(int*, ud)); /* number of arguments */ - StkId firstArg = L->top - n; /* first argument */ + StkId firstArg = L->top.p - n; /* first argument */ CallInfo *ci = L->ci; if (L->status == LUA_OK) /* starting a coroutine? */ - ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */ + ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); L->status = LUA_OK; /* mark that it is running (again) */ - luaE_incCstack(L); /* control the C stack */ if (isLua(ci)) { /* yielded inside a hook? */ - L->top = firstArg; /* discard arguments */ + L->top.p = firstArg; /* discard arguments */ luaV_execute(L, ci); /* just continue running Lua code */ } else { /* 'common' yield */ @@ -797,12 +855,15 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ return resume_error(L, "cannot resume non-suspended coroutine", nargs); - else if (L->top - (L->ci->func + 1) == nargs) /* no function? */ + else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */ return resume_error(L, "cannot resume dead coroutine", nargs); } else if (L->status != LUA_YIELD) /* ended with errors? */ return resume_error(L, "cannot resume dead coroutine", nargs); L->nCcalls = (from) ? getCcalls(from) : 0; + if (getCcalls(L) >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow", nargs); + L->nCcalls++; luai_userstateresume(L, nargs); api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); @@ -812,11 +873,11 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ L->status = cast_byte(status); /* mark thread as 'dead' */ - luaD_seterrorobj(L, status, L->top); /* push error message */ - L->ci->top = L->top; + luaD_seterrorobj(L, status, L->top.p); /* push error message */ + L->ci->top.p = L->top.p; } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield - : cast_int(L->top - (L->ci->func + 1)); + : cast_int(L->top.p - (L->ci->func.p + 1)); lua_unlock(L); return status; } @@ -971,7 +1032,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; p.dyd.label.arr = NULL; p.dyd.label.size = 0; luaZ_initbuffer(L, &p.buff); - status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); luaZ_freebuffer(L, &p.buff); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); diff --git a/third_party/lua/ldo.h b/third_party/lua/ldo.h index 314913c8b..e01b15929 100644 --- a/third_party/lua/ldo.h +++ b/third_party/lua/ldo.h @@ -16,7 +16,7 @@ ** at every check. */ #define luaD_checkstackaux(L,n,pre,pos) \ - if (l_unlikely(L->stack_last - L->top <= (n))) \ + if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ { pre; luaD_growstack(L, n, 1); pos; } \ else { condmovestack(L,pre,pos); } @@ -25,11 +25,18 @@ -#define savestack(L,p) ((char *)(p) - (char *)L->stack) -#define restorestack(L,n) ((StkId)((char *)L->stack + (n))) +#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) +#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) /* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC, preserving 'p' */ #define checkstackGCp(L,n,p) \ luaD_checkstackaux(L, n, \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ @@ -51,11 +58,12 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); -LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); +LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta); LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); -LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); +LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func); LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); diff --git a/third_party/lua/ldump.c b/third_party/lua/ldump.c index a1e9666b1..fb12151d4 100644 --- a/third_party/lua/ldump.c +++ b/third_party/lua/ldump.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ldump_c #define LUA_CORE + #include "third_party/lua/lobject.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" @@ -71,8 +72,11 @@ static void dumpByte (DumpState *D, int y) { } -/* dumpInt Buff Size */ -#define DIBS ((sizeof(size_t) * 8 / 7) + 1) +/* +** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" +** rounds up the division.) +*/ +#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) static void dumpSize (DumpState *D, size_t x) { lu_byte buff[DIBS]; diff --git a/third_party/lua/lfunc.c b/third_party/lua/lfunc.c index 7ff7cafd0..a6cb6aedf 100644 --- a/third_party/lua/lfunc.c +++ b/third_party/lua/lfunc.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lfunc_c #define LUA_CORE + #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -66,8 +67,8 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { for (i = 0; i < cl->nupvalues; i++) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); - uv->v = &uv->u.value; /* make it closed */ - setnilvalue(uv->v); + uv->v.p = &uv->u.value; /* make it closed */ + setnilvalue(uv->v.p); cl->upvals[i] = uv; luaC_objbarrier(L, cl, uv); } @@ -78,12 +79,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { ** Create a new upvalue at the given level, and link it to the list of ** open upvalues of 'L' after entry 'prev'. **/ -static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) { +static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); UpVal *next = *prev; - uv->v = s2v(level); /* current value lives in the stack */ - uv->tbc = tbc; + uv->v.p = s2v(level); /* current value lives in the stack */ uv->u.open.next = next; /* link it to list of open upvalues */ uv->u.open.previous = prev; if (next) @@ -112,7 +112,7 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { pp = &p->u.open.next; } /* not found: create a new upvalue after 'pp' */ - return newupval(L, 0, level, pp); + return newupval(L, level, pp); } @@ -122,12 +122,12 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { ** (This function assumes EXTRA_STACK.) */ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { - StkId top = L->top; + StkId top = L->top.p; const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); setobj2s(L, top, tm); /* will call metamethod... */ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ - L->top = top + 3; /* add function and arguments */ + L->top.p = top + 3; /* add function and arguments */ if (yy) luaD_call(L, top, 0); else @@ -142,7 +142,7 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { static void checkclosemth (lua_State *L, StkId level) { const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); if (ttisnil(tm)) { /* no metamethod? */ - int idx = cast_int(level - L->ci->func); /* variable index */ + int idx = cast_int(level - L->ci->func.p); /* variable index */ const char *vname = luaG_findlocal(L, L->ci, idx, NULL); if (vname == NULL) vname = "?"; luaG_runerror(L, "variable '%s' got a non-closable value", vname); @@ -176,23 +176,23 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { ** is used.) */ #define MAXDELTA \ - ((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1) + ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) /* ** Insert a variable in the list of to-be-closed variables. */ void luaF_newtbcupval (lua_State *L, StkId level) { - lua_assert(level > L->tbclist); + lua_assert(level > L->tbclist.p); if (l_isfalse(s2v(level))) return; /* false doesn't need to be closed */ checkclosemth(L, level); /* value must have a close method */ - while (cast_uint(level - L->tbclist) > MAXDELTA) { - L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ - L->tbclist->tbclist.delta = 0; + while (cast_uint(level - L->tbclist.p) > MAXDELTA) { + L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist.p->tbclist.delta = 0; } - level->tbclist.delta = cast(unsigned short, level - L->tbclist); - L->tbclist = level; + level->tbclist.delta = cast(unsigned short, level - L->tbclist.p); + L->tbclist.p = level; } @@ -212,10 +212,10 @@ void luaF_closeupval (lua_State *L, StkId level) { StkId upl; /* stack index pointed by 'uv' */ while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { TValue *slot = &uv->u.value; /* new position for value */ - lua_assert(uplevel(uv) < L->top); + lua_assert(uplevel(uv) < L->top.p); luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ - setobj(L, slot, uv->v); /* move value to upvalue slot */ - uv->v = slot; /* now current value lives here */ + setobj(L, slot, uv->v.p); /* move value to upvalue slot */ + uv->v.p = slot; /* now current value lives here */ if (!iswhite(uv)) { /* neither white nor dead? */ nw2black(uv); /* closed upvalues cannot be gray */ luaC_barrier(L, uv, slot); @@ -225,31 +225,32 @@ void luaF_closeupval (lua_State *L, StkId level) { /* -** Remove firt element from the tbclist plus its dummy nodes. +** Remove first element from the tbclist plus its dummy nodes. */ static void poptbclist (lua_State *L) { - StkId tbc = L->tbclist; + StkId tbc = L->tbclist.p; lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ tbc -= tbc->tbclist.delta; - while (tbc > L->stack && tbc->tbclist.delta == 0) + while (tbc > L->stack.p && tbc->tbclist.delta == 0) tbc -= MAXDELTA; /* remove dummy nodes */ - L->tbclist = tbc; + L->tbclist.p = tbc; } /* ** Close all upvalues and to-be-closed variables up to the given stack -** level. +** level. Return restored 'level'. */ -void luaF_close (lua_State *L, StkId level, int status, int yy) { +StkId luaF_close (lua_State *L, StkId level, int status, int yy) { ptrdiff_t levelrel = savestack(L, level); luaF_closeupval(L, level); /* first, close the upvalues */ - while (L->tbclist >= level) { /* traverse tbc's down to that level */ - StkId tbc = L->tbclist; /* get variable index */ + while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ + StkId tbc = L->tbclist.p; /* get variable index */ poptbclist(L); /* remove it from list */ prepcallclosemth(L, tbc, status, yy); /* close variable */ level = restorestack(L, levelrel); } + return level; } diff --git a/third_party/lua/lfunc.h b/third_party/lua/lfunc.h index 68c26419a..c149811d2 100644 --- a/third_party/lua/lfunc.h +++ b/third_party/lua/lfunc.h @@ -22,10 +22,10 @@ #define MAXUPVAL 255 -#define upisopen(up) ((up)->v != &(up)->u.value) +#define upisopen(up) ((up)->v.p != &(up)->u.value) -#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v)) +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) /* @@ -47,7 +47,7 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); -LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); LUAI_FUNC void luaF_unlinkupval (UpVal *uv); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, diff --git a/third_party/lua/lgc.c b/third_party/lua/lgc.c index a52142624..9f4037ba1 100644 --- a/third_party/lua/lgc.c +++ b/third_party/lua/lgc.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lgc_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -268,12 +269,13 @@ void luaC_fix (lua_State *L, GCObject *o) { /* -** create a new collectable object (with given type and size) and link -** it to 'allgc' list. +** create a new collectable object (with given type, size, and offset) +** and link it to 'allgc' list. */ -GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { +GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { global_State *g = G(L); - GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); + char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); + GCObject *o = cast(GCObject *, p + offset); o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; @@ -281,6 +283,11 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { return o; } + +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { + return luaC_newobjdt(L, tt, sz, 0); +} + /* }====================================================== */ @@ -317,7 +324,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { set2gray(uv); /* open upvalues are kept gray */ else set2black(uv); /* closed upvalues are visited here */ - markvalue(g, uv->v); /* mark its content */ + markvalue(g, uv->v.p); /* mark its content */ break; } case LUA_VUSERDATA: { @@ -392,7 +399,7 @@ static int remarkupvals (global_State *g) { work++; if (!iswhite(uv)) { /* upvalue already visited? */ lua_assert(upisopen(uv) && isgray(uv)); - markvalue(g, uv->v); /* mark its value */ + markvalue(g, uv->v.p); /* mark its value */ } } } @@ -636,19 +643,19 @@ static int traverseLclosure (global_State *g, LClosure *cl) { */ static int traversethread (global_State *g, lua_State *th) { UpVal *uv; - StkId o = th->stack; + StkId o = th->stack.p; if (isold(th) || g->gcstate == GCSpropagate) linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) return 1; /* stack not completely built yet */ lua_assert(g->gcstate == GCSatomic || th->openupval == NULL || isintwups(th)); - for (; o < th->top; o++) /* mark live elements in the stack */ + for (; o < th->top.p; o++) /* mark live elements in the stack */ markvalue(g, s2v(o)); for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) markobject(g, uv); /* open upvalues cannot be collected */ if (g->gcstate == GCSatomic) { /* final traversal? */ - for (; o < th->stack_last + EXTRA_STACK; o++) + for (; o < th->stack_last.p + EXTRA_STACK; o++) setnilvalue(s2v(o)); /* clear dead stack slice */ /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { @@ -908,7 +915,7 @@ static GCObject *udata2finalize (global_State *g) { static void dothecall (lua_State *L, void *ud) { UNUSED(ud); - luaD_callnoyield(L, L->top - 2, 0); + luaD_callnoyield(L, L->top.p - 2, 0); } @@ -922,19 +929,19 @@ static void GCTM (lua_State *L) { if (!notm(tm)) { /* is there a finalizer? */ int status; lu_byte oldah = L->allowhook; - int running = g->gcrunning; + int oldgcstp = g->gcstp; + g->gcstp |= GCSTPGC; /* avoid GC steps */ L->allowhook = 0; /* stop debug hooks during GC metamethod */ - g->gcrunning = 0; /* avoid GC steps */ - setobj2s(L, L->top++, tm); /* push finalizer... */ - setobj2s(L, L->top++, &v); /* ... and its argument */ + setobj2s(L, L->top.p++, tm); /* push finalizer... */ + setobj2s(L, L->top.p++, &v); /* ... and its argument */ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ - status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->allowhook = oldah; /* restore hooks */ - g->gcrunning = running; /* restore state */ + g->gcstp = oldgcstp; /* restore state */ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ - luaE_warnerror(L, "__gc metamethod"); - L->top--; /* pops error object */ + luaE_warnerror(L, "__gc"); + L->top.p--; /* pops error object */ } } } @@ -1027,7 +1034,8 @@ static void correctpointers (global_State *g, GCObject *o) { void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (tofinalize(o) || /* obj. is already marked... */ - gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ + gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ + (g->gcstp & GCSTPCLS)) /* or closing state? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; @@ -1056,7 +1064,25 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { ** ======================================================= */ -static void setpause (global_State *g); + +/* +** Set the "time" to wait before starting a new GC cycle; cycle will +** start when memory use hits the threshold of ('estimate' * pause / +** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, +** because Lua cannot even start with less than PAUSEADJ bytes). +*/ +static void setpause (global_State *g) { + l_mem threshold, debt; + int pause = getgcparam(g->gcpause); + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); + threshold = (pause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * pause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = gettotalbytes(g) - threshold; + if (debt > 0) debt = 0; + luaE_setdebt(g, debt); +} /* @@ -1300,6 +1326,15 @@ static void atomic2gen (lua_State *L, global_State *g) { } +/* +** Set debt for the next minor collection, which will happen when +** memory grows 'genminormul'%. +*/ +static void setminordebt (global_State *g) { + luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); +} + + /* ** Enter generational mode. Must go until the end of an atomic cycle ** to ensure that all objects are correctly marked and weak tables @@ -1312,6 +1347,7 @@ static lu_mem entergen (lua_State *L, global_State *g) { luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ numobjs = atomic(L); /* propagates all and then do the atomic stuff */ atomic2gen(L, g); + setminordebt(g); /* set debt assuming next cycle will be minor */ return numobjs; } @@ -1357,15 +1393,6 @@ static lu_mem fullgen (lua_State *L, global_State *g) { } -/* -** Set debt for the next minor collection, which will happen when -** memory grows 'genminormul'%. -*/ -static void setminordebt (global_State *g) { - luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); -} - - /* ** Does a major collection after last collection was a "bad collection". ** @@ -1437,8 +1464,8 @@ static void genstep (lua_State *L, global_State *g) { lu_mem numobjs = fullgen(L, g); /* do a major collection */ if (gettotalbytes(g) < majorbase + (majorinc / 2)) { /* collected at least half of memory growth since last major - collection; keep doing minor collections */ - setminordebt(g); + collection; keep doing minor collections. */ + lua_assert(g->lastatomic == 0); } else { /* bad collection */ g->lastatomic = numobjs; /* signal that last collection was bad */ @@ -1464,26 +1491,6 @@ static void genstep (lua_State *L, global_State *g) { */ -/* -** Set the "time" to wait before starting a new GC cycle; cycle will -** start when memory use hits the threshold of ('estimate' * pause / -** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, -** because Lua cannot even start with less than PAUSEADJ bytes). -*/ -static void setpause (global_State *g) { - l_mem threshold, debt; - int pause = getgcparam(g->gcpause); - l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ - lua_assert(estimate > 0); - threshold = (pause < MAX_LMEM / estimate) /* overflow? */ - ? estimate * pause /* no overflow */ - : MAX_LMEM; /* overflow; truncate to maximum */ - debt = gettotalbytes(g) - threshold; - if (debt > 0) debt = 0; - luaE_setdebt(g, debt); -} - - /* ** Enter first sweep phase. ** The call to 'sweeptolive' makes the pointer point to an object @@ -1518,12 +1525,13 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { */ void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); + g->gcstp = GCSTPCLS; /* no extra finalizers after here */ luaC_changemode(L, KGC_INC); separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L); deletelist(L, g->allgc, obj2gco(g->mainthread)); - deletelist(L, g->finobj, NULL); + lua_assert(g->finobj == NULL); /* no new finalizers */ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } @@ -1663,6 +1671,7 @@ void luaC_runtilstate (lua_State *L, int statesmask) { } + /* ** Performs a basic incremental step. The debt and step size are ** converted from bytes to "units of work"; then the function loops @@ -1689,12 +1698,15 @@ static void incstep (lua_State *L, global_State *g) { } /* -** performs a basic GC step if collector is running +** Performs a basic GC step if collector is running. (If collector is +** not running, set a reasonable debt to avoid it being called at +** every single check.) */ void luaC_step (lua_State *L) { global_State *g = G(L); - lua_assert(!g->gcemergency); - if (g->gcrunning) { /* running? */ + if (!gcrunning(g)) /* not running? */ + luaE_setdebt(g, -2000); + else { if(isdecGCmodegen(g)) genstep(L, g); else diff --git a/third_party/lua/lgc.h b/third_party/lua/lgc.h index f01f15b7a..f498ba168 100644 --- a/third_party/lua/lgc.h +++ b/third_party/lua/lgc.h @@ -142,6 +142,16 @@ */ #define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) + +/* +** Control when GC is running: +*/ +#define GCSTPUSR 1 /* bit true when GC stopped by user */ +#define GCSTPGC 2 /* bit true when GC stopped by itself */ +#define GCSTPCLS 4 /* bit true when closing Lua state */ +#define gcrunning(g) ((g)->gcstp == 0) + + /* ** Does one step of collection when debt becomes positive. 'pre'/'pos' ** allows some adjustments to be done only when needed. macro @@ -156,24 +166,27 @@ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) -#define luaC_barrier(L,p,v) ( \ - (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ - luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) - -#define luaC_barrierback(L,p,v) ( \ - (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ - luaC_barrierback_(L,p) : cast_void(0)) - #define luaC_objbarrier(L,p,o) ( \ (isblack(p) && iswhite(o)) ? \ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) +#define luaC_barrier(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0)) + +#define luaC_objbarrierback(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0)) + LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, + size_t offset); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); diff --git a/third_party/lua/linit.c b/third_party/lua/linit.c index 2ebb7fbd0..ef479289f 100644 --- a/third_party/lua/linit.c +++ b/third_party/lua/linit.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define linit_c #define LUA_LIB + /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a @@ -42,6 +43,7 @@ ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ + #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" diff --git a/third_party/lua/liolib.c b/third_party/lua/liolib.c index c8bc69076..a668b0c1c 100644 --- a/third_party/lua/liolib.c +++ b/third_party/lua/liolib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define liolib_c #define LUA_LIB + #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" diff --git a/third_party/lua/ljumptab.inc b/third_party/lua/ljumptab.inc index f928a406b..6407c0b20 100644 --- a/third_party/lua/ljumptab.inc +++ b/third_party/lua/ljumptab.inc @@ -1,9 +1,3 @@ -/* -** $Id: ljumptab.h $ -** Jump Table for the Lua interpreter -** See Copyright Notice in lua.h -*/ - #undef vmdispatch #undef vmcase #undef vmbreak diff --git a/third_party/lua/llex.c b/third_party/lua/llex.c index 688d2a3b0..8e2e1d988 100644 --- a/third_party/lua/llex.c +++ b/third_party/lua/llex.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define llex_c #define LUA_CORE + #include "third_party/lua/llex.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" @@ -142,7 +143,7 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) { ** ensuring there is only one copy of each unique string. The table ** here is used as a set: the string enters as the key, while its value ** is irrelevant. We use the string itself as the value only because it -** is a TValue readly available. Later, the code generation can change +** is a TValue readily available. Later, the code generation can change ** this value. */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { @@ -152,12 +153,12 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { if (!ttisnil(o)) /* string already present? */ ts = keystrval(nodefromval(o)); /* get saved copy */ else { /* not in use yet */ - TValue *stv = s2v(L->top++); /* reserve stack space for string */ + TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ setsvalue(L, stv, ts); /* temporarily anchor the string */ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); - L->top--; /* remove string from stack */ + L->top.p--; /* remove string from stack */ } return ts; } @@ -408,9 +409,9 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) { int c; /* final character to be saved */ save_and_next(ls); /* keep '\\' for error messages */ switch (ls->current) { - case 'e': c = '\e'; goto read_save; case 'a': c = '\a'; goto read_save; case 'b': c = '\b'; goto read_save; + case 'e': c = '\e'; goto read_save; // [jart] case 'f': c = '\f'; goto read_save; case 'n': c = '\n'; goto read_save; case 'r': c = '\r'; goto read_save; diff --git a/third_party/lua/llex.h b/third_party/lua/llex.h index b9222e851..ed93f8134 100644 --- a/third_party/lua/llex.h +++ b/third_party/lua/llex.h @@ -1,9 +1,3 @@ -/* -** $Id: llex.h $ -** Lexical Analyzer -** See Copyright Notice in lua.h -*/ - #ifndef llex_h #define llex_h diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index cc202ff79..c005bb0f6 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -62,11 +62,24 @@ typedef signed char ls_byte; /* -** conversion of pointer to unsigned integer: -** this is for hashing only; there is no problem if the integer -** cannot hold the whole pointer value +** conversion of pointer to unsigned integer: this is for hashing only; +** there is no problem if the integer cannot hold the whole pointer +** value. (In strict ISO C this may cause undefined behavior, but no +** actual machine seems to bother.) */ -#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L + +#if defined(UINTPTR_MAX) /* even in C99 this type is optional */ +#define L_P2I uintptr_t +#else /* no 'intptr'? */ +#define L_P2I uintmax_t /* use the largest available integer */ +#endif +#else /* C89 option */ +#define L_P2I size_t +#endif + +#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX)) @@ -155,6 +168,20 @@ typedef LUAI_UACINT l_uacInt; #endif +/* +** Inline functions +*/ +#if !defined(LUA_USE_C89) +#define l_inline inline +#elif defined(__GNUC__) +#define l_inline __inline__ +#else +#define l_inline /* empty */ +#endif + +#define l_sinline static l_inline + + /* ** type for virtual-machine instructions; ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) @@ -337,7 +364,7 @@ typedef l_uint32 Instruction; #define condchangemem(L,pre,pos) ((void)0) #else #define condchangemem(L,pre,pos) \ - { if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } } + { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif diff --git a/third_party/lua/lmathlib.c b/third_party/lua/lmathlib.c index 8b1a291f4..ed10473d8 100644 --- a/third_party/lua/lmathlib.c +++ b/third_party/lua/lmathlib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lmathlib_c #define LUA_LIB + #include "libc/calls/calls.h" #include "libc/math.h" #include "libc/nt/struct/msg.h" @@ -282,7 +283,7 @@ static int math_type (lua_State *L) { /* try to find an integer type with at least 64 bits */ -#if (ULONG_MAX >> 31 >> 31) >= 3 +#if ((ULONG_MAX >> 31) >> 31) >= 3 /* 'long' has at least 64 bits */ #define Rand64 unsigned long @@ -292,9 +293,9 @@ static int math_type (lua_State *L) { /* there is a 'long long' type (which must have at least 64 bits) */ #define Rand64 unsigned long long -#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3 +#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 -/* 'lua_Integer' has at least 64 bits */ +/* 'lua_Unsigned' has at least 64 bits */ #define Rand64 lua_Unsigned #endif @@ -490,7 +491,7 @@ static lua_Number I2d (Rand64 x) { /* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ #define scaleFIG \ - ((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33))) + (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) /* ** use FIGS - 32 bits from lower half, throwing out the other @@ -501,7 +502,7 @@ static lua_Number I2d (Rand64 x) { /* ** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) */ -#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0) +#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0)) static lua_Number I2d (Rand64 x) { @@ -515,12 +516,12 @@ static lua_Number I2d (Rand64 x) { /* convert a 'Rand64' to a 'lua_Unsigned' */ static lua_Unsigned I2UInt (Rand64 x) { - return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l); + return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l); } /* convert a 'lua_Unsigned' to a 'Rand64' */ static Rand64 Int2I (lua_Unsigned n) { - return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n); + return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n); } #endif /* } */ diff --git a/third_party/lua/lmem.c b/third_party/lua/lmem.c index 780e01bf8..9f03298a3 100644 --- a/third_party/lua/lmem.c +++ b/third_party/lua/lmem.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lmem_c #define LUA_CORE + #include "libc/log/log.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -39,26 +40,6 @@ __static_yoink("lua_notice"); -#if defined(EMERGENCYGCTESTS) -/* -** First allocation will fail whenever not building initial state. -** (This fail will trigger 'tryagain' and a full GC cycle at every -** allocation.) -*/ -static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { - if (completestate(g) && ns > 0) /* frees never fail */ - return NULL; /* fail */ - else /* normal allocation */ - return (*g->frealloc)(g->ud, block, os, ns); -} -#else -#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) -#endif - - - - - /* ** About the realloc function: ** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); @@ -77,6 +58,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { */ +/* +** Macro to call the allocation function. +*/ +#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) + + +/* +** When an allocation fails, it will try again after an emergency +** collection, except when it cannot run a collection. The GC should +** not be called while the state is not fully built, as the collector +** is not yet fully initialized. Also, it should not be called when +** 'gcstopem' is true, because then the interpreter is in the middle of +** a collection step. +*/ +#define cantryagain(g) (completestate(g) && !g->gcstopem) + + + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail except when freeing a block (frees never +** fail) and when it cannot try again; this fail will trigger 'tryagain' +** and a full GC cycle at every allocation. +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ns > 0 && cantryagain(g)) + return NULL; /* fail */ + else /* normal allocation */ + return callfrealloc(g, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns) +#endif + + + /* @@ -149,7 +167,7 @@ l_noret luaM_toobig (lua_State *L) { void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); - (*g->frealloc)(g->ud, block, osize, 0); + callfrealloc(g, block, osize, 0); g->GCdebt -= osize; } @@ -157,20 +175,16 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { /* ** In case of allocation fail, this function will do an emergency ** collection to free some memory and then try the allocation again. -** The GC should not be called while state is not fully built, as the -** collector is not yet fully initialized. Also, it should not be called -** when 'gcstopem' is true, because then the interpreter is in the -** middle of a collection step. */ static void *tryagain (lua_State *L, void *block, size_t osize, size_t nsize) { global_State *g = G(L); - if (completestate(g) && !g->gcstopem) { - WARNF("reacting to malloc() failure by running lua garbage collector..."); + if (cantryagain(g)) { + WARNF("reacting to malloc() failure by running lua garbage collector..."); // [jart] luaC_fullgc(L, 1); /* try to free some memory... */ - return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ + return callfrealloc(g, block, osize, nsize); /* try again */ } - else return NULL; /* cannot free any memory without a full state */ + else return NULL; /* cannot run an emergency collection */ } diff --git a/third_party/lua/lnotice.c b/third_party/lua/lnotice.c index 9eb4e4e17..69da2a8ab 100644 --- a/third_party/lua/lnotice.c +++ b/third_party/lua/lnotice.c @@ -1,3 +1,3 @@ __notice(lua_notice, "\ -Lua 5.4.3 (MIT License)\n\ -Copyright 1994–2021 Lua.org, PUC-Rio."); +Lua 5.4.6 (MIT License)\n\ +Copyright 1994–2023 Lua.org, PUC-Rio."); diff --git a/third_party/lua/loadlib.c b/third_party/lua/loadlib.c index 43bf0168d..c8d187380 100644 --- a/third_party/lua/loadlib.c +++ b/third_party/lua/loadlib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define loadlib_c #define LUA_LIB + #include "libc/dlopen/dlfcn.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -36,14 +37,16 @@ #include "third_party/lua/lualib.h" __static_yoink("lua_notice"); + /* -** This module contains an implementation of loadlib for Unix systems +** +** [jart] This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Windows, and a stub for other ** systems. */ -const char *g_lua_path_default = LUA_PATH_DEFAULT; +const char *g_lua_path_default = LUA_PATH_DEFAULT; // [jart] /* @@ -728,8 +731,13 @@ static const luaL_Reg ll_funcs[] = { static void createsearcherstable (lua_State *L) { - static const lua_CFunction searchers[] = - {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; + static const lua_CFunction searchers[] = { + searcher_preload, + searcher_Lua, + searcher_C, + searcher_Croot, + NULL + }; int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); @@ -757,7 +765,7 @@ static void createclibstable (lua_State *L) { static const char *GetLuaPathDefault(void) { - return g_lua_path_default; + return g_lua_path_default; // [jart] } diff --git a/third_party/lua/lobject.c b/third_party/lua/lobject.c index d855ac166..2ff1a05c6 100644 --- a/third_party/lua/lobject.c +++ b/third_party/lua/lobject.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lobject_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" @@ -41,6 +42,12 @@ __static_yoink("lua_notice"); +/* +** Computes ceil(log2(x)) +*/ +// [jart] moved to lobject.h + + static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, lua_Integer v2) { switch (op) { @@ -53,7 +60,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); - case LUA_OPSHR: return luaV_shiftl(v1, -v2); + case LUA_OPSHR: return luaV_shiftr(v1, v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; @@ -155,7 +162,7 @@ static int isneg (const char **s) { */ static lua_Number lua_strx2number (const char *s, char **endptr) { int dot = lua_getlocaledecpoint(); - lua_Number r = 0.0; /* result (accumulator) */ + lua_Number r = l_mathop(0.0); /* result (accumulator) */ int sigdig = 0; /* number of significant digits */ int nosigdig = 0; /* number of non-significant digits */ int e = 0; /* exponent correction */ @@ -165,7 +172,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); /* check sign */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ - return 0.0; /* invalid format (no '0x') */ + return l_mathop(0.0); /* invalid format (no '0x') */ for (s += 2; ; s++) { /* skip '0x' and read numeral */ if (*s == dot) { if (hasdot) break; /* second dot? stop loop */ @@ -175,14 +182,14 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ - r = (r * cast_num(16.0)) + luaO_hexavalue(*s); + r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ } if (nosigdig + sigdig == 0) /* no digits? */ - return 0.0; /* invalid format */ + return l_mathop(0.0); /* invalid format */ *endptr = cast_charp(s); /* valid up to here */ e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ @@ -191,7 +198,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { s++; /* skip 'p' */ neg1 = isneg(&s); /* sign */ if (!lisdigit(cast_uchar(*s))) - return 0.0; /* invalid; must have at least one digit */ + return l_mathop(0.0); /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; @@ -262,9 +269,9 @@ static const char *l_str2d (const char *s, lua_Number *result) { #define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) -#define MAXBY8 cast(lua_Unsigned, LUA_MAXINTEGER / 8) +#define MAXBY8 cast(lua_Unsigned, LUA_MAXINTEGER / 8) // [jart] #define MAXLASTD cast_int(LUA_MAXINTEGER % 10) -#define MAXLASTD8 cast_int(LUA_MAXINTEGER % 8) +#define MAXLASTD8 cast_int(LUA_MAXINTEGER % 8) // [jart] static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; @@ -396,29 +403,39 @@ void luaO_tostring (lua_State *L, TValue *obj) { ** =================================================================== */ -/* size for buffer space used by 'luaO_pushvfstring' */ -#define BUFVFS 200 +/* +** Size for buffer space used by 'luaO_pushvfstring'. It should be +** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the buffer. +*/ +#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95) /* buffer used by 'luaO_pushvfstring' */ typedef struct BuffFS { lua_State *L; - int pushed; /* number of string pieces already on the stack */ + int pushed; /* true if there is a part of the result on the stack */ int blen; /* length of partial string in 'space' */ char space[BUFVFS]; /* holds last part of the result */ } BuffFS; /* -** Push given string to the stack, as part of the buffer, and -** join the partial strings in the stack into one. +** Push given string to the stack, as part of the result, and +** join it to previous partial result if there is one. +** It may call 'luaV_concat' while using one slot from EXTRA_STACK. +** This call cannot invoke metamethods, as both operands must be +** strings. It can, however, raise an error if the result is too +** long. In that case, 'luaV_concat' frees the extra slot before +** raising the error. */ -static void pushstr (BuffFS *buff, const char *str, size_t l) { +static void pushstr (BuffFS *buff, const char *str, size_t lstr) { lua_State *L = buff->L; - setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); - L->top++; /* may use one extra slot */ - buff->pushed++; - luaV_concat(L, buff->pushed); /* join partial results into one */ - buff->pushed = 1; + setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr)); + L->top.p++; /* may use one slot from EXTRA_STACK */ + if (!buff->pushed) /* no previous string on the stack? */ + buff->pushed = 1; /* now there is one */ + else /* join previous string with new one */ + luaV_concat(L, 2); } @@ -464,7 +481,7 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { /* -** Add a number to the buffer. +** Add a numeral to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { char *numbuff = getbuff(buff, MAXNUMBER2STR); @@ -542,7 +559,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ clearbuff(&buff); /* empty buffer into the stack */ lua_assert(buff.pushed == 1); - return svalue(s2v(L->top - 1)); + return svalue(s2v(L->top.p - 1)); } diff --git a/third_party/lua/lobject.h b/third_party/lua/lobject.h index 9f3bd5e24..4e36e5b7b 100644 --- a/third_party/lua/lobject.h +++ b/third_party/lua/lobject.h @@ -13,11 +13,14 @@ #define LUA_TPROTO (LUA_NUMTYPES + 1) /* function prototypes */ #define LUA_TDEADKEY (LUA_NUMTYPES + 2) /* removed keys in tables */ + + /* ** number of all possible types (including LUA_TNONE but excluding DEADKEY) */ #define LUA_TOTALTYPES (LUA_TPROTO + 2) + /* ** tags for Tagged Values have the following use of bits: ** bits 0-3: actual tag (a LUA_T* constant) @@ -39,6 +42,8 @@ typedef union Value { lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ + /* not used, but may avoid warnings for uninitialized value */ + lu_byte ub; } Value; @@ -55,7 +60,7 @@ typedef struct TValue { #define val_(o) ((o)->value_) -#define valraw(o) (&val_(o)) +#define valraw(o) (val_(o)) /* raw type tag of a TValue */ @@ -99,7 +104,7 @@ typedef struct TValue { #define settt_(o,t) ((o)->tt_=(t)) -/* main macro to copy values (from 'obj1' to 'obj2') */ +/* main macro to copy values (from 'obj2' to 'obj1') */ #define setobj(L,obj1,obj2) \ { TValue *io1=(obj1); const TValue *io2=(obj2); \ io1->value_ = io2->value_; settt_(io1, io2->tt_); \ @@ -142,6 +147,17 @@ typedef union StackValue { /* index to stack elements */ typedef StackValue *StkId; + +/* +** When reallocating the stack, change all pointers to the stack into +** proper offsets. +*/ +typedef union { + StkId p; /* actual pointer */ + ptrdiff_t offset; /* used while the stack is being reallocated */ +} StkIdRel; + + /* convert a 'StackValue' to a 'TValue' */ #define s2v(o) (&(o)->val) @@ -602,8 +618,10 @@ typedef struct Proto { */ typedef struct UpVal { CommonHeader; - lu_byte tbc; /* true if it represents a to-be-closed variable */ - TValue *v; /* points to stack or to its own value */ + union { + TValue *p; /* points to stack or to its own value */ + ptrdiff_t offset; /* used while the stack is being reallocated */ + } v; union { struct { /* (when open) */ struct UpVal *next; /* linked list */ @@ -785,7 +803,7 @@ LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen); ** Computes ceil(log2(x)) */ static inline int luaO_ceillog2 (unsigned int x) { - return --x ? bsr(x) + 1 : 0; + return --x ? _bsr(x) + 1 : 0; // [jart] } #endif diff --git a/third_party/lua/lopcodes.c b/third_party/lua/lopcodes.c index 90d726553..082a46556 100644 --- a/third_party/lua/lopcodes.c +++ b/third_party/lua/lopcodes.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lopcodes_c #define LUA_CORE + #include "third_party/lua/lopcodes.h" #include "third_party/lua/lprefix.h" __static_yoink("lua_notice"); diff --git a/third_party/lua/lopcodes.h b/third_party/lua/lopcodes.h index e84553c11..197d14f5a 100644 --- a/third_party/lua/lopcodes.h +++ b/third_party/lua/lopcodes.h @@ -15,7 +15,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) | iAx Ax(25) | Op(7) | -isJ sJ(25) | Op(7) | +isJ sJ (signed)(25) | Op(7) | A signed argument is represented in excess K: the represented value is the written unsigned value minus K, where K is half the maximum for the @@ -184,7 +184,8 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ /* -** grep "ORDER OP" if you change these enums +** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*) +** has extra descriptions in the notes after the enumeration. */ typedef enum { @@ -197,7 +198,7 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */ OP_LOADK,/* A Bx R[A] := K[Bx] */ OP_LOADKX,/* A R[A] := K[extra arg] */ OP_LOADFALSE,/* A R[A] := false */ -OP_LFALSESKIP,/*A R[A] := false; pc++ */ +OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */ OP_LOADTRUE,/* A R[A] := true */ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */ OP_GETUPVAL,/* A B R[A] := UpValue[B] */ @@ -248,7 +249,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ OP_SHL,/* A B C R[A] := R[B] << R[C] */ OP_SHR,/* A B C R[A] := R[B] >> R[C] */ -OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ @@ -274,7 +275,7 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ OP_TEST,/* A k if (not R[A] == k) then pc++ */ -OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ @@ -309,6 +310,18 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ /*=========================================================================== Notes: + + (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean + value, in a code equivalent to (not cond ? false : true). (It + produces false and skips the next instruction producing true.) + + (*) Opcodes OP_MMBIN and variants follow each arithmetic and + bitwise opcode. If the operation succeeds, it skips this next + opcode. Otherwise, this opcode calls the corresponding metamethod. + + (*) Opcode OP_TESTSET is used in short-circuit expressions that need + both to jump and to produce a value, such as (a = b or c). + (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then 'top' is set to last_result+1, so next open instruction (OP_CALL, OP_RETURN*, OP_SETLIST) may use 'top'. diff --git a/third_party/lua/lopnames.inc b/third_party/lua/lopnames.inc index 2ed5a34db..a4170d1a1 100644 --- a/third_party/lua/lopnames.inc +++ b/third_party/lua/lopnames.inc @@ -1,14 +1,7 @@ -/* -** $Id: lopnames.h $ -** Opcode names -** See Copyright Notice in lua.h -*/ - -#if !defined(lopnames_h) +#ifndef lopnames_h #define lopnames_h - /* ORDER OP */ static const char *const opnames[] = { diff --git a/third_party/lua/loslib.c b/third_party/lua/loslib.c index 2c8e120e3..25339455b 100644 --- a/third_party/lua/loslib.c +++ b/third_party/lua/loslib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define loslib_c #define LUA_LIB + #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -51,23 +52,14 @@ __static_yoink("lua_notice"); */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */ -/* options for ANSI C 89 (only 1-char options) */ -#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" - -/* options for ISO C 99 and POSIX */ -#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ - "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ - -/* options for Windows */ -#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ - "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ - #if defined(LUA_USE_WINDOWS) -#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN -#elif defined(LUA_USE_C89) -#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ +#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" #else /* C99 specification */ -#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 +#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ #endif #endif /* } */ @@ -135,6 +127,7 @@ __static_yoink("lua_notice"); #define LUA_TMPNAMBUFSIZE 128 +// [jart] #define lua_tmpnam(b,e) { \ strlcpy(b, __get_tmpdir(), LUA_TMPNAMBUFSIZE); \ e = strlcat(b, "lua_XXXXXX", LUA_TMPNAMBUFSIZE) >= LUA_TMPNAMBUFSIZE; \ @@ -154,12 +147,21 @@ __static_yoink("lua_notice"); /* }================================================================== */ +#if !defined(l_system) +#if defined(LUA_USE_IOS) +/* Despite claiming to be ISO C, iOS does not implement 'system'. */ +#define l_system(cmd) ((cmd) == NULL ? 0 : -1) +#else +#define l_system(cmd) system(cmd) /* default definition */ +#endif +#endif + static int os_execute (lua_State *L) { const char *cmd = luaL_optstring(L, 1, NULL); int stat; errno = 0; - stat = system(cmd); + stat = l_system(cmd); if (cmd != NULL) return luaL_execresult(L, stat); else { @@ -276,9 +278,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { res = d; } else { - /* unsigned avoids overflow when lua_Integer has 32 bits */ - if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta - : (lua_Integer)INT_MIN + delta <= res)) + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } diff --git a/third_party/lua/lparser.c b/third_party/lua/lparser.c index 79e598fc1..acdfe0f3e 100644 --- a/third_party/lua/lparser.c +++ b/third_party/lua/lparser.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lparser_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -431,6 +432,17 @@ static void markupval (FuncState *fs, int level) { } +/* +** Mark that current block has a to-be-closed variable. +*/ +static void marktobeclosed (FuncState *fs) { + BlockCnt *bl = fs->bl; + bl->upval = 1; + bl->insidetbc = 1; + fs->needclose = 1; +} + + /* ** Find a variable with the given name 'n'. If it is an upvalue, add ** this upvalue into all intermediate functions. If it is a global, set @@ -472,6 +484,7 @@ static void singlevar (LexState *ls, expdesc *var) { expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ lua_assert(var->k != VVOID); /* this one must exist */ + luaK_exp2anyregup(fs, var); /* but could be a constant */ codestring(&key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } @@ -524,12 +537,12 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { /* ** Solves the goto at index 'g' to given 'label' and removes it -** from the list of pending goto's. +** from the list of pending gotos. ** If it jumps into the scope of some variable, raises an error. */ static void solvegoto (LexState *ls, int g, Labeldesc *label) { int i; - Labellist *gl = &ls->dyd->gt; /* list of goto's */ + Labellist *gl = &ls->dyd->gt; /* list of gotos */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ lua_assert(eqstr(gt->name, label->name)); if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ @@ -583,7 +596,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) { /* ** Solves forward jumps. Check whether new label 'lb' matches any ** pending gotos in current block and solves them. Return true -** if any of the goto's need to close upvalues. +** if any of the gotos need to close upvalues. */ static int solvegotos (LexState *ls, Labeldesc *lb) { Labellist *gl = &ls->dyd->gt; @@ -604,7 +617,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) { /* ** Create a new label with the given 'name' at the given 'line'. ** 'last' tells whether label is the last non-op statement in its -** block. Solves all pending goto's to this new label and adds +** block. Solves all pending gotos to this new label and adds ** a close instruction if necessary. ** Returns true iff it added a close instruction. */ @@ -677,19 +690,19 @@ static void leaveblock (FuncState *fs) { LexState *ls = fs->ls; int hasclose = 0; int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ - if (bl->isloop) /* fix pending breaks? */ + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop) /* has to fix pending breaks? */ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); - if (!hasclose && bl->previous && bl->upval) + if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); - fs->bl = bl->previous; - removevars(fs, bl->nactvar); - lua_assert(bl->nactvar == fs->nactvar); fs->freereg = stklevel; /* free registers */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ - if (bl->previous) /* inner block? */ - movegotosout(fs, bl); /* update pending gotos to outer block */ + fs->bl = bl->previous; /* current block now is previous one */ + if (bl->previous) /* was it a nested block? */ + movegotosout(fs, bl); /* update pending gotos to enclosing block */ else { - if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ + if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } } @@ -1614,7 +1627,7 @@ static void forlist (LexState *ls, TString *indexname) { line = ls->linenumber; adjust_assign(ls, 4, explist(ls, &e), &e); adjustlocalvars(ls, 4); /* control variables */ - markupval(fs, fs->nactvar); /* last control var. must be closed */ + marktobeclosed(fs); /* last control var. must be closed */ luaK_checkstack(fs, 3); /* extra space to call generator */ forbody(ls, base, line, nvars - 4, 1); } @@ -1718,11 +1731,9 @@ static int getlocalattribute (LexState *ls) { } -static void checktoclose (LexState *ls, int level) { +static void checktoclose (FuncState *fs, int level) { if (level != -1) { /* is there a to-be-closed variable? */ - FuncState *fs = ls->fs; - markupval(fs, level + 1); - fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ + marktobeclosed(fs); luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0); } } @@ -1766,7 +1777,7 @@ static void localstat (LexState *ls) { adjust_assign(ls, nvars, nexps, &e); adjustlocalvars(ls, nvars); } - checktoclose(ls, toclose); + checktoclose(fs, toclose); } @@ -1791,6 +1802,7 @@ static void funcstat (LexState *ls, int line) { luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); body(ls, &b, ismethod, line); + check_readonly(ls, &v); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } @@ -1948,10 +1960,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LexState lexstate; FuncState funcstate; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ - setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */ + setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ luaD_inctop(L); lexstate.h = luaH_new(L); /* create table for scanner */ - sethvalue2s(L, L->top, lexstate.h); /* anchor it */ + sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ luaD_inctop(L); funcstate.f = cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); @@ -1965,7 +1977,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); - L->top--; /* remove scanner's table */ + L->top.p--; /* remove scanner's table */ return cl; /* closure is on the stack, too */ } diff --git a/third_party/lua/lprefix.h b/third_party/lua/lprefix.h index 373580fe4..3f713a921 100644 --- a/third_party/lua/lprefix.h +++ b/third_party/lua/lprefix.h @@ -1,6 +1,7 @@ #ifndef lprefix_h #define lprefix_h + /* ** Allows POSIX/XSI stuff */ @@ -22,6 +23,7 @@ #endif /* } */ + /* ** Windows stuff */ diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index ec65fd069..b56548a6b 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ diff --git a/third_party/lua/lstate.c b/third_party/lua/lstate.c index 0151a17c3..ae810c655 100644 --- a/third_party/lua/lstate.c +++ b/third_party/lua/lstate.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lstate_c #define LUA_CORE + #include "libc/str/str.h" #include "libc/time.h" #include "third_party/lua/lapi.h" @@ -180,7 +181,7 @@ void luaE_checkcstack (lua_State *L) { if (getCcalls(L) == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ } @@ -194,33 +195,33 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) { static void stack_init (lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ - L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); - L1->tbclist = L1->stack; + L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + L1->tbclist.p = L1->stack.p; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) - setnilvalue(s2v(L1->stack + i)); /* erase new stack */ - L1->top = L1->stack; - L1->stack_last = L1->stack + BASIC_STACK_SIZE; + setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ + L1->top.p = L1->stack.p; + L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; ci->callstatus = CIST_C; - ci->func = L1->top; + ci->func.p = L1->top.p; ci->u.c.k = NULL; ci->nresults = 0; - setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */ - L1->top++; - ci->top = L1->top + LUA_MINSTACK; + setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ + L1->top.p++; + ci->top.p = L1->top.p + LUA_MINSTACK; L1->ci = ci; } static void freestack (lua_State *L) { - if (L->stack == NULL) + if (L->stack.p == NULL) return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); lua_assert(L->nci == 0); - luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */ + luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ } @@ -250,7 +251,7 @@ static void f_luaopen (lua_State *L, void *ud) { luaS_init(L); luaT_init(L); luaX_init(L); - g->gcrunning = 1; /* allow gc */ + g->gcstp = 0; /* allow gc */ setnilvalue(&g->nilvalue); /* now state is complete */ luai_userstateopen(L); } @@ -262,7 +263,7 @@ static void f_luaopen (lua_State *L, void *ud) { */ static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; - L->stack = NULL; + L->stack.p = NULL; L->ci = NULL; L->nci = 0; L->twups = L; /* thread has no upvalues */ @@ -283,8 +284,9 @@ static void preinit_thread (lua_State *L, global_State *g) { static void close_state (lua_State *L) { global_State *g = G(L); if (!completestate(g)) /* closing a partially built state? */ - luaC_freeallobjects(L); /* jucst collect its objects */ + luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ + L->ci = &L->base_ci; /* unwind CallInfo list */ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); @@ -307,20 +309,16 @@ static void close_state (lua_State *L) { * Threads are subject to garbage collection, like any Lua object. */ LUA_API lua_State *lua_newthread (lua_State *L) { - global_State *g; + global_State *g = G(L); + GCObject *o; lua_State *L1; lua_lock(L); - g = G(L); luaC_checkGC(L); /* create new thread */ - L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; - L1->marked = luaC_white(g); - L1->tt = LUA_VTHREAD; - /* link it on list 'allgc' */ - L1->next = g->allgc; - g->allgc = obj2gco(L1); + o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l)); + L1 = gco2th(o); /* anchor it on L stack */ - setthvalue2s(L, L->top, L1); + setthvalue2s(L, L->top.p, L1); api_incr_top(L); preinit_thread(L1, g); L1->hookmask = L->hookmask; @@ -339,7 +337,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { void luaE_freethread (lua_State *L, lua_State *L1) { LX *l = fromstate(L1); - luaF_closeupval(L1, L1->stack); /* close all upvalues */ + luaF_closeupval(L1, L1->stack.p); /* close all upvalues */ lua_assert(L1->openupval == NULL); luai_userstatefree(L, L1); freestack(L1); @@ -349,32 +347,41 @@ void luaE_freethread (lua_State *L, lua_State *L1) { int luaE_resetthread (lua_State *L, int status) { CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ - setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ - ci->func = L->stack; + setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ + ci->func.p = L->stack.p; ci->callstatus = CIST_C; if (status == LUA_YIELD) status = LUA_OK; + L->status = LUA_OK; /* so it can run __close metamethods */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ - luaD_seterrorobj(L, status, L->stack + 1); + luaD_seterrorobj(L, status, L->stack.p + 1); else - L->top = L->stack + 1; - ci->top = L->top + LUA_MINSTACK; - L->status = cast_byte(status); - luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); + L->top.p = L->stack.p + 1; + ci->top.p = L->top.p + LUA_MINSTACK; + luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); return status; } -LUA_API int lua_resetthread (lua_State *L) { +LUA_API int lua_closethread (lua_State *L, lua_State *from) { int status; lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; status = luaE_resetthread(L, L->status); lua_unlock(L); return status; } +/* +** Deprecated! Use 'lua_closethread' instead. +*/ +LUA_API int lua_resetthread (lua_State *L) { + return lua_closethread(L, NULL); +} + + LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { int i; lua_State *L; @@ -396,7 +403,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->ud_warn = NULL; g->mainthread = L; g->seed = luai_makeseed(L); - g->gcrunning = 0; /* no GC while building state */ + g->gcstp = GCSTPGC; /* no GC while building state */ g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); @@ -449,7 +456,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) { ** Generate a warning from an error message */ void luaE_warnerror (lua_State *L, const char *where) { - TValue *errobj = s2v(L->top - 1); /* error object */ + TValue *errobj = s2v(L->top.p - 1); /* error object */ const char *msg = (ttisstring(errobj)) ? svalue(errobj) : "error object is not a string"; diff --git a/third_party/lua/lstate.h b/third_party/lua/lstate.h index 3e3318d96..6f93150fe 100644 --- a/third_party/lua/lstate.h +++ b/third_party/lua/lstate.h @@ -1,6 +1,9 @@ #ifndef lstate_h #define lstate_h +/* Some header files included here need this definition */ +typedef struct CallInfo CallInfo; + #include "libc/calls/calls.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lua.h" @@ -132,7 +135,7 @@ struct lua_longjmp; /* defined in ldo.c */ #define BASIC_STACK_SIZE (2*LUA_MINSTACK) -#define stacksize(th) cast_int((th)->stack_last - (th)->stack) +#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) /* kinds of Garbage Collection */ @@ -158,13 +161,13 @@ typedef struct stringtable { ** - field 'nyield' is used only while a function is "doing" an ** yield (from the yield until the next resume); ** - field 'nres' is used only while closing tbc variables when -** returning from a C function; +** returning from a function; ** - field 'transferinfo' is used only during call/returnhooks, ** before the function starts or after it ends. */ -typedef struct CallInfo { - StkId func; /* function index in the stack */ - StkId top; /* top for this function */ +struct CallInfo { + StkIdRel func; /* function index in the stack */ + StkIdRel top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ @@ -189,7 +192,7 @@ typedef struct CallInfo { } u2; short nresults; /* expected number of results from this function */ unsigned short callstatus; -} CallInfo; +}; /* @@ -202,7 +205,7 @@ typedef struct CallInfo { #define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ #define CIST_TAIL (1<<5) /* call was tail called */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ -#define CIST_FIN (1<<7) /* call is running a finalizer */ +#define CIST_FIN (1<<7) /* function "called" a finalizer */ #define CIST_TRAN (1<<8) /* 'ci' has transfer information */ #define CIST_CLSRET (1<<9) /* function is closing tbc variables */ /* Bits 10-12 are used for CIST_RECST (see below) */ @@ -256,7 +259,7 @@ typedef struct global_State { lu_byte gcstopem; /* stops emergency collections */ lu_byte genminormul; /* control for minor generational collections */ lu_byte genmajormul; /* control for major generational collections */ - lu_byte gcrunning; /* true if GC is running */ + lu_byte gcstp; /* control whether GC is running */ lu_byte gcemergency; /* true if this is an emergency collection */ lu_byte gcpause; /* size of pause between successive GCs */ lu_byte gcstepmul; /* GC "speed" */ @@ -284,7 +287,7 @@ typedef struct global_State { struct lua_State *mainthread; TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ - struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ + struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ @@ -299,13 +302,13 @@ struct lua_State { lu_byte status; lu_byte allowhook; unsigned short nci; /* number of items in 'ci' list */ - StkId top; /* first free slot in the stack */ + StkIdRel top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ - StkId stack_last; /* end of stack (last element + 1) */ - StkId stack; /* stack base */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ - StkId tbclist; /* list of to-be-closed variables */ + StkIdRel tbclist; /* list of to-be-closed variables */ GCObject *gclist; struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ diff --git a/third_party/lua/lstring.c b/third_party/lua/lstring.c index 68efe07a0..91c525524 100644 --- a/third_party/lua/lstring.c +++ b/third_party/lua/lstring.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lstring_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" diff --git a/third_party/lua/lstrlib.c b/third_party/lua/lstrlib.c index 60db68c4a..9c8c7a1ba 100644 --- a/third_party/lua/lstrlib.c +++ b/third_party/lua/lstrlib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lstrlib_c #define LUA_LIB + #include "libc/math.h" #include "libc/str/str.h" #include "third_party/lua/cosmo.h" @@ -62,7 +63,7 @@ __static_yoink("lua_notice"); -static int str_format(lua_State *); +static int str_format(lua_State *); // [jart] static int str_len (lua_State *L) { @@ -607,7 +608,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) { static const char *match (MatchState *ms, const char *s, const char *p) { if (l_unlikely(ms->matchdepth-- == 0)) luaL_error(ms->L, "pattern too complex"); - init: /* using goto's to optimize tail recursion */ + init: /* using goto to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ switch (*p) { case '(': { /* start capture */ @@ -1127,13 +1128,31 @@ static int lua_number2strx (lua_State *L, char *buff, int sz, /* valid flags in a format specification */ -#if !defined(L_FMTFLAGS) -#define L_FMTFLAGS "-+ #0" +#if !defined(L_FMTFLAGSF) + +/* valid flags for a, A, e, E, f, F, g, and G conversions */ +#define L_FMTFLAGSF "-+#0 " + +/* valid flags for o, x, and X conversions */ +#define L_FMTFLAGSX "-#0" + +/* valid flags for d and i conversions */ +#define L_FMTFLAGSI "-+0 " + +/* valid flags for u conversions */ +#define L_FMTFLAGSU "-0" + +/* valid flags for c, p, and s conversions */ +#define L_FMTFLAGSC "-" + #endif /* -** maximum size of each format specification (such as "%-099.99d") +** Maximum size of each format specification (such as "%-099.99d"): +** Initial '%', flags (up to 5), width (2), period, precision (2), +** length modifier (8), conversion specifier, and final '\0', plus some +** extra. */ #define MAX_FORMAT 32 @@ -1226,25 +1245,53 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { } -static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { - const char *p = strfrmt; - while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */ - if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char)) - luaL_error(L, "invalid format (repeated flags)"); - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ +static const char *get2digits (const char *s) { + if (isdigit(uchar(*s))) { + s++; + if (isdigit(uchar(*s))) s++; /* (2 digits at most) */ } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); + return s; +} + + +/* +** Check whether a conversion specification is valid. When called, +** first character in 'form' must be '%' and last character must +** be a valid conversion specifier. 'flags' are the accepted flags; +** 'precision' signals whether to accept a precision. +*/ +static void checkformat (lua_State *L, const char *form, const char *flags, + int precision) { + const char *spec = form + 1; /* skip '%' */ + spec += strspn(spec, flags); /* skip flags */ + if (*spec != '0') { /* a width cannot start with '0' */ + spec = get2digits(spec); /* skip width */ + if (*spec == '.' && precision) { + spec++; + spec = get2digits(spec); /* skip precision */ + } + } + if (!isalpha(uchar(*spec))) /* did not go to the end? */ + luaL_error(L, "invalid conversion specification: '%s'", form); + } + + +/* +** Get a conversion specification and copy it to 'form'. +** Return the address of its last character. +*/ +static const char *getformat (lua_State *L, const char *strfrmt, + char *form) { + /* spans flags, width, and precision ('0' is included as a flag) */ + size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789."); + len++; /* adds following character (should be the specifier) */ + /* still needs space for '%', '\0', plus a length modifier */ + if (len >= MAX_FORMAT - 10) + luaL_error(L, "invalid format (too long)"); *(form++) = '%'; - memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); - form += (p - strfrmt) + 1; - *form = '\0'; - return p; + memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; } @@ -1267,6 +1314,7 @@ static int str_format (lua_State *L) { size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt+sfl; + const char *flags; luaL_Buffer b; luaL_buffinit(L, &b); while (strfrmt < strfrmt_end) { @@ -1276,25 +1324,35 @@ static int str_format (lua_State *L) { luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format ('%...') */ - int maxitem = MAX_ITEM; - char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */ - int nb = 0; /* number of bytes in added item */ + int maxitem = MAX_ITEM; /* maximum length for the result */ + char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ + int nb = 0; /* number of bytes in result */ if (++arg > top) return luaL_argerror(L, arg, "no value"); - strfrmt = scanformat(L, strfrmt, form); + strfrmt = getformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { + checkformat(L, form, L_FMTFLAGSC, 0); nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); break; } case 'd': case 'i': - case 'o': case 'u': case 'x': case 'X': { + flags = L_FMTFLAGSI; + goto intcase; + case 'u': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': case 'x': case 'X': + flags = L_FMTFLAGSX; + intcase: { lua_Integer n = luaL_checkinteger(L, arg); + checkformat(L, form, flags, 1); addlenmod(form, LUA_INTEGER_FRMLEN); nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); break; } case 'a': case 'A': + checkformat(L, form, L_FMTFLAGSF, 1); addlenmod(form, LUA_NUMBER_FRMLEN); nb = lua_number2strx(L, buff, maxitem, form, luaL_checknumber(L, arg)); @@ -1305,12 +1363,14 @@ static int str_format (lua_State *L) { /* FALLTHROUGH */ case 'e': case 'E': case 'g': case 'G': { lua_Number n = luaL_checknumber(L, arg); + checkformat(L, form, L_FMTFLAGSF, 1); addlenmod(form, LUA_NUMBER_FRMLEN); nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); break; } case 'p': { const void *p = lua_topointer(L, arg); + checkformat(L, form, L_FMTFLAGSC, 0); if (p == NULL) { /* avoid calling 'printf' with argument NULL */ p = "(null)"; /* result */ form[strlen(form) - 1] = 's'; /* format it as a string */ @@ -1331,7 +1391,8 @@ static int str_format (lua_State *L) { luaL_addvalue(&b); /* keep entire string */ else { luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); - if (!strchr(form, '.') && l >= 100) { + checkformat(L, form, L_FMTFLAGSC, 1); + if (strchr(form, '.') == NULL && l >= 100) { /* no precision and string is too long to be formatted */ luaL_addvalue(&b); /* keep entire string */ } @@ -1389,15 +1450,6 @@ static const union { } nativeendian = {1}; -/* dummy structure to get native alignment requirements */ -struct cD { - char c; - union { double d; void *p; lua_Integer i; lua_Number n; } u; -}; - -#define MAXALIGN (offsetof(struct cD, u)) - - /* ** information to pack/unpack stuff */ @@ -1472,6 +1524,8 @@ static void initheader (lua_State *L, Header *h) { ** Read and classify next option. 'size' is filled with option's size. */ static KOption getoption (Header *h, const char **fmt, int *size) { + /* dummy structure to get native alignment requirements */ + struct cD { char c; union { LUAI_MAXALIGN; } u; }; int opt = *((*fmt)++); *size = 0; /* default */ switch (opt) { @@ -1502,7 +1556,11 @@ static KOption getoption (Header *h, const char **fmt, int *size) { case '<': h->islittle = 1; break; case '>': h->islittle = 0; break; case '=': h->islittle = nativeendian.little; break; - case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break; + case '!': { + const int maxalign = offsetof(struct cD, u); + h->maxalign = getnumlimit(h, fmt, maxalign); + break; + } default: luaL_error(h->L, "invalid format option '%c'", opt); } return Knop; diff --git a/third_party/lua/ltable.c b/third_party/lua/ltable.c index 63f2867f8..10c8e5fb9 100644 --- a/third_party/lua/ltable.c +++ b/third_party/lua/ltable.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ltable_c #define LUA_CORE + #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -100,8 +101,6 @@ __static_yoink("lua_notice"); #define hashstr(t,str) hashpow2(t, (str)->hash) #define hashboolean(t,p) hashpow2(t, p) -#define hashint(t,i) hashpow2(t, i) - #define hashpointer(t,p) hashmod(t, point2uint(p)) @@ -117,6 +116,20 @@ static const Node dummynode_ = { static const TValue absentkey = {ABSTKEYCONSTANT}; +/* +** Hash for integers. To allow a good hash, use the remainder operator +** ('%'). If integer fits as a non-negative int, compute an int +** remainder, which is faster. Otherwise, use an unsigned-integer +** remainder, which uses all bits and ensures a non-negative result. +*/ +static Node *hashint (const Table *t, lua_Integer i) { + lua_Unsigned ui = l_castS2U(i); + if (ui <= cast_uint(INT_MAX)) + return hashmod(t, cast_int(ui)); + else + return hashmod(t, ui); +} + /* ** Hash for floating-point numbers. @@ -150,26 +163,24 @@ static int l_hashfloat (lua_Number n) { /* ** returns the 'main' position of an element in a table (that is, -** the index of its hash value). The key comes broken (tag in 'ktt' -** and value in 'vkl') so that we can call it on keys inserted into -** nodes. +** the index of its hash value). */ -static Node *mainposition (const Table *t, int ktt, const Value *kvl) { - switch (withvariant(ktt)) { +static Node *mainpositionTV (const Table *t, const TValue *key) { + switch (ttypetag(key)) { case LUA_VNUMINT: { - lua_Integer key = ivalueraw(*kvl); - return hashint(t, key); + lua_Integer i = ivalue(key); + return hashint(t, i); } case LUA_VNUMFLT: { - lua_Number n = fltvalueraw(*kvl); + lua_Number n = fltvalue(key); return hashmod(t, l_hashfloat(n)); } case LUA_VSHRSTR: { - TString *ts = tsvalueraw(*kvl); + TString *ts = tsvalue(key); return hashstr(t, ts); } case LUA_VLNGSTR: { - TString *ts = tsvalueraw(*kvl); + TString *ts = tsvalue(key); return hashpow2(t, luaS_hashlongstr(ts)); } case LUA_VFALSE: @@ -177,26 +188,25 @@ static Node *mainposition (const Table *t, int ktt, const Value *kvl) { case LUA_VTRUE: return hashboolean(t, 1); case LUA_VLIGHTUSERDATA: { - void *p = pvalueraw(*kvl); + void *p = pvalue(key); return hashpointer(t, p); } case LUA_VLCF: { - lua_CFunction f = fvalueraw(*kvl); + lua_CFunction f = fvalue(key); return hashpointer(t, f); } default: { - GCObject *o = gcvalueraw(*kvl); + GCObject *o = gcvalue(key); return hashpointer(t, o); } } } -/* -** Returns the main position of an element given as a 'TValue' -*/ -static Node *mainpositionTV (const Table *t, const TValue *key) { - return mainposition(t, rawtt(key), valraw(key)); +l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { + TValue key; + getnodekey(cast(lua_State *, NULL), &key, nd); + return mainpositionTV(t, &key); } @@ -264,9 +274,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) { size |= (size >> 2); size |= (size >> 4); size |= (size >> 8); +#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */ size |= (size >> 16); #if (UINT_MAX >> 30) > 3 size |= (size >> 32); /* unsigned int has more than 32 bits */ +#endif #endif size++; lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); @@ -495,7 +507,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); - for (i = 0; i < (int)size; i++) { + for (i = 0; i < cast_int(size); i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilkey(n); @@ -695,7 +707,7 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { return; } lua_assert(!isdummy(t)); - othern = mainposition(t, keytt(mp), &keyval(mp)); + othern = mainpositionfromnode(t, mp); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ while (othern + gnext(othern) != mp) /* find previous */ @@ -982,6 +994,4 @@ Node *luaH_mainposition (const Table *t, const TValue *key) { return mainpositionTV(t, key); } -int luaH_isdummy (const Table *t) { return isdummy(t); } - #endif diff --git a/third_party/lua/ltable.h b/third_party/lua/ltable.h index 7a4d8d5f2..1278aa6e0 100644 --- a/third_party/lua/ltable.h +++ b/third_party/lua/ltable.h @@ -53,7 +53,6 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); -LUAI_FUNC int luaH_isdummy (const Table *t); #endif diff --git a/third_party/lua/ltablib.c b/third_party/lua/ltablib.c index fe8a1fe62..9f8bd786d 100644 --- a/third_party/lua/ltablib.c +++ b/third_party/lua/ltablib.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ltablib_c #define LUA_LIB + #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/str/str.h" @@ -76,8 +77,9 @@ static void checktab (lua_State *L, int arg, int what) { static int tinsert (lua_State *L) { - lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */ lua_Integer pos; /* where to insert new element */ + lua_Integer e = aux_getn(L, 1, TAB_RW); + e = luaL_intop(+, e, 1); /* first empty element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ @@ -109,7 +111,7 @@ static int tremove (lua_State *L) { lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ /* check whether 'pos' is in [1, size + 1] */ - luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, "position out of bounds"); lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { @@ -164,7 +166,7 @@ static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { lua_geti(L, 1, i); if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", - luaL_typename(L, -1), i); + luaL_typename(L, -1), (LUAI_UACINT)i); luaL_addvalue(b); } diff --git a/third_party/lua/ltests.c b/third_party/lua/ltests.c index 10f18d0dc..8a9a66347 100644 --- a/third_party/lua/ltests.c +++ b/third_party/lua/ltests.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ltests_c #define LUA_CORE + #include "third_party/lua/lapi.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lcode.h" @@ -55,7 +56,7 @@ __static_yoink("lua_notice"); void *l_Trick = 0; -#define obj_at(L,k) s2v(L->ci->func + (k)) +#define obj_at(L,k) s2v(L->ci->func.p + (k)) static int runC (lua_State *L, lua_State *L1, const char *pc); @@ -68,7 +69,7 @@ static void setnameval (lua_State *L, const char *name, int val) { static void pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); + setobj2s(L, L->top.p, o); api_incr_top(L); } @@ -430,7 +431,7 @@ static void checkLclosure (global_State *g, LClosure *cl) { if (uv) { checkobjrefN(g, clgc, uv); if (!upisopen(uv)) - checkvalref(g, obj2gco(uv), uv->v); + checkvalref(g, obj2gco(uv), uv->v.p); } } } @@ -439,7 +440,7 @@ static void checkLclosure (global_State *g, LClosure *cl) { static int lua_checkpc (CallInfo *ci) { if (!isLua(ci)) return 1; else { - StkId f = ci->func; + StkId f = ci->func.p; Proto *p = clLvalue(s2v(f))->p; return p->code <= ci->u.l.savedpc && ci->u.l.savedpc <= p->code + p->sizecode; @@ -452,19 +453,19 @@ static void checkstack (global_State *g, lua_State *L1) { CallInfo *ci; UpVal *uv; assert(!isdead(g, L1)); - if (L1->stack == NULL) { /* incomplete thread? */ + if (L1->stack.p == NULL) { /* incomplete thread? */ assert(L1->openupval == NULL && L1->ci == NULL); return; } for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next) assert(upisopen(uv)); /* must be open */ - assert(L1->top <= L1->stack_last); - assert(L1->tbclist <= L1->top); + assert(L1->top.p <= L1->stack_last.p); + assert(L1->tbclist.p <= L1->top.p); for (ci = L1->ci; ci != NULL; ci = ci->previous) { - assert(ci->top <= L1->stack_last); + assert(ci->top.p <= L1->stack_last.p); assert(lua_checkpc(ci)); } - for (o = L1->stack; o < L1->stack_last; o++) + for (o = L1->stack.p; o < L1->stack_last.p; o++) checkliveness(L1, s2v(o)); /* entire stack must have valid values */ } @@ -476,7 +477,7 @@ static void checkrefs (global_State *g, GCObject *o) { break; } case LUA_VUPVAL: { - checkvalref(g, o, gco2upv(o)->v); + checkvalref(g, o, gco2upv(o)->v.p); break; } case LUA_VTABLE: { @@ -544,7 +545,7 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, static lu_mem checkgraylist (global_State *g, GCObject *o) { int total = 0; /* count number of elements in the list */ - ((void)g); /* better to keep it available if we need to print an object */ + cast_void(g); /* better to keep it if we need to print an object */ while (o) { assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2)); assert(!testbit(o->marked, TESTBIT)); @@ -991,7 +992,7 @@ static int hash_query (lua_State *L) { static int stacklevel (lua_State *L) { unsigned long a = 0; - lua_pushinteger(L, (L->top - L->stack)); + lua_pushinteger(L, (L->top.p - L->stack.p)); lua_pushinteger(L, stacksize(L)); lua_pushinteger(L, L->nCcalls); lua_pushinteger(L, L->nci); @@ -1051,7 +1052,7 @@ static int string_query (lua_State *L) { TString *ts; int n = 0; for (ts = tb->hash[s]; ts != NULL; ts = ts->u.hnext) { - setsvalue2s(L, L->top, ts); + setsvalue2s(L, L->top.p, ts); api_incr_top(L); n++; } @@ -1066,7 +1067,7 @@ static int tref (lua_State *L) { luaL_checkany(L, 1); lua_pushvalue(L, 1); lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX)); - (void)level; /* to avoid warnings */ + cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level+1); /* +1 for result */ return 1; } @@ -1074,7 +1075,7 @@ static int tref (lua_State *L) { static int getref (lua_State *L) { int level = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1)); - (void)level; /* to avoid warnings */ + cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level+1); return 1; } @@ -1082,7 +1083,7 @@ static int getref (lua_State *L) { static int unref (lua_State *L) { int level = lua_gettop(L); luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1))); - (void)level; /* to avoid warnings */ + cast_void(level); /* to avoid warnings */ lua_assert(lua_gettop(L) == level); return 0; } @@ -1258,7 +1259,7 @@ static int panicback (lua_State *L) { b = (struct Aux *)lua_touserdata(L, -1); lua_pop(L, 1); /* remove 'Aux' struct */ runC(b->L, L, b->paniccode); /* run optional panic code */ - gclongjmp(b->jb, 1); + gclongjmp(b->jb, 1); // [jart] return 1; /* to avoid warnings */ } @@ -1544,7 +1545,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_newthread(L1); } else if EQ("resetthread") { - lua_pushinteger(L1, lua_resetthread(L1)); + lua_pushinteger(L1, lua_resetthread(L1)); /* deprecated */ } else if EQ("newuserdata") { lua_newuserdata(L1, getnum); @@ -1719,7 +1720,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_error(L1); } else if EQ("abort") { - __die(); + __die(); // [jart] } else if EQ("throw") { #if defined(__cplusplus) @@ -1751,9 +1752,12 @@ static struct X { int x; } x; else if EQ("tostring") { const char *s = lua_tostring(L1, getindex); const char *s1 = lua_pushstring(L1, s); - (void)s1; /* to avoid warnings */ + cast_void(s1); /* to avoid warnings */ lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0); } + else if EQ("Ltolstring") { + luaL_tolstring(L1, getindex, NULL); + } else if EQ("type") { lua_pushstring(L1, luaL_typename(L1, getnum)); } diff --git a/third_party/lua/ltests.h b/third_party/lua/ltests.h index 6c00b98fa..2ce9ffb54 100644 --- a/third_party/lua/ltests.h +++ b/third_party/lua/ltests.h @@ -1,5 +1,6 @@ #ifndef ltests_h #define ltests_h + #include "third_party/lua/lua.h" @@ -117,6 +118,13 @@ LUA_API void *debug_realloc (void *ud, void *block, #define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; } +/* +** This one is not compatible with tests for opcode optimizations, +** as it blocks some optimizations +#define MAXINDEXRK 0 +*/ + + /* make stack-overflow tests run faster */ #undef LUAI_MAXSTACK #define LUAI_MAXSTACK 50000 diff --git a/third_party/lua/ltm.c b/third_party/lua/ltm.c index b7a0d3914..20dbbe686 100644 --- a/third_party/lua/ltm.c +++ b/third_party/lua/ltm.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ltm_c #define LUA_CORE + #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -118,12 +119,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) { void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3) { - StkId func = L->top; + StkId func = L->top.p; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ setobj2s(L, func + 3, p3); /* 3rd argument */ - L->top = func + 4; + L->top.p = func + 4; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 0); @@ -135,18 +136,18 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, StkId res) { ptrdiff_t result = savestack(L, res); - StkId func = L->top; + StkId func = L->top.p; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ - L->top += 3; + L->top.p += 3; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 1); else luaD_callnoyield(L, func, 1); res = restorestack(L, result); - setobjs2s(L, res, --L->top); /* move result to its place */ + setobjs2s(L, res, --L->top.p); /* move result to its place */ } @@ -181,7 +182,7 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, void luaT_tryconcatTM (lua_State *L) { - StkId top = L->top; + StkId top = L->top.p; if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))) luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); @@ -216,15 +217,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, */ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { - if (callbinTM(L, p1, p2, L->top, event)) /* try original event */ - return !l_isfalse(s2v(L->top)); + if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */ + return !l_isfalse(s2v(L->top.p)); #if defined(LUA_COMPAT_LT_LE) else if (event == TM_LE) { /* try '!(p2 < p1)' for '(p1 <= p2)' */ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ - if (callbinTM(L, p2, p1, L->top, TM_LT)) { + if (callbinTM(L, p2, p1, L->top.p, TM_LT)) { L->ci->callstatus ^= CIST_LEQ; /* clear mark */ - return l_isfalse(s2v(L->top)); + return l_isfalse(s2v(L->top.p)); } /* else error will remove this 'ci'; no need to clear mark */ } @@ -254,20 +255,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, const Proto *p) { int i; - int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ + int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ int nextra = actual - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); /* copy function to the top of the stack */ - setobjs2s(L, L->top++, ci->func); + setobjs2s(L, L->top.p++, ci->func.p); /* move fixed parameters to the top of the stack */ for (i = 1; i <= nfixparams; i++) { - setobjs2s(L, L->top++, ci->func + i); - setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */ + setobjs2s(L, L->top.p++, ci->func.p + i); + setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ } - ci->func += actual + 1; - ci->top += actual + 1; - lua_assert(L->top <= ci->top && ci->top <= L->stack_last); + ci->func.p += actual + 1; + ci->top.p += actual + 1; + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); } @@ -277,10 +278,10 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { if (wanted < 0) { wanted = nextra; /* get all extra arguments available */ checkstackGCp(L, nextra, where); /* ensure stack space */ - L->top = where + nextra; /* next instruction will need top */ + L->top.p = where + nextra; /* next instruction will need top */ } for (i = 0; i < wanted && i < nextra; i++) - setobjs2s(L, where + i, ci->func - nextra + i); + setobjs2s(L, where + i, ci->func.p - nextra + i); for (; i < wanted; i++) /* complete required results with nil */ setnilvalue(s2v(where + i)); } diff --git a/third_party/lua/ltm.h b/third_party/lua/ltm.h index b369bde8c..f2883b199 100644 --- a/third_party/lua/ltm.h +++ b/third_party/lua/ltm.h @@ -1,9 +1,18 @@ #ifndef ltm_h #define ltm_h + #include "third_party/lua/lobject.h" +#include "third_party/lua/lstate.h" #include "third_party/lua/luaconf.h" #include "third_party/lua/tms.h" + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" and "ORDER OP" +*/ +// [jart] moved to tms.h + /* ** Mask with 1 in all fast-access methods. A 1 in any of these bits ** in the flag of a (meta)table means the metatable does not have the @@ -54,8 +63,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int inv, int isfloat, TMS event); LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, - struct CallInfo *ci, const Proto *p); -LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, + CallInfo *ci, const Proto *p); +LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted); diff --git a/third_party/lua/lua.h b/third_party/lua/lua.h index 430f74216..f17da40f3 100644 --- a/third_party/lua/lua.h +++ b/third_party/lua/lua.h @@ -1,18 +1,21 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_LUA_LUA_H_ #define COSMOPOLITAN_THIRD_PARTY_LUA_LUA_H_ + #include "third_party/lua/luaconf.h" + COSMOPOLITAN_C_START_ + #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "3" +#define LUA_VERSION_RELEASE "6" #define LUA_VERSION_NUM 504 -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2021 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" @@ -118,6 +121,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); /* @@ -140,7 +153,8 @@ extern const char lua_ident[]; LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); -LUA_API int (lua_resetthread) (lua_State *L); +LUA_API int (lua_closethread) (lua_State *L, lua_State *from); +LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */ LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); @@ -429,12 +443,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx); #define LUA_MASKLINE (1 << LUA_HOOKLINE) #define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) -typedef struct lua_Debug lua_Debug; /* activation record */ - - -/* Functions to be called by the debugger in specific events */ -typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); - LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); @@ -477,7 +485,7 @@ struct lua_Debug { /* }====================================================================== */ -extern const char *g_lua_path_default; +extern const char *g_lua_path_default; // [jart] COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_THIRD_PARTY_LUA_LUA_H_ */ diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index a49b7ae58..dcc10ff4d 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -26,6 +26,7 @@ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lua_c + #include "third_party/lua/lua.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" @@ -52,6 +53,7 @@ #include "tool/args/args.h" __static_yoink("lua_notice"); + STATIC_STACK_ALIGN(GetStackSize()); #if !defined(LUA_PROGNAME) @@ -81,7 +83,8 @@ static void print_usage (const char *badoption) { "Available options are:\n" " -e stat execute string 'stat'\n" " -i enter interactive mode after executing 'script'\n" - " -l name require library 'name' into global 'name'\n" + " -l mod require library 'mod' into global 'mod'\n" + " -l g=mod require library 'mod' into global 'g'\n" " -v show version information\n" " -E ignore environment variables\n" " -W turn warnings on\n" @@ -105,10 +108,11 @@ static void print_version (void) { ** to the script (everything after 'script') go to positive indices; ** other arguments (before the script name) go to negative indices. ** If there is no script name, assume interpreter's name as base. +** (If there is no interpreter's name either, 'script' is -1, so +** table sizes are zero.) */ static void createargtable (lua_State *L, char **argv, int argc, int script) { int i, narg; - if (script == argc) script = 0; /* no script name? */ narg = argc - (script + 1); /* number of positive indices */ lua_createtable(L, narg, script + 1); for (i = 0; i < argc; i++) { @@ -136,16 +140,22 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* -** Calls 'require(name)' and stores the result in a global variable -** with the given name. +** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ -static int dolibrary (lua_State *L, const char *name) { +static int dolibrary (lua_State *L, char *globname) { int status; + char *modname = strchr(globname, '='); + if (modname == NULL) /* no explicit name? */ + modname = globname; /* module name is equal to global name */ + else { + *modname = '\0'; /* global name ends here */ + modname++; /* module name starts after the '=' */ + } lua_getglobal(L, "require"); - lua_pushstring(L, name); - status = lua_runchunk(L, 1, 1); /* call 'require(name)' */ + lua_pushstring(L, modname); + status = lua_runchunk(L, 1, 1); /* call 'require(modname)' */ if (status == LUA_OK) - lua_setglobal(L, name); /* global[name] = require return */ + lua_setglobal(L, globname); /* globname = require(modname) */ return lua_report(L, status); } @@ -190,14 +200,23 @@ static int handle_script (lua_State *L, char **argv) { /* ** Traverses all arguments from 'argv', returning a mask with those -** needed before running any Lua code (or an error code if it finds -** any invalid argument). 'first' returns the first not-handled argument -** (either the script name or a bad argument in case of error). +** needed before running any Lua code or an error code if it finds any +** invalid argument. In case of error, 'first' is the index of the bad +** argument. Otherwise, 'first' is -1 if there is no program name, +** 0 if there is no script name, or the index of the script name. */ static int collectargs (char **argv, int *first) { int args = 0; int i; - for (i = 1; argv[i] != NULL; i++) { + if (argv[0] != NULL) { /* is there a program name? */ + if (argv[0][0]) /* not empty? */ + lua_progname = argv[0]; /* save it */ + } + else { /* no program name */ + *first = -1; + return 0; + } + for (i = 1; argv[i] != NULL; i++) { /* handle arguments */ *first = i; if (argv[i][0] != '-') /* not an option? */ return args; /* stop handling options */ @@ -238,7 +257,7 @@ static int collectargs (char **argv, int *first) { return has_error; } } - *first = i; /* no script name */ + *first = 0; /* no script name */ return args; } @@ -256,7 +275,7 @@ static int runargs (lua_State *L, char **argv, int n) { switch (option) { case 'e': case 'l': { int status; - const char *extra = argv[i] + 2; /* both options need an argument */ + char *extra = argv[i] + 2; /* both options need an argument */ if (*extra == '\0') extra = argv[++i]; lua_assert(extra != NULL); status = (option == 'e') @@ -344,9 +363,8 @@ static int pmain (lua_State *L) { char **argv = (char **)lua_touserdata(L, 2); int script; int args = collectargs(argv, &script); + int optlim = (script > 0) ? script : argc; /* first argv not an option */ luaL_checkversion(L); /* check that interpreter has correct version */ - lua_progname = LUA_PROGNAME; - if (argv[0] && argv[0][0]) lua_progname = argv[0]; if (args == has_error) { /* bad arg? */ print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; @@ -361,19 +379,21 @@ static int pmain (lua_State *L) { luaL_requiref(L, "unix", LuaUnix, 1); lua_pop(L, 1); createargtable(L, argv, argc, script); /* create table 'arg' */ - lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ + lua_gc(L, LUA_GCRESTART); /* start GC... */ + lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } - if (!runargs(L, argv, script)) /* execute arguments -e and -l */ + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ return 0; /* something failed */ - if (script < argc && /* execute main script (if there is one) */ - handle_script(L, argv + script) != LUA_OK) - return 0; + if (script > 0) { /* execute main script (if there is one) */ + if (handle_script(L, argv + script) != LUA_OK) + return 0; /* interrupt in case of error */ + } if (args & has_i) /* -i option? */ doREPL(L); /* do read-eval-print loop */ - else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ + else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */ if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); doREPL(L); /* do read-eval-print loop */ @@ -397,6 +417,7 @@ int main (int argc, char **argv) { lua_l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } + lua_gc(L, LUA_GCSTOP); /* stop GC while building state */ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ diff --git a/third_party/lua/luac.main.c b/third_party/lua/luac.main.c index 8b22aa237..456343e2b 100644 --- a/third_party/lua/luac.main.c +++ b/third_party/lua/luac.main.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define luac_c #define LUA_CORE + #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" @@ -45,6 +46,7 @@ #include "third_party/lua/lundump.h" __static_yoink("lua_notice"); + static void PrintFunction(const Proto* f, int full); #define luaU_print PrintFunction @@ -141,7 +143,7 @@ static int doargs(int argc, char* argv[]) return i; } -#define FUNCTION "(function()end)();" +#define FUNCTION "(function()end)();\n" static const char* reader(lua_State* L, void* ud, size_t* size) { @@ -158,7 +160,7 @@ static const char* reader(lua_State* L, void* ud, size_t* size) } } -#define toproto(L,i) getproto(s2v(L->top+(i))) +#define toproto(L,i) getproto(s2v(L->top.p+(i))) static const Proto* combine(lua_State* L, int n) { @@ -175,7 +177,6 @@ static const Proto* combine(lua_State* L, int n) f->p[i]=toproto(L,i-n-1); if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; } - f->sizelineinfo=0; return f; } } @@ -620,11 +621,11 @@ static void PrintCode(const Proto* f) if (c==0) printf("all out"); else printf("%d out",c-1); break; case OP_TAILCALL: - printf("%d %d %d",a,b,c); + printf("%d %d %d%s",a,b,c,ISK); printf(COMMENT "%d in",b-1); break; case OP_RETURN: - printf("%d %d %d",a,b,c); + printf("%d %d %d%s",a,b,c,ISK); printf(COMMENT); if (b==0) printf("all out"); else printf("%d out",b-1); break; @@ -639,7 +640,7 @@ static void PrintCode(const Proto* f) break; case OP_FORPREP: printf("%d %d",a,bx); - printf(COMMENT "to %d",pc+bx+2); + printf(COMMENT "exit to %d",pc+bx+3); break; case OP_TFORPREP: printf("%d %d",a,bx); diff --git a/third_party/lua/luaconf.h b/third_party/lua/luaconf.h index d3f2519a5..30baa4ef4 100644 --- a/third_party/lua/luaconf.h +++ b/third_party/lua/luaconf.h @@ -1,5 +1,6 @@ #ifndef luaconf_h #define luaconf_h + #include "libc/assert.h" #include "libc/dce.h" #include "libc/fmt/conv.h" @@ -7,8 +8,9 @@ #include "libc/stdio/stdio.h" #include "libc/str/unicode.h" -#define LUA_USE_POSIX + #define LUA_USE_LINENOISE +#define LUA_USE_POSIX /* @@ -69,6 +71,12 @@ #endif +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + /* @@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. */ @@ -422,7 +430,6 @@ @@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. @@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. @@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. -@@ LUA_UNSIGNEDBITS is the number of bits in a LUA_UNSIGNED. @@ lua_integer2str converts an integer to a string. */ @@ -443,9 +450,6 @@ #define LUA_UNSIGNED unsigned LUAI_UACINT -#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT) - - /* now the variable definitions */ #if LUA_INT_TYPE == LUA_INT_INT /* { int */ @@ -667,7 +671,7 @@ ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack ** space (and to reserve some numbers for pseudo-indices). -** (It must fit into max(size_t)/32.) +** (It must fit into max(size_t)/32 and max(int)/2.) */ #if LUAI_IS32INT #define LUAI_MAXSTACK 1000000 @@ -686,14 +690,15 @@ /* @@ LUA_IDSIZE gives the maximum size for the description of the source -@@ of a function in debug information. +** of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* -@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. */ #define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) diff --git a/third_party/lua/lualib.h b/third_party/lua/lualib.h index ae0207cdf..237ae8064 100644 --- a/third_party/lua/lualib.h +++ b/third_party/lua/lualib.h @@ -1,5 +1,6 @@ #ifndef lualib_h #define lualib_h + #include "third_party/lua/lua.h" diff --git a/third_party/lua/lundump.c b/third_party/lua/lundump.c index c993542dc..1813822e2 100644 --- a/third_party/lua/lundump.c +++ b/third_party/lua/lundump.c @@ -1,9 +1,30 @@ -/* -** $Id: lundump.c $ -** load precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lundump_c #define LUA_CORE @@ -18,6 +39,7 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" #include "third_party/lua/lzio.h" +__static_yoink("lua_notice"); #if !defined(luai_verifycode) @@ -115,10 +137,10 @@ static TString *loadStringN (LoadState *S, Proto *p) { } else { /* long string */ ts = luaS_createlngstrobj(L, size); /* create string */ - setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */ + setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ luaD_inctop(L); loadVector(S, getstr(ts), size); /* load directly in final place */ - L->top--; /* pop string */ + L->top.p--; /* pop string */ } luaC_objbarrier(L, p, ts); return ts; @@ -243,6 +265,8 @@ static void loadDebug (LoadState *S, Proto *f) { f->locvars[i].endpc = loadInt(S); } n = loadInt(S); + if (n != 0) /* does it have debug information? */ + n = f->sizeupvalues; /* must be this many */ for (i = 0; i < n; i++) f->upvalues[i].name = loadStringN(S, f); } @@ -316,7 +340,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { S.Z = Z; checkHeader(&S); cl = luaF_newLclosure(L, loadByte(&S)); - setclLvalue2s(L, L->top, cl); + setclLvalue2s(L, L->top.p, cl); luaD_inctop(L); cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); diff --git a/third_party/lua/lundump.h b/third_party/lua/lundump.h index 03efd23e5..d7224f5e8 100644 --- a/third_party/lua/lundump.h +++ b/third_party/lua/lundump.h @@ -1,5 +1,6 @@ #ifndef lundump_h #define lundump_h + #include "third_party/lua/llimits.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lzio.h" diff --git a/third_party/lua/lutf8lib.c b/third_party/lua/lutf8lib.c index 00e2b1ff3..fafc521da 100644 --- a/third_party/lua/lutf8lib.c +++ b/third_party/lua/lutf8lib.c @@ -1,9 +1,30 @@ -/* -** $Id: lutf8lib.c $ -** Standard library for UTF-8 manipulation -** See Copyright Notice in lua.h -*/ - +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lutf8lib_c #define LUA_LIB @@ -11,12 +32,16 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +__static_yoink("lua_notice"); #define MAXUNICODE 0x10FFFFu #define MAXUTF 0x7FFFFFFFu + +#define MSGInvalid "invalid UTF-8 code" + /* ** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. */ @@ -27,7 +52,8 @@ typedef unsigned long utfint; #endif -#define iscont(p) ((*(p) & 0xC0) == 0x80) +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) /* from strlib */ @@ -57,7 +83,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) { int count = 0; /* to count number of continuation bytes */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ unsigned int cc = (unsigned char)s[++count]; /* read next byte */ - if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ + if (!iscont(cc)) /* not a continuation byte? */ return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } @@ -132,7 +158,7 @@ static int codepoint (lua_State *L) { utfint code; s = utf8_decode(s, &code, !lax); if (s == NULL) - return luaL_error(L, "invalid UTF-8 code"); + return luaL_error(L, MSGInvalid); lua_pushinteger(L, code); n++; } @@ -182,16 +208,16 @@ static int byteoffset (lua_State *L) { "position out of bounds"); if (n == 0) { /* find beginning of current byte sequence */ - while (posi > 0 && iscont(s + posi)) posi--; + while (posi > 0 && iscontp(s + posi)) posi--; } else { - if (iscont(s + posi)) + if (iscontp(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ do { /* find beginning of previous character */ posi--; - } while (posi > 0 && iscont(s + posi)); + } while (posi > 0 && iscontp(s + posi)); n++; } } @@ -200,7 +226,7 @@ static int byteoffset (lua_State *L) { while (n > 0 && posi < (lua_Integer)len) { do { /* find beginning of next character */ posi++; - } while (iscont(s + posi)); /* (cannot pass final '\0') */ + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ n--; } } @@ -216,20 +242,17 @@ static int byteoffset (lua_State *L) { static int iter_aux (lua_State *L, int strict) { size_t len; const char *s = luaL_checklstring(L, 1, &len); - lua_Integer n = lua_tointeger(L, 2) - 1; - if (n < 0) /* first iteration? */ - n = 0; /* start from here */ - else if (n < (lua_Integer)len) { - n++; /* skip current byte */ - while (iscont(s + n)) n++; /* and its continuations */ + lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); + if (n < len) { + while (iscontp(s + n)) n++; /* go to next character */ } - if (n >= (lua_Integer)len) + if (n >= len) /* (also handles original 'n' being negative) */ return 0; /* no more codepoints */ else { utfint code; const char *next = utf8_decode(s + n, &code, strict); - if (next == NULL) - return luaL_error(L, "invalid UTF-8 code"); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); lua_pushinteger(L, n + 1); lua_pushinteger(L, code); return 2; @@ -248,7 +271,8 @@ static int iter_auxlax (lua_State *L) { static int iter_codes (lua_State *L) { int lax = lua_toboolean(L, 2); - luaL_checkstring(L, 1); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); lua_pushvalue(L, 1); lua_pushinteger(L, 0); diff --git a/third_party/lua/lvm.c b/third_party/lua/lvm.c index 36414ef32..8c33f21aa 100644 --- a/third_party/lua/lvm.c +++ b/third_party/lua/lvm.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lvm_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -419,7 +420,7 @@ static int l_strcmp (const TString *ls, const TString *rs) { ** from float to int.) ** When 'f' is NaN, comparisons must result in false. */ -static int LTintfloat (lua_Integer i, lua_Number f) { +l_sinline int LTintfloat (lua_Integer i, lua_Number f) { if (l_intfitsf(i)) return luai_numlt(cast_num(i), f); /* compare them as floats */ else { /* i < f <=> i < ceil(f) */ @@ -436,7 +437,7 @@ static int LTintfloat (lua_Integer i, lua_Number f) { ** Check whether integer 'i' is less than or equal to float 'f'. ** See comments on previous function. */ -static int LEintfloat (lua_Integer i, lua_Number f) { +l_sinline int LEintfloat (lua_Integer i, lua_Number f) { if (l_intfitsf(i)) return luai_numle(cast_num(i), f); /* compare them as floats */ else { /* i <= f <=> i <= floor(f) */ @@ -453,7 +454,7 @@ static int LEintfloat (lua_Integer i, lua_Number f) { ** Check whether float 'f' is less than integer 'i'. ** See comments on previous function. */ -static int LTfloatint (lua_Number f, lua_Integer i) { +l_sinline int LTfloatint (lua_Number f, lua_Integer i) { if (l_intfitsf(i)) return luai_numlt(f, cast_num(i)); /* compare them as floats */ else { /* f < i <=> floor(f) < i */ @@ -470,7 +471,7 @@ static int LTfloatint (lua_Number f, lua_Integer i) { ** Check whether float 'f' is less than or equal to integer 'i'. ** See comments on previous function. */ -static int LEfloatint (lua_Number f, lua_Integer i) { +l_sinline int LEfloatint (lua_Number f, lua_Integer i) { if (l_intfitsf(i)) return luai_numle(f, cast_num(i)); /* compare them as floats */ else { /* f <= i <=> ceil(f) <= i */ @@ -486,7 +487,7 @@ static int LEfloatint (lua_Number f, lua_Integer i) { /* ** Return 'l < r', for numbers. */ -static int LTnum (const TValue *l, const TValue *r) { +l_sinline int LTnum (const TValue *l, const TValue *r) { lua_assert(ttisnumber(l) && ttisnumber(r)); if (ttisinteger(l)) { lua_Integer li = ivalue(l); @@ -508,7 +509,7 @@ static int LTnum (const TValue *l, const TValue *r) { /* ** Return 'l <= r', for numbers. */ -static int LEnum (const TValue *l, const TValue *r) { +l_sinline int LEnum (const TValue *l, const TValue *r) { lua_assert(ttisnumber(l) && ttisnumber(r)); if (ttisinteger(l)) { lua_Integer li = ivalue(l); @@ -621,8 +622,8 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { if (tm == NULL) /* no TM? */ return 0; /* objects are different */ else { - luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */ - return !l_isfalse(s2v(L->top)); + luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !l_isfalse(s2v(L->top.p)); } } @@ -646,17 +647,17 @@ static void copy2buff (StkId top, int n, char *buff) { /* ** Main operation for concatenation: concat 'total' values in the stack, -** from 'L->top - total' up to 'L->top - 1'. +** from 'L->top.p - total' up to 'L->top.p - 1'. */ void luaV_concat (lua_State *L, int total) { if (total == 1) return; /* "all" values already concatenated */ do { - StkId top = L->top; + StkId top = L->top.p; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || !tostring(L, s2v(top - 1))) - luaT_tryconcatTM(L); + luaT_tryconcatTM(L); /* may invalidate 'top' */ else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ @@ -669,8 +670,10 @@ void luaV_concat (lua_State *L, int total) { /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { size_t l = vslen(s2v(top - n - 1)); - if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) + if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) { + L->top.p = top - total; /* pop strings to avoid wasting stack */ luaG_runerror(L, "string length overflow"); + } tl += l; } if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ @@ -684,8 +687,8 @@ void luaV_concat (lua_State *L, int total) { } setsvalue2s(L, top - n, ts); /* create result */ } - total -= n-1; /* got 'n' strings to create 1 new */ - L->top -= n-1; /* popped 'n' strings and pushed one */ + total -= n - 1; /* got 'n' strings to create one new */ + L->top.p -= n - 1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } @@ -776,11 +779,10 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { /* number of bits in an integer */ #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + /* ** Shift left operation. (Shift right just negates 'y'.) */ -#define luaV_shiftr(x,y) luaV_shiftl(x,-(y)) - lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { if (y < 0) { /* shift right? */ if (y <= -NBITS) return 0; @@ -820,26 +822,26 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, */ void luaV_finishOp (lua_State *L) { CallInfo *ci = L->ci; - StkId base = ci->func + 1; + StkId base = ci->func.p + 1; Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { - setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top); + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p); break; } case OP_UNM: case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETFIELD: case OP_SELF: { - setobjs2s(L, base + GETARG_A(inst), --L->top); + setobjs2s(L, base + GETARG_A(inst), --L->top.p); break; } case OP_LT: case OP_LE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ - int res = !l_isfalse(s2v(L->top - 1)); - L->top--; + int res = !l_isfalse(s2v(L->top.p - 1)); + L->top.p--; #if defined(LUA_COMPAT_LT_LE) if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ ci->callstatus ^= CIST_LEQ; /* clear mark */ @@ -852,18 +854,27 @@ void luaV_finishOp (lua_State *L) { break; } case OP_CONCAT: { - StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */ + StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */ int a = GETARG_A(inst); /* first element to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ setobjs2s(L, top - 2, top); /* put TM result in proper position */ - L->top = top - 1; /* top is one after last element (at top-2) */ + L->top.p = top - 1; /* top is one after last element (at top-2) */ luaV_concat(L, total); /* concat them (may yield again) */ break; } - case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */ + case OP_CLOSE: { /* yielded closing variables */ ci->u.l.savedpc--; /* repeat instruction to close other vars. */ break; } + case OP_RETURN: { /* yielded closing variables */ + StkId ra = base + GETARG_A(inst); + /* adjust top to signal correct number of returns, in case the + return is "up to top" ('isIT') */ + L->top.p = ra + ci->u2.nres; + /* repeat instruction to close other vars. and complete the return */ + ci->u.l.savedpc--; + break; + } default: { /* only these other opcodes can yield */ lua_assert(op == OP_TFORCALL || op == OP_CALL || @@ -901,6 +912,7 @@ void luaV_finishOp (lua_State *L) { ** operation, 'fop' is the float operation. */ #define op_arithI(L,iop,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ int imm = GETARG_sC(i); \ if (ttisinteger(v1)) { \ @@ -929,6 +941,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over floats and others with register operands. */ #define op_arithf(L,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arithf_aux(L, v1, v2, fop); } @@ -938,6 +951,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations with K operands for floats. */ #define op_arithfK(L,fop) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } @@ -947,6 +961,7 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over integers and floats. */ #define op_arith_aux(L,v1,v2,iop,fop) { \ + StkId ra = RA(i); \ if (ttisinteger(v1) && ttisinteger(v2)) { \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ @@ -976,6 +991,7 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with constant operand. */ #define op_bitwiseK(L,op) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); \ lua_Integer i1; \ @@ -989,6 +1005,7 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with register operands. */ #define op_bitwise(L,op) { \ + StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ lua_Integer i1; lua_Integer i2; \ @@ -1003,6 +1020,7 @@ void luaV_finishOp (lua_State *L) { ** integers. */ #define op_order(L,opi,opn,other) { \ + StkId ra = RA(i); \ int cond; \ TValue *rb = vRB(i); \ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ @@ -1022,6 +1040,7 @@ void luaV_finishOp (lua_State *L) { ** always small enough to have an exact representation as a float.) */ #define op_orderI(L,opi,opf,inv,tm) { \ + StkId ra = RA(i); \ int cond; \ int im = GETARG_sB(i); \ if (ttisinteger(s2v(ra))) \ @@ -1064,7 +1083,7 @@ void luaV_finishOp (lua_State *L) { #define updatetrap(ci) (trap = ci->u.l.trap) -#define updatebase(ci) (base = ci->func + 1) +#define updatebase(ci) (base = ci->func.p + 1) #define updatestack(ci) \ @@ -1099,7 +1118,7 @@ void luaV_finishOp (lua_State *L) { ** Whenever code can raise errors, the global 'pc' and the global ** 'top' must be correct to report occasional errors. */ -#define savestate(L,ci) (savepc(L), L->top = ci->top) +#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) /* @@ -1112,14 +1131,14 @@ void luaV_finishOp (lua_State *L) { #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) /* -** Protect code that can only raise errors. (That is, it cannnot change +** Protect code that can only raise errors. (That is, it cannot change ** the stack or hooks.) */ #define halfProtect(exp) (savestate(L,ci), (exp)) /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ - { luaC_condGC(L, (savepc(L), L->top = (c)), \ + { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ updatetrap(ci)); \ luai_threadyield(L); } @@ -1131,7 +1150,6 @@ void luaV_finishOp (lua_State *L) { updatebase(ci); /* correct stack */ \ } \ i = *(pc++); \ - ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ } #define vmdispatch(o) switch(o) @@ -1151,7 +1169,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { startfunc: trap = L->hookmask; returning: /* trap already set */ - cl = clLvalue(s2v(ci->func)); + cl = clLvalue(s2v(ci->func.p)); k = cl->p->k; pc = ci->u.l.savedpc; if (l_unlikely(trap)) { @@ -1163,58 +1181,68 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } ci->u.l.trap = 1; /* assume trap is on, for now */ } - base = ci->func + 1; + base = ci->func.p + 1; /* main loop of interpreter */ for (;;) { Instruction i; /* instruction being executed */ - StkId ra; /* instruction's A register */ vmfetch(); -// low-level line tracing for debugging Lua -// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); - lua_assert(base == ci->func + 1); - lua_assert(base <= L->top && L->top < L->stack_last); + #if 0 + /* low-level line tracing for debugging Lua */ + printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); + #endif + lua_assert(base == ci->func.p + 1); + lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); /* invalidate top for instructions not expecting it */ - lua_assert(isIT(i) || (cast_void(L->top = base), 1)); + lua_assert(isIT(i) || (cast_void(L->top.p = base), 1)); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { + StkId ra = RA(i); setobjs2s(L, ra, RB(i)); vmbreak; } vmcase(OP_LOADI) { + StkId ra = RA(i); lua_Integer b = GETARG_sBx(i); setivalue(s2v(ra), b); vmbreak; } vmcase(OP_LOADF) { + StkId ra = RA(i); int b = GETARG_sBx(i); setfltvalue(s2v(ra), cast_num(b)); vmbreak; } vmcase(OP_LOADK) { + StkId ra = RA(i); TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADKX) { + StkId ra = RA(i); TValue *rb; rb = k + GETARG_Ax(*pc); pc++; setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADFALSE) { + StkId ra = RA(i); setbfvalue(s2v(ra)); vmbreak; } vmcase(OP_LFALSESKIP) { + StkId ra = RA(i); setbfvalue(s2v(ra)); pc++; /* skip next instruction */ vmbreak; } vmcase(OP_LOADTRUE) { + StkId ra = RA(i); setbtvalue(s2v(ra)); vmbreak; } vmcase(OP_LOADNIL) { + StkId ra = RA(i); int b = GETARG_B(i); do { setnilvalue(s2v(ra++)); @@ -1222,19 +1250,22 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETUPVAL) { + StkId ra = RA(i); int b = GETARG_B(i); - setobj2s(L, ra, cl->upvals[b]->v); + setobj2s(L, ra, cl->upvals[b]->v.p); vmbreak; } vmcase(OP_SETUPVAL) { + StkId ra = RA(i); UpVal *uv = cl->upvals[GETARG_B(i)]; - setobj(L, uv->v, s2v(ra)); + setobj(L, uv->v.p, s2v(ra)); luaC_barrier(L, uv, s2v(ra)); vmbreak; } vmcase(OP_GETTABUP) { + StkId ra = RA(i); const TValue *slot; - TValue *upval = cl->upvals[GETARG_B(i)]->v; + TValue *upval = cl->upvals[GETARG_B(i)]->v.p; TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a string */ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { @@ -1245,6 +1276,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETTABLE) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = vRC(i); @@ -1259,6 +1291,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETI) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); int c = GETARG_C(i); @@ -1273,6 +1306,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_GETFIELD) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = KC(i); @@ -1286,7 +1320,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_SETTABUP) { const TValue *slot; - TValue *upval = cl->upvals[GETARG_A(i)]->v; + TValue *upval = cl->upvals[GETARG_A(i)]->v.p; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a string */ @@ -1298,6 +1332,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETTABLE) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); /* key (table is in 'ra') */ TValue *rc = RKC(i); /* value */ @@ -1312,6 +1347,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETI) { + StkId ra = RA(i); const TValue *slot; int c = GETARG_B(i); TValue *rc = RKC(i); @@ -1326,6 +1362,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SETFIELD) { + StkId ra = RA(i); const TValue *slot; TValue *rb = KB(i); TValue *rc = RKC(i); @@ -1338,6 +1375,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_NEWTABLE) { + StkId ra = RA(i); int b = GETARG_B(i); /* log2(hash size) + 1 */ int c = GETARG_C(i); /* array size */ Table *t; @@ -1347,7 +1385,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { if (TESTARG_k(i)) /* non-zero extra argument? */ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ pc++; /* skip extra argument */ - L->top = ra + 1; /* correct top in case of emergency GC */ + L->top.p = ra + 1; /* correct top in case of emergency GC */ t = luaH_new(L); /* memory allocation */ sethvalue2s(L, ra, t); if (b != 0 || c != 0) @@ -1356,6 +1394,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SELF) { + StkId ra = RA(i); const TValue *slot; TValue *rb = vRB(i); TValue *rc = RKC(i); @@ -1385,6 +1424,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MODK) { + savestate(L, ci); /* in case of division by 0 */ op_arithK(L, luaV_mod, luaV_modf); vmbreak; } @@ -1397,6 +1437,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_IDIVK) { + savestate(L, ci); /* in case of division by 0 */ op_arithK(L, luaV_idiv, luai_numidiv); vmbreak; } @@ -1413,6 +1454,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SHRI) { + StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; @@ -1422,6 +1464,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_SHLI) { + StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; @@ -1443,6 +1486,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MOD) { + savestate(L, ci); /* in case of division by 0 */ op_arith(L, luaV_mod, luaV_modf); vmbreak; } @@ -1455,6 +1499,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_IDIV) { /* floor division */ + savestate(L, ci); /* in case of division by 0 */ op_arith(L, luaV_idiv, luai_numidiv); vmbreak; } @@ -1479,6 +1524,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBIN) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ TValue *rb = vRB(i); TMS tm = (TMS)GETARG_C(i); @@ -1488,6 +1534,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBINI) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ int imm = GETARG_sB(i); TMS tm = (TMS)GETARG_C(i); @@ -1497,6 +1544,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_MMBINK) { + StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ TValue *imm = KB(i); TMS tm = (TMS)GETARG_C(i); @@ -1506,6 +1554,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_UNM) { + StkId ra = RA(i); TValue *rb = vRB(i); lua_Number nb; if (ttisinteger(rb)) { @@ -1520,6 +1569,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_BNOT) { + StkId ra = RA(i); TValue *rb = vRB(i); lua_Integer ib; if (tointegerns(rb, &ib)) { @@ -1530,6 +1580,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_NOT) { + StkId ra = RA(i); TValue *rb = vRB(i); if (l_isfalse(rb)) setbtvalue(s2v(ra)); @@ -1538,21 +1589,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_LEN) { + StkId ra = RA(i); Protect(luaV_objlen(L, ra, vRB(i))); vmbreak; } vmcase(OP_CONCAT) { + StkId ra = RA(i); int n = GETARG_B(i); /* number of elements to concatenate */ - L->top = ra + n; /* mark the end of concat operands */ + L->top.p = ra + n; /* mark the end of concat operands */ ProtectNT(luaV_concat(L, n)); - checkGC(L, L->top); /* 'luaV_concat' ensures correct top */ + checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */ vmbreak; } vmcase(OP_CLOSE) { + StkId ra = RA(i); Protect(luaF_close(L, ra, LUA_OK, 1)); vmbreak; } vmcase(OP_TBC) { + StkId ra = RA(i); /* create new to-be-closed upvalue */ halfProtect(luaF_newtbcupval(L, ra)); vmbreak; @@ -1562,6 +1617,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQ) { + StkId ra = RA(i); int cond; TValue *rb = vRB(i); Protect(cond = luaV_equalobj(L, s2v(ra), rb)); @@ -1577,6 +1633,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQK) { + StkId ra = RA(i); TValue *rb = KB(i); /* basic types do not use '__eq'; we can use raw equality */ int cond = luaV_rawequalobj(s2v(ra), rb); @@ -1584,6 +1641,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_EQI) { + StkId ra = RA(i); int cond; int im = GETARG_sB(i); if (ttisinteger(s2v(ra))) @@ -1612,11 +1670,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_TEST) { + StkId ra = RA(i); int cond = !l_isfalse(s2v(ra)); docondjump(); vmbreak; } vmcase(OP_TESTSET) { + StkId ra = RA(i); TValue *rb = vRB(i); if (l_isfalse(rb) == GETARG_k(i)) pc++; @@ -1627,78 +1687,74 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_CALL) { + StkId ra = RA(i); CallInfo *newci; int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) /* fixed number of arguments? */ - L->top = ra + b; /* top signals number of arguments */ + L->top.p = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ savepc(L); /* in case of errors */ if ((newci = luaD_precall(L, ra, nresults)) == NULL) updatetrap(ci); /* C call; nothing else to be done */ else { /* Lua call: run function in this same C frame */ ci = newci; - ci->callstatus = 0; /* call re-uses 'luaV_execute' */ goto startfunc; } vmbreak; } vmcase(OP_TAILCALL) { + StkId ra = RA(i); int b = GETARG_B(i); /* number of arguments + 1 (function) */ + int n; /* number of results when calling a C function */ int nparams1 = GETARG_C(i); /* delta is virtual 'func' - real 'func' (vararg functions) */ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; if (b != 0) - L->top = ra + b; + L->top.p = ra + b; else /* previous instruction set top */ - b = cast_int(L->top - ra); + b = cast_int(L->top.p - ra); savepc(ci); /* several calls here can raise errors */ if (TESTARG_k(i)) { luaF_closeupval(L, base); /* close upvalues from current call */ - lua_assert(L->tbclist < base); /* no pending tbc variables */ - lua_assert(base == ci->func + 1); + lua_assert(L->tbclist.p < base); /* no pending tbc variables */ + lua_assert(base == ci->func.p + 1); } - while (!ttisfunction(s2v(ra))) { /* not a function? */ - luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ - b++; /* there is now one extra argument */ - checkstackGCp(L, 1, ra); - } - if (!ttisLclosure(s2v(ra))) { /* C function? */ - luaD_precall(L, ra, LUA_MULTRET); /* call it */ - updatetrap(ci); - updatestack(ci); /* stack may have been relocated */ - ci->func -= delta; /* restore 'func' (if vararg) */ - luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */ + if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ + goto startfunc; /* execute the callee */ + else { /* C function? */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ + luaD_poscall(L, ci, n); /* finish caller */ updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; /* caller returns after the tail call */ } - ci->func -= delta; /* restore 'func' (if vararg) */ - luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ - goto startfunc; /* execute the callee */ } vmcase(OP_RETURN) { + StkId ra = RA(i); int n = GETARG_B(i) - 1; /* number of results */ int nparams1 = GETARG_C(i); if (n < 0) /* not fixed? */ - n = cast_int(L->top - ra); /* get what is available */ + n = cast_int(L->top.p - ra); /* get what is available */ savepc(ci); if (TESTARG_k(i)) { /* may there be open upvalues? */ - if (L->top < ci->top) - L->top = ci->top; + ci->u2.nres = n; /* save number of returns */ + if (L->top.p < ci->top.p) + L->top.p = ci->top.p; luaF_close(L, base, CLOSEKTOP, 1); updatetrap(ci); updatestack(ci); } if (nparams1) /* vararg function? */ - ci->func -= ci->u.l.nextraargs + nparams1; - L->top = ra + n; /* set call for 'luaD_poscall' */ + ci->func.p -= ci->u.l.nextraargs + nparams1; + L->top.p = ra + n; /* set call for 'luaD_poscall' */ luaD_poscall(L, ci, n); updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; } vmcase(OP_RETURN0) { if (l_unlikely(L->hookmask)) { - L->top = ra; + StkId ra = RA(i); + L->top.p = ra; savepc(ci); luaD_poscall(L, ci, 0); /* no hurry... */ trap = 1; @@ -1706,15 +1762,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) { else { /* do the 'poscall' here */ int nres; L->ci = ci->previous; /* back to caller */ - L->top = base - 1; + L->top.p = base - 1; for (nres = ci->nresults; l_unlikely(nres > 0); nres--) - setnilvalue(s2v(L->top++)); /* all results are nil */ + setnilvalue(s2v(L->top.p++)); /* all results are nil */ } goto ret; } vmcase(OP_RETURN1) { if (l_unlikely(L->hookmask)) { - L->top = ra + 1; + StkId ra = RA(i); + L->top.p = ra + 1; savepc(ci); luaD_poscall(L, ci, 1); /* no hurry... */ trap = 1; @@ -1723,12 +1780,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) { int nres = ci->nresults; L->ci = ci->previous; /* back to caller */ if (nres == 0) - L->top = base - 1; /* asked for no results */ + L->top.p = base - 1; /* asked for no results */ else { + StkId ra = RA(i); setobjs2s(L, base - 1, ra); /* at least this result */ - L->top = base; + L->top.p = base; for (; l_unlikely(nres > 1); nres--) - setnilvalue(s2v(L->top++)); /* complete missing results */ + setnilvalue(s2v(L->top.p++)); /* complete missing results */ } } ret: /* return from a Lua function */ @@ -1740,6 +1798,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } } vmcase(OP_FORLOOP) { + StkId ra = RA(i); if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); if (count > 0) { /* still more iterations? */ @@ -1758,12 +1817,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_FORPREP) { + StkId ra = RA(i); savestate(L, ci); /* in case of errors */ if (forprep(L, ra)) pc += GETARG_Bx(i) + 1; /* skip the loop */ vmbreak; } vmcase(OP_TFORPREP) { + StkId ra = RA(i); /* create to-be-closed upvalue (if needed) */ halfProtect(luaF_newtbcupval(L, ra + 3)); pc += GETARG_Bx(i); @@ -1772,7 +1833,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { goto l_tforcall; } vmcase(OP_TFORCALL) { - l_tforcall: + l_tforcall: { + StkId ra = RA(i); /* 'ra' has the iterator function, 'ra + 1' has the state, 'ra + 2' has the control variable, and 'ra + 3' has the to-be-closed variable. The call will use the stack after @@ -1780,29 +1842,31 @@ void luaV_execute (lua_State *L, CallInfo *ci) { */ /* push function, state, and control variable */ memcpy(ra + 4, ra, 3 * sizeof(*ra)); - L->top = ra + 4 + 3; + L->top.p = ra + 4 + 3; ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ updatestack(ci); /* stack may have changed */ i = *(pc++); /* go to next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); goto l_tforloop; - } + }} vmcase(OP_TFORLOOP) { - l_tforloop: + l_tforloop: { + StkId ra = RA(i); if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ setobjs2s(L, ra + 2, ra + 4); /* save control variable */ pc -= GETARG_Bx(i); /* jump back */ } vmbreak; - } + }} vmcase(OP_SETLIST) { + StkId ra = RA(i); int n = GETARG_B(i); unsigned int last = GETARG_C(i); Table *h = hvalue(s2v(ra)); if (n == 0) - n = cast_int(L->top - ra) - 1; /* get up to the top */ + n = cast_int(L->top.p - ra) - 1; /* get up to the top */ else - L->top = ci->top; /* correct top in case of emergency GC */ + L->top.p = ci->top.p; /* correct top in case of emergency GC */ last += n; if (TESTARG_k(i)) { last += GETARG_Ax(*pc) * (MAXARG_C + 1); @@ -1819,12 +1883,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_CLOSURE) { + StkId ra = RA(i); Proto *p = cl->p->p[GETARG_Bx(i)]; halfProtect(pushclosure(L, p, cl->upvals, base, ra)); checkGC(L, ra + 1); vmbreak; } vmcase(OP_VARARG) { + StkId ra = RA(i); int n = GETARG_C(i) - 1; /* required results */ Protect(luaT_getvarargs(L, ci, ra, n)); vmbreak; diff --git a/third_party/lua/lvm.h b/third_party/lua/lvm.h index ff2d47ed7..adc13c360 100644 --- a/third_party/lua/lvm.h +++ b/third_party/lua/lvm.h @@ -1,5 +1,6 @@ #ifndef lvm_h #define lvm_h + #include "third_party/lua/ldo.h" #include "third_party/lua/lobject.h" #include "third_party/lua/ltm.h" @@ -102,6 +103,11 @@ typedef enum { luaC_barrierback(L, gcvalue(t), v); } +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); diff --git a/third_party/lua/lzio.c b/third_party/lua/lzio.c index 3c674495f..ff92fae6f 100644 --- a/third_party/lua/lzio.c +++ b/third_party/lua/lzio.c @@ -3,7 +3,7 @@ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ -│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ @@ -27,6 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lzio_c #define LUA_CORE + #include "libc/str/str.h" #include "third_party/lua/llimits.h" #include "third_party/lua/lmem.h" diff --git a/third_party/lua/lzio.h b/third_party/lua/lzio.h index cff150c3e..367bcea17 100644 --- a/third_party/lua/lzio.h +++ b/third_party/lua/lzio.h @@ -1,9 +1,3 @@ -/* -** $Id: lzio.h $ -** Buffered streams -** See Copyright Notice in lua.h -*/ - #ifndef lzio_h #define lzio_h diff --git a/third_party/lua/test/all.lua b/third_party/lua/test/all.lua index 25810aff4..24727fca2 100644 --- a/third_party/lua/test/all.lua +++ b/third_party/lua/test/all.lua @@ -163,6 +163,7 @@ f() dofile('db.lua') assert(dofile('calls.lua') == deep and deep) +_G.deep = nil olddofile('strings.lua') olddofile('literals.lua') dofile('tpack.lua') diff --git a/third_party/lua/test/api.lua b/third_party/lua/test/api.lua index b9b4312da..6a3305bb9 100644 --- a/third_party/lua/test/api.lua +++ b/third_party/lua/test/api.lua @@ -14,7 +14,7 @@ local pack = table.pack -- standard error message for memory errors local MEMERRMSG = "not enough memory" -function tcheck (t1, t2) +local function tcheck (t1, t2) assert(t1.n == (t2.n or #t2) + 1) for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end end @@ -28,7 +28,7 @@ end print('testing C API') -a = T.testC("pushvalue R; return 1") +local a = T.testC("pushvalue R; return 1") assert(a == debug.getregistry()) @@ -43,10 +43,10 @@ a = T.d2s(12458954321123.0) assert(a == string.pack("d", 12458954321123.0)) assert(T.s2d(a) == 12458954321123.0) -a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2") +local a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2") assert(a == 2 and b == 3 and not c) -f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2") +local f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2") a,b,c = f() assert(a == 2 and b == 3 and not c) @@ -61,7 +61,7 @@ assert(a==false and b==true and c==false) a,b,c = T.testC("gettop; return 2", 10, 20, 30, 40) assert(a == 40 and b == 5 and not c) -t = pack(T.testC("settop 5; return *", 2, 3)) +local t = pack(T.testC("settop 5; return *", 2, 3)) tcheck(t, {n=4,2,3}) t = pack(T.testC("settop 0; settop 15; return 10", 3, 1, 23)) @@ -166,16 +166,17 @@ end -- testing globals -_G.a = 14; _G.b = "a31" +_G.AA = 14; _G.BB = "a31" local a = {T.testC[[ - getglobal a; - getglobal b; - getglobal b; - setglobal a; + getglobal AA; + getglobal BB; + getglobal BB; + setglobal AA; return * ]]} -assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.a == "a31") +assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.AA == "a31") +_G.AA, _G.BB = nil -- testing arith assert(T.testC("pushnum 10; pushnum 20; arith /; return 1") == 0.5) @@ -199,13 +200,14 @@ a,b,c = T.testC([[pushnum 1; pushstring 10; arith _; pushstring 5; return 3]]) assert(a == 1 and b == -10 and c == "5") -mt = {__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end, +local mt = { + __add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end, __mod = function (a,b) return setmetatable({a[1] % b[1]}, mt) end, __unm = function (a) return setmetatable({a[1]* 2}, mt) end} a,b,c = setmetatable({4}, mt), setmetatable({8}, mt), setmetatable({-3}, mt) -x,y,z = T.testC("arith +; return 2", 10, a, b) +local x,y,z = T.testC("arith +; return 2", 10, a, b) assert(x == 10 and y[1] == 12 and z == nil) assert(T.testC("arith %; return 1", a, c)[1] == 4%-3) assert(T.testC("arith _; arith +; arith %; return 1", b, a, c)[1] == @@ -312,9 +314,9 @@ assert(T.testC("concat 1; return 1", "xuxu") == "xuxu") -- testing lua_is -function B(x) return x and 1 or 0 end +local function B (x) return x and 1 or 0 end -function count (x, n) +local function count (x, n) n = n or 2 local prog = [[ isnumber %d; @@ -345,7 +347,7 @@ assert(count(nil, 15) == 100) -- testing lua_to... -function to (s, x, n) +local function to (s, x, n) n = n or 2 return T.testC(string.format("%s %d; return 1", s, n), x) end @@ -486,11 +488,12 @@ a = T.testC([[ pushvalue 3; insert -2; pcall 1 1 0; pcall 0 0 0; return 1 -]], "x=150", function (a) assert(a==nil); return 3 end) +]], "XX=150", function (a) assert(a==nil); return 3 end) -assert(type(a) == 'string' and x == 150) +assert(type(a) == 'string' and XX == 150) +_G.XX = nil -function check3(p, ...) +local function check3(p, ...) local arg = {...} assert(#arg == 3) assert(string.find(arg[3], p)) @@ -500,7 +503,7 @@ check3("%.", T.testC("loadfile 2; return *", ".")) check3("xxxx", T.testC("loadfile 2; return *", "xxxx")) -- test errors in non protected threads -function checkerrnopro (code, msg) +local function checkerrnopro (code, msg) local th = coroutine.create(function () end) -- create new thread local stt, err = pcall(T.testC, th, code) -- run code there assert(not stt and string.find(err, msg)) @@ -510,8 +513,9 @@ if not _soft then collectgarbage("stop") -- avoid __gc with full stack checkerrnopro("pushnum 3; call 0 0", "attempt to call") print"testing stack overflow in unprotected thread" - function f () f() end - checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow") + function F () F() end + checkerrnopro("getglobal 'F'; call 0 0;", "stack overflow") + F = nil collectgarbage("restart") end print"+" @@ -588,7 +592,7 @@ assert(a[a] == "x") b = setmetatable({p = a}, {}) getmetatable(b).__index = function (t, i) return t.p[i] end -k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x") +local k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x") assert(x == 15 and k == 35) k = T.testC("getfield 2 y, return 1", b) assert(k == 12) @@ -748,8 +752,8 @@ local i = T.ref{} T.unref(i) assert(T.ref{} == i) -Arr = {} -Lim = 100 +local Arr = {} +local Lim = 100 for i=1,Lim do -- lock many objects Arr[i] = T.ref({}) end @@ -761,7 +765,7 @@ for i=1,Lim do -- unlock all them T.unref(Arr[i]) end -function printlocks () +local function printlocks () local f = T.makeCfunc("gettable R; return 1") local n = f("n") print("n", n) @@ -793,8 +797,8 @@ assert(type(T.getref(a)) == 'table') -- colect in cl the `val' of all collected userdata -tt = {} -cl = {n=0} +local tt = {} +local cl = {n=0} A = nil; B = nil local F F = function (x) @@ -804,20 +808,20 @@ F = function (x) d = nil assert(debug.getmetatable(x).__gc == F) assert(load("table.insert({}, {})"))() -- create more garbage - collectgarbage() -- force a GC during GC - assert(debug.getmetatable(x).__gc == F) -- previous GC did not mess this? + assert(not collectgarbage()) -- GC during GC (no op) local dummy = {} -- create more garbage during GC if A ~= nil then assert(type(A) == "userdata") assert(T.udataval(A) == B) debug.getmetatable(A) -- just access it end - A = x -- ressucita userdata + A = x -- ressurect userdata B = udval return 1,2,3 end tt.__gc = F + -- test whether udate collection frees memory in the right time do collectgarbage(); @@ -854,9 +858,9 @@ end collectgarbage("stop") -- create 3 userdatas with tag `tt' -a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a) -b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b) -c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c) +a = T.newuserdata(0); debug.setmetatable(a, tt); local na = T.udataval(a) +b = T.newuserdata(0); debug.setmetatable(b, tt); local nb = T.udataval(b) +c = T.newuserdata(0); debug.setmetatable(c, tt); local nc = T.udataval(c) -- create userdata without meta table x = T.newuserdata(4) @@ -867,9 +871,9 @@ checkerr("FILE%* expected, got userdata", io.input, x) assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil) -d=T.ref(a); -e=T.ref(b); -f=T.ref(c); +local d = T.ref(a); +local e = T.ref(b); +local f = T.ref(c); t = {T.getref(d), T.getref(e), T.getref(f)} assert(t[1] == a and t[2] == b and t[3] == c) @@ -889,7 +893,7 @@ tt=nil -- frees tt for GC A = nil b = nil T.unref(d); -n5 = T.newuserdata(0) +local n5 = T.newuserdata(0) debug.setmetatable(n5, {__gc=F}) n5 = T.udataval(n5) collectgarbage() @@ -960,11 +964,11 @@ print'+' -- testing changing hooks during hooks -_G.t = {} +_G.TT = {} T.sethook([[ # set a line hook after 3 count hooks sethook 4 0 ' - getglobal t; + getglobal TT; pushvalue -3; append -2 pushvalue -2; append -2 ']], "c", 3) @@ -974,12 +978,13 @@ a = 1 -- count hook (set line hook) a = 1 -- line hook a = 1 -- line hook debug.sethook() -t = _G.t +local t = _G.TT assert(t[1] == "line") -line = t[2] +local line = t[2] assert(t[3] == "line" and t[4] == line + 1) assert(t[5] == "line" and t[6] == line + 2) assert(t[7] == nil) +_G.TT = nil ------------------------------------------------------------------------- @@ -1004,6 +1009,7 @@ do -- testing errors during GC collectgarbage("restart") warn("@on") end +_G.A = nil ------------------------------------------------------------------------- -- test for userdata vals do @@ -1033,8 +1039,8 @@ assert(a == 'alo' and b == '3') T.doremote(L1, "_ERRORMESSAGE = nil") -- error: `sin' is not defined -a, _, b = T.doremote(L1, "return sin(1)") -assert(a == nil and b == 2) -- 2 == run-time error +a, b, c = T.doremote(L1, "return sin(1)") +assert(a == nil and c == 2) -- 2 == run-time error -- error: syntax error a, b, c = T.doremote(L1, "return a+") @@ -1205,7 +1211,7 @@ T.alloccount() -- remove limit -- o that we get memory errors in all allocations of a given -- task, until there is enough memory to complete the task without -- errors. -function testbytes (s, f) +local function testbytes (s, f) collectgarbage() local M = T.totalmem() local oldM = M @@ -1230,7 +1236,7 @@ end -- task, until there is enough allocations to complete the task without -- errors. -function testalloc (s, f) +local function testalloc (s, f) collectgarbage() local M = 0 local a,b = nil @@ -1297,12 +1303,12 @@ end) -- testing threads -- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1) -mt = T.testC("rawgeti R 1; return 1") +local mt = T.testC("rawgeti R 1; return 1") assert(type(mt) == "thread" and coroutine.running() == mt) -function expand (n,s) +local function expand (n,s) if n==0 then return "" end local e = string.rep("=", n) return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n", @@ -1312,9 +1318,10 @@ end G=0; collectgarbage(); a =collectgarbage("count") load(expand(20,"G=G+1"))() assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1) +G = nil testamem("running code on new thread", function () - return T.doonnewstack("x=1") == 0 -- try to create thread + return T.doonnewstack("local x=1") == 0 -- try to create thread end) @@ -1328,13 +1335,13 @@ end) local testprog = [[ local function foo () return end local t = {"x"} -a = "aaa" -for i = 1, #t do a=a..t[i] end +AA = "aaa" +for i = 1, #t do AA = AA .. t[i] end return true ]] -- testing memory x dofile -_G.a = nil +_G.AA = nil local t =os.tmpname() local f = assert(io.open(t, "w")) f:write(testprog) @@ -1344,7 +1351,7 @@ testamem("dofile", function () return a and a() end) assert(os.remove(t)) -assert(_G.a == "aaax") +assert(_G.AA == "aaax") -- other generic tests @@ -1361,6 +1368,8 @@ testamem("dump/undump", function () return a and a() end) +_G.AA = nil + local t = os.tmpname() testamem("file creation", function () local f = assert(io.open(t, 'w')) @@ -1382,7 +1391,7 @@ testamem("constructors", function () end) local a = 1 -close = nil +local close = nil testamem("closure creation", function () function close (b) return function (x) return b + x end diff --git a/third_party/lua/test/attrib.lua b/third_party/lua/test/attrib.lua index fd598288b..93b2ae314 100644 --- a/third_party/lua/test/attrib.lua +++ b/third_party/lua/test/attrib.lua @@ -85,7 +85,7 @@ local DIR = "libs" .. dirsep -- prepend DIR to a name and correct directory separators local function D (x) - x = string.gsub(x, "/", dirsep) + local x = string.gsub(x, "/", dirsep) return DIR .. x end @@ -106,7 +106,7 @@ local function createfiles (files, preextras, posextras) end end -function removefiles (files) +local function removefiles (files) for n in pairs(files) do os.remove(D(n)) end @@ -154,10 +154,9 @@ local try = function (p, n, r, ext) assert(ext == x) end -a = require"names" +local a = require"names" assert(a[1] == "names" and a[2] == D"names.lua") -_G.a = nil local st, msg = pcall(require, "err") assert(not st and string.find(msg, "arithmetic") and B == 15) st, msg = pcall(require, "synerr") @@ -191,6 +190,7 @@ try("X", "XXxX", AA, "libs/XXxX") removefiles(files) +NAME, REQUIRED, AA, B = nil -- testing require of sub-packages @@ -223,7 +223,7 @@ assert(require"P1" == m and m.AA == 10) removefiles(files) - +AA = nil package.path = "" assert(not pcall(require, "file_does_not_exist")) @@ -305,6 +305,7 @@ else assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") assert(string.find(ext, "libs/lib1", 1, true)) assert(fs.id(45) == 45) + _ENV.x, _ENV.y = nil end _ENV = _G @@ -338,10 +339,10 @@ print("testing assignments, logical operators, and constructors") local res, res2 = 27 -a, b = 1, 2+3 +local a, b = 1, 2+3 assert(a==1 and b==5) a={} -function f() return 10, 11, 12 end +local function f() return 10, 11, 12 end a.x, b, a[1] = 1, 2, f() assert(a.x==1 and b==2 and a[1]==10) a[f()], b, a[f()+3] = f(), a, 'x' @@ -353,15 +354,15 @@ do local a,b,c a,b = 0, f(1) assert(a == 0 and b == 1) - A,b = 0, f(1) - assert(A == 0 and b == 1) + a,b = 0, f(1) + assert(a == 0 and b == 1) a,b,c = 0,5,f(4) assert(a==0 and b==5 and c==1) a,b,c = 0,5,f(0) assert(a==0 and b==5 and c==nil) end -a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6 +local a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6 assert(not a and b and c and d==6) d = 20 @@ -419,6 +420,7 @@ assert(not pcall(function () local a = {[nil] = 10} end)) assert(a[nil] == undef) a = nil +local a, b, c a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'} a, a.x, a.y = a, a[-3] assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y) @@ -434,6 +436,16 @@ a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 == 10) +do + -- _ENV constant + local function foo () + local _ENV = 11 + X = "hi" + end + local st, msg = pcall(foo) + assert(not st and string.find(msg, "number")) +end + -- test of large float/integer indices @@ -445,7 +457,7 @@ while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do maxint = maxint // 2 end -maxintF = maxint + 0.0 -- float version +local maxintF = maxint + 0.0 -- float version assert(maxintF == maxint and math.type(maxintF) == "float" and maxintF >= 2.0^14) diff --git a/third_party/lua/test/big.lua b/third_party/lua/test/big.lua index fcd2f5e90..aa7dafb5b 100644 --- a/third_party/lua/test/big.lua +++ b/third_party/lua/test/big.lua @@ -32,7 +32,7 @@ setmetatable(env, { }) X = nil -co = coroutine.wrap(f) +local co = coroutine.wrap(f) assert(co() == 's') assert(co() == 'g') assert(co() == 'g') diff --git a/third_party/lua/test/bitwise.lua b/third_party/lua/test/bitwise.lua index 426ecce18..47de9dabb 100644 --- a/third_party/lua/test/bitwise.lua +++ b/third_party/lua/test/bitwise.lua @@ -38,6 +38,18 @@ d = d << 32 assert(a | b ~ c & d == 0xF4000000 << 32) assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1) + +do -- constant folding + local code = string.format("return -1 >> %d", math.maxinteger) + assert(load(code)() == 0) + local code = string.format("return -1 >> %d", math.mininteger) + assert(load(code)() == 0) + local code = string.format("return -1 << %d", math.maxinteger) + assert(load(code)() == 0) + local code = string.format("return -1 << %d", math.mininteger) + assert(load(code)() == 0) +end + assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000) assert(-1 >> (numbits - 1) == 1) assert(-1 >> numbits == 0 and @@ -45,6 +57,11 @@ assert(-1 >> numbits == 0 and -1 << numbits == 0 and -1 << -numbits == 0) +assert(1 >> math.mininteger == 0) +assert(1 >> math.maxinteger == 0) +assert(1 << math.mininteger == 0) +assert(1 << math.maxinteger == 0) + assert((2^30 - 1) << 2^30 == 0) assert((2^30 - 1) >> 2^30 == 0) diff --git a/third_party/lua/test/calls.lua b/third_party/lua/test/calls.lua index 5a16d5210..0d9765575 100644 --- a/third_party/lua/test/calls.lua +++ b/third_party/lua/test/calls.lua @@ -16,7 +16,7 @@ assert(type(nil) == 'nil' and type(type) == 'function') assert(type(assert) == type(print)) -function f (x) return a:x (x) end +local function f (x) return a:x (x) end assert(type(f) == 'function') assert(not pcall(type)) @@ -33,10 +33,11 @@ do assert(fact(5) == 120) end assert(fact == false) +fact = nil -- testing declarations -a = {i = 10} -self = 20 +local a = {i = 10} +local self = 20 function a:x (x) return x+self.i end function a.y (x) return x+self end @@ -72,6 +73,8 @@ f(1,2, -- this one too 3,4) assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a') +t = nil -- delete 't' + function fat(x) if x <= 1 then return 1 else return x*load("return fat(" .. x-1 .. ")", "")() @@ -80,26 +83,29 @@ end assert(load "load 'assert(fat(6)==720)' () ")() a = load('return fat(5), 3') -a,b = a() +local a,b = a() assert(a == 120 and b == 3) +fat = nil print('+') -function err_on_n (n) +local function err_on_n (n) if n==0 then error(); exit(1); else err_on_n (n-1); exit(1); end end do - function dummy (n) + local function dummy (n) if n > 0 then assert(not pcall(err_on_n, n)) dummy(n-1) end end + + dummy(10) end -dummy(10) +_G.deep = nil -- "declaration" (used by 'all.lua') function deep (n) if n>0 then deep(n-1) end @@ -151,6 +157,16 @@ do -- tail calls x varargs end +do -- C-stack overflow while handling C-stack overflow + local function loop () + assert(pcall(loop)) + end + + local err, msg = xpcall(loop, loop) + assert(not err and string.find(msg, "error")) +end + + do -- tail calls x chain of __call local n = 10000 -- depth @@ -199,7 +215,7 @@ assert(a == 23 and (function (x) return x*2 end)(20) == 40) -- testing closures -- fixed-point operator -Z = function (le) +local Z = function (le) local function a (f) return le(function (x) return f(f)(x) end) end @@ -209,14 +225,14 @@ Z = function (le) -- non-recursive factorial -F = function (f) +local F = function (f) return function (n) if n == 0 then return 1 else return n*f(n-1) end end end -fat = Z(F) +local fat = Z(F) assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4)) @@ -227,22 +243,21 @@ local function g (z) return f(z,z+1,z+2,z+3) end -f = g(10) +local f = g(10) assert(f(9, 16) == 10+11+12+13+10+9+16+10) -Z, F, f = nil print('+') -- testing multiple returns -function unlpack (t, i) +local function unlpack (t, i) i = i or 1 if (i <= #t) then return t[i], unlpack(t, i+1) end end -function equaltab (t1, t2) +local function equaltab (t1, t2) assert(#t1 == #t2) for i = 1, #t1 do assert(t1[i] == t2[i]) @@ -251,8 +266,8 @@ end local pack = function (...) return (table.pack(...)) end -function f() return 1,2,30,4 end -function ret2 (a,b) return a,b end +local function f() return 1,2,30,4 end +local function ret2 (a,b) return a,b end local a,b,c,d = unlpack{1,2,3} assert(a==1 and b==2 and c==3 and d==nil) @@ -281,7 +296,7 @@ table.sort({10,9,8,4,19,23,0,0}, function (a,b) return ay) and x or y == 2); x,y=2,1; assert((x>y) and x or y == 2); @@ -77,13 +78,13 @@ do -- testing operators with diffent kinds of constants local gab = f(o1, o2) _ENV.XX = o1 - code = string.format("return XX %s %s", op, o2) - res = assert(load(code))() + local code = string.format("return XX %s %s", op, o2) + local res = assert(load(code))() assert(res == gab) _ENV.XX = o2 - local code = string.format("return (%s) %s XX", o1, op) - local res = assert(load(code))() + code = string.format("return (%s) %s XX", o1, op) + res = assert(load(code))() assert(res == gab) code = string.format("return (%s) %s %s", o1, op, o2) @@ -92,6 +93,7 @@ do -- testing operators with diffent kinds of constants end end end + _ENV.XX = nil end @@ -100,10 +102,35 @@ repeat until 1; repeat until true; while false do end; while nil do end; do -- test old bug (first name could not be an `upvalue') - local a; function f(x) x={a=1}; x={x=1}; x={G=1} end + local a; local function f(x) x={a=1}; x={x=1}; x={G=1} end end -function f (i) + +do -- bug since 5.4.0 + -- create code with a table using more than 256 constants + local code = {"local x = {"} + for i = 1, 257 do + code[#code + 1] = i .. ".1," + end + code[#code + 1] = "};" + code = table.concat(code) + + -- add "ret" to the end of that code and checks that + -- it produces the expected value "val" + local function check (ret, val) + local code = code .. ret + code = load(code) + assert(code() == val) + end + + check("return (1 ~ (2 or 3))", 1 ~ 2) + check("return (1 | (2 or 3))", 1 | 2) + check("return (1 + (2 or 3))", 1 + 2) + check("return (1 << (2 or 3))", 1 << 2) +end + + +local function f (i) if type(i) ~= 'number' then return i,'jojo'; end; if i > 0 then return i, f(i-1); end; end @@ -129,10 +156,10 @@ end assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil) for i=1,1000 do break; end; -n=100; -i=3; -t = {}; -a=nil +local n=100; +local i=3; +local t = {}; +local a=nil while not a do a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end; end @@ -175,14 +202,14 @@ a={y=1} x = {a.y} assert(x[1] == 1) -function f(i) +local function f (i) while 1 do if i>0 then i=i-1; else return; end; end; end; -function g(i) +local function g(i) while 1 do if i>0 then i=i-1 else return end @@ -247,7 +274,7 @@ function g (a,b,c,d,e) if not (a>=b or c or d and e or nil) then return 0; else return 1; end; end -function h (a,b,c,d,e) +local function h (a,b,c,d,e) while (a>=b or c or (d and e) or nil) do return 1; end; return 0; end; @@ -275,7 +302,7 @@ do assert(a==2) end -function F(a) +local function F (a) assert(debug.getinfo(1, "n").name == 'F') return a,2,3 end @@ -368,6 +395,8 @@ for n = 1, level do if i % 60000 == 0 then print('+') end end end +IX = nil +_G.GLOB1 = nil ------------------------------------------------------------------ -- testing some syntax errors (chosen through 'gcov') diff --git a/third_party/lua/test/coroutine.lua b/third_party/lua/test/coroutine.lua index fd8b04041..4f6ca2469 100644 --- a/third_party/lua/test/coroutine.lua +++ b/third_party/lua/test/coroutine.lua @@ -30,7 +30,8 @@ local function eqtab (t1, t2) end _G.x = nil -- declare x -function foo (a, ...) +_G.f = nil -- declare f +local function foo (a, ...) local x, y = coroutine.running() assert(x == f and y == false) -- next call should not corrupt coroutine (but must fail, @@ -67,10 +68,11 @@ assert(coroutine.status(f) == "dead") s, a = coroutine.resume(f, "xuxu") assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") +_G.f = nil -- yields in tail calls local function foo (i) return coroutine.yield(i) end -f = coroutine.wrap(function () +local f = coroutine.wrap(function () for i=1,10 do assert(foo(i) == _G.x) end @@ -79,8 +81,10 @@ end) for i=1,10 do _G.x = i; assert(f(i) == i) end _G.x = 'xuxu'; assert(f('xuxu') == 'a') +_G.x = nil + -- recursive -function pf (n, i) +local function pf (n, i) coroutine.yield(n) pf(n*i, i+1) end @@ -93,14 +97,14 @@ for i=1,10 do end -- sieve -function gen (n) +local function gen (n) return coroutine.wrap(function () for i=2,n do coroutine.yield(i) end end) end -function filter (p, g) +local function filter (p, g) return coroutine.wrap(function () while 1 do local n = g() @@ -136,6 +140,10 @@ do assert(coroutine.status(co) == "dead") local st, msg = coroutine.close(co) assert(st and msg == nil) + -- also ok to close it again + st, msg = coroutine.close(co) + assert(st and msg == nil) + -- cannot close the running coroutine local st, msg = pcall(coroutine.close, coroutine.running()) @@ -149,6 +157,22 @@ do assert(not st and string.find(msg, "normal")) end))() + -- cannot close a coroutine while closing it + do + local co + co = coroutine.create( + function() + local x = func2close(function() + coroutine.close(co) -- try to close it again + end) + coroutine.yield(20) + end) + local st, msg = coroutine.resume(co) + assert(st and msg == 20) + st, msg = coroutine.close(co) + assert(not st and string.find(msg, "running coroutine")) + end + -- to-be-closed variables in coroutines local X @@ -158,6 +182,9 @@ do assert(not st and msg == 100) st, msg = coroutine.close(co) assert(not st and msg == 100) + -- after closing, no more errors + st, msg = coroutine.close(co) + assert(st and msg == nil) co = coroutine.create(function () local x = func2close(function (self, err) @@ -189,23 +216,26 @@ do local st, msg = coroutine.close(co) assert(st == false and coroutine.status(co) == "dead" and msg == 200) assert(x == 200) + -- after closing, no more errors + st, msg = coroutine.close(co) + assert(st and msg == nil) end do -- versus pcall in coroutines local X = false local Y = false - function foo () + local function foo () local x = func2close(function (self, err) Y = debug.getinfo(2) X = err end) error(43) end - co = coroutine.create(function () return pcall(foo) end) + local co = coroutine.create(function () return pcall(foo) end) local st1, st2, err = coroutine.resume(co) assert(st1 and not st2 and err == 43) - assert(X == 43 and Y.name == "pcall") + assert(X == 43 and Y.what == "C") -- recovering from errors in __close metamethods local track = {} @@ -249,7 +279,7 @@ end -- yielding across C boundaries -co = coroutine.wrap(function() +local co = coroutine.wrap(function() assert(not pcall(table.sort,{1,2,3}, coroutine.yield)) assert(coroutine.isyieldable()) coroutine.yield(20) @@ -277,15 +307,15 @@ local r1, r2, v = f1(nil) assert(r1 and not r2 and v[1] == (10 + 1)*10/2) -function f (a, b) a = coroutine.yield(a); error{a + b} end -function g(x) return x[1]*2 end +local function f (a, b) a = coroutine.yield(a); error{a + b} end +local function g(x) return x[1]*2 end co = coroutine.wrap(function () coroutine.yield(xpcall(f, g, 10, 20)) end) assert(co() == 10) -r, msg = co(100) +local r, msg = co(100) assert(not r and msg == 240) @@ -347,9 +377,10 @@ assert(not a and b == foo and coroutine.status(x) == "dead") a,b = coroutine.resume(x) assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") +goo = nil -- co-routines x for loop -function all (a, n, k) +local function all (a, n, k) if k == 0 then coroutine.yield(a) else for i=1,n do @@ -389,7 +420,7 @@ assert(f() == 43 and f() == 53) -- old bug: attempt to resume itself -function co_func (current_co) +local function co_func (current_co) assert(coroutine.running() == current_co) assert(coroutine.resume(current_co) == false) coroutine.yield(10, 20) @@ -419,7 +450,7 @@ do local X = false A = coroutine.wrap(function() - local _ = setmetatable({}, {__close = function () X = true end}) + local _ = func2close(function () X = true end) return pcall(A, 1) end) st, res = A() @@ -427,6 +458,22 @@ do end +-- bug in 5.4.1 +do + -- coroutine ran close metamethods with invalid status during a + -- reset. + local co + co = coroutine.wrap(function() + local x = func2close(function() return pcall(co) end) + error(111) + end) + local st, errobj = pcall(co) + assert(not st and errobj == 111) + st, errobj = pcall(co) + assert(not st and string.find(errobj, "dead coroutine")) +end + + -- attempt to resume 'normal' coroutine local co1, co2 co1 = coroutine.create(function () return co2() end) @@ -449,15 +496,16 @@ a = nil -- access to locals of erroneous coroutines local x = coroutine.create (function () local a = 10 - _G.f = function () a=a+1; return a end + _G.F = function () a=a+1; return a end error('x') end) assert(not coroutine.resume(x)) -- overwrite previous position of local `a' assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) -assert(_G.f() == 11) -assert(_G.f() == 12) +assert(_G.F() == 11) +assert(_G.F() == 12) +_G.F = nil if not T then @@ -468,7 +516,7 @@ else local turn - function fact (t, x) + local function fact (t, x) assert(turn == t) if x == 0 then return 1 else return x*fact(t, x-1) @@ -537,6 +585,7 @@ else _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil) _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20) assert(co() == 10) + _G.X = nil -- testing yields in count hook co = coroutine.wrap(function () @@ -614,6 +663,8 @@ else assert(X == 'a a a' and Y == 'OK') + X, Y = nil + -- resuming running coroutine C = coroutine.create(function () @@ -659,7 +710,7 @@ else X = function (x) coroutine.yield(x, 'BB'); return 'CC' end; return 'ok']])) - t = table.pack(T.testC(state, [[ + local t = table.pack(T.testC(state, [[ rawgeti R 1 # get main thread pushstring 'XX' getglobal X # get function for body @@ -688,31 +739,28 @@ end -- leaving a pending coroutine open -_X = coroutine.wrap(function () +_G.TO_SURVIVE = coroutine.wrap(function () local a = 10 local x = function () a = a+1 end coroutine.yield() end) -_X() +_G.TO_SURVIVE() if not _soft then -- bug (stack overflow) - local j = 2^9 - local lim = 1000000 -- (C stack limit; assume 32-bit machine) - local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1} + local lim = 1000000 -- stack limit; assume 32-bit machine + local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1, lim + 5} for i = 1, #t do local j = t[i] - co = coroutine.create(function() - local t = {} - for i = 1, j do t[i] = i end - return table.unpack(t) + local co = coroutine.create(function() + return table.unpack({}, 1, j) end) local r, msg = coroutine.resume(co) - assert(not r) + -- must fail for unpacking larger than stack limit + assert(j < lim or not r) end - co = nil end @@ -896,7 +944,7 @@ assert(run(function () do local _ENV = _ENV f = function () AAA = BBB + 1; return AAA end end -g = new(10); g.k.BBB = 10; +local g = new(10); g.k.BBB = 10; debug.setupvalue(f, 1, g) assert(run(f, {"idx", "nidx", "idx"}) == 11) assert(g.k.AAA == 11) @@ -1036,6 +1084,8 @@ assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34) -- testing yields with continuations +local y + co = coroutine.wrap(function (...) return T.testC([[ # initial function yieldk 1 2 @@ -1088,6 +1138,9 @@ assert(x == "YIELD" and y == 4) assert(not pcall(co)) -- coroutine should be dead +_G.ctx = nil +_G.status = nil + -- bug in nCcalls local co = coroutine.wrap(function () diff --git a/third_party/lua/test/cstack.lua b/third_party/lua/test/cstack.lua index b0f653c79..d54a3cbc4 100644 --- a/third_party/lua/test/cstack.lua +++ b/third_party/lua/test/cstack.lua @@ -84,6 +84,32 @@ do -- bug in 5.4.0 end +do -- bug since 5.4.0 + local count = 0 + print("chain of 'coroutine.close'") + -- create N coroutines forming a list so that each one, when closed, + -- closes the previous one. (With a large enough N, previous Lua + -- versions crash in this test.) + local coro = false + for i = 1, 1000 do + local previous = coro + coro = coroutine.create(function() + local cc = setmetatable({}, {__close=function() + count = count + 1 + if previous then + assert(coroutine.close(previous)) + end + end}) + coroutine.yield() -- leaves 'cc' pending to be closed + end) + assert(coroutine.resume(coro)) -- start it and run until it yields + end + local st, msg = coroutine.close(coro) + assert(not st and string.find(msg, "C stack overflow")) + print("final count: ", count) +end + + do print("nesting of resuming yielded coroutines") local count = 0 @@ -103,6 +129,20 @@ do end +do -- bug in 5.4.2 + print("nesting coroutines running after recoverable errors") + local count = 0 + local function foo() + count = count + 1 + pcall(1) -- create an error + -- running now inside 'precover' ("protected recover") + coroutine.wrap(foo)() -- call another coroutine + end + checkerror("C stack overflow", foo) + print("final count: ", count) +end + + if T then print("testing stack recovery") local N = 0 -- trace number of calls diff --git a/third_party/lua/test/db.lua b/third_party/lua/test/db.lua index e8e2a17cc..431525421 100644 --- a/third_party/lua/test/db.lua +++ b/third_party/lua/test/db.lua @@ -16,7 +16,7 @@ end assert(not debug.gethook()) local testline = 19 -- line where 'test' is defined -function test (s, l, p) -- this must be line 19 +local function test (s, l, p) -- this must be line 19 collectgarbage() -- avoid gc during trace local function f (event, line) assert(event == 'line') @@ -50,7 +50,7 @@ end -- test file and string names truncation -a = "function f () end" +local a = "function f () end" local function dostring (s, x) return load(s, x)() end dostring(a) assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a)) @@ -72,7 +72,8 @@ dostring(a, string.format("=%s", string.rep('x', 500))) assert(string.find(debug.getinfo(f).short_src, "^x*$")) dostring(a, "=") assert(debug.getinfo(f).short_src == "") -a = nil; f = nil; +_G.a = nil; _G.f = nil; +_G[string.rep("p", 400)] = nil repeat @@ -120,6 +121,7 @@ else end ]], {2,3,4,7}) + test([[ local function foo() end @@ -128,6 +130,7 @@ A = 1 A = 2 A = 3 ]], {2, 3, 2, 4, 5, 6}) +_G.A = nil test([[-- @@ -175,6 +178,8 @@ end test([[for i=1,4 do a=1 end]], {1,1,1,1}) +_G.a = nil + do -- testing line info/trace with large gaps in source @@ -194,6 +199,50 @@ do -- testing line info/trace with large gaps in source end end end +_G.a = nil + + +do -- testing active lines + local function checkactivelines (f, lines) + local t = debug.getinfo(f, "SL") + for _, l in pairs(lines) do + l = l + t.linedefined + assert(t.activelines[l]) + t.activelines[l] = undef + end + assert(next(t.activelines) == nil) -- no extra lines + end + + checkactivelines(function (...) -- vararg function + -- 1st line is empty + -- 2nd line is empty + -- 3th line is empty + local a = 20 + -- 5th line is empty + local b = 30 + -- 7th line is empty + end, {4, 6, 8}) + + checkactivelines(function (a) + -- 1st line is empty + -- 2nd line is empty + local a = 20 + local b = 30 + -- 5th line is empty + end, {3, 4, 6}) + + checkactivelines(function (a, b, ...) end, {0}) + + checkactivelines(function (a, b) + end, {1}) + + for _, n in pairs{0, 1, 2, 10, 50, 100, 1000, 10000} do + checkactivelines( + load(string.format("%s return 1", string.rep("\n", n))), + {n + 1}) + end + +end print'+' @@ -244,7 +293,6 @@ foo(200, 3, 4) local a = {} for i = 1, (_soft and 100 or 1000) do a[i] = i end foo(table.unpack(a)) -a = nil @@ -264,13 +312,14 @@ do -- test hook presence in debug info debug.sethook() assert(count == 4) end +_ENV.a = nil -- hook table has weak keys assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k') -a = {}; L = nil +a = {}; local L = nil local glob = 1 local oldglob = glob debug.sethook(function (e,l) @@ -311,7 +360,7 @@ function foo() end; foo() -- set L -- check line counting inside strings and empty lines -_ = 'alo\ +local _ = 'alo\ alo' .. [[ ]] @@ -360,6 +409,7 @@ function g(a,b) return (a+1) + f() end assert(g(0,0) == 30) +_G.f, _G.g = nil debug.sethook(nil); assert(not debug.gethook()) @@ -403,7 +453,7 @@ local function collectlocals (level) end -X = nil +local X = nil a = {} function a:f (a, b, ...) local arg = {...}; local c = 13 end debug.sethook(function (e) @@ -426,6 +476,7 @@ a:f(1,2,3,4,5) assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil) assert(XX == 12) assert(not debug.gethook()) +_G.XX = nil -- testing access to local variables in return hook (bug in 5.2) @@ -550,6 +601,7 @@ end debug.sethook() +local g, g1 -- tests for tail calls local function f (x) @@ -595,7 +647,7 @@ h(false) debug.sethook() assert(b == 2) -- two tail calls -lim = _soft and 3000 or 30000 +local lim = _soft and 3000 or 30000 local function foo (x) if x==0 then assert(debug.getinfo(2).what == "main") @@ -844,7 +896,7 @@ do -- testing debug info for finalizers -- create a piece of garbage with a finalizer setmetatable({}, {__gc = function () - local t = debug.getinfo(2) -- get callee information + local t = debug.getinfo(1) -- get function information assert(t.namewhat == "metamethod") name = t.name end}) @@ -897,7 +949,7 @@ end print("testing debug functions on chunk without debug info") -prog = [[-- program to be loaded without debug information (strip) +local prog = [[-- program to be loaded without debug information (strip) local debug = require'debug' local a = 12 -- a local variable diff --git a/third_party/lua/test/errors.lua b/third_party/lua/test/errors.lua index b1d1b137d..1046bf478 100644 --- a/third_party/lua/test/errors.lua +++ b/third_party/lua/test/errors.lua @@ -26,7 +26,7 @@ end local function checkmessage (prog, msg, debug) local m = doit(prog) - if debug then print(m) end + if debug then print(m, msg) end assert(string.find(m, msg, 1, true)) end @@ -114,12 +114,14 @@ checkmessage("a = {} | 1", "bitwise operation") checkmessage("a = {} < 1", "attempt to compare") checkmessage("a = {} <= 1", "attempt to compare") -checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'") -checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'") +checkmessage("aaa=1; bbbb=2; aaa=math.sin(3)+bbbb(3)", "global 'bbbb'") +checkmessage("aaa={}; do local aaa=1 end aaa:bbbb(3)", "method 'bbbb'") checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'") -assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'")) -checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number") -checkmessage("a=(1)..{}", "a table value") +assert(not string.find(doit"aaa={13}; local bbbb=1; aaa[bbbb](3)", "'bbbb'")) +checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number") +checkmessage("aaa=(1)..{}", "a table value") + +_G.aaa, _G.bbbb = nil -- calls checkmessage("local a; a(13)", "local 'a'") @@ -134,12 +136,13 @@ checkmessage([[ -- tail calls checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'") -checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'") +checkmessage("aaa={}; do local aaa=1 end; return aaa:bbbb(3)", "method 'bbbb'") -checkmessage("a = #print", "length of a function value") -checkmessage("a = #3", "length of a number value") +checkmessage("aaa = #print", "length of a function value") +checkmessage("aaa = #3", "length of a number value") + +_G.aaa = nil -aaa = nil checkmessage("aaa.bbb:ddd(9)", "global 'aaa'") checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'") checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'") @@ -152,15 +155,16 @@ checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'") checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'") -checkmessage("b=1; local aaa={}; x=aaa+b", "local 'aaa'") +checkmessage("BB=1; local aaa={}; x=aaa+BB", "local 'aaa'") checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'") -checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'") +checkmessage("aaa=2; BB=nil;x=aaa*BB", "global 'BB'") checkmessage("aaa={}; x=-aaa", "global 'aaa'") -- short circuit -checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)", +checkmessage("aaa=1; local aaa,bbbb=2,3; aaa = math.sin(1) and bbbb(3)", + "local 'bbbb'") +checkmessage("aaa=1; local aaa,bbbb=2,3; aaa = bbbb(1) or aaa(3)", "local 'bbbb'") -checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'") checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'") checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value") assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'")) @@ -187,8 +191,8 @@ checkmessage("return ~-3e40", "has no integer representation") checkmessage("return ~-3.009", "has no integer representation") checkmessage("return 3.009 & 1", "has no integer representation") checkmessage("return 34 >> {}", "table value") -checkmessage("a = 24 // 0", "divide by zero") -checkmessage("a = 1 % 0", "'n%0'") +checkmessage("aaa = 24 // 0", "divide by zero") +checkmessage("aaa = 1 % 0", "'n%0'") -- type error for an object which is neither in an upvalue nor a register. @@ -228,6 +232,22 @@ do -- named objects (field '__name') checkmessage("return {} < XX", "table with My Type") checkmessage("return XX < io.stdin", "My Type with FILE*") _G.XX = nil + + if T then -- extra tests for 'luaL_tolstring' + -- bug in 5.4.3; 'luaL_tolstring' with negative indices + local x = setmetatable({}, {__name="TABLE"}) + assert(T.testC("Ltolstring -1; return 1", x) == tostring(x)) + + local a, b = T.testC("pushint 10; Ltolstring -2; return 2", x) + assert(a == 10 and b == tostring(x)) + + setmetatable(x, {__tostring=function (o) + assert(o == x) + return "ABC" + end}) + local a, b, c = T.testC("pushint 10; Ltolstring -2; return 3", x) + assert(a == x and b == 10 and c == "ABC") + end end -- global functions @@ -253,13 +273,13 @@ end -- tests for field accesses after RK limit local t = {} for i = 1, 1000 do - t[i] = "a = x" .. i + t[i] = "aaa = x" .. i end local s = table.concat(t, "; ") t = nil -checkmessage(s.."; a = bbb + 1", "global 'bbb'") -checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'") -checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'") +checkmessage(s.."; aaa = bbb + 1", "global 'bbb'") +checkmessage("local _ENV=_ENV;"..s.."; aaa = bbb + 1", "global 'bbb'") +checkmessage(s.."; local t = {}; aaa = t.bbb + 1", "field 'bbb'") checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'") checkmessage([[aaa=9 @@ -289,7 +309,7 @@ end]], "global 'insert'") checkmessage([[ -- tail call return math.sin("a") -]], "'sin'") +]], "sin") checkmessage([[collectgarbage("nooption")]], "invalid option") @@ -308,14 +328,17 @@ main() ]], "global 'NoSuchName'") print'+' -a = {}; setmetatable(a, {__index = string}) -checkmessage("a:sub()", "bad self") +aaa = {}; setmetatable(aaa, {__index = string}) +checkmessage("aaa:sub()", "bad self") checkmessage("string.sub('a', {})", "#2") checkmessage("('a'):sub{}", "#1") checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'") checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'") +_G.aaa = nil + + -- tests for errors in coroutines local function f (n) @@ -333,7 +356,7 @@ checkerr("yield across", f) -- testing size of 'source' info; size of buffer for that info is -- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'. -idsize = 60 - 1 +local idsize = 60 - 1 local function checksize (source) -- syntax error local _, msg = load("x", source) @@ -395,13 +418,14 @@ x local p = [[ function g() f() end - function f(x) error('a', X) end + function f(x) error('a', XX) end g() ]] -X=3;lineerror((p), 3) -X=0;lineerror((p), false) -X=1;lineerror((p), 2) -X=2;lineerror((p), 1) +XX=3;lineerror((p), 3) +XX=0;lineerror((p), false) +XX=1;lineerror((p), 2) +XX=2;lineerror((p), 1) +_G.XX, _G.g, _G.f = nil lineerror([[ @@ -420,6 +444,14 @@ if not b then end end]], 5) + +-- bug in 5.4.0 +lineerror([[ + local a = 0 + local b = 1 + local c = b % a +]], 3) + do -- Force a negative estimate for base line. Error in instruction 2 -- (after VARARGPREP, GETGLOBAL), with first absolute line information @@ -433,11 +465,11 @@ if not _soft then -- several tests that exaust the Lua stack collectgarbage() print"testing stack overflow" - C = 0 + local C = 0 -- get line where stack overflow will happen local l = debug.getinfo(1, "l").currentline + 1 local function auxy () C=C+1; auxy() end -- produce a stack overflow - function y () + function YY () collectgarbage("stop") -- avoid running finalizers without stack space auxy() collectgarbage("restart") @@ -449,9 +481,11 @@ if not _soft then return (string.find(m, "stack overflow")) end -- repeated stack overflows (to check stack recovery) - assert(checkstackmessage(doit('y()'))) - assert(checkstackmessage(doit('y()'))) - assert(checkstackmessage(doit('y()'))) + assert(checkstackmessage(doit('YY()'))) + assert(checkstackmessage(doit('YY()'))) + assert(checkstackmessage(doit('YY()'))) + + _G.YY = nil -- error lines in stack overflow @@ -545,7 +579,7 @@ do end -- xpcall with arguments -a, b, c = xpcall(string.find, error, "alo", "al") +local a, b, c = xpcall(string.find, error, "alo", "al") assert(a and b == 1 and c == 2) a, b, c = xpcall(string.find, function (x) return {} end, true, "al") assert(not a and type(b) == "table" and c == nil) @@ -565,11 +599,12 @@ checksyntax("a\1a = 1", "", "<\\1>", 1) -- test 255 as first char in a chunk checksyntax("\255a = 1", "", "<\\255>", 1) -doit('I = load("a=9+"); a=3') -assert(a==3 and not I) +doit('I = load("a=9+"); aaa=3') +assert(_G.aaa==3 and not _G.I) +_G.I,_G.aaa = nil print('+') -lim = 1000 +local lim = 1000 if _soft then lim = 100 end for i=1,lim do doit('a = ') diff --git a/third_party/lua/test/events.lua b/third_party/lua/test/events.lua index 71dee4590..6f8f79ca1 100644 --- a/third_party/lua/test/events.lua +++ b/third_party/lua/test/events.lua @@ -420,6 +420,9 @@ assert(i == 3 and x[1] == 3 and x[3] == 5) assert(_G.X == 20) +_G.X, _G.B = nil + + print'+' local _g = _G diff --git a/third_party/lua/test/files.lua b/third_party/lua/test/files.lua index 96ac499ec..a6450f8b2 100644 --- a/third_party/lua/test/files.lua +++ b/third_party/lua/test/files.lua @@ -507,15 +507,17 @@ load((io.lines(file, 1)))() assert(_G.X == 4) load((io.lines(file, 3)))() assert(_G.X == 8) +_G.X = nil print('+') local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'" io.output(file) -assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1))) +assert(io.write(string.format("X2 = %q\n-- comment without ending EOS", x1))) io.close() assert(loadfile(file))() -assert(x1 == x2) +assert(x1 == _G.X2) +_G.X2 = nil print('+') assert(os.remove(file)) assert(not os.remove(file)) @@ -610,6 +612,7 @@ do assert(os.remove(file)) end + io.output(file) assert(io.write("qualquer coisa\n")) assert(io.write("mais qualquer coisa")) @@ -743,7 +746,7 @@ if not _port then {"exit 129", "exit", 129}, {"kill -s HUP $$", "signal", 1}, {"kill -s KILL $$", "signal", 9}, - {"sh -c 'kill -s HUP $$'", "signal", 1}, + {"sh -c 'kill -s HUP $$'", "signal", 1}, -- [jart] {progname .. ' -e " "', "ok"}, {progname .. ' -e "os.exit(0, true)"', "ok"}, {progname .. ' -e "os.exit(20, true)"', "exit", 20}, @@ -833,8 +836,17 @@ checkerr("missing", os.time, {hour = 12}) -- missing date if string.packsize("i") == 4 then -- 4-byte ints checkerr("field 'year' is out-of-bound", os.time, {year = -(1 << 31) + 1899, month = 1, day = 1}) + + checkerr("field 'year' is out-of-bound", os.time, + {year = -(1 << 31), month = 1, day = 1}) + + if math.maxinteger > 2^31 then -- larger lua_integer? + checkerr("field 'year' is out-of-bound", os.time, + {year = (1 << 31) + 1900, month = 1, day = 1}) + end end + if not _port then -- test Posix-specific modifiers assert(type(os.date("%Ex")) == 'string') diff --git a/third_party/lua/test/gc.lua b/third_party/lua/test/gc.lua index f6eb7c9f0..a7db7ef2e 100644 --- a/third_party/lua/test/gc.lua +++ b/third_party/lua/test/gc.lua @@ -125,7 +125,7 @@ do end a:test() - + _G.temp = nil end @@ -134,7 +134,7 @@ do local f = function () end end print("functions with errors") -prog = [[ +local prog = [[ do a = 10; function foo(x,y) @@ -153,22 +153,25 @@ do end end end +rawset(_G, "a", nil) +_G.x = nil +do foo = nil print('long strings') -x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" + local x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" assert(string.len(x)==80) -s = '' -k = math.min(300, (math.maxinteger // 80) // 2) -for n = 1, k do s = s..x; j=tostring(n) end + local s = '' + local k = math.min(300, (math.maxinteger // 80) // 2) + for n = 1, k do s = s..x; local j=tostring(n) end assert(string.len(s) == k*80) s = string.sub(s, 1, 10000) -s, i = string.gsub(s, '(%d%d%d%d)', '') + local s, i = string.gsub(s, '(%d%d%d%d)', '') assert(i==10000 // 4) -s = nil -x = nil assert(_G["while"] == 234) + _G["while"] = nil +end -- @@ -227,8 +230,8 @@ end print("clearing tables") -lim = 15 -a = {} +local lim = 15 +local a = {} -- fill a with `collectable' indices for i=1,lim do a[{}] = i end b = {} @@ -371,7 +374,7 @@ if T then warn("@on"); warn("@store") collectgarbage() - assert(string.find(_WARN, "error in __gc metamethod")) + assert(string.find(_WARN, "error in __gc")) assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false for i = 8, 10 do assert(s[i]) end @@ -552,6 +555,7 @@ do for i=1,1000 do _ENV.a = {} end -- no collection during the loop until gcinfo() > 2 * x collectgarbage"restart" + _ENV.a = nil end @@ -676,11 +680,13 @@ end -- just to make sure assert(collectgarbage'isrunning') -do -- check that the collector is reentrant in incremental mode +do -- check that the collector is not reentrant in incremental mode + local res = true setmetatable({}, {__gc = function () - collectgarbage() + res = collectgarbage() end}) collectgarbage() + assert(not res) end diff --git a/third_party/lua/test/libs/makefile b/third_party/lua/test/libs/makefile index a13309208..9c0c4e3f7 100644 --- a/third_party/lua/test/libs/makefile +++ b/third_party/lua/test/libs/makefile @@ -11,17 +11,17 @@ CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so touch all -lib1.so: lib1.c $(LUA_DIR)/luaconf.h +lib1.so: lib1.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h $(CC) $(CFLAGS) -o lib1.so lib1.c -lib11.so: lib11.c $(LUA_DIR)/luaconf.h +lib11.so: lib11.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h $(CC) $(CFLAGS) -o lib11.so lib11.c -lib2.so: lib2.c $(LUA_DIR)/luaconf.h +lib2.so: lib2.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h $(CC) $(CFLAGS) -o lib2.so lib2.c -lib21.so: lib21.c $(LUA_DIR)/luaconf.h +lib21.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h $(CC) $(CFLAGS) -o lib21.so lib21.c -lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h +lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h $(CC) $(CFLAGS) -o lib2-v2.so lib22.c diff --git a/third_party/lua/test/literals.lua b/third_party/lua/test/literals.lua index 4e368292c..4831534a2 100644 --- a/third_party/lua/test/literals.lua +++ b/third_party/lua/test/literals.lua @@ -10,6 +10,7 @@ local function dostring (x) return assert(load(x), "")() end dostring("x \v\f = \t\r 'a\0a' \v\f\f") assert(x == 'a\0a' and string.len(x) == 3) +_G.x = nil -- escape sequences assert('\n\"\'\\' == [[ @@ -129,16 +130,16 @@ end -- long variable names -var1 = string.rep('a', 15000) .. '1' -var2 = string.rep('a', 15000) .. '2' -prog = string.format([[ +local var1 = string.rep('a', 15000) .. '1' +local var2 = string.rep('a', 15000) .. '2' +local prog = string.format([[ %s = 5 %s = %s + 1 return function () return %s - %s end ]], var1, var2, var1, var1, var2) local f = dostring(prog) assert(_G[var1] == 5 and _G[var2] == 6 and f() == -1) -var1, var2, f = nil +_G[var1], _G[var2] = nil print('+') -- escapes -- @@ -150,13 +151,13 @@ assert([[ $debug]] == "\n $debug") assert([[ [ ]] ~= [[ ] ]]) -- long strings -- -b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" +local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" assert(string.len(b) == 960) prog = [=[ print('+') -a1 = [["this is a 'string' with several 'quotes'"]] -a2 = "'quotes'" +local a1 = [["this is a 'string' with several 'quotes'"]] +local a2 = "'quotes'" assert(string.find(a1, a2) == 34) print('+') @@ -164,12 +165,13 @@ print('+') a1 = [==[temp = [[an arbitrary value]]; ]==] assert(load(a1))() assert(temp == 'an arbitrary value') +_G.temp = nil -- long strings -- -b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" +local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" assert(string.len(b) == 960) print('+') -a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789 +local a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789 00123456789012345678901234567890123456789123456789012345678901234567890123456789 00123456789012345678901234567890123456789123456789012345678901234567890123456789 00123456789012345678901234567890123456789123456789012345678901234567890123456789 @@ -199,19 +201,41 @@ x = 1 ]=] print('+') -x = nil +_G.x = nil dostring(prog) assert(x) +_G.x = nil -prog = nil -a = nil -b = nil + + +do -- reuse of long strings + + -- get the address of a string + local function getadd (s) return string.format("%p", s) end + + local s1 = "01234567890123456789012345678901234567890123456789" + local s2 = "01234567890123456789012345678901234567890123456789" + local s3 = "01234567890123456789012345678901234567890123456789" + local function foo() return s1 end + local function foo1() return s3 end + local function foo2() + return "01234567890123456789012345678901234567890123456789" + end + local a1 = getadd(s1) + assert(a1 == getadd(s2)) + assert(a1 == getadd(foo())) + assert(a1 == getadd(foo1())) + assert(a1 == getadd(foo2())) + + local sd = "0123456789" .. "0123456789012345678901234567890123456789" + assert(sd == s1 and getadd(sd) ~= a1) +end -- testing line ends prog = [[ -a = 1 -- a comment -b = 2 +local a = 1 -- a comment +local b = 2 x = [=[ @@ -228,10 +252,11 @@ for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do assert(dostring(prog) == nn) assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n") end +_G.x, _G.y = nil -- testing comments and strings with long brackets -a = [==[]=]==] +local a = [==[]=]==] assert(a == "]=") a = [==[[===[[=[]]=][====[]]===]===]==] diff --git a/third_party/lua/test/locals.lua b/third_party/lua/test/locals.lua index 2af78913d..e35a2e5bd 100644 --- a/third_party/lua/test/locals.lua +++ b/third_party/lua/test/locals.lua @@ -37,7 +37,7 @@ end f = nil local f -x = 1 +local x = 1 a = nil load('local a = {}')() @@ -152,7 +152,7 @@ local dummy local _ENV = (function (...) return ... end)(_G, dummy) -- { do local _ENV = {assert=assert}; assert(true) end -mt = {_G = _G} +local mt = {_G = _G} local foo,x A = false -- "declare" A do local _ENV = mt @@ -174,6 +174,8 @@ do local _ENV = {assert=assert, A=10}; end assert(x==20) +A = nil + do -- constants local a, b, c = 10, 20, 30 @@ -187,6 +189,8 @@ do -- constants checkro("y", "local x, y , z = 10, 20, 30; x = 11; y = 12") checkro("x", "local x , y, z = 10, 20, 30; x = 11") checkro("z", "local x , y, z = 10, 20, 30; y = 10; z = 11") + checkro("foo", "local foo = 10; function foo() end") + checkro("foo", "local foo = {}; function foo() end") checkro("z", [[ local a, z , b = 10; @@ -335,6 +339,49 @@ do end +do + -- bug in 5.4.3: previous condition (calls cannot be tail in the + -- scope of to-be-closed variables) must be valid for tbc variables + -- created by 'for' loops. + + local closed = false + + local function foo () + return function () return true end, 0, 0, + func2close(function () closed = true end) + end + + local function tail() return closed end + + local function foo1 () + for k in foo() do return tail() end + end + + assert(foo1() == false) + assert(closed == true) +end + + +do + -- bug in 5.4.4: 'break' may generate wrong 'close' instruction when + -- leaving a loop block. + + local closed = false + + local o1 = setmetatable({}, {__close=function() closed = true end}) + + local function test() + for k, v in next, {}, nil, o1 do + local function f() return k end -- create an upvalue + break + end + assert(closed) + end + + test() +end + + do print("testing errors in __close") -- original error is in __close @@ -567,6 +614,28 @@ end if rawget(_G, "T") then + do + -- bug in 5.4.3 + -- 'lua_settop' may use a pointer to stack invalidated by 'luaF_close' + + -- reduce stack size + collectgarbage(); collectgarbage(); collectgarbage() + + -- force a stack reallocation + local function loop (n) + if n < 400 then loop(n + 1) end + end + + -- close metamethod will reallocate the stack + local o = setmetatable({}, {__close = function () loop(0) end}) + + local script = [[toclose 2; settop 1; return 1]] + + assert(T.testC(script, o) == script) + + end + + -- memory error inside closing function local function foo () local y = func2close(function () T.alloccount() end) @@ -644,7 +713,7 @@ if rawget(_G, "T") then collectgarbage(); collectgarbage() - m = T.totalmem() + local m = T.totalmem() collectgarbage("stop") -- error in the first buffer allocation @@ -790,6 +859,65 @@ do end +do + -- yielding inside closing metamethods while returning + -- (bug in 5.4.3) + + local extrares -- result from extra yield (if any) + + local function check (body, extra, ...) + local t = table.pack(...) -- expected returns + local co = coroutine.wrap(body) + if extra then + extrares = co() -- runs until first (extra) yield + end + local res = table.pack(co()) -- runs until yield inside '__close' + assert(res.n == 2 and res[2] == nil) + local res2 = table.pack(co()) -- runs until end of function + assert(res2.n == t.n) + for i = 1, #t do + if t[i] == "x" then + assert(res2[i] == res[1]) -- value that was closed + else + assert(res2[i] == t[i]) + end + end + end + + local function foo () + local x = func2close(coroutine.yield) + local extra = func2close(function (self) + assert(self == extrares) + coroutine.yield(100) + end) + extrares = extra + return table.unpack{10, x, 30} + end + check(foo, true, 10, "x", 30) + assert(extrares == 100) + + local function foo () + local x = func2close(coroutine.yield) + return + end + check(foo, false) + + local function foo () + local x = func2close(coroutine.yield) + local y, z = 20, 30 + return x + end + check(foo, false, "x") + + local function foo () + local x = func2close(coroutine.yield) + local extra = func2close(coroutine.yield) + return table.unpack({}, 1, 100) -- 100 nils + end + check(foo, true, table.unpack({}, 1, 100)) + +end + do -- yielding inside closing metamethods after an error diff --git a/third_party/lua/test/main.lua b/third_party/lua/test/main.lua index 1fa8c0fb5..0586e2f6b 100644 --- a/third_party/lua/test/main.lua +++ b/third_party/lua/test/main.lua @@ -94,6 +94,33 @@ RUN('echo "print(10)\nprint(2)\n" | lua > %s', out) checkout("10\n2\n") +-- testing BOM +prepfile("\xEF\xBB\xBF") +RUN('lua %s > %s', prog, out) +checkout("") + +prepfile("\xEF\xBB\xBFprint(3)") +RUN('lua %s > %s', prog, out) +checkout("3\n") + +prepfile("\xEF\xBB\xBF# comment!!\nprint(3)") +RUN('lua %s > %s', prog, out) +checkout("3\n") + +-- bad BOMs +prepfile("\xEF") +NoRun("unexpected symbol", 'lua %s > %s', prog, out) + +prepfile("\xEF\xBB") +NoRun("unexpected symbol", 'lua %s > %s', prog, out) + +prepfile("\xEFprint(3)") +NoRun("unexpected symbol", 'lua %s > %s', prog, out) + +prepfile("\xEF\xBBprint(3)") +NoRun("unexpected symbol", 'lua %s > %s', prog, out) + + -- test option '-' RUN('echo "print(arg[1])" | lua - -h > %s', out) checkout("-h\n") @@ -190,6 +217,11 @@ prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog) RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out) checkout("1\n2\n15\n2\n15\n") +-- test explicit global names in -l +prepfile("print(str.upper'alo alo', m.max(10, 20))") +RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out) +checkout("0.0\nALO ALO\t20\n") + -- test 'arg' table local a = [[ assert(#arg == 3 and arg[1] == 'a' and @@ -256,6 +288,34 @@ u2 = setmetatable({}, {__gc = function () error("ZYX") end}) RUN('lua -W %s 2> %s', prog, out) checkprogout("ZYX)\nXYZ)\n") +-- bug since 5.2: finalizer called when closing a state could +-- subvert finalization order +prepfile[[ +-- should be called last +print("creating 1") +setmetatable({}, {__gc = function () print(1) end}) + +print("creating 2") +setmetatable({}, {__gc = function () + print("2") + print("creating 3") + -- this finalizer should not be called, as object will be + -- created after 'lua_close' has been called + setmetatable({}, {__gc = function () print(3) end}) + print(collectgarbage()) -- cannot call collector here + os.exit(0, true) +end}) +]] +RUN('lua -W %s > %s', prog, out) +checkout[[ +creating 1 +creating 2 +2 +creating 3 +nil +1 +]] + -- test many arguments prepfile[[print(({...})[30])]] @@ -279,7 +339,7 @@ prepfile("a = [[b\nc\nd\ne]]\n=a") RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) checkprogout("b\nc\nd\ne\n\n") -prompt = "alo" +local prompt = "alo" prepfile[[ -- a = 2 ]] @@ -339,7 +399,7 @@ NoRun("error object is a table value", [[lua %s]], prog) -- chunk broken in many lines -s = [=[ -- +local s = [=[ -- function f ( x ) local a = [[ xuxu @@ -361,12 +421,10 @@ checkprogout("101\n13\t22\n\n") prepfile[[#comment in 1st line without \n at the end]] RUN('lua %s', prog) -prepfile[[#test line number when file starts with comment line -debug = require"debug" -print(debug.getinfo(1).currentline) -]] +-- first-line comment with binary file +prepfile("#comment\n" .. string.dump(load("print(3)"))) RUN('lua %s > %s', prog, out) -checkprogout('3\n') +checkout('3\n') -- close Lua with an open file prepfile(string.format([[io.output(%q); io.write('alo')]], out)) diff --git a/third_party/lua/test/math.lua b/third_party/lua/test/math.lua index d8954b6c4..b880a5099 100644 --- a/third_party/lua/test/math.lua +++ b/third_party/lua/test/math.lua @@ -50,7 +50,7 @@ end local msgf2i = "number.* has no integer representation" -- float equality -function eq (a,b,limit) +local function eq (a,b,limit) if not limit then if floatbits >= 50 then limit = 1E-11 else limit = 1E-5 @@ -62,7 +62,7 @@ end -- equality with types -function eqT (a,b) +local function eqT (a,b) return a == b and math.type(a) == math.type(b) end @@ -83,7 +83,7 @@ end do local x = -1 local mz = 0/x -- minus zero - t = {[0] = 10, 20, 30, 40, 50} + local t = {[0] = 10, 20, 30, 40, 50} assert(t[mz] == t[0] and t[-0] == t[0]) end @@ -849,6 +849,7 @@ do math.randomseed(x, y) -- again should repeat the state assert(math.random(0) == res) -- keep the random seed for following tests + print(string.format("random seeds: %d, %d", x, y)) end do -- test random for floats diff --git a/third_party/lua/test/nextvar.lua b/third_party/lua/test/nextvar.lua index 8b39dc6f1..ad7884e3b 100644 --- a/third_party/lua/test/nextvar.lua +++ b/third_party/lua/test/nextvar.lua @@ -9,6 +9,16 @@ local function checkerror (msg, f, ...) end +local function check (t, na, nh) + if not T then return end + local a, h = T.querytab(t) + if a ~= na or h ~= nh then + print(na, nh, a, h) + assert(nil) + end +end + + local a = {} -- make sure table has lots of space in hash part @@ -20,6 +30,25 @@ for i=1,100 do assert(#a == i) end + +do -- rehash moving elements from array to hash + local a = {} + for i = 1, 100 do a[i] = i end + check(a, 128, 0) + + for i = 5, 95 do a[i] = nil end + check(a, 128, 0) + + a.x = 1 -- force a re-hash + check(a, 4, 8) + + for i = 1, 4 do assert(a[i] == i) end + for i = 5, 95 do assert(a[i] == nil) end + for i = 96, 100 do assert(a[i] == i) end + assert(a.x == 1) +end + + -- testing ipairs local x = 0 for k,v in ipairs{10,20,30;x=12} do @@ -43,6 +72,14 @@ assert(i == 4) assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{}) +do -- overflow (must wrap-around) + local f = ipairs{} + local k, v = f({[math.mininteger] = 10}, math.maxinteger) + assert(k == math.mininteger and v == 10) + k, v = f({[math.mininteger] = 10}, k) + assert(k == nil) +end + if not T then (Message or print) ('\n >>> testC not active: skipping tests for table sizes <<<\n') @@ -57,15 +94,6 @@ local function mp2 (n) -- minimum power of 2 >= n end -local function check (t, na, nh) - local a, h = T.querytab(t) - if a ~= na or h ~= nh then - print(na, nh, a, h) - assert(nil) - end -end - - -- testing C library sizes do local s = 0 @@ -161,7 +189,7 @@ end -- size tests for vararg lim = 35 -function foo (n, ...) +local function foo (n, ...) local arg = {...} check(arg, n, 0) assert(select('#', ...) == n) @@ -499,6 +527,15 @@ do -- testing table library with metamethods end +do -- testing overflow in table.insert (must wrap-around) + + local t = setmetatable({}, + {__len = function () return math.maxinteger end}) + table.insert(t, 20) + local k, v = next(t) + assert(k == math.mininteger and v == 20) +end + if not T then (Message or print) ('\n >>> testC not active: skipping tests for table library on non-tables <<<\n') @@ -764,4 +801,25 @@ for k,v in ipairs(a) do end assert(i == a.n) + +-- testing yield inside __pairs +do + local t = setmetatable({10, 20, 30}, {__pairs = function (t) + local inc = coroutine.yield() + return function (t, i) + if i > 1 then return i - inc, t[i - inc] else return nil end + end, t, #t + 1 + end}) + + local res = {} + local co = coroutine.wrap(function () + for i,p in pairs(t) do res[#res + 1] = p end + end) + + co() -- start coroutine + co(1) -- continue after yield + assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3) + +end + print"OK" diff --git a/third_party/lua/test/pm.lua b/third_party/lua/test/pm.lua index 7b38fd6ce..c24a1784a 100644 --- a/third_party/lua/test/pm.lua +++ b/third_party/lua/test/pm.lua @@ -9,12 +9,12 @@ local function checkerror (msg, f, ...) end -function f(s, p) +local function f (s, p) local i,e = string.find(s, p) if i then return string.sub(s, i, e) end end -a,b = string.find('', '') -- empty patterns are tricky +local a,b = string.find('', '') -- empty patterns are tricky assert(a == 1 and b == 0); a,b = string.find('alo', '') assert(a == 1 and b == 0) @@ -88,7 +88,7 @@ assert(f("alo alo", "%C+") == "alo alo") print('+') -function f1(s, p) +local function f1 (s, p) p = string.gsub(p, "%%([0-9])", function (s) return "%" .. (tonumber(s)+1) end) @@ -113,7 +113,7 @@ local abc = string.char(range(0, 127)) .. string.char(range(128, 255)); assert(string.len(abc) == 256) -function strset (p) +local function strset (p) local res = {s=''} string.gsub(abc, p, function (c) res.s = res.s .. c end) return res.s @@ -147,7 +147,7 @@ assert(string.gsub(' assert(string.gsub('alo lo ', ' +$', '') == 'alo lo') -- trim assert(string.gsub(' alo alo ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ') -t = "ab d" +local t = "ab d" a, b = string.gsub(t, '(.)', '%1@') assert('@'..a == string.gsub(t, '', '@') and b == 5) a, b = string.gsub('abd', '(.)', '%0@', 2) @@ -184,6 +184,7 @@ do local function setglobal (n,v) rawset(_G, n, v) end string.gsub("a=roberto,roberto=a", "(%w+)=(%w%w*)", setglobal) assert(_G.a=="roberto" and _G.roberto=="a") + _G.a = nil; _G.roberto = nil end function f(a,b) return string.gsub(a,'.',b) end @@ -195,20 +196,21 @@ assert(string.gsub("alo $a='x'$ novamente $return a$", "$([^$]*)%$", dostring) == "alo novamente x") -x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$", +local x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$", "$([^$]*)%$", dostring) assert(x == ' assim vai para ALO') +_G.a, _G.x = nil -t = {} -s = 'a alo jose joao' -r = string.gsub(s, '()(%w+)()', function (a,w,b) - assert(string.len(w) == b-a); - t[a] = b-a; - end) +local t = {} +local s = 'a alo jose joao' +local r = string.gsub(s, '()(%w+)()', function (a,w,b) + assert(string.len(w) == b-a); + t[a] = b-a; + end) assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4) -function isbalanced (s) +local function isbalanced (s) return not string.find(string.gsub(s, "%b()", ""), "[()]") end @@ -251,7 +253,7 @@ if not _soft then end -- recursive nest of gsubs -function rev (s) +local function rev (s) return string.gsub(s, "(.)(.+)", function (c,s1) return rev(s1)..c end) end diff --git a/third_party/lua/test/sort.lua b/third_party/lua/test/sort.lua index 7c8c484ef..02789bda3 100644 --- a/third_party/lua/test/sort.lua +++ b/third_party/lua/test/sort.lua @@ -20,7 +20,7 @@ end checkerror("wrong number of arguments", table.insert, {}, 2, 3, 4) local x,y,z,a,n -a = {}; lim = _soft and 200 or 2000 +a = {}; local lim = _soft and 200 or 2000 for i=1, lim do a[i]=i end assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim) x = unpack(a) @@ -222,7 +222,7 @@ a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", table.sort(a) check(a) -function perm (s, n) +local function perm (s, n) n = n or #s if n == 1 then local t = {unpack(s)} @@ -248,7 +248,7 @@ perm{1,2,3,3,5} perm{1,2,3,4,5,6} perm{2,2,3,3,5,6} -function timesort (a, n, func, msg, pre) +local function timesort (a, n, func, msg, pre) local x = os.clock() table.sort(a, func) x = (os.clock() - x) * 1000 @@ -257,7 +257,7 @@ function timesort (a, n, func, msg, pre) check(a, func) end -limit = 50000 +local limit = 50000 if _soft then limit = 5000 end a = {} @@ -274,7 +274,7 @@ for i=1,limit do a[i] = math.random() end -x = os.clock(); i=0 +local x = os.clock(); local i = 0 table.sort(a, function(x,y) i=i+1; return y64k)" -- template to create a very big test file -prog = [[$ +local prog = [[$ local a,b @@ -85,7 +85,7 @@ function b:xxx (a,b) return a+b end assert(b:xxx(10, 12) == 22) -- pushself with non-constant index b["xxx"] = undef -s = 0; n=0 +local s = 0; local n=0 for a,b in pairs(b) do s=s+b; n=n+1 end -- with 32-bit floats, exact value of 's' depends on summation order assert(81800000.0 < s and s < 81860000 and n == 70001) @@ -93,7 +93,7 @@ assert(81800000.0 < s and s < 81860000 and n == 70001) a = nil; b = nil print'+' -function f(x) b=x end +local function f(x) b=x end a = f{$3$} or 10 @@ -118,7 +118,7 @@ local function sig (x) return (x % 2 == 0) and '' or '-' end -F = { +local F = { function () -- $1$ for i=10,50009 do io.write('a', i, ' = ', sig(i), 5+((i-10)/2), ',\n') @@ -138,14 +138,14 @@ function () -- $3$ end, } -file = os.tmpname() +local file = os.tmpname() io.output(file) for s in string.gmatch(prog, "$([^$]+)") do local n = tonumber(s) if not n then io.write(s) else F[n]() end end io.close() -result = dofile(file) +local result = dofile(file) assert(os.remove(file)) print'OK' return result