Compare commits

...

33 Commits

Author SHA1 Message Date
Roberto Ierusalimschy
1ab3208a1f 'lua.h' back to redundancy in version definitions
Several tools inspect 'lua.h' to extract version information, and
they assume the file will have some specific format.
2024-06-13 15:13:52 -03:00
Roberto Ierusalimschy
21ff8de33a Bug: Tricky _PROMPT may trigger undefined behavior 2024-06-05 13:37:16 -03:00
Roberto Ierusalimschy
7eb1ed21b7 More permissive use of 'errno'
Assume that no function will put garbage on errno (although ISO C allows
that). If any function during an operation set errno, and the operation
result in an error, assume that errno has something to say.
2024-06-05 11:50:42 -03:00
Roberto Ierusalimschy
2db966fcbf Bug: luaL_traceback may need more than 5 stack slots 2024-06-04 16:40:53 -03:00
Roberto Ierusalimschy
ae9a0cbbb4 Bug: overlapping assignments
ISO C forbids assignment of a union field to another field of the same
union.
2024-06-04 12:52:52 -03:00
Roberto Ierusalimschy
d5212c13b0 More disciplined use of 'errno'
Set errno to zero before calling any function where we may use its
errno, and check errno for zero before using it (as functions may
not set it even in error).
2024-06-04 12:48:29 -03:00
Roberto Ierusalimschy
e0efebdbe4 Detail in the manual
Function 'lua_toclose' can raise a non-memory error (but not a
memory error).
2024-05-14 17:59:54 -03:00
Roberto Ierusalimschy
e84f7bf198 Details
Typos in comments.
2024-05-13 13:23:45 -03:00
Roberto Ierusalimschy
dfbde4c7d5 Bug: Active-lines for stripped vararg functions
Lua seg. faults when asked to create the 'activelines' table for a
vararg function with no debug information.
2024-05-13 13:10:35 -03:00
Roberto Ierusalimschy
de794a6527 Towards release 5.4.7 2024-04-26 14:55:18 -03:00
Roberto Ierusalimschy
8b83417de9 Avoids a warning when lua_Number is 'float' 2024-02-07 14:17:03 -03:00
Roberto Ierusalimschy
e288c5a918 Bug: Yielding in a hook stops in the wrong instruction
Yielding in a hook must decrease the program counter, because it already
counted an instruction that, in the end, was not executed. However,
that decrement should be done only when about to restart the thread.
Otherwise, inspecting the thread with the debug library shows it one
instruction behind of where it really is.
2024-01-11 13:44:16 -03:00
Roberto Ierusalimschy
5853c37a83 Bug: Buffer overflow in string concatenation
Even if the string fits in size_t, the whole size of the TString object
can overflow when we add the header.
2023-12-21 13:37:51 -03:00
Roberto Ierusalimschy
842a83f09c Panic functions should not raise errors
The standard panic function was using 'lua_tostring', which may raise
a memory-allocation error if error value is a number.
2023-11-24 16:08:55 -03:00
Roberto Ierusalimschy
7923dbbf72 Bug: Recursion in 'getobjname' can stack overflow
'getobjname' now broken in two, a basic version that handles locals,
upvalues, and constants, and a full version, which uses the basic
version to handle table accesses (globals and fields).
2023-11-01 12:00:54 -03:00
Roberto Ierusalimschy
81e4fce530 Simpler test in 'luaH_getint'
The test whether key is inside the array part of a table uses a bit
trick to avoid computing the real size of the array part.
2023-10-26 16:12:25 -03:00
Roberto Ierusalimschy
6baee9ef9d Removed test for "corrupted binary dump"
Test is too non portable. (For instance, it does not work for
different number types.)
2023-09-08 16:19:21 -03:00
Roberto Ierusalimschy
edd8589f47 Avoid casts from unsigned long to floating-point
Old Microsoft compilers do not support those casts.
2023-09-08 11:34:39 -03:00
Roberto Ierusalimschy
07a9eab23a Cannot use 'getshrstr' before setting 'shrlen' 2023-08-25 15:55:14 -03:00
Roberto Ierusalimschy
9363a8b990 Documentation for "LUA_NOENV"
Registry field "LUA_NOENV" (that signals to libraries that option -E
is on) now part of the "official" API of Lua stand-alone.
2023-08-23 13:50:38 -03:00
Roberto Ierusalimschy
5ab6a5756b Bug: Wrong line number for function calls 2023-08-23 13:49:27 -03:00
Roberto Ierusalimschy
9b4f39ab14 More disciplined use of 'getstr' and 'tsslen'
We may want to add other string variants in the future; this change
documents better where the code may need to handle those variants.
2023-08-17 15:59:28 -03:00
Roberto Ierusalimschy
f4211a5ea4 More control over encoding of test files
The few UTF-8 test files are commented as such, and there is only one
non UTF-8 test file (to test non UTF-8 sources).
2023-08-17 10:42:56 -03:00
Roberto Ierusalimschy
1b3f507f62 Bug: Call hook may be called twice when count hook yields
Took the opportunity and moved the code that controls call hooks
in 'luaV_execute' into a function.
2023-07-25 16:50:44 -03:00
Roberto Ierusalimschy
6b51133a98 Thread stacks resized in the atomic phase
Although stack resize can be a little expensive, it seems unusual to
have too many threads needing resize during one GC cycle. On the other
hand, the change allows full collections to skip the propagate phase,
going straight from a pause to the atomic phase.
2023-07-13 14:55:46 -03:00
Roberto Ierusalimschy
cbae016202 Details 2023-07-03 14:12:54 -03:00
Roberto Ierusalimschy
ea39042e13 Removed redundancy in definitions of version/release
String rendering now derived from the numeric original definitions.
2023-06-21 15:04:24 -03:00
Roberto Ierusalimschy
05ec55f16b Avoid inclusion loop in 'ltm.h' 2023-06-16 11:52:14 -03:00
Roberto Ierusalimschy
f623b96932 Bug: read overflow in 'l_strcmp'
Equality according to 'strcoll' does not imply that strings have
the same length.
2023-06-14 14:38:07 -03:00
Roberto Ierusalimschy
9be74ccc21 Several functions turned 'static'
Several functions that were already being used only inside their
own file have been declared as 'static'.
2023-05-22 14:47:54 -03:00
Roberto Ierusalimschy
09f3c2372f Option '-l' discards version sufix from file name
Like 'require', the command-line option '-l' discards an optional
version suffix (everything after an hyphen) from a file name when
creating the module name.
2023-05-15 13:46:38 -03:00
Roberto Ierusalimschy
c197885cb0 Small improvements in tests 2023-05-15 10:20:13 -03:00
Roberto Ierusalimschy
934e77a286 Details
- Better comments about short strings in opcodes.
- luaH_newkey made static.
2023-05-15 10:07:25 -03:00
42 changed files with 509 additions and 339 deletions

4
lapi.c
View File

@@ -417,9 +417,9 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
o = index2value(L, idx); /* previous call may reallocate the stack */ o = index2value(L, idx); /* previous call may reallocate the stack */
} }
if (len != NULL) if (len != NULL)
*len = vslen(o); *len = tsslen(tsvalue(o));
lua_unlock(L); lua_unlock(L);
return svalue(o); return getstr(tsvalue(o));
} }

View File

@@ -80,6 +80,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
int top = lua_gettop(L); int top = lua_gettop(L);
lua_getinfo(L, "f", ar); /* push function */ lua_getinfo(L, "f", ar); /* push function */
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */
if (findfield(L, top + 1, 2)) { if (findfield(L, top + 1, 2)) {
const char *name = lua_tostring(L, -1); const char *name = lua_tostring(L, -1);
if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
@@ -249,11 +250,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
return 1; return 1;
} }
else { else {
const char *msg;
luaL_pushfail(L); luaL_pushfail(L);
msg = (en != 0) ? strerror(en) : "(no extra info)";
if (fname) if (fname)
lua_pushfstring(L, "%s: %s", fname, strerror(en)); lua_pushfstring(L, "%s: %s", fname, msg);
else else
lua_pushstring(L, strerror(en)); lua_pushstring(L, msg);
lua_pushinteger(L, en); lua_pushinteger(L, en);
return 3; return 3;
} }
@@ -732,9 +735,12 @@ static const char *getF (lua_State *L, void *ud, size_t *size) {
static int errfile (lua_State *L, const char *what, int fnameindex) { static int errfile (lua_State *L, const char *what, int fnameindex) {
const char *serr = strerror(errno); int err = errno;
const char *filename = lua_tostring(L, fnameindex) + 1; const char *filename = lua_tostring(L, fnameindex) + 1;
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); if (err != 0)
lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err));
else
lua_pushfstring(L, "cannot %s %s", what, filename);
lua_remove(L, fnameindex); lua_remove(L, fnameindex);
return LUA_ERRFILE; return LUA_ERRFILE;
} }
@@ -787,6 +793,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
} }
else { else {
lua_pushfstring(L, "@%s", filename); lua_pushfstring(L, "@%s", filename);
errno = 0;
lf.f = fopen(filename, "r"); lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex); if (lf.f == NULL) return errfile(L, "open", fnameindex);
} }
@@ -796,6 +803,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
if (c == LUA_SIGNATURE[0]) { /* binary file? */ if (c == LUA_SIGNATURE[0]) { /* binary file? */
lf.n = 0; /* remove possible newline */ lf.n = 0; /* remove possible newline */
if (filename) { /* "real" file? */ if (filename) { /* "real" file? */
errno = 0;
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex); if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
skipcomment(lf.f, &c); /* re-read initial portion */ skipcomment(lf.f, &c); /* re-read initial portion */
@@ -803,6 +811,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
} }
if (c != EOF) if (c != EOF)
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
errno = 0;
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
readstatus = ferror(lf.f); readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (filename) fclose(lf.f); /* close file (even in case of errors) */
@@ -933,7 +942,7 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues"); luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */ for (; l->name != NULL; l++) { /* fill the table with given functions */
if (l->func == NULL) /* place holder? */ if (l->func == NULL) /* placeholder? */
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
else { else {
int i; int i;
@@ -1025,9 +1034,14 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
} }
/*
** Standard panic funcion just prints an error message. The test
** with 'lua_type' avoids possible memory errors in 'lua_tostring'.
*/
static int panic (lua_State *L) { static int panic (lua_State *L) {
const char *msg = lua_tostring(L, -1); const char *msg = (lua_type(L, -1) == LUA_TSTRING)
if (msg == NULL) msg = "error object is not a string"; ? lua_tostring(L, -1)
: "error object is not a string";
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
msg); msg);
return 0; /* return to Lua to abort */ return 0; /* return to Lua to abort */

35
lcode.c
View File

@@ -415,7 +415,7 @@ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
/* /*
** Format and emit an 'iAsBx' instruction. ** Format and emit an 'iAsBx' instruction.
*/ */
int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) { static int codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
unsigned int b = bc + OFFSET_sBx; unsigned int b = bc + OFFSET_sBx;
lua_assert(getOpMode(o) == iAsBx); lua_assert(getOpMode(o) == iAsBx);
lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
@@ -671,7 +671,7 @@ static int fitsBx (lua_Integer i) {
void luaK_int (FuncState *fs, int reg, lua_Integer i) { void luaK_int (FuncState *fs, int reg, lua_Integer i) {
if (fitsBx(i)) if (fitsBx(i))
luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i)); codeAsBx(fs, OP_LOADI, reg, cast_int(i));
else else
luaK_codek(fs, reg, luaK_intK(fs, i)); luaK_codek(fs, reg, luaK_intK(fs, i));
} }
@@ -680,7 +680,7 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
static void luaK_float (FuncState *fs, int reg, lua_Number f) { static void luaK_float (FuncState *fs, int reg, lua_Number f) {
lua_Integer fi; lua_Integer fi;
if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
else else
luaK_codek(fs, reg, luaK_numberK(fs, f)); luaK_codek(fs, reg, luaK_numberK(fs, f));
} }
@@ -776,7 +776,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
break; break;
} }
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
e->u.info = e->u.var.ridx; int temp = e->u.var.ridx;
e->u.info = temp; /* (can't do a direct assignment; values overlap) */
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
break; break;
} }
@@ -1025,7 +1026,7 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
** in the range of R/K indices). ** in the range of R/K indices).
** Returns 1 iff expression is K. ** Returns 1 iff expression is K.
*/ */
int luaK_exp2RK (FuncState *fs, expdesc *e) { static int exp2RK (FuncState *fs, expdesc *e) {
if (luaK_exp2K(fs, e)) if (luaK_exp2K(fs, e))
return 1; return 1;
else { /* not a constant in the right range: put it in a register */ else { /* not a constant in the right range: put it in a register */
@@ -1037,7 +1038,7 @@ int luaK_exp2RK (FuncState *fs, expdesc *e) {
static void codeABRK (FuncState *fs, OpCode o, int a, int b, static void codeABRK (FuncState *fs, OpCode o, int a, int b,
expdesc *ec) { expdesc *ec) {
int k = luaK_exp2RK(fs, ec); int k = exp2RK(fs, ec);
luaK_codeABCk(fs, o, a, b, ec->u.info, k); luaK_codeABCk(fs, o, a, b, ec->u.info, k);
} }
@@ -1215,7 +1216,7 @@ static void codenot (FuncState *fs, expdesc *e) {
/* /*
** Check whether expression 'e' is a small literal string ** Check whether expression 'e' is a short literal string
*/ */
static int isKstr (FuncState *fs, expdesc *e) { static int isKstr (FuncState *fs, expdesc *e) {
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
@@ -1225,7 +1226,7 @@ static int isKstr (FuncState *fs, expdesc *e) {
/* /*
** Check whether expression 'e' is a literal integer. ** Check whether expression 'e' is a literal integer.
*/ */
int luaK_isKint (expdesc *e) { static int isKint (expdesc *e) {
return (e->k == VKINT && !hasjumps(e)); return (e->k == VKINT && !hasjumps(e));
} }
@@ -1235,7 +1236,7 @@ int luaK_isKint (expdesc *e) {
** proper range to fit in register C ** proper range to fit in register C
*/ */
static int isCint (expdesc *e) { static int isCint (expdesc *e) {
return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); return isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
} }
@@ -1244,7 +1245,7 @@ static int isCint (expdesc *e) {
** proper range to fit in register sC ** proper range to fit in register sC
*/ */
static int isSCint (expdesc *e) { static int isSCint (expdesc *e) {
return luaK_isKint(e) && fitsC(e->u.ival); return isKint(e) && fitsC(e->u.ival);
} }
@@ -1283,15 +1284,17 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */ luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) { if (t->k == VUPVAL) {
t->u.ind.t = t->u.info; /* upvalue index */ int temp = t->u.info; /* upvalue index */
t->u.ind.idx = k->u.info; /* literal string */ lua_assert(isKstr(fs, k));
t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
t->u.ind.idx = k->u.info; /* literal short string */
t->k = VINDEXUP; t->k = VINDEXUP;
} }
else { else {
/* register index of the table */ /* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info; t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
if (isKstr(fs, k)) { if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */ t->u.ind.idx = k->u.info; /* literal short string */
t->k = VINDEXSTR; t->k = VINDEXSTR;
} }
else if (isCint(k)) { else if (isCint(k)) {
@@ -1459,7 +1462,7 @@ static void codebinK (FuncState *fs, BinOpr opr,
*/ */
static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2, static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
OpCode op, int line, TMS event) { OpCode op, int line, TMS event) {
if (!luaK_isKint(e2)) if (!isKint(e2))
return 0; /* not an integer constant */ return 0; /* not an integer constant */
else { else {
lua_Integer i2 = e2->u.ival; lua_Integer i2 = e2->u.ival;
@@ -1592,7 +1595,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
op = OP_EQI; op = OP_EQI;
r2 = im; /* immediate operand */ r2 = im; /* immediate operand */
} }
else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */ else if (exp2RK(fs, e2)) { /* 2nd expression is constant? */
op = OP_EQK; op = OP_EQK;
r2 = e2->u.info; /* constant index */ r2 = e2->u.info; /* constant index */
} }
@@ -1658,7 +1661,7 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
} }
case OPR_EQ: case OPR_NE: { case OPR_EQ: case OPR_NE: {
if (!tonumeral(v, NULL)) if (!tonumeral(v, NULL))
luaK_exp2RK(fs, v); exp2RK(fs, v);
/* else keep numeral, which may be an immediate operand */ /* else keep numeral, which may be an immediate operand */
break; break;
} }

View File

@@ -61,10 +61,8 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
int B, int C, int k); int B, int C, int k);
LUAI_FUNC int luaK_isKint (expdesc *e);
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
@@ -76,7 +74,6 @@ LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);

218
ldebug.c
View File

@@ -31,7 +31,7 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) #define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
static const char *funcnamefromcall (lua_State *L, CallInfo *ci, static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
@@ -254,7 +254,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) { static void funcinfo (lua_Debug *ar, Closure *cl) {
if (noLuaClosure(cl)) { if (!LuaClosure(cl)) {
ar->source = "=[C]"; ar->source = "=[C]";
ar->srclen = LL("=[C]"); ar->srclen = LL("=[C]");
ar->linedefined = -1; ar->linedefined = -1;
@@ -288,29 +288,31 @@ static int nextline (const Proto *p, int currentline, int pc) {
static void collectvalidlines (lua_State *L, Closure *f) { static void collectvalidlines (lua_State *L, Closure *f) {
if (noLuaClosure(f)) { if (!LuaClosure(f)) {
setnilvalue(s2v(L->top.p)); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
} }
else { else {
int i;
TValue v;
const Proto *p = f->l.p; const Proto *p = f->l.p;
int currentline = p->linedefined; int currentline = p->linedefined;
Table *t = luaH_new(L); /* new table to store active lines */ Table *t = luaH_new(L); /* new table to store active lines */
sethvalue2s(L, L->top.p, t); /* push it on stack */ sethvalue2s(L, L->top.p, t); /* push it on stack */
api_incr_top(L); api_incr_top(L);
setbtvalue(&v); /* boolean 'true' to be the value of all indices */ if (p->lineinfo != NULL) { /* proto with debug information? */
if (!p->is_vararg) /* regular function? */ int i;
i = 0; /* consider all instructions */ TValue v;
else { /* vararg function */ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); if (!p->is_vararg) /* regular function? */
currentline = nextline(p, currentline, 0); i = 0; /* consider all instructions */
i = 1; /* skip first instruction (OP_VARARGPREP) */ else { /* vararg function */
} lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
for (; i < p->sizelineinfo; i++) { /* for each instruction */ currentline = nextline(p, currentline, 0);
currentline = nextline(p, currentline, i); /* get its line */ i = 1; /* skip first instruction (OP_VARARGPREP) */
luaH_setint(L, t, currentline, &v); /* table[line] = true */ }
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 */
}
} }
} }
} }
@@ -339,7 +341,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
} }
case 'u': { case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues; ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
if (noLuaClosure(f)) { if (!LuaClosure(f)) {
ar->isvararg = 1; ar->isvararg = 1;
ar->nparams = 0; ar->nparams = 0;
} }
@@ -417,40 +419,6 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
** ======================================================= ** =======================================================
*/ */
static const char *getobjname (const Proto *p, int lastpc, int reg,
const char **name);
/*
** Find a "name" for the constant 'c'.
*/
static void kname (const Proto *p, int c, const char **name) {
TValue *kvalue = &p->k[c];
*name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
}
/*
** Find a "name" for the register 'c'.
*/
static void rname (const Proto *p, int pc, int c, const char **name) {
const char *what = getobjname(p, pc, c, name); /* search for 'c' */
if (!(what && *what == 'c')) /* did not find a constant name? */
*name = "?";
}
/*
** Find a "name" for a 'C' value in an RK instruction.
*/
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
int c = GETARG_C(i); /* key index */
if (GETARG_k(i)) /* is 'c' a constant? */
kname(p, c, name);
else /* 'c' is a register */
rname(p, pc, c, name);
}
static int filterpc (int pc, int jmptarget) { static int filterpc (int pc, int jmptarget) {
if (pc < jmptarget) /* is code conditional (inside a jump)? */ if (pc < jmptarget) /* is code conditional (inside a jump)? */
@@ -509,28 +477,29 @@ static int findsetreg (const Proto *p, int lastpc, int reg) {
/* /*
** Check whether table being indexed by instruction 'i' is the ** Find a "name" for the constant 'c'.
** environment '_ENV'
*/ */
static const char *gxf (const Proto *p, int pc, Instruction i, int isup) { static const char *kname (const Proto *p, int index, const char **name) {
int t = GETARG_B(i); /* table index */ TValue *kvalue = &p->k[index];
const char *name; /* name of indexed variable */ if (ttisstring(kvalue)) {
if (isup) /* is an upvalue? */ *name = getstr(tsvalue(kvalue));
name = upvalname(p, t); return "constant";
else }
getobjname(p, pc, t, &name); else {
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; *name = "?";
return NULL;
}
} }
static const char *getobjname (const Proto *p, int lastpc, int reg, static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
const char **name) { const char **name) {
int pc; int pc = *ppc;
*name = luaF_getlocalname(p, reg + 1, lastpc); *name = luaF_getlocalname(p, reg + 1, pc);
if (*name) /* is a local? */ if (*name) /* is a local? */
return "local"; return "local";
/* else try symbolic execution */ /* else try symbolic execution */
pc = findsetreg(p, lastpc, reg); *ppc = pc = findsetreg(p, pc, reg);
if (pc != -1) { /* could find instruction? */ if (pc != -1) { /* could find instruction? */
Instruction i = p->code[pc]; Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i); OpCode op = GET_OPCODE(i);
@@ -538,18 +507,80 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
case OP_MOVE: { case OP_MOVE: {
int b = GETARG_B(i); /* move from 'b' to 'a' */ int b = GETARG_B(i); /* move from 'b' to 'a' */
if (b < GETARG_A(i)) if (b < GETARG_A(i))
return getobjname(p, pc, b, name); /* get name for 'b' */ return basicgetobjname(p, ppc, b, name); /* get name for 'b' */
break; break;
} }
case OP_GETUPVAL: {
*name = upvalname(p, GETARG_B(i));
return "upvalue";
}
case OP_LOADK: return kname(p, GETARG_Bx(i), name);
case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
default: break;
}
}
return NULL; /* could not find reasonable name */
}
/*
** Find a "name" for the register 'c'.
*/
static void rname (const Proto *p, int pc, int c, const char **name) {
const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */
if (!(what && *what == 'c')) /* did not find a constant name? */
*name = "?";
}
/*
** Find a "name" for a 'C' value in an RK instruction.
*/
static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
int c = GETARG_C(i); /* key index */
if (GETARG_k(i)) /* is 'c' a constant? */
kname(p, c, name);
else /* 'c' is a register */
rname(p, pc, c, name);
}
/*
** Check whether table being indexed by instruction 'i' is the
** environment '_ENV'
*/
static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
int t = GETARG_B(i); /* table index */
const char *name; /* name of indexed variable */
if (isup) /* is 't' an upvalue? */
name = upvalname(p, t);
else /* 't' is a register */
basicgetobjname(p, &pc, t, &name);
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
}
/*
** Extend 'basicgetobjname' to handle table accesses
*/
static const char *getobjname (const Proto *p, int lastpc, int reg,
const char **name) {
const char *kind = basicgetobjname(p, &lastpc, reg, name);
if (kind != NULL)
return kind;
else if (lastpc != -1) { /* could find instruction? */
Instruction i = p->code[lastpc];
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_GETTABUP: { case OP_GETTABUP: {
int k = GETARG_C(i); /* key index */ int k = GETARG_C(i); /* key index */
kname(p, k, name); kname(p, k, name);
return gxf(p, pc, i, 1); return isEnv(p, lastpc, i, 1);
} }
case OP_GETTABLE: { case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */ int k = GETARG_C(i); /* key index */
rname(p, pc, k, name); rname(p, lastpc, k, name);
return gxf(p, pc, i, 0); return isEnv(p, lastpc, i, 0);
} }
case OP_GETI: { case OP_GETI: {
*name = "integer index"; *name = "integer index";
@@ -558,24 +589,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
case OP_GETFIELD: { case OP_GETFIELD: {
int k = GETARG_C(i); /* key index */ int k = GETARG_C(i); /* key index */
kname(p, k, name); kname(p, k, name);
return gxf(p, pc, i, 0); return isEnv(p, lastpc, i, 0);
}
case OP_GETUPVAL: {
*name = upvalname(p, GETARG_B(i));
return "upvalue";
}
case OP_LOADK:
case OP_LOADKX: {
int b = (op == OP_LOADK) ? GETARG_Bx(i)
: GETARG_Ax(p->code[pc + 1]);
if (ttisstring(&p->k[b])) {
*name = svalue(&p->k[b]);
return "constant";
}
break;
} }
case OP_SELF: { case OP_SELF: {
rkname(p, pc, i, name); rkname(p, lastpc, i, name);
return "method"; return "method";
} }
default: break; /* go through to return NULL */ default: break; /* go through to return NULL */
@@ -627,7 +644,7 @@ static const char *funcnamefromcode (lua_State *L, const Proto *p,
default: default:
return NULL; /* cannot find a reasonable name */ return NULL; /* cannot find a reasonable name */
} }
*name = getstr(G(L)->tmname[tm]) + 2; *name = getshrstr(G(L)->tmname[tm]) + 2;
return "metamethod"; return "metamethod";
} }
@@ -865,6 +882,28 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
} }
/*
** Traces Lua calls. If code is running the first instruction of a function,
** and function is not vararg, and it is not coming from an yield,
** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall'
** after adjusting its variable arguments; otherwise, they could call
** a line/count hook before the call hook. Functions coming from
** an yield already called 'luaD_hookcall' before yielding.)
*/
int luaG_tracecall (lua_State *L) {
CallInfo *ci = L->ci;
Proto *p = ci_func(ci)->p;
ci->u.l.trap = 1; /* ensure hooks will be checked */
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
if (p->is_vararg)
return 0; /* hooks will start at VARARGPREP instruction */
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */
luaD_hookcall(L, ci); /* check 'call' hook */
}
return 1; /* keep 'trap' on */
}
/* /*
** Traces the execution of a Lua function. Called before the execution ** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last ** of each opcode, when debug is on. 'L->oldpc' stores the last
@@ -888,12 +927,12 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
} }
pc++; /* reference is always next instruction */ pc++; /* reference is always next instruction */
ci->u.l.savedpc = pc; /* save 'pc' */ ci->u.l.savedpc = pc; /* save 'pc' */
counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
if (counthook) if (counthook)
resethookcount(L); /* reset count */ resethookcount(L); /* reset count */
else if (!(mask & LUA_MASKLINE)) else if (!(mask & LUA_MASKLINE))
return 1; /* no line hook and count != 0; nothing to be done now */ return 1; /* no line hook and count != 0; nothing to be done now */
if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return 1; /* do not call hook again (VM yielded, so it did not move) */ return 1; /* do not call hook again (VM yielded, so it did not move) */
} }
@@ -915,7 +954,6 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
if (L->status == LUA_YIELD) { /* did hook yield? */ if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook) if (counthook)
L->hookcount = 1; /* undo decrement to zero */ L->hookcount = 1; /* undo decrement to zero */
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
luaD_throw(L, LUA_YIELD); luaD_throw(L, LUA_YIELD);
} }

View File

@@ -58,6 +58,7 @@ LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line); TString *src, int line);
LUAI_FUNC l_noret luaG_errormsg (lua_State *L); LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
LUAI_FUNC int luaG_tracecall (lua_State *L);
#endif #endif

10
ldo.c
View File

@@ -409,7 +409,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
** stack, below original 'func', so that 'luaD_precall' can call it. Raise ** stack, below original 'func', so that 'luaD_precall' can call it. Raise
** an error if there is no '__call' metafield. ** an error if there is no '__call' metafield.
*/ */
StkId luaD_tryfuncTM (lua_State *L, StkId func) { static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm; const TValue *tm;
StkId p; StkId p;
checkstackGCp(L, 1, func); /* space for metamethod */ checkstackGCp(L, 1, func); /* space for metamethod */
@@ -568,7 +568,7 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
return -1; return -1;
} }
default: { /* not a function */ default: { /* not a function */
func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ func = tryfuncTM(L, func); /* try to get '__call' metamethod */
/* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */
narg1++; narg1++;
goto retry; /* try again */ goto retry; /* try again */
@@ -609,7 +609,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
return ci; return ci;
} }
default: { /* not a function */ default: { /* not a function */
func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ func = tryfuncTM(L, func); /* try to get '__call' metamethod */
/* return luaD_precall(L, func, nresults); */ /* return luaD_precall(L, func, nresults); */
goto retry; /* try again with metamethod */ goto retry; /* try again with metamethod */
} }
@@ -792,6 +792,10 @@ static void resume (lua_State *L, void *ud) {
lua_assert(L->status == LUA_YIELD); lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */ L->status = LUA_OK; /* mark that it is running (again) */
if (isLua(ci)) { /* yielded inside a hook? */ if (isLua(ci)) { /* yielded inside a hook? */
/* undo increment made by 'luaG_traceexec': instruction was not
executed yet */
lua_assert(ci->callstatus & CIST_HOOKYIELD);
ci->u.l.savedpc--;
L->top.p = firstArg; /* discard arguments */ L->top.p = firstArg; /* discard arguments */
luaV_execute(L, ci); /* just continue running Lua code */ luaV_execute(L, ci); /* just continue running Lua code */
} }

1
ldo.h
View File

@@ -71,7 +71,6 @@ LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); 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_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
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_closeprotected (lua_State *L, ptrdiff_t level, int status);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef); ptrdiff_t oldtop, ptrdiff_t ef);

20
lgc.c
View File

@@ -542,10 +542,12 @@ static void traversestrongtable (global_State *g, Table *h) {
static lu_mem traversetable (global_State *g, Table *h) { static lu_mem traversetable (global_State *g, Table *h) {
const char *weakkey, *weakvalue; const char *weakkey, *weakvalue;
const TValue *mode = gfasttm(g, h->metatable, TM_MODE); const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
TString *smode;
markobjectN(g, h->metatable); markobjectN(g, h->metatable);
if (mode && ttisstring(mode) && /* is there a weak mode? */ if (mode && ttisshrstring(mode) && /* is there a weak mode? */
(cast_void(weakkey = strchr(svalue(mode), 'k')), (cast_void(smode = tsvalue(mode)),
cast_void(weakvalue = strchr(svalue(mode), 'v')), cast_void(weakkey = strchr(getshrstr(smode), 'k')),
cast_void(weakvalue = strchr(getshrstr(smode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */ (weakkey || weakvalue))) { /* is really weak? */
if (!weakkey) /* strong keys? */ if (!weakkey) /* strong keys? */
traverseweakvalue(g, h); traverseweakvalue(g, h);
@@ -638,7 +640,9 @@ static int traversethread (global_State *g, lua_State *th) {
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */ markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
for (; o < th->stack_last.p + EXTRA_STACK; o++) if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */
for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++)
setnilvalue(s2v(o)); /* clear dead stack slice */ setnilvalue(s2v(o)); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
@@ -646,8 +650,6 @@ static int traversethread (global_State *g, lua_State *th) {
g->twups = th; g->twups = th;
} }
} }
else if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */
return 1 + stacksize(th); return 1 + stacksize(th);
} }
@@ -1409,7 +1411,7 @@ static void stepgenfull (lua_State *L, global_State *g) {
setminordebt(g); setminordebt(g);
} }
else { /* another bad collection; stay in incremental mode */ else { /* another bad collection; stay in incremental mode */
g->GCestimate = gettotalbytes(g); /* first estimate */; g->GCestimate = gettotalbytes(g); /* first estimate */
entersweep(L); entersweep(L);
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
setpause(g); setpause(g);
@@ -1604,7 +1606,7 @@ static lu_mem singlestep (lua_State *L) {
case GCSenteratomic: { case GCSenteratomic: {
work = atomic(L); /* work is what was traversed by 'atomic' */ work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L); entersweep(L);
g->GCestimate = gettotalbytes(g); /* first estimate */; g->GCestimate = gettotalbytes(g); /* first estimate */
break; break;
} }
case GCSswpallgc: { /* sweep "regular" objects */ case GCSswpallgc: { /* sweep "regular" objects */
@@ -1710,6 +1712,8 @@ static void fullinc (lua_State *L, global_State *g) {
entersweep(L); /* sweep everything to turn them back to white */ entersweep(L); /* sweep everything to turn them back to white */
/* finish any pending sweep phase to start a new cycle */ /* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpause));
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
g->gcstate = GCSenteratomic; /* go straight to atomic phase */
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
/* estimate must be correct after a full GC cycle */ /* estimate must be correct after a full GC cycle */
lua_assert(g->GCestimate == gettotalbytes(g)); lua_assert(g->GCestimate == gettotalbytes(g));

View File

@@ -245,8 +245,8 @@ static int f_gc (lua_State *L) {
*/ */
static int io_fclose (lua_State *L) { static int io_fclose (lua_State *L) {
LStream *p = tolstream(L); LStream *p = tolstream(L);
int res = fclose(p->f); errno = 0;
return luaL_fileresult(L, (res == 0), NULL); return luaL_fileresult(L, (fclose(p->f) == 0), NULL);
} }
@@ -272,6 +272,7 @@ static int io_open (lua_State *L) {
LStream *p = newfile(L); LStream *p = newfile(L);
const char *md = mode; /* to traverse/check mode */ const char *md = mode; /* to traverse/check mode */
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
errno = 0;
p->f = fopen(filename, mode); p->f = fopen(filename, mode);
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
} }
@@ -292,6 +293,7 @@ static int io_popen (lua_State *L) {
const char *mode = luaL_optstring(L, 2, "r"); const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L); LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
errno = 0;
p->f = l_popen(L, filename, mode); p->f = l_popen(L, filename, mode);
p->closef = &io_pclose; p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
@@ -300,6 +302,7 @@ static int io_popen (lua_State *L) {
static int io_tmpfile (lua_State *L) { static int io_tmpfile (lua_State *L) {
LStream *p = newfile(L); LStream *p = newfile(L);
errno = 0;
p->f = tmpfile(); p->f = tmpfile();
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
} }
@@ -567,6 +570,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1; int nargs = lua_gettop(L) - 1;
int n, success; int n, success;
clearerr(f); clearerr(f);
errno = 0;
if (nargs == 0) { /* no arguments? */ if (nargs == 0) { /* no arguments? */
success = read_line(L, f, 1); success = read_line(L, f, 1);
n = first + 1; /* to return 1 result */ n = first + 1; /* to return 1 result */
@@ -660,6 +664,7 @@ static int io_readline (lua_State *L) {
static int g_write (lua_State *L, FILE *f, int arg) { static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - arg; int nargs = lua_gettop(L) - arg;
int status = 1; int status = 1;
errno = 0;
for (; nargs--; arg++) { for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) { if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */ /* optimization: could be done exactly as for strings */
@@ -678,7 +683,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
} }
if (l_likely(status)) if (l_likely(status))
return 1; /* file handle already on stack top */ return 1; /* file handle already on stack top */
else return luaL_fileresult(L, status, NULL); else
return luaL_fileresult(L, status, NULL);
} }
@@ -703,6 +709,7 @@ static int f_seek (lua_State *L) {
l_seeknum offset = (l_seeknum)p3; l_seeknum offset = (l_seeknum)p3;
luaL_argcheck(L, (lua_Integer)offset == p3, 3, luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range"); "not an integer in proper range");
errno = 0;
op = l_fseek(f, offset, mode[op]); op = l_fseek(f, offset, mode[op]);
if (l_unlikely(op)) if (l_unlikely(op))
return luaL_fileresult(L, 0, NULL); /* error */ return luaL_fileresult(L, 0, NULL); /* error */
@@ -719,19 +726,25 @@ static int f_setvbuf (lua_State *L) {
FILE *f = tofile(L); FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames); int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], (size_t)sz); int res;
errno = 0;
res = setvbuf(f, NULL, mode[op], (size_t)sz);
return luaL_fileresult(L, res == 0, NULL); return luaL_fileresult(L, res == 0, NULL);
} }
static int io_flush (lua_State *L) { static int io_flush (lua_State *L) {
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); FILE *f = getiofile(L, IO_OUTPUT);
errno = 0;
return luaL_fileresult(L, fflush(f) == 0, NULL);
} }
static int f_flush (lua_State *L) { static int f_flush (lua_State *L) {
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); FILE *f = tofile(L);
errno = 0;
return luaL_fileresult(L, fflush(f) == 0, NULL);
} }
@@ -773,7 +786,7 @@ static const luaL_Reg meth[] = {
** metamethods for file handles ** metamethods for file handles
*/ */
static const luaL_Reg metameth[] = { static const luaL_Reg metameth[] = {
{"__index", NULL}, /* place holder */ {"__index", NULL}, /* placeholder */
{"__gc", f_gc}, {"__gc", f_gc},
{"__close", f_gc}, {"__close", f_gc},
{"__tostring", f_tostring}, {"__tostring", f_tostring},

View File

@@ -249,6 +249,15 @@ static int math_type (lua_State *L) {
** =================================================================== ** ===================================================================
*/ */
/*
** This code uses lots of shifts. ANSI C does not allow shifts greater
** than or equal to the width of the type being shifted, so some shifts
** are written in convoluted ways to match that restriction. For
** preprocessor tests, it assumes a width of 32 bits, so the maximum
** shift there is 31 bits.
*/
/* number of binary digits in the mantissa of a float */ /* number of binary digits in the mantissa of a float */
#define FIGS l_floatatt(MANT_DIG) #define FIGS l_floatatt(MANT_DIG)
@@ -271,16 +280,19 @@ static int math_type (lua_State *L) {
/* 'long' has at least 64 bits */ /* 'long' has at least 64 bits */
#define Rand64 unsigned long #define Rand64 unsigned long
#define SRand64 long
#elif !defined(LUA_USE_C89) && defined(LLONG_MAX) #elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
/* there is a 'long long' type (which must have at least 64 bits) */ /* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long #define Rand64 unsigned long long
#define SRand64 long long
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 #elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
/* 'lua_Unsigned' has at least 64 bits */ /* 'lua_Unsigned' has at least 64 bits */
#define Rand64 lua_Unsigned #define Rand64 lua_Unsigned
#define SRand64 lua_Integer
#endif #endif
@@ -319,23 +331,30 @@ static Rand64 nextrand (Rand64 *state) {
} }
/* must take care to not shift stuff by more than 63 slots */
/* /*
** Convert bits from a random integer into a float in the ** Convert bits from a random integer into a float in the
** interval [0,1), getting the higher FIG bits from the ** interval [0,1), getting the higher FIG bits from the
** random unsigned integer and converting that to a float. ** random unsigned integer and converting that to a float.
** Some old Microsoft compilers cannot cast an unsigned long
** to a floating-point number, so we use a signed long as an
** intermediary. When lua_Number is float or double, the shift ensures
** that 'sx' is non negative; in that case, a good compiler will remove
** the correction.
*/ */
/* must throw out the extra (64 - FIGS) bits */ /* must throw out the extra (64 - FIGS) bits */
#define shift64_FIG (64 - FIGS) #define shift64_FIG (64 - FIGS)
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */ /* 2^(-FIGS) == 2^-1 / 2^(FIGS-1) */
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) #define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
static lua_Number I2d (Rand64 x) { static lua_Number I2d (Rand64 x) {
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG; SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG);
lua_Number res = (lua_Number)(sx) * scaleFIG;
if (sx < 0)
res += l_mathop(1.0); /* correct the two's complement if negative */
lua_assert(0 <= res && res < 1);
return res;
} }
/* convert a 'Rand64' to a 'lua_Unsigned' */ /* convert a 'Rand64' to a 'lua_Unsigned' */
@@ -471,8 +490,6 @@ static lua_Number I2d (Rand64 x) {
#else /* 32 < FIGS <= 64 */ #else /* 32 < FIGS <= 64 */
/* must take care to not shift stuff by more than 31 slots */
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ /* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
#define scaleFIG \ #define scaleFIG \
(l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))

View File

@@ -24,15 +24,6 @@
#include "lualib.h" #include "lualib.h"
/*
** LUA_IGMARK is a mark to ignore all before it when building the
** luaopen_ function name.
*/
#if !defined (LUA_IGMARK)
#define LUA_IGMARK "-"
#endif
/* /*
** LUA_CSUBSEP is the character that replaces dots in submodule names ** LUA_CSUBSEP is the character that replaces dots in submodule names
** when searching for a C loader. ** when searching for a C loader.

View File

@@ -542,7 +542,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */ clearbuff(&buff); /* empty buffer into the stack */
lua_assert(buff.pushed == 1); lua_assert(buff.pushed == 1);
return svalue(s2v(L->top.p - 1)); return getstr(tsvalue(s2v(L->top.p - 1)));
} }

View File

@@ -386,7 +386,7 @@ typedef struct GCObject {
typedef struct TString { typedef struct TString {
CommonHeader; CommonHeader;
lu_byte extra; /* reserved words for short strings; "has hash" for longs */ lu_byte extra; /* reserved words for short strings; "has hash" for longs */
lu_byte shrlen; /* length for short strings */ lu_byte shrlen; /* length for short strings, 0xFF for long strings */
unsigned int hash; unsigned int hash;
union { union {
size_t lnglen; /* length for long strings */ size_t lnglen; /* length for long strings */
@@ -398,19 +398,17 @@ typedef struct TString {
/* /*
** Get the actual string (array of bytes) from a 'TString'. ** Get the actual string (array of bytes) from a 'TString'. (Generic
** version and specialized versions for long and short strings.)
*/ */
#define getstr(ts) ((ts)->contents) #define getstr(ts) ((ts)->contents)
#define getlngstr(ts) check_exp((ts)->shrlen == 0xFF, (ts)->contents)
#define getshrstr(ts) check_exp((ts)->shrlen != 0xFF, (ts)->contents)
/* get the actual string (array of bytes) from a Lua value */
#define svalue(o) getstr(tsvalue(o))
/* get string length from 'TString *s' */ /* get string length from 'TString *s' */
#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen) #define tsslen(s) \
((s)->shrlen != 0xFF ? (s)->shrlen : (s)->u.lnglen)
/* get string length from 'TValue *o' */
#define vslen(o) tsslen(tsvalue(o))
/* }================================================================== */ /* }================================================================== */

View File

@@ -210,15 +210,15 @@ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */ OP_GETUPVAL,/* A B R[A] := UpValue[B] */
OP_SETUPVAL,/* A B UpValue[B] := R[A] */ OP_SETUPVAL,/* A B UpValue[B] := R[A] */
OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */ OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:shortstring] */
OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */ OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
OP_GETI,/* A B C R[A] := R[B][C] */ OP_GETI,/* A B C R[A] := R[B][C] */
OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */ OP_GETFIELD,/* A B C R[A] := R[B][K[C]:shortstring] */
OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */ OP_SETTABUP,/* A B C UpValue[A][K[B]:shortstring] := RK(C) */
OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */ OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */
OP_SETI,/* A B C R[A][B] := RK(C) */ OP_SETI,/* A B C R[A][B] := RK(C) */
OP_SETFIELD,/* A B C R[A][K[B]:string] := RK(C) */ OP_SETFIELD,/* A B C R[A][K[B]:shortstring] := RK(C) */
OP_NEWTABLE,/* A B C k R[A] := {} */ OP_NEWTABLE,/* A B C k R[A] := {} */

View File

@@ -155,6 +155,7 @@ static int os_execute (lua_State *L) {
static int os_remove (lua_State *L) { static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
errno = 0;
return luaL_fileresult(L, remove(filename) == 0, filename); return luaL_fileresult(L, remove(filename) == 0, filename);
} }
@@ -162,6 +163,7 @@ static int os_remove (lua_State *L) {
static int os_rename (lua_State *L) { static int os_rename (lua_State *L) {
const char *fromname = luaL_checkstring(L, 1); const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2); const char *toname = luaL_checkstring(L, 2);
errno = 0;
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
} }

View File

@@ -1022,10 +1022,11 @@ static int explist (LexState *ls, expdesc *v) {
} }
static void funcargs (LexState *ls, expdesc *f, int line) { static void funcargs (LexState *ls, expdesc *f) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
expdesc args; expdesc args;
int base, nparams; int base, nparams;
int line = ls->linenumber;
switch (ls->t.token) { switch (ls->t.token) {
case '(': { /* funcargs -> '(' [ explist ] ')' */ case '(': { /* funcargs -> '(' [ explist ] ')' */
luaX_next(ls); luaX_next(ls);
@@ -1063,8 +1064,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
} }
init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
luaK_fixline(fs, line); luaK_fixline(fs, line);
fs->freereg = base+1; /* call remove function and arguments and leaves fs->freereg = base+1; /* call removes function and arguments and leaves
(unless changed) one result */ one result (unless changed later) */
} }
@@ -1103,7 +1104,6 @@ static void suffixedexp (LexState *ls, expdesc *v) {
/* suffixedexp -> /* suffixedexp ->
primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int line = ls->linenumber;
primaryexp(ls, v); primaryexp(ls, v);
for (;;) { for (;;) {
switch (ls->t.token) { switch (ls->t.token) {
@@ -1123,12 +1123,12 @@ static void suffixedexp (LexState *ls, expdesc *v) {
luaX_next(ls); luaX_next(ls);
codename(ls, &key); codename(ls, &key);
luaK_self(fs, v, &key); luaK_self(fs, v, &key);
funcargs(ls, v, line); funcargs(ls, v);
break; break;
} }
case '(': case TK_STRING: case '{': { /* funcargs */ case '(': case TK_STRING: case '{': { /* funcargs */
luaK_exp2nextreg(fs, v); luaK_exp2nextreg(fs, v);
funcargs(ls, v, line); funcargs(ls, v);
break; break;
} }
default: return; default: return;

View File

@@ -119,7 +119,7 @@ CallInfo *luaE_extendCI (lua_State *L) {
/* /*
** free all CallInfo structures not in use by a thread ** free all CallInfo structures not in use by a thread
*/ */
void luaE_freeCI (lua_State *L) { static void freeCI (lua_State *L) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
CallInfo *next = ci->next; CallInfo *next = ci->next;
ci->next = NULL; ci->next = NULL;
@@ -204,7 +204,7 @@ static void freestack (lua_State *L) {
if (L->stack.p == NULL) if (L->stack.p == NULL)
return; /* stack not completely built yet */ return; /* stack not completely built yet */
L->ci = &L->base_ci; /* free the entire 'ci' list */ L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L); freeCI(L);
lua_assert(L->nci == 0); lua_assert(L->nci == 0);
luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
} }
@@ -433,7 +433,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
void luaE_warnerror (lua_State *L, const char *where) { void luaE_warnerror (lua_State *L, const char *where) {
TValue *errobj = s2v(L->top.p - 1); /* error object */ TValue *errobj = s2v(L->top.p - 1); /* error object */
const char *msg = (ttisstring(errobj)) const char *msg = (ttisstring(errobj))
? svalue(errobj) ? getstr(tsvalue(errobj))
: "error object is not a string"; : "error object is not a string";
/* produce warning "error in %s (%s)" (where, msg) */ /* produce warning "error in %s (%s)" (where, msg) */
luaE_warning(L, "error in ", 1); luaE_warning(L, "error in ", 1);

View File

@@ -181,7 +181,7 @@ struct CallInfo {
union { union {
struct { /* only for Lua functions */ struct { /* only for Lua functions */
const Instruction *savedpc; const Instruction *savedpc;
volatile l_signalT trap; volatile l_signalT trap; /* function is tracing lines/counts */
int nextraargs; /* # of extra arguments in vararg functions */ int nextraargs; /* # of extra arguments in vararg functions */
} l; } l;
struct { /* only for C functions */ struct { /* only for C functions */
@@ -396,7 +396,6 @@ union GCUnion {
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L);

View File

@@ -36,7 +36,7 @@ int luaS_eqlngstr (TString *a, TString *b) {
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
return (a == b) || /* same instance or... */ return (a == b) || /* same instance or... */
((len == b->u.lnglen) && /* equal length and ... */ ((len == b->u.lnglen) && /* equal length and ... */
(memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */
} }
@@ -52,7 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
lua_assert(ts->tt == LUA_VLNGSTR); lua_assert(ts->tt == LUA_VLNGSTR);
if (ts->extra == 0) { /* no hash? */ if (ts->extra == 0) { /* no hash? */
size_t len = ts->u.lnglen; size_t len = ts->u.lnglen;
ts->hash = luaS_hash(getstr(ts), len, ts->hash); ts->hash = luaS_hash(getlngstr(ts), len, ts->hash);
ts->extra = 1; /* now it has its hash */ ts->extra = 1; /* now it has its hash */
} }
return ts->hash; return ts->hash;
@@ -157,6 +157,7 @@ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
TString *luaS_createlngstrobj (lua_State *L, size_t l) { TString *luaS_createlngstrobj (lua_State *L, size_t l) {
TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
ts->u.lnglen = l; ts->u.lnglen = l;
ts->shrlen = 0xFF; /* signals that it is a long string */
return ts; return ts;
} }
@@ -193,7 +194,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
TString **list = &tb->hash[lmod(h, tb->size)]; TString **list = &tb->hash[lmod(h, tb->size)];
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
for (ts = *list; ts != NULL; ts = ts->u.hnext) { for (ts = *list; ts != NULL; ts = ts->u.hnext) {
if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { if (l == ts->shrlen && (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) {
/* found! */ /* found! */
if (isdead(g, ts)) /* dead (but not collected yet)? */ if (isdead(g, ts)) /* dead (but not collected yet)? */
changewhite(ts); /* resurrect it */ changewhite(ts); /* resurrect it */
@@ -206,8 +207,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
} }
ts = createstrobj(L, l, LUA_VSHRSTR, h); ts = createstrobj(L, l, LUA_VSHRSTR, h);
memcpy(getstr(ts), str, l * sizeof(char));
ts->shrlen = cast_byte(l); ts->shrlen = cast_byte(l);
memcpy(getshrstr(ts), str, l * sizeof(char));
ts->u.hnext = *list; ts->u.hnext = *list;
*list = ts; *list = ts;
tb->nuse++; tb->nuse++;
@@ -223,10 +224,10 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return internshrstr(L, str, l); return internshrstr(L, str, l);
else { else {
TString *ts; TString *ts;
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) if (l_unlikely(l * sizeof(char) >= (MAX_SIZE - sizeof(TString))))
luaM_toobig(L); luaM_toobig(L);
ts = luaS_createlngstrobj(L, l); ts = luaS_createlngstrobj(L, l);
memcpy(getstr(ts), str, l * sizeof(char)); memcpy(getlngstr(ts), str, l * sizeof(char));
return ts; return ts;
} }
} }

View File

@@ -252,7 +252,7 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
return t->alimit; /* this is the size */ return t->alimit; /* this is the size */
else { else {
unsigned int size = t->alimit; unsigned int size = t->alimit;
/* compute the smallest power of 2 not smaller than 'n' */ /* compute the smallest power of 2 not smaller than 'size' */
size |= (size >> 1); size |= (size >> 1);
size |= (size >> 2); size |= (size >> 2);
size |= (size >> 4); size |= (size >> 4);
@@ -662,7 +662,8 @@ static Node *getfreepos (Table *t) {
** put new key in its main position; otherwise (colliding node is in its main ** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position. ** position), new key goes to an empty position.
*/ */
void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { static void luaH_newkey (lua_State *L, Table *t, const TValue *key,
TValue *value) {
Node *mp; Node *mp;
TValue aux; TValue aux;
if (l_unlikely(ttisnil(key))) if (l_unlikely(ttisnil(key)))
@@ -721,22 +722,36 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
/* /*
** Search function for integers. If integer is inside 'alimit', get it ** Search function for integers. If integer is inside 'alimit', get it
** directly from the array part. Otherwise, if 'alimit' is not equal to ** directly from the array part. Otherwise, if 'alimit' is not
** the real size of the array, key still can be in the array part. In ** the real size of the array, the key still can be in the array part.
** this case, try to avoid a call to 'luaH_realasize' when key is just ** In this case, do the "Xmilia trick" to check whether 'key-1' is
** one more than the limit (so that it can be incremented without ** smaller than the real size.
** changing the real size of the array). ** The trick works as follow: let 'p' be an integer such that
** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'.
** That is, 2^(p+1) is the real size of the array, and 'p' is the highest
** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'.
** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will
** have the 'p' bit cleared. If the key is outside the array, that is,
** 'key-1 >= 2^(p+1)', then 'res' will have some bit on higher than 'p',
** therefore it will be larger or equal to 'alimit', and the check
** will fail. If 'key-1 < 2^(p+1)', then 'res' has no bit on higher than
** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller
** than 2^p, therefore smaller than 'alimit', and the check succeeds.
** As special cases, when 'alimit' is 0 the condition is trivially false,
** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'.
** If key is 0 or negative, 'res' will have its higher bit on, so that
** if cannot be smaller than alimit.
*/ */
const TValue *luaH_getint (Table *t, lua_Integer key) { const TValue *luaH_getint (Table *t, lua_Integer key) {
if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */ lua_Unsigned alimit = t->alimit;
if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */
return &t->array[key - 1]; return &t->array[key - 1];
else if (!limitequalsasize(t) && /* key still may be in the array part? */ else if (!isrealasize(t) && /* key still may be in the array part? */
(l_castS2U(key) == t->alimit + 1 || (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) {
l_castS2U(key) - 1u < luaH_realasize(t))) {
t->alimit = cast_uint(key); /* probably '#t' is here now */ t->alimit = cast_uint(key); /* probably '#t' is here now */
return &t->array[key - 1]; return &t->array[key - 1];
} }
else { else { /* key is not in the array part; check the hash */
Node *n = hashint(t, key); Node *n = hashint(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
if (keyisinteger(n) && keyival(n) == key) if (keyisinteger(n) && keyival(n) == key)

View File

@@ -41,8 +41,6 @@ LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
TValue *value); TValue *value);
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,

View File

@@ -73,8 +73,9 @@ static void badexit (const char *fmt, const char *s1, const char *s2) {
static int tpanic (lua_State *L) { static int tpanic (lua_State *L) {
const char *msg = lua_tostring(L, -1); const char *msg = (lua_type(L, -1) == LUA_TSTRING)
if (msg == NULL) msg = "error object is not a string"; ? lua_tostring(L, -1)
: "error object is not a string";
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n", return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
msg, NULL), msg, NULL),
0); /* do not return to Lua */ 0); /* do not return to Lua */
@@ -1649,6 +1650,11 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
int nres; int nres;
status = lua_resume(lua_tothread(L1, i), L, getnum, &nres); status = lua_resume(lua_tothread(L1, i), L, getnum, &nres);
} }
else if EQ("traceback") {
const char *msg = getstring;
int level = getnum;
luaL_traceback(L1, L1, msg, level);
}
else if EQ("return") { else if EQ("return") {
int n = getnum; int n = getnum;
if (L1 != L) { if (L1 != L) {

5
ltm.h
View File

@@ -9,7 +9,6 @@
#include "lobject.h" #include "lobject.h"
#include "lstate.h"
/* /*
@@ -96,8 +95,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event); int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
CallInfo *ci, const Proto *p); struct CallInfo *ci, const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci, LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
StkId where, int wanted); StkId where, int wanted);

17
lua.c
View File

@@ -115,12 +115,13 @@ static void l_message (const char *pname, const char *msg) {
/* /*
** Check whether 'status' is not OK and, if so, prints the error ** Check whether 'status' is not OK and, if so, prints the error
** message on the top of the stack. It assumes that the error object ** message on the top of the stack.
** is a string, as it was either generated by Lua or by 'msghandler'.
*/ */
static int report (lua_State *L, int status) { static int report (lua_State *L, int status) {
if (status != LUA_OK) { if (status != LUA_OK) {
const char *msg = lua_tostring(L, -1); const char *msg = lua_tostring(L, -1);
if (msg == NULL)
msg = "(error message not a string)";
l_message(progname, msg); l_message(progname, msg);
lua_pop(L, 1); /* remove message */ lua_pop(L, 1); /* remove message */
} }
@@ -210,12 +211,17 @@ static int dostring (lua_State *L, const char *s, const char *name) {
/* /*
** Receives 'globname[=modname]' and runs 'globname = require(modname)'. ** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
** If there is no explicit modname and globname contains a '-', cut
** the suffix after '-' (the "version") to make the global name.
*/ */
static int dolibrary (lua_State *L, char *globname) { static int dolibrary (lua_State *L, char *globname) {
int status; int status;
char *suffix = NULL;
char *modname = strchr(globname, '='); char *modname = strchr(globname, '=');
if (modname == NULL) /* no explicit name? */ if (modname == NULL) { /* no explicit name? */
modname = globname; /* module name is equal to global name */ modname = globname; /* module name is equal to global name */
suffix = strchr(modname, *LUA_IGMARK); /* look for a suffix mark */
}
else { else {
*modname = '\0'; /* global name ends here */ *modname = '\0'; /* global name ends here */
modname++; /* module name starts after the '=' */ modname++; /* module name starts after the '=' */
@@ -223,8 +229,11 @@ static int dolibrary (lua_State *L, char *globname) {
lua_getglobal(L, "require"); lua_getglobal(L, "require");
lua_pushstring(L, modname); lua_pushstring(L, modname);
status = docall(L, 1, 1); /* call 'require(modname)' */ status = docall(L, 1, 1); /* call 'require(modname)' */
if (status == LUA_OK) if (status == LUA_OK) {
if (suffix != NULL) /* is there a suffix mark? */
*suffix = '\0'; /* remove suffix from global name */
lua_setglobal(L, globname); /* globname = require(modname) */ lua_setglobal(L, globname); /* globname = require(modname) */
}
return report(L, status); return report(L, status);
} }

8
lua.h
View File

@@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4" #define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "6" #define LUA_VERSION_RELEASE "7"
#define LUA_VERSION_NUM 504 #define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 7)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -497,7 +497,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2023 Lua.org, PUC-Rio. * Copyright (C) 1994-2024 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the

View File

@@ -257,6 +257,15 @@
#endif #endif
/*
** LUA_IGMARK is a mark to ignore all after it when building the
** module name (e.g., used to build the luaopen_ function name).
** Typically, the suffix after the mark is the module version,
** as in "mod-v1.2.so".
*/
#define LUA_IGMARK "-"
/* }================================================================== */ /* }================================================================== */

View File

@@ -81,7 +81,7 @@ static size_t loadUnsigned (LoadState *S, size_t limit) {
static size_t loadSize (LoadState *S) { static size_t loadSize (LoadState *S) {
return loadUnsigned(S, ~(size_t)0); return loadUnsigned(S, MAX_SIZET);
} }
@@ -122,7 +122,7 @@ static TString *loadStringN (LoadState *S, Proto *p) {
ts = luaS_createlngstrobj(L, size); /* create string */ ts = luaS_createlngstrobj(L, size); /* create string */
setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L); luaD_inctop(L);
loadVector(S, getstr(ts), size); /* load directly in final place */ loadVector(S, getlngstr(ts), size); /* load directly in final place */
L->top.p--; /* pop string */ L->top.p--; /* pop string */
} }
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);

View File

@@ -21,8 +21,7 @@
/* /*
** Encode major-minor version in one byte, one nibble for each ** Encode major-minor version in one byte, one nibble for each
*/ */
#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */ #define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100)
#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
#define LUAC_FORMAT 0 /* this is the official format */ #define LUAC_FORMAT 0 /* this is the official format */

78
lvm.c
View File

@@ -91,8 +91,10 @@ static int l_strton (const TValue *obj, TValue *result) {
lua_assert(obj != result); lua_assert(obj != result);
if (!cvt2num(obj)) /* is object not a string? */ if (!cvt2num(obj)) /* is object not a string? */
return 0; return 0;
else else {
return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1); TString *st = tsvalue(obj);
return (luaO_str2num(getstr(st), result) == tsslen(st) + 1);
}
} }
@@ -366,30 +368,32 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
/* /*
** Compare two strings 'ls' x 'rs', returning an integer less-equal- ** Compare two strings 'ts1' x 'ts2', returning an integer less-equal-
** -greater than zero if 'ls' is less-equal-greater than 'rs'. ** -greater than zero if 'ts1' is less-equal-greater than 'ts2'.
** The code is a little tricky because it allows '\0' in the strings ** The code is a little tricky because it allows '\0' in the strings
** and it uses 'strcoll' (to respect locales) for each segments ** and it uses 'strcoll' (to respect locales) for each segment
** of the strings. ** of the strings. Note that segments can compare equal but still
** have different lengths.
*/ */
static int l_strcmp (const TString *ls, const TString *rs) { static int l_strcmp (const TString *ts1, const TString *ts2) {
const char *l = getstr(ls); const char *s1 = getstr(ts1);
size_t ll = tsslen(ls); size_t rl1 = tsslen(ts1); /* real length */
const char *r = getstr(rs); const char *s2 = getstr(ts2);
size_t lr = tsslen(rs); size_t rl2 = tsslen(ts2);
for (;;) { /* for each segment */ for (;;) { /* for each segment */
int temp = strcoll(l, r); int temp = strcoll(s1, s2);
if (temp != 0) /* not equal? */ if (temp != 0) /* not equal? */
return temp; /* done */ return temp; /* done */
else { /* strings are equal up to a '\0' */ else { /* strings are equal up to a '\0' */
size_t len = strlen(l); /* index of first '\0' in both strings */ size_t zl1 = strlen(s1); /* index of first '\0' in 's1' */
if (len == lr) /* 'rs' is finished? */ size_t zl2 = strlen(s2); /* index of first '\0' in 's2' */
return (len == ll) ? 0 : 1; /* check 'ls' */ if (zl2 == rl2) /* 's2' is finished? */
else if (len == ll) /* 'ls' is finished? */ return (zl1 == rl1) ? 0 : 1; /* check 's1' */
return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */ else if (zl1 == rl1) /* 's1' is finished? */
/* both strings longer than 'len'; go on comparing after the '\0' */ return -1; /* 's1' is less than 's2' ('s2' is not finished) */
len++; /* both strings longer than 'zl'; go on comparing after the '\0' */
l += len; ll -= len; r += len; lr -= len; zl1++; zl2++;
s1 += zl1; rl1 -= zl1; s2 += zl2; rl2 -= zl2;
} }
} }
} }
@@ -624,8 +628,9 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
static void copy2buff (StkId top, int n, char *buff) { static void copy2buff (StkId top, int n, char *buff) {
size_t tl = 0; /* size already copied */ size_t tl = 0; /* size already copied */
do { do {
size_t l = vslen(s2v(top - n)); /* length of string being copied */ TString *st = tsvalue(s2v(top - n));
memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char)); size_t l = tsslen(st); /* length of string being copied */
memcpy(buff + tl, getstr(st), l * sizeof(char));
tl += l; tl += l;
} while (--n > 0); } while (--n > 0);
} }
@@ -651,12 +656,12 @@ void luaV_concat (lua_State *L, int total) {
} }
else { else {
/* at least two non-empty string values; get as many as possible */ /* at least two non-empty string values; get as many as possible */
size_t tl = vslen(s2v(top - 1)); size_t tl = tsslen(tsvalue(s2v(top - 1)));
TString *ts; TString *ts;
/* collect total length and number of strings */ /* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
size_t l = vslen(s2v(top - n - 1)); size_t l = tsslen(tsvalue(s2v(top - n - 1)));
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) { if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) {
L->top.p = top - total; /* pop strings to avoid wasting stack */ L->top.p = top - total; /* pop strings to avoid wasting stack */
luaG_runerror(L, "string length overflow"); luaG_runerror(L, "string length overflow");
} }
@@ -669,7 +674,7 @@ void luaV_concat (lua_State *L, int total) {
} }
else { /* long string; copy strings directly to final result */ else { /* long string; copy strings directly to final result */
ts = luaS_createlngstrobj(L, tl); ts = luaS_createlngstrobj(L, tl);
copy2buff(top, n, getstr(ts)); copy2buff(top, n, getlngstr(ts));
} }
setsvalue2s(L, top - n, ts); /* create result */ setsvalue2s(L, top - n, ts); /* create result */
} }
@@ -1155,18 +1160,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
startfunc: startfunc:
trap = L->hookmask; trap = L->hookmask;
returning: /* trap already set */ returning: /* trap already set */
cl = clLvalue(s2v(ci->func.p)); cl = ci_func(ci);
k = cl->p->k; k = cl->p->k;
pc = ci->u.l.savedpc; pc = ci->u.l.savedpc;
if (l_unlikely(trap)) { if (l_unlikely(trap))
if (pc == cl->p->code) { /* first instruction (not resuming)? */ trap = luaG_tracecall(L);
if (cl->p->is_vararg)
trap = 0; /* hooks will start after VARARGPREP instruction */
else /* check 'call' hook */
luaD_hookcall(L, ci);
}
ci->u.l.trap = 1; /* assume trap is on, for now */
}
base = ci->func.p + 1; base = ci->func.p + 1;
/* main loop of interpreter */ /* main loop of interpreter */
for (;;) { for (;;) {
@@ -1253,7 +1251,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot; const TValue *slot;
TValue *upval = cl->upvals[GETARG_B(i)]->v.p; TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
TValue *rc = KC(i); TValue *rc = KC(i);
TString *key = tsvalue(rc); /* key must be a string */ TString *key = tsvalue(rc); /* key must be a short string */
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
setobj2s(L, ra, slot); setobj2s(L, ra, slot);
} }
@@ -1296,7 +1294,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); TValue *rb = vRB(i);
TValue *rc = KC(i); TValue *rc = KC(i);
TString *key = tsvalue(rc); /* key must be a string */ TString *key = tsvalue(rc); /* key must be a short string */
if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) {
setobj2s(L, ra, slot); setobj2s(L, ra, slot);
} }
@@ -1309,7 +1307,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
TValue *upval = cl->upvals[GETARG_A(i)]->v.p; TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
TValue *rb = KB(i); TValue *rb = KB(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
TString *key = tsvalue(rb); /* key must be a string */ TString *key = tsvalue(rb); /* key must be a short string */
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
luaV_finishfastset(L, upval, slot, rc); luaV_finishfastset(L, upval, slot, rc);
} }
@@ -1352,7 +1350,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot; const TValue *slot;
TValue *rb = KB(i); TValue *rb = KB(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
TString *key = tsvalue(rb); /* key must be a string */ TString *key = tsvalue(rb); /* key must be a short string */
if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) {
luaV_finishfastset(L, s2v(ra), slot, rc); luaV_finishfastset(L, s2v(ra), slot, rc);
} }

View File

@@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<p> <p>
<small> <small>
<a href="http://www.lua.org/copyright.html">Copyright</a> <a href="http://www.lua.org/copyright.html">Copyright</a>
&copy; 2023 Lua.org, PUC-Rio. All rights reserved. &copy; 2024 Lua.org, PUC-Rio. All rights reserved.
</small> </small>
<hr> <hr>

View File

@@ -289,7 +289,7 @@ Whenever there is an error,
an @def{error object} an @def{error object}
is propagated with information about the error. is propagated with information about the error.
Lua itself only generates errors whose error object is a string, Lua itself only generates errors whose error object is a string,
but programs may generate errors with but programs can generate errors with
any value as the error object. any value as the error object.
It is up to the Lua program or its host to handle such error objects. It is up to the Lua program or its host to handle such error objects.
For historical reasons, For historical reasons,
@@ -298,7 +298,7 @@ even though it does not have to be a string.
When you use @Lid{xpcall} (or @Lid{lua_pcall}, in C) When you use @Lid{xpcall} (or @Lid{lua_pcall}, in C)
you may give a @def{message handler} you can give a @def{message handler}
to be called in case of errors. to be called in case of errors.
This function is called with the original error object This function is called with the original error object
and returns a new error object. and returns a new error object.
@@ -343,7 +343,7 @@ which is then called a @def{metamethod}.
In the previous example, the key is the string @St{__add} In the previous example, the key is the string @St{__add}
and the metamethod is the function that performs the addition. and the metamethod is the function that performs the addition.
Unless stated otherwise, Unless stated otherwise,
a metamethod may in fact be any @x{callable value}, a metamethod can in fact be any @x{callable value},
which is either a function or a value with a @idx{__call} metamethod. which is either a function or a value with a @idx{__call} metamethod.
You can query the metatable of any value You can query the metatable of any value
@@ -1417,7 +1417,7 @@ labels in Lua are considered statements too:
A label is visible in the entire block where it is defined, A label is visible in the entire block where it is defined,
except inside nested functions. except inside nested functions.
A goto may jump to any visible label as long as it does not A goto can jump to any visible label as long as it does not
enter into the scope of a local variable. enter into the scope of a local variable.
A label should not be declared A label should not be declared
where a label with the same name is visible, where a label with the same name is visible,
@@ -4416,7 +4416,7 @@ otherwise, returns @id{NULL}.
} }
@APIEntry{void lua_toclose (lua_State *L, int index);| @APIEntry{void lua_toclose (lua_State *L, int index);|
@apii{0,0,m} @apii{0,0,v}
Marks the given index in the stack as a Marks the given index in the stack as a
to-be-closed slot @see{to-be-closed}. to-be-closed slot @see{to-be-closed}.
@@ -4433,6 +4433,9 @@ A slot marked as to-be-closed should not be removed from the stack
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}, by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop},
unless previously deactivated by @Lid{lua_closeslot}. unless previously deactivated by @Lid{lua_closeslot}.
This function raises an error if the value at the given slot
neither has a @idx{__close} metamethod nor is a false value.
This function should not be called for an index This function should not be called for an index
that is equal to or below an active to-be-closed slot. that is equal to or below an active to-be-closed slot.
@@ -4486,6 +4489,10 @@ This string always has a zero (@Char{\0})
after its last character (as @N{in C}), after its last character (as @N{in C}),
but can contain other zeros in its body. but can contain other zeros in its body.
This function can raise memory errors only
when converting a number to a string
(as then it may create a new string).
} }
@APIEntry{lua_Number lua_tonumber (lua_State *L, int index);| @APIEntry{lua_Number lua_tonumber (lua_State *L, int index);|
@@ -8769,13 +8776,13 @@ The returned table can contain all the fields returned by @Lid{lua_getinfo},
with the string @id{what} describing which fields to fill in. with the string @id{what} describing which fields to fill in.
The default for @id{what} is to get all information available, The default for @id{what} is to get all information available,
except the table of valid lines. except the table of valid lines.
If present, The option @Char{f}
the option @Char{f}
adds a field named @id{func} with the function itself. adds a field named @id{func} with the function itself.
If present, The option @Char{L} adds a field named @id{activelines}
the option @Char{L} with the table of valid lines,
adds a field named @id{activelines} with the table of provided the function is a Lua function.
valid lines. If the function has no debug information,
the table is empty.
For instance, the expression @T{debug.getinfo(1,"n").name} returns For instance, the expression @T{debug.getinfo(1,"n").name} returns
a name for the current function, a name for the current function,
@@ -9026,6 +9033,10 @@ Lua does not consult any environment variables.
In particular, In particular,
the values of @Lid{package.path} and @Lid{package.cpath} the values of @Lid{package.path} and @Lid{package.cpath}
are set with the default paths defined in @id{luaconf.h}. are set with the default paths defined in @id{luaconf.h}.
To signal to the libraries that this option is on,
the stand-alone interpreter sets the field
@idx{"LUA_NOENV"} in the registry to a true value.
Other libraries may consult this field for the same purpose.
The options @T{-e}, @T{-l}, and @T{-W} are handled in The options @T{-e}, @T{-l}, and @T{-W} are handled in
the order they appear. the order they appear.

View File

@@ -342,20 +342,6 @@ do -- another bug (in 5.4.0)
end end
do -- another bug (since 5.2)
-- corrupted binary dump: list of upvalue names is larger than number
-- of upvalues, overflowing the array of upvalues.
local code =
"\x1b\x4c\x75\x61\x54\x00\x19\x93\x0d\x0a\x1a\x0a\x04\x08\x08\x78\x56\z
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x77\x40\x00\x86\x40\z
\x74\x65\x6d\x70\x81\x81\x01\x00\x02\x82\x48\x00\x02\x00\xc7\x00\x01\z
\x00\x80\x80\x80\x82\x00\x00\x80\x81\x82\x78\x80\x82\x81\x86\x40\x74\z
\x65\x6d\x70"
assert(load(code)) -- segfaults in previous versions
end
x = string.dump(load("x = 1; return x")) x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b")) a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1) assert(a() == 1 and _G.x == 1)

View File

@@ -610,18 +610,20 @@ else
-- (bug in 5.2/5.3) -- (bug in 5.2/5.3)
c = coroutine.create(function (a, ...) c = coroutine.create(function (a, ...)
T.sethook("yield 0", "l") -- will yield on next two lines T.sethook("yield 0", "l") -- will yield on next two lines
assert(a == 10) local b = a
return ... return ...
end) end)
assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
local n,v = debug.getlocal(c, 0, 1) -- check its local local n,v = debug.getlocal(c, 0, 1) -- check its local
assert(n == "a" and v == 1) assert(n == "a" and v == 1 and debug.getlocal(c, 0, 2) ~= "b")
assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal' assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
local t = debug.getinfo(c, 0) -- test 'getinfo' local t = debug.getinfo(c, 0) -- test 'getinfo'
assert(t.currentline == t.linedefined + 1) assert(t.currentline == t.linedefined + 2)
assert(not debug.getinfo(c, 1)) -- no other level assert(not debug.getinfo(c, 1)) -- no other level
assert(coroutine.resume(c)) -- run next line assert(coroutine.resume(c)) -- run next line
local n,v = debug.getlocal(c, 0, 2) -- check next local
assert(n == "b" and v == 10)
v = {coroutine.resume(c)} -- finish coroutine v = {coroutine.resume(c)} -- finish coroutine
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
assert(not coroutine.resume(c)) assert(not coroutine.resume(c))

View File

@@ -49,6 +49,15 @@ do
end end
-- bug in 5.4.4-5.4.6: activelines in vararg functions
-- without debug information
do
local func = load(string.dump(load("print(10)"), true))
local actl = debug.getinfo(func, "L").activelines
assert(#actl == 0) -- no line info
end
-- test file and string names truncation -- test file and string names truncation
local a = "function f () end" local a = "function f () end"
local function dostring (s, x) return load(s, x)() end local function dostring (s, x) return load(s, x)() end
@@ -345,7 +354,7 @@ function f(a,b)
local _, y = debug.getlocal(1, 2) local _, y = debug.getlocal(1, 2)
assert(x == a and y == b) assert(x == a and y == b)
assert(debug.setlocal(2, 3, "pera") == "AA".."AA") assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
assert(debug.setlocal(2, 4, "maçã") == "B") assert(debug.setlocal(2, 4, "manga") == "B")
x = debug.getinfo(2) x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
@@ -373,9 +382,9 @@ function g (...)
local arg = {...} local arg = {...}
do local a,b,c; a=math.sin(40); end do local a,b,c; a=math.sin(40); end
local feijao local feijao
local AAAA,B = "xuxu", "mamão" local AAAA,B = "xuxu", "abacate"
f(AAAA,B) f(AAAA,B)
assert(AAAA == "pera" and B == "maçã") assert(AAAA == "pera" and B == "manga")
do do
local B = 13 local B = 13
local x,y = debug.getlocal(1,5) local x,y = debug.getlocal(1,5)
@@ -928,7 +937,7 @@ do
local cl = countlines(rest) local cl = countlines(rest)
-- at most 10 lines in first part, 11 in second, plus '...' -- at most 10 lines in first part, 11 in second, plus '...'
assert(cl <= 10 + 11 + 1) assert(cl <= 10 + 11 + 1)
local brk = string.find(rest, "%.%.%.") local brk = string.find(rest, "%.%.%.\t%(skip")
if brk then -- does message have '...'? if brk then -- does message have '...'?
local rest1 = string.sub(rest, 1, brk) local rest1 = string.sub(rest, 1, brk)
local rest2 = string.sub(rest, brk, #rest) local rest2 = string.sub(rest, brk, #rest)

View File

@@ -91,7 +91,7 @@ end
if not T then if not T then
(Message or print) (Message or print)
('\n >>> testC not active: skipping memory message test <<<\n') ('\n >>> testC not active: skipping tests for messages in C <<<\n')
else else
print "testing memory error message" print "testing memory error message"
local a = {} local a = {}
@@ -104,6 +104,19 @@ else
end) end)
T.totalmem(0) T.totalmem(0)
assert(not st and msg == "not enough" .. " memory") assert(not st and msg == "not enough" .. " memory")
-- stack space for luaL_traceback (bug in 5.4.6)
local res = T.testC[[
# push 16 elements on the stack
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1;
# traceback should work with 4 remaining slots
traceback xuxu 1;
return 1
]]
assert(string.find(res, "xuxu.-main chunk"))
end end
@@ -121,6 +134,9 @@ 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={13}; local bbbb=1; aaa[bbbb](3)", "number")
checkmessage("aaa=(1)..{}", "a table value") checkmessage("aaa=(1)..{}", "a table value")
-- bug in 5.4.6
checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'")
_G.aaa, _G.bbbb = nil _G.aaa, _G.bbbb = nil
-- calls -- calls
@@ -392,19 +408,19 @@ lineerror("a\n=\n-\n\nprint\n;", 3)
lineerror([[ lineerror([[
a a
( ( -- <<
23) 23)
]], 1) ]], 2)
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}
a a
. .
x x
( ( -- <<
23 23
) )
]], 2) ]], 5)
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}

View File

@@ -92,8 +92,8 @@ assert(io.output():seek("end") == string.len("alo joao"))
assert(io.output():seek("set") == 0) assert(io.output():seek("set") == 0)
assert(io.write('"álo"', "{a}\n", "second line\n", "third line \n")) assert(io.write('"alo"', "{a}\n", "second line\n", "third line \n"))
assert(io.write('çfourth_line')) assert(io.write('Xfourth_line'))
io.output(io.stdout) io.output(io.stdout)
collectgarbage() -- file should be closed by GC collectgarbage() -- file should be closed by GC
assert(io.input() == io.stdin and rawequal(io.output(), io.stdout)) assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
@@ -300,14 +300,14 @@ do -- test error returns
end end
checkerr("invalid format", io.read, "x") checkerr("invalid format", io.read, "x")
assert(io.read(0) == "") -- not eof assert(io.read(0) == "") -- not eof
assert(io.read(5, 'l') == '"álo"') assert(io.read(5, 'l') == '"alo"')
assert(io.read(0) == "") assert(io.read(0) == "")
assert(io.read() == "second line") assert(io.read() == "second line")
local x = io.input():seek() local x = io.input():seek()
assert(io.read() == "third line ") assert(io.read() == "third line ")
assert(io.input():seek("set", x)) assert(io.input():seek("set", x))
assert(io.read('L') == "third line \n") assert(io.read('L') == "third line \n")
assert(io.read(1) == "ç") assert(io.read(1) == "X")
assert(io.read(string.len"fourth_line") == "fourth_line") assert(io.read(string.len"fourth_line") == "fourth_line")
assert(io.input():seek("cur", -string.len"fourth_line")) assert(io.input():seek("cur", -string.len"fourth_line"))
assert(io.read() == "fourth_line") assert(io.read() == "fourth_line")

View File

@@ -27,17 +27,19 @@ do
end end
print("progname: "..progname) print("progname: "..progname)
local prepfile = function (s, p)
p = p or prog local prepfile = function (s, mod, p)
io.output(p) mod = mod and "wb" or "w" -- mod true means binary files
io.write(s) p = p or prog -- file to write the program
assert(io.close()) local f = io.open(p, mod)
f:write(s)
assert(f:close())
end end
local function getoutput () local function getoutput ()
io.input(out) local f = io.open(out)
local t = io.read("a") local t = f:read("a")
io.input():close() f:close()
assert(os.remove(out)) assert(os.remove(out))
return t return t
end end
@@ -65,10 +67,11 @@ local function RUN (p, ...)
assert(os.execute(s)) assert(os.execute(s))
end end
local function NoRun (msg, p, ...) local function NoRun (msg, p, ...)
p = string.gsub(p, "lua", '"'..progname..'"', 1) p = string.gsub(p, "lua", '"'..progname..'"', 1)
local s = string.format(p, ...) local s = string.format(p, ...)
s = string.format("%s 2> %s", s, out) -- will send error to 'out' s = string.format("%s >%s 2>&1", s, out) -- send output and error to 'out'
assert(not os.execute(s)) assert(not os.execute(s))
assert(string.find(getoutput(), msg, 1, true)) -- check error message assert(string.find(getoutput(), msg, 1, true)) -- check error message
end end
@@ -108,17 +111,17 @@ RUN('lua %s > %s', prog, out)
checkout("3\n") checkout("3\n")
-- bad BOMs -- bad BOMs
prepfile("\xEF") prepfile("\xEF", true)
NoRun("unexpected symbol", 'lua %s > %s', prog, out) NoRun("unexpected symbol", 'lua %s', prog)
prepfile("\xEF\xBB") prepfile("\xEF\xBB", true)
NoRun("unexpected symbol", 'lua %s > %s', prog, out) NoRun("unexpected symbol", 'lua %s', prog)
prepfile("\xEFprint(3)") prepfile("\xEFprint(3)", true)
NoRun("unexpected symbol", 'lua %s > %s', prog, out) NoRun("unexpected symbol", 'lua %s', prog)
prepfile("\xEF\xBBprint(3)") prepfile("\xEF\xBBprint(3)", true)
NoRun("unexpected symbol", 'lua %s > %s', prog, out) NoRun("unexpected symbol", 'lua %s', prog)
-- test option '-' -- test option '-'
@@ -213,7 +216,7 @@ convert("a;b;;c")
-- test -l over multiple libraries -- test -l over multiple libraries
prepfile("print(1); a=2; return {x=15}") prepfile("print(1); a=2; return {x=15}")
prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog) prepfile(("print(a); print(_G['%s'].x)"):format(prog), false, otherprog)
RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out) 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") checkout("1\n2\n15\n2\n15\n")
@@ -222,6 +225,13 @@ 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) RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out)
checkout("0.0\nALO ALO\t20\n") checkout("0.0\nALO ALO\t20\n")
-- test module names with version sufix ("libs/lib2-v2")
RUN("env LUA_CPATH='./libs/?.so' lua -l lib2-v2 -e 'print(lib2.id())' > %s",
out)
checkout("true\n")
-- test 'arg' table -- test 'arg' table
local a = [[ local a = [[
assert(#arg == 3 and arg[1] == 'a' and assert(#arg == 3 and arg[1] == 'a' and
@@ -237,7 +247,7 @@ RUN('lua "-e " -- %s a b c', prog) -- "-e " runs an empty command
-- test 'arg' availability in libraries -- test 'arg' availability in libraries
prepfile"assert(arg)" prepfile"assert(arg)"
prepfile("assert(arg)", otherprog) prepfile("assert(arg)", false, otherprog)
RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog) RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
-- test messing up the 'arg' table -- test messing up the 'arg' table
@@ -413,7 +423,7 @@ prepfile[[#comment in 1st line without \n at the end]]
RUN('lua %s', prog) RUN('lua %s', prog)
-- first-line comment with binary file -- first-line comment with binary file
prepfile("#comment\n" .. string.dump(load("print(3)"))) prepfile("#comment\n" .. string.dump(load("print(3)")), true)
RUN('lua %s > %s', prog, out) RUN('lua %s > %s', prog, out)
checkout('3\n') checkout('3\n')

View File

@@ -1,6 +1,9 @@
-- $Id: testes/pm.lua $ -- $Id: testes/pm.lua $
-- See Copyright Notice in file all.lua -- See Copyright Notice in file all.lua
-- UTF-8 file
print('testing pattern matching') print('testing pattern matching')
local function checkerror (msg, f, ...) local function checkerror (msg, f, ...)
@@ -50,6 +53,20 @@ assert(f('aLo_ALO', '%a*') == 'aLo')
assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu") assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu")
-- Adapt a pattern to UTF-8
local function PU (p)
-- reapply '?' into each individual byte of a character.
-- (For instance, "á?" becomes "\195?\161?".)
p = string.gsub(p, "(" .. utf8.charpattern .. ")%?", function (c)
return string.gsub(c, ".", "%0?")
end)
-- change '.' to utf-8 character patterns
p = string.gsub(p, "%.", utf8.charpattern)
return p
end
assert(f('aaab', 'a*') == 'aaa'); assert(f('aaab', 'a*') == 'aaa');
assert(f('aaa', '^.*$') == 'aaa'); assert(f('aaa', '^.*$') == 'aaa');
assert(f('aaa', 'b*') == ''); assert(f('aaa', 'b*') == '');
@@ -73,16 +90,16 @@ assert(f('aaa', '^.-$') == 'aaa')
assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab') assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab')
assert(f('aabaaabaaabaaaba', 'b.-b') == 'baaab') assert(f('aabaaabaaabaaaba', 'b.-b') == 'baaab')
assert(f('alo xo', '.o$') == 'xo') assert(f('alo xo', '.o$') == 'xo')
assert(f(' \n isto é assim', '%S%S*') == 'isto') assert(f(' \n isto é assim', '%S%S*') == 'isto')
assert(f(' \n isto é assim', '%S*$') == 'assim') assert(f(' \n isto é assim', '%S*$') == 'assim')
assert(f(' \n isto é assim', '[a-z]*$') == 'assim') assert(f(' \n isto é assim', '[a-z]*$') == 'assim')
assert(f('um caracter ? extra', '[^%sa-z]') == '?') assert(f('um caracter ? extra', '[^%sa-z]') == '?')
assert(f('', 'a?') == '') assert(f('', 'a?') == '')
assert(f('á', 'á?') == 'á') assert(f('á', PU'á?') == 'á')
assert(f('ábl', 'á?b?l?') == 'ábl') assert(f('ábl', PU'á?b?l?') == 'ábl')
assert(f(' ábl', 'á?b?l?') == '') assert(f(' ábl', PU'á?b?l?') == '')
assert(f('aa', '^aa?a?a') == 'aa') assert(f('aa', '^aa?a?a') == 'aa')
assert(f(']]]áb', '[^]]') == 'á') assert(f(']]]áb', '[^]]+') == 'áb')
assert(f("0alo alo", "%x*") == "0a") assert(f("0alo alo", "%x*") == "0a")
assert(f("alo alo", "%C+") == "alo alo") assert(f("alo alo", "%C+") == "alo alo")
print('+') print('+')
@@ -136,28 +153,28 @@ assert(string.match("alo xyzK", "(%w+)K") == "xyz")
assert(string.match("254 K", "(%d*)K") == "") assert(string.match("254 K", "(%d*)K") == "")
assert(string.match("alo ", "(%w*)$") == "") assert(string.match("alo ", "(%w*)$") == "")
assert(not string.match("alo ", "(%w+)$")) assert(not string.match("alo ", "(%w+)$"))
assert(string.find("(álo)", "%(á") == 1) assert(string.find("(álo)", "%(á") == 1)
local a, b, c, d, e = string.match("âlo alo", "^(((.).).* (%w*))$") local a, b, c, d, e = string.match("âlo alo", PU"^(((.).). (%w*))$")
assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil) assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil)
a, b, c, d = string.match('0123456789', '(.+(.?)())') a, b, c, d = string.match('0123456789', '(.+(.?)())')
assert(a == '0123456789' and b == '' and c == 11 and d == nil) assert(a == '0123456789' and b == '' and c == 11 and d == nil)
print('+') print('+')
assert(string.gsub('ülo ülo', 'ü', 'x') == 'xlo xlo') assert(string.gsub('ülo ülo', 'ü', 'x') == 'xlo xlo')
assert(string.gsub('alo úlo ', ' +$', '') == 'alo úlo') -- trim 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 ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim
assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ') assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ')
local t = "abç d" local t = "abç d"
a, b = string.gsub(t, '(.)', '%1@') a, b = string.gsub(t, PU'(.)', '%1@')
assert('@'..a == string.gsub(t, '', '@') and b == 5) assert(a == "a@b@ç@ @d@" and b == 5)
a, b = string.gsub('abçd', '(.)', '%0@', 2) a, b = string.gsub('abçd', PU'(.)', '%0@', 2)
assert(a == 'a@b@çd' and b == 2) assert(a == 'a@b@çd' and b == 2)
assert(string.gsub('alo alo', '()[al]', '%1') == '12o 56o') assert(string.gsub('alo alo', '()[al]', '%1') == '12o 56o')
assert(string.gsub("abc=xyz", "(%w*)(%p)(%w+)", "%3%2%1-%0") == assert(string.gsub("abc=xyz", "(%w*)(%p)(%w+)", "%3%2%1-%0") ==
"xyz=abc-abc=xyz") "xyz=abc-abc=xyz")
assert(string.gsub("abc", "%w", "%1%0") == "aabbcc") assert(string.gsub("abc", "%w", "%1%0") == "aabbcc")
assert(string.gsub("abc", "%w+", "%0%1") == "abcabc") assert(string.gsub("abc", "%w+", "%0%1") == "abcabc")
assert(string.gsub('áéí', '$', '\0óú') == 'áéí\0óú') assert(string.gsub('áéí', '$', '\0óú') == 'áéí\0óú')
assert(string.gsub('', '^', 'r') == 'r') assert(string.gsub('', '^', 'r') == 'r')
assert(string.gsub('', '$', 'r') == 'r') assert(string.gsub('', '$', 'r') == 'r')
print('+') print('+')
@@ -188,8 +205,8 @@ do
end end
function f(a,b) return string.gsub(a,'.',b) end function f(a,b) return string.gsub(a,'.',b) end
assert(string.gsub("trocar tudo em |teste|b| é |beleza|al|", "|([^|]*)|([^|]*)|", f) == assert(string.gsub("trocar tudo em |teste|b| é |beleza|al|", "|([^|]*)|([^|]*)|", f) ==
"trocar tudo em bbbbb é alalalalalal") "trocar tudo em bbbbb é alalalalalal")
local function dostring (s) return load(s, "")() or "" end local function dostring (s) return load(s, "")() or "" end
assert(string.gsub("alo $a='x'$ novamente $return a$", assert(string.gsub("alo $a='x'$ novamente $return a$",

View File

@@ -289,7 +289,7 @@ timesort(a, limit, function(x,y) return nil end, "equal")
for i,v in pairs(a) do assert(v == false) end for i,v in pairs(a) do assert(v == false) end
AA = {"álo", "\0first :-)", "alo", "then this one", "45", "and a new"} AA = {"\xE1lo", "\0first :-)", "alo", "then this one", "45", "and a new"}
table.sort(AA) table.sort(AA)
check(AA) check(AA)

View File

@@ -1,6 +1,9 @@
-- $Id: testes/strings.lua $ -- $Id: testes/strings.lua $
-- See Copyright Notice in file all.lua -- See Copyright Notice in file all.lua
-- ISO Latin encoding
print('testing strings and string library') print('testing strings and string library')
local maxi <const> = math.maxinteger local maxi <const> = math.maxinteger

View File

@@ -1,6 +1,8 @@
-- $Id: testes/utf8.lua $ -- $Id: testes/utf8.lua $
-- See Copyright Notice in file all.lua -- See Copyright Notice in file all.lua
-- UTF-8 file
print "testing UTF-8 library" print "testing UTF-8 library"
local utf8 = require'utf8' local utf8 = require'utf8'