diff options
author | 2023-03-07 01:39:42 +1100 | |
---|---|---|
committer | 2023-03-07 01:39:42 +1100 | |
commit | b5475eb38b8b79a13a0a9e8a6cb68755b84f0c7b (patch) | |
tree | 005b70ffe81cab56d55873ac6ee68725e5ba9d36 /3rdparty/lua/src/lua.c | |
parent | 2102c32d2f3f94e69e3baf60294be3c04ca8b09f (diff) |
Various updates, mostly around Lua:
Compile Lua as C++. When Lua is compiled as C, it uses setjmp/longjmp
for error handling, resulting in failure to unwind intermediate stack
frames. Trying to ensure no objects with non-trivial destructors are in
scope when raising a Lua error is error-prone. In particular,
converting an exception to a Lua error becomes convoluted, and raising a
Lua error from a constructor is effectively impossible.
Updated Lua to 5.4.4 - this includes a brand-new garbage collector
implementation with better performance. The main thing removed is the
deprecated bitlib.
Updated sol2 to version 3.3.0 - this adds support for Lua 5.4 and fixes
a number of issues, including not correctly handling errors when Lua is
built as C++.
Updated LuaFileSystem to version 1.8.0 - this adds support for symbolic
links on Windows, as well as Lua 5.4 compatibility.
Updated LuaSQLite3 to version 0.9.5 - this fixes issues in
multi-threaded environments, as well as Lua 5.4 compatibility.
Fixed double-free after attempting to construct a debugger expression
from Lua with an invalid string, and exposed expression error to Lua in
a better way.
Added warning level print function to Lua.
Fixed saving cheats with shift operators in expressions, although this
code isn't actually used as there's no cheat editor.
Diffstat (limited to '3rdparty/lua/src/lua.c')
-rw-r--r-- | 3rdparty/lua/src/lua.c | 486 |
1 files changed, 270 insertions, 216 deletions
diff --git a/3rdparty/lua/src/lua.c b/3rdparty/lua/src/lua.c index 3f082da6be6..0f190044449 100644 --- a/3rdparty/lua/src/lua.c +++ b/3rdparty/lua/src/lua.c @@ -1,5 +1,5 @@ /* -** $Id: lua.c,v 1.230 2017/01/12 17:14:26 roberto Exp $ +** $Id: lua.c $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ @@ -9,31 +9,22 @@ #include "lprefix.h" -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> + #include "lua.h" #include "lauxlib.h" #include "lualib.h" - -#if !defined(LUA_PROMPT) -#define LUA_PROMPT "> " -#define LUA_PROMPT2 ">> " -#endif - #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" #endif -#if !defined(LUA_MAXINPUT) -#define LUA_MAXINPUT 512 -#endif - #if !defined(LUA_INIT_VAR) #define LUA_INIT_VAR "LUA_INIT" #endif @@ -41,68 +32,29 @@ #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX -/* -** lua_stdin_is_tty detects whether the standard input is a 'tty' (that -** is, whether we're running lua interactively). -*/ -#if !defined(lua_stdin_is_tty) /* { */ - -#if defined(LUA_USE_POSIX) /* { */ - -#include <unistd.h> -#define lua_stdin_is_tty() isatty(0) - -#elif defined(LUA_USE_WINDOWS) /* }{ */ - -#include <io.h> -#include <windows.h> - -#define lua_stdin_is_tty() _isatty(_fileno(stdin)) - -#else /* }{ */ - -/* ISO C definition */ -#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +static lua_State *globalL = NULL; -#endif /* } */ +static const char *progname = LUA_PROGNAME; -#endif /* } */ +#if defined(LUA_USE_POSIX) /* { */ /* -** lua_readline defines how to show a prompt and then read a line from -** the standard input. -** lua_saveline defines how to "save" a read line in a "history". -** lua_freeline defines how to free a line read by lua_readline. +** Use 'sigaction' when available. */ -#if !defined(lua_readline) /* { */ - -#if defined(LUA_USE_READLINE) /* { */ - -#include <readline/readline.h> -#include <readline/history.h> -#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,line) ((void)L, add_history(line)) -#define lua_freeline(L,b) ((void)L, free(b)) - -#else /* }{ */ - -#define lua_readline(L,b,p) \ - ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,line) { (void)L; (void)line; } -#define lua_freeline(L,b) { (void)L; (void)b; } - -#endif /* } */ - -#endif /* } */ - - +static void setsignal (int sig, void (*handler)(int)) { + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); /* do not mask any signal */ + sigaction(sig, &sa, NULL); +} +#else /* }{ */ -static lua_State *globalL = NULL; +#define setsignal signal -static const char *progname = LUA_PROGNAME; +#endif /* } */ /* @@ -122,8 +74,9 @@ static void lstop (lua_State *L, lua_Debug *ar) { ** interpreter. */ static void laction (int i) { - signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ - lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); + int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; + setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ + lua_sethook(globalL, lstop, flag, 1); } @@ -136,13 +89,15 @@ static void print_usage (const char *badoption) { lua_writestringerror( "usage: %s [options] [script [args]]\n" "Available options are:\n" - " -e stat execute string 'stat'\n" - " -i enter interactive mode after executing 'script'\n" - " -l name require library 'name'\n" - " -v show version information\n" - " -E ignore environment variables\n" - " -- stop handling options\n" - " - stop handling options and execute stdin\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\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" + " -- stop handling options\n" + " - stop handling options and execute stdin\n" , progname); } @@ -201,9 +156,9 @@ static int docall (lua_State *L, int narg, int nres) { lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ globalL = L; /* to be available to 'laction' */ - signal(SIGINT, laction); /* set C-signal handler */ + setsignal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); - signal(SIGINT, SIG_DFL); /* reset C-signal handler */ + setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */ lua_remove(L, base); /* remove message handler from the stack */ return status; } @@ -253,29 +208,253 @@ 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 = docall(L, 1, 1); /* call 'require(name)' */ + lua_pushstring(L, modname); + status = docall(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 report(L, status); } /* -** Returns the string to be used as a prompt by the interpreter. +** Push on the stack the contents of table 'arg' from 1 to #arg +*/ +static int pushargs (lua_State *L) { + int i, n; + if (lua_getglobal(L, "arg") != LUA_TTABLE) + luaL_error(L, "'arg' is not a table"); + n = (int)luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many arguments to script"); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); + lua_remove(L, -i); /* remove table from the stack */ + return n; +} + + +static int handle_script (lua_State *L, char **argv) { + int status; + const char *fname = argv[0]; + if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + if (status == LUA_OK) { + int n = pushargs(L); /* push arguments to script */ + status = docall(L, n, LUA_MULTRET); + } + return report(L, status); +} + + +/* bits of various argument indicators in 'args' */ +#define has_error 1 /* bad option */ +#define has_i 2 /* -i */ +#define has_v 4 /* -v */ +#define has_e 8 /* -e */ +#define has_E 16 /* -E */ + + +/* +** 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). +*/ +static int collectargs (char **argv, int *first) { + int args = 0; + int i; + for (i = 1; argv[i] != NULL; i++) { + *first = i; + if (argv[i][0] != '-') /* not an option? */ + return args; /* stop handling options */ + switch (argv[i][1]) { /* else check option */ + case '-': /* '--' */ + if (argv[i][2] != '\0') /* extra characters after '--'? */ + return has_error; /* invalid option */ + *first = i + 1; + return args; + case '\0': /* '-' */ + return args; /* script "name" is '-' */ + case 'E': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_E; + break; + case 'W': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + break; + case 'i': + args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ + case 'v': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_v; + break; + case 'e': + args |= has_e; /* FALLTHROUGH */ + case 'l': /* both options need an argument */ + if (argv[i][2] == '\0') { /* no concatenated argument? */ + i++; /* try next 'argv' */ + if (argv[i] == NULL || argv[i][0] == '-') + return has_error; /* no next argument or it is another option */ + } + break; + default: /* invalid option */ + return has_error; + } + } + *first = i; /* no script name */ + return args; +} + + +/* +** Processes options 'e' and 'l', which involve running Lua code, and +** 'W', which also affects the state. +** Returns 0 if some code raises an error. +*/ +static int runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + int option = argv[i][1]; + lua_assert(argv[i][0] == '-'); /* already checked */ + switch (option) { + case 'e': case 'l': { + int status; + char *extra = argv[i] + 2; /* both options need an argument */ + if (*extra == '\0') extra = argv[++i]; + lua_assert(extra != NULL); + status = (option == 'e') + ? dostring(L, extra, "=(command line)") + : dolibrary(L, extra); + if (status != LUA_OK) return 0; + break; + } + case 'W': + lua_warning(L, "@on", 0); /* warnings on */ + break; + } + } + return 1; +} + + +static int handle_luainit (lua_State *L) { + const char *name = "=" LUA_INITVARVERSION; + const char *init = getenv(name + 1); + if (init == NULL) { + name = "=" LUA_INIT_VAR; + init = getenv(name + 1); /* try alternative name */ + } + if (init == NULL) return LUA_OK; + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, name); +} + + +/* +** {================================================================== +** Read-Eval-Print Loop (REPL) +** =================================================================== +*/ + +#if !defined(LUA_PROMPT) +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " +#endif + +#if !defined(LUA_MAXINPUT) +#define LUA_MAXINPUT 512 +#endif + + +/* +** lua_stdin_is_tty detects whether the standard input is a 'tty' (that +** is, whether we're running lua interactively). +*/ +#if !defined(lua_stdin_is_tty) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include <unistd.h> +#define lua_stdin_is_tty() isatty(0) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + +#include <io.h> +#include <windows.h> + +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) + +#else /* }{ */ + +/* ISO C definition */ +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ + +#endif /* } */ + +#endif /* } */ + + +/* +** lua_readline defines how to show a prompt and then read a line from +** the standard input. +** lua_saveline defines how to "save" a read line in a "history". +** lua_freeline defines how to free a line read by lua_readline. +*/ +#if !defined(lua_readline) /* { */ + +#if defined(LUA_USE_READLINE) /* { */ + +#include <readline/readline.h> +#include <readline/history.h> +#define lua_initreadline(L) ((void)L, rl_readline_name="lua") +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,line) ((void)L, add_history(line)) +#define lua_freeline(L,b) ((void)L, free(b)) + +#else /* }{ */ + +#define lua_initreadline(L) ((void)L) +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,line) { (void)L; (void)line; } +#define lua_freeline(L,b) { (void)L; (void)b; } + +#endif /* } */ + +#endif /* } */ + + +/* +** Return the string to be used as a prompt by the interpreter. Leave +** the string (or nil, if using the default value) on the stack, to keep +** it anchored. */ static const char *get_prompt (lua_State *L, int firstline) { - const char *p; - lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); - p = lua_tostring(L, -1); - if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); - return p; + if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL) + return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */ + else { /* apply 'tostring' over the value */ + const char *p = luaL_tolstring(L, -1, NULL); + lua_remove(L, -2); /* remove original value */ + return p; + } } /* mark in error messages for incomplete statements */ @@ -406,6 +585,7 @@ static void doREPL (lua_State *L) { int status; const char *oldprogname = progname; progname = NULL; /* no 'progname' on errors in interactive mode */ + lua_initreadline(L); while ((status = loadline(L)) != -1) { if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); @@ -417,134 +597,7 @@ static void doREPL (lua_State *L) { progname = oldprogname; } - -/* -** Push on the stack the contents of table 'arg' from 1 to #arg -*/ -static int pushargs (lua_State *L) { - int i, n; - if (lua_getglobal(L, "arg") != LUA_TTABLE) - luaL_error(L, "'arg' is not a table"); - n = (int)luaL_len(L, -1); - luaL_checkstack(L, n + 3, "too many arguments to script"); - for (i = 1; i <= n; i++) - lua_rawgeti(L, -i, i); - lua_remove(L, -i); /* remove table from the stack */ - return n; -} - - -static int handle_script (lua_State *L, char **argv) { - int status; - const char *fname = argv[0]; - if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) - fname = NULL; /* stdin */ - status = luaL_loadfile(L, fname); - if (status == LUA_OK) { - int n = pushargs(L); /* push arguments to script */ - status = docall(L, n, LUA_MULTRET); - } - return report(L, status); -} - - - -/* bits of various argument indicators in 'args' */ -#define has_error 1 /* bad option */ -#define has_i 2 /* -i */ -#define has_v 4 /* -v */ -#define has_e 8 /* -e */ -#define has_E 16 /* -E */ - -/* -** 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). -*/ -static int collectargs (char **argv, int *first) { - int args = 0; - int i; - for (i = 1; argv[i] != NULL; i++) { - *first = i; - if (argv[i][0] != '-') /* not an option? */ - return args; /* stop handling options */ - switch (argv[i][1]) { /* else check option */ - case '-': /* '--' */ - if (argv[i][2] != '\0') /* extra characters after '--'? */ - return has_error; /* invalid option */ - *first = i + 1; - return args; - case '\0': /* '-' */ - return args; /* script "name" is '-' */ - case 'E': - if (argv[i][2] != '\0') /* extra characters after 1st? */ - return has_error; /* invalid option */ - args |= has_E; - break; - case 'i': - args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ - case 'v': - if (argv[i][2] != '\0') /* extra characters after 1st? */ - return has_error; /* invalid option */ - args |= has_v; - break; - case 'e': - args |= has_e; /* FALLTHROUGH */ - case 'l': /* both options need an argument */ - if (argv[i][2] == '\0') { /* no concatenated argument? */ - i++; /* try next 'argv' */ - if (argv[i] == NULL || argv[i][0] == '-') - return has_error; /* no next argument or it is another option */ - } - break; - default: /* invalid option */ - return has_error; - } - } - *first = i; /* no script name */ - return args; -} - - -/* -** Processes options 'e' and 'l', which involve running Lua code. -** Returns 0 if some code raises an error. -*/ -static int runargs (lua_State *L, char **argv, int n) { - int i; - for (i = 1; i < n; i++) { - int option = argv[i][1]; - lua_assert(argv[i][0] == '-'); /* already checked */ - if (option == 'e' || option == 'l') { - int status; - const char *extra = argv[i] + 2; /* both options need an argument */ - if (*extra == '\0') extra = argv[++i]; - lua_assert(extra != NULL); - status = (option == 'e') - ? dostring(L, extra, "=(command line)") - : dolibrary(L, extra); - if (status != LUA_OK) return 0; - } - } - return 1; -} - - - -static int handle_luainit (lua_State *L) { - const char *name = "=" LUA_INITVARVERSION; - const char *init = getenv(name + 1); - if (init == NULL) { - name = "=" LUA_INIT_VAR; - init = getenv(name + 1); /* try alternative name */ - } - if (init == NULL) return LUA_OK; - else if (init[0] == '@') - return dofile(L, init+1); - else - return dostring(L, init, name); -} +/* }================================================================== */ /* @@ -570,6 +623,7 @@ static int pmain (lua_State *L) { } luaL_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ + lua_gc(L, LUA_GCGEN, 0, 0); /* GC 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 */ |