Compare commits

...

68 Commits

Author SHA1 Message Date
Roberto I
2a7cf4f319 More effort in avoiding errors in finalizers
Before calling a finalizer, Lua not only checks stack limits, but
actually ensures that a minimum number of slots are already allocated
for the call. (If it cannot ensure that, it postpones the finalizer.)
That avoids finalizers not running due to memory errors that the
programmer cannot control.
2026-01-11 15:36:03 -03:00
Roberto I
5cfc725a8b Special case for 'string.rep' over an empty string 2026-01-04 16:39:22 -03:00
Roberto I
45c7ae5b1b BUG: Possible overflow in 'string.packsize'
'string.packsize' can overflow result in 32-bit machines using 64-bit
integers, as LUA_MAXINTEGER may not fit into size_t.
2026-01-04 16:31:17 -03:00
Roberto I
962f444a75 Details
In an assignment like 'a = &b', is looks suspicious if 'a' has a scope
larger than 'b'.
2026-01-04 16:27:54 -03:00
Roberto I
c4e2c91973 Details
Some comments still talked about bit 'isrealasize', which has been
removed.
2025-12-30 10:50:49 -03:00
Roberto I
632a71b24d BUG: Arithmetic overflow in 'collectgarbage"step"'
The computation of a new debt could overflow when we give a too large
step to 'collectgarbage"step"' and the current debt was already
negative. This is only an issue if your platform cares for it or if you
compile Lua with an option like '-fsanitize=undefined'.
2025-12-27 16:22:13 -03:00
Roberto I
578ae5745c Details
typo in comment + formatting + logical 'and' was written as a bitwise
operation (makes code more fragile)
2025-12-23 14:44:06 -03:00
Roberto I
a5522f06d2 GC checks stack space before running finalizer
If the stack does not have some minimum available space, the GC defers
calling a finalizer until the next cycle. That avoids errors while
running a finalizer that the programmer cannot control.
2025-12-13 16:16:59 -03:00
Roberto I
3d03ae5bd6 'luaL_newstate' starts state with warnings on
It is easier to forget to turn them on then to turn them off.
2025-12-13 11:00:30 -03:00
Roberto I
82d721a855 Format adjust in the manual
Lists in inline code don't get a space after commas. (That keeps the
code more compact and avoids line breaks in the middle of the code.)
2025-12-10 10:35:05 -03:00
Roberto I
104b0fc700 Details
- Avoid fixing name "_ENV" in the code
- Small improvements in the manual
2025-12-08 13:09:47 -03:00
Roberto I
8164d09338 Wrong assert in 'luaK_indexed' 2025-12-08 11:08:12 -03:00
Roberto I
985ef32248 In luaB_close, running coroutines do not go to default
This should had been corrected in commit fd897027f1.
2025-12-01 10:25:44 -03:00
Roberto I
a07f6a8241 Functions with vararg tables don't need hidden args.
Vararg functions with vararg tables don't use the arguments hidden in
the stack; therfore, it doesn't need to build/keep them.
2025-11-28 15:12:51 -03:00
Roberto I
f33cc4ddec New conceptual model for vararg
Conceptually, all functions get their vararg arguments in a vararg
table. The storing of vararg arguments in the stack is always treated
as an optimization.
2025-11-26 11:18:29 -03:00
Roberto I
d94f7ba304 Details
Comments, capitalization in the manual, globals in test 'heady.lua'
2025-11-24 11:39:46 -03:00
Roberto I
4cf498210e '__pairs' can also return a to-be-closed object 2025-11-11 15:11:06 -03:00
Roberto I
5b7d998764 External strings are as good as internal ones
A '__mode' metafield and an "n" key both can be external strings.
2025-11-11 14:40:30 -03:00
Roberto I
81f4def54f Correction in line info for semantic errors
Semantic errors should refer the last used token, not the next one.
2025-11-11 14:36:16 -03:00
Roberto I
e44f3a2ffc Global initialization checks name conflict
Initialization "global a = 10" raises an error if global 'a' is already
defined, that is, it has a non-nil value.
2025-11-08 11:43:42 -03:00
Roberto I
f791bb6906 Details
- New macro l_strcoll to ease changing 'strcoll' to something else.
- MAXINDEXRK==1 in 'ltests.h' is enough to run test 'code.lua'.
- Removed unused '#include' in 'lutf8lib.c'.
2025-10-31 14:48:55 -03:00
Roberto I
d342328e5b Vertical bar removed from syntax of vararg table
The syntax 'function foo (a, b, ...arg)' is already used by JavaScript
for this same semantics, so it seems natural to use the same notation in
Lua.
2025-10-30 11:07:01 -03:00
Roberto Ierusalimschy
0149b781d4 Case VVARGIND added to luaK_storevar
In a global initialization, the variable does not pass through
'check_readonly', and therefore a VVARGIND is not normalized to a
VINDEXED.
2025-10-30 10:39:55 -03:00
Roberto I
d4eff00234 Fixed initialization of global variables
When calling 'luaK_storevar', the 'expdesc' for the variable must be
created before the one for the expression, to satisfy the assumptions
for register allocation. So, in a statement like 'global a = exp', where
'a' is actually '_ENV.a', this variable must be handled before the
initializing expression 'exp'.
2025-10-29 13:14:48 -03:00
Roberto I
fca974486d Small change in 'trymt'
Operation name can be diferent from metamethod name.
2025-10-18 10:34:42 -03:00
Roberto I
26755cad99 Added "attribute internal" to __MACH__ platforms
Also, makefile does not add compiling options (LOCAL) to linker
flags (MYLDFLAGS).
2025-10-18 10:30:12 -03:00
Roberto I
b352217b84 Standard allocator function added to the API
That makes easier to redefine luaL_newstate.
2025-10-17 13:53:35 -03:00
Roberto I
9c66903cc5 Details
- Functions luaK_goiffalse, luaS_hash made private.
- Removed unused macro log2maxs.
2025-10-14 13:50:24 -03:00
Roberto I
30a7b93439 Two new memory tests
For external strings and for vararg tables.
2025-10-12 15:13:28 -03:00
Roberto I
7a92f3f99a Change in dumping of NULL strings
When dumping a string, adding 2 to its size may overflow a size_t for
external strings, which may not have a header. (Adding 1 is Ok, because
all strings end with a '\0' not included in their size.) The new method
for saving NULL strings code them as a repeated string, using the
reserved index 0.
2025-10-10 15:28:41 -03:00
Roberto I
3347c9d32d Initialization of too many locals break assertion
The check for limit of local variables is made after generating code to
initialize them. If there are too many local variables not initialized,
the coding of instruction OP_LOADNIL could overflow an argument.
2025-10-10 13:22:19 -03:00
Roberto I
25c54fe60e Optimization for vararg tables
A vararg table can be virtual. If the vararg table is used only as
a base in indexing expressions, the code does not need to create an
actual table for it. Instead, it compiles the indexing expressions
into direct accesses to the internal vararg data.
2025-09-24 18:33:08 -03:00
Roberto I
0cc3c9447c Small tweaks in makefile 2025-09-18 11:03:55 -03:00
Roberto I
8fb1af0e33 Varag parameter is a new kind of variable
To allow some optimizations on its use.
2025-09-17 16:07:48 -03:00
Roberto I
140b672e2e Vararg table
Not yet optimized nor documented.
2025-09-16 13:26:24 -03:00
Roberto I
9ea06e61f2 Details
- LUAMOD_API defined as 'extern "C"' in C++.
- "ANSI C" is in fact "ISO C" (comments)
- Removed option -std from makefile in testes/libs. (Easier to change
  to C++ for tests).
2025-09-05 15:36:47 -03:00
Roberto I
ffbcadfb41 In C++, 'throw' must go to the correct handler.
In C, we may have several "setjmp" nested, and the "longjmp" will go
to the one given by the corresponding "jmp_buf". In C++, a "throw"
will always go to the inner "catch". So, the "catch" must check
whether it is the recipient of the "throw" and, if not, rethrow
the exception to the outer level.
2025-09-05 15:29:15 -03:00
Roberto Ierusalimschy
0b73ed8f08 Allows LUA_32BITS to be defined externally
An external definition for LUA_32BITS can change the API, but libraries
check number-format compatibility when loading. So, any incompatible
modules will report a clear error.
2025-08-30 16:16:02 -03:00
Roberto I
f87416f1a3 Added limit to number of elements in a constructor
The reasoning in commit 519c57d5 is wrong: A sequence of nils generates
several fields with just one OP_LOADNIL.
2025-08-27 10:30:54 -03:00
Roberto I
03a3473687 'ltests.h' should not use LUAI_FUNC
LUAI_FUNC is now defined in llimits.h.
2025-08-27 10:28:31 -03:00
Roberto Ierusalimschy
9a3940380a New compile option LUA_USE_OFF_T
Allows non-Posix systems to use off_t and related functions for
file offsets.
2025-08-26 12:30:34 -03:00
Roberto Ierusalimschy
711890624f update in 'onelua.c' 2025-08-26 12:30:05 -03:00
Roberto Ierusalimschy
13d2326a23 Some definitions moved from luaconf.h to llimits.h
These definitions were in luaconf.h only because the standard libraries
need them. Now that llimits.h is included by the libraries, it offers a
more private place for these definitions.
2025-08-21 10:51:17 -03:00
Roberto I
06c5d3825f Removed code for compatibility with version 5.3 2025-08-20 16:10:54 -03:00
Roberto I
c345877e4c Better documentation for LUA_ERRERR
Not all errors in a message handler generate a LUA_ERRERR.
2025-08-20 15:29:46 -03:00
Roberto I
907d172c11 Added lock/unlock to API function 'lua_rawlen'
The call to 'luaH_getn' can change the "field" 'lenhint' of a table.
2025-08-20 15:00:53 -03:00
Roberto I
88aa4049ad Keep the order left-right in shifts
Opcodes OP_SHLI-OP_SHRI and the cases for opcodes OP_SHL-OP_SHR were
out of order.
2025-08-20 14:31:07 -03:00
Roberto I
c688b00f73 Detail in 'obj2gco'
Its check should use the type of the object, not its tag. (Change only
relevant in test mode.)
2025-08-20 14:18:12 -03:00
Roberto I
dd095677e3 Small cleaning services 2025-08-20 13:59:08 -03:00
Roberto I
53dc5a3bba Functions 'frexp'-'ldexp' back to the math library
They are basic for anything that handles the representation of
floating numbers.
2025-08-09 15:15:20 -03:00
Roberto I
5b179eaf6a Details 2025-08-09 15:08:53 -03:00
Roberto Ierusalimschy
8fddca81e7 'onelua' can use the test library
Just add -DLUA_USER_H='"ltests.h"' when compiling it.
2025-07-29 14:35:04 -03:00
Roberto Ierusalimschy
c33bb08ffe Added some casts for 32-bit machines
When both 'int' and 'l_obj' have 32 bits, an unsigned int needs a
cast to be assigned to 'l_obj'. (As long as 'l_obj' can count the
total memory used by the system, these casts should be safe.)
2025-07-29 11:50:20 -03:00
Roberto Ierusalimschy
e3716ee161 Fix in string.rep
The cast of n (number of repetitions) to size_t may truncate its value,
causing a buffer overflow later. Better to check the buffer size
using lua_Integer, as all string lengths must fit in a lua_Integer and
n already is a lua_Integer. If everything fits in MAX_SIZE, then we can
safely convert n to size_t and compute the buffer size as a size_t.

As a corner case, n can be larger than size_t if the strings being
repeated have length zero, but in this case it will be multiplied by
zero, so an overflow in the cast is irrelevant.
2025-07-23 18:12:53 -03:00
Roberto Ierusalimschy
303f415559 Randomness added to table length computation
A bad actor could fill only a few entries in a table (power of twos in
decreasing order, see tests) and produce a small table with a huge
length. If your program builds a table with external data and iterates
over its length, this behavior could be an issue.
2025-07-18 16:18:30 -03:00
Roberto Ierusalimschy
ccb8b307f1 Correction in utf8.offset
Wrong utf-8 character may have no continuation bytes.
2025-07-18 16:10:28 -03:00
Roberto Ierusalimschy
60b6599e83 Short strings can be external, too
That complicates a little object equality (and therefore table access
for long strings), but the old behavior was somewhat weird. (Short
strings, a concept otherwise absent from the manual, could not be
external.)
2025-07-15 14:40:27 -03:00
Roberto Ierusalimschy
c612685d4b lua.c doesn't use function pointers with LUA_READLINE
Bugs in macOS prevent assigning 'add_history' to 'l_addhist' without
a warning.
2025-07-09 14:43:31 -03:00
Roberto Ierusalimschy
85a3c1699c New method to unload DLLs
External strings created by DLLs may need the DLL code to be
deallocated. This implies that a DLL can only be unloaded after all
its strings were deallocated, which happen only after the run of all
finalizers. To ensure that order, we create a 'library string' to
represent each DLL and keep it locked. When this string is deallocated
(after the deallocation of any string created by the DLL) it closes its
corresponding DLL.
2025-07-09 14:40:36 -03:00
Roberto Ierusalimschy
f65d1f9e02 lua option '--' may not be followed by script 2025-07-08 15:40:59 -03:00
Roberto Ierusalimschy
942c10a5e3 Optional initialization for global declarations 2025-07-08 13:33:57 -03:00
Roberto Ierusalimschy
8485687908 Correction in definition of CIST_FRESH
The cast must be made before the shift. If int has 16 bits, the shift
would zero the value and the cast would cast 0 to 0.
2025-07-07 15:03:45 -03:00
Roberto Ierusalimschy
03d672a95c Details (comments) 2025-07-07 15:02:09 -03:00
Roberto Ierusalimschy
03bf7fdd4f Added missing casts from lua_Unsigned to size_t
size_t can be smaller than lua_Usigned.
2025-07-01 16:07:03 -03:00
Roberto Ierusalimschy
59a1adf194 LUAI_MAXSTACK defined privately
LUAI_MAXSTACK is limited to INT_MAX/2, so can use INT_MAX/2 to define
pseudo-indices (LUA_REGISTRYINDEX) in 'lua.h'. A change in the maximum
stack size does not need to change the Lua-C ABI.
2025-07-01 10:57:02 -03:00
Roberto Ierusalimschy
cfce6f4b20 Warning in loslib.c (signed-unsigned comparison) 2025-06-27 14:47:11 -03:00
Roberto Ierusalimschy
1da89da62f Manual updated to version 5.5 2025-06-27 14:46:41 -03:00
Roberto Ierusalimschy
f6c627af20 Cast added to 'add_history'
MacOS defines 'add_history' with a "wrong" type (it returns 'int'
instead of 'void').
2025-06-26 11:45:42 -03:00
73 changed files with 1899 additions and 911 deletions

23
lapi.c
View File

@ -366,7 +366,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
}
LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff) {
LUA_API unsigned lua_numbertocstring (lua_State *L, int idx, char *buff) {
const TValue *o = index2value(L, idx);
if (ttisnumber(o)) {
unsigned len = luaO_tostringbuff(o, buff);
@ -440,7 +440,13 @@ LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen);
case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen);
case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len);
case LUA_VTABLE: return luaH_getn(hvalue(o));
case LUA_VTABLE: {
lua_Unsigned res;
lua_lock(L);
res = luaH_getn(L, hvalue(o));
lua_unlock(L);
return res;
}
default: return 0;
}
}
@ -478,7 +484,7 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
/*
** Returns a pointer to the internal representation of an object.
** Note that ANSI C does not allow the conversion of a pointer to
** Note that ISO C does not allow the conversion of a pointer to
** function to a 'void*', so the conversion here goes through
** a 'size_t'. (As the returned pointer is only informative, this
** conversion should not be a problem.)
@ -679,7 +685,7 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
/*
** The following function assumes that the registry cannot be a weak
** table, so that en mergency collection while using the global table
** table; so, an emergency collection while using the global table
** cannot collect it.
*/
static void getGlobalTable (lua_State *L, TValue *gt) {
@ -1195,11 +1201,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
case LUA_GCSTEP: {
lu_byte oldstp = g->gcstp;
l_mem n = cast(l_mem, va_arg(argp, size_t));
l_mem newdebt;
int work = 0; /* true if GC did some work */
g->gcstp = 0; /* allow GC to run (other bits must be zero here) */
if (n <= 0)
n = g->GCdebt; /* force to run one basic step */
luaE_setdebt(g, g->GCdebt - n);
newdebt = 0; /* force to run one basic step */
else if (g->GCdebt >= n - MAX_LMEM) /* no overflow? */
newdebt = g->GCdebt - n;
else /* overflow */
newdebt = -MAX_LMEM; /* set debt to miminum value */
luaE_setdebt(g, newdebt);
luaC_condGC(L, (void)0, work = 1);
if (work && g->gcstate == GCSpause) /* end of cycle? */
res = 1; /* signal it */

View File

@ -742,7 +742,7 @@ typedef struct LoadF {
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L; /* not used */
UNUSED(L);
if (lf->n > 0) { /* are there pre-read characters to be read? */
*size = lf->n; /* return them (chars already in buffer) */
lf->n = 0; /* no more pre-read characters */
@ -856,7 +856,7 @@ typedef struct LoadS {
static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud;
(void)L; /* not used */
UNUSED(L);
if (ls->size == 0) return NULL;
*size = ls->size;
ls->size = 0;
@ -1046,8 +1046,8 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s,
}
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
UNUSED(ud); UNUSED(osize);
if (nsize == 0) {
free(ptr);
return NULL;
@ -1172,16 +1172,20 @@ static unsigned int luai_makeseed (void) {
LUALIB_API unsigned int luaL_makeseed (lua_State *L) {
(void)L; /* unused */
UNUSED(L);
return luai_makeseed();
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL, luai_makeseed());
/*
** Use the name with parentheses so that headers can redefine it
** as a macro.
*/
LUALIB_API lua_State *(luaL_newstate) (void) {
lua_State *L = lua_newstate(luaL_alloc, NULL, luaL_makeseed(NULL));
if (l_likely(L)) {
lua_atpanic(L, &panic);
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
lua_setwarnf(L, warnfon, L);
}
return L;
}

View File

@ -81,6 +81,9 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
LUALIB_API void *(luaL_alloc) (void *ud, void *ptr, size_t osize,
size_t nsize);
/* predefined references */
#define LUA_NOREF (-2)
@ -100,7 +103,7 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API unsigned luaL_makeseed (lua_State *L);
LUALIB_API unsigned (luaL_makeseed) (lua_State *L);
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);

View File

@ -279,21 +279,22 @@ static int luaB_next (lua_State *L) {
static int pairscont (lua_State *L, int status, lua_KContext k) {
(void)L; (void)status; (void)k; /* unused */
return 3;
return 4; /* __pairs did all the work, just return its results */
}
static int luaB_pairs (lua_State *L) {
luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, luaB_next); /* will return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
lua_pushcfunction(L, luaB_next); /* will return generator and */
lua_pushvalue(L, 1); /* state */
lua_pushnil(L); /* initial value */
lua_pushnil(L); /* to-be-closed object */
}
else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
lua_callk(L, 1, 4, 0, pairscont); /* get 4 values from metamethod */
}
return 3;
return 4;
}

138
lcode.c
View File

@ -45,6 +45,7 @@ l_noret luaK_semerror (LexState *ls, const char *fmt, ...) {
va_list argp;
pushvfstring(ls->L, argp, fmt, msg);
ls->t.token = 0; /* remove "near <token>" from final message */
ls->linenumber = ls->lastline; /* back to line of last used token */
luaX_syntaxerror(ls, msg);
}
@ -565,20 +566,20 @@ static int k2proto (FuncState *fs, TValue *key, TValue *v) {
TValue val;
Proto *f = fs->f;
int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */
int k;
if (!tagisempty(tag)) { /* is there an index there? */
k = cast_int(ivalue(&val));
int k = cast_int(ivalue(&val));
/* collisions can happen only for float keys */
lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v));
return k; /* reuse index */
}
/* constant not found; create a new entry */
k = addk(fs, f, v);
/* cache it for reuse; numerical value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */
setivalue(&val, k);
luaH_set(fs->ls->L, fs->kcache, key, &val);
return k;
else { /* constant not found; create a new entry */
int k = addk(fs, f, v);
/* cache it for reuse; numerical value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */
setivalue(&val, k);
luaH_set(fs->ls->L, fs->kcache, key, &val);
return k;
}
}
@ -604,13 +605,14 @@ static int luaK_intK (FuncState *fs, lua_Integer n) {
/*
** Add a float to list of constants and return its index. Floats
** with integral values need a different key, to avoid collision
** with actual integers. To that, we add to the number its smaller
** with actual integers. To that end, we add to the number its smaller
** power-of-two fraction that is still significant in its scale.
** For doubles, that would be 1/2^52.
** (For doubles, the fraction would be 2^-52).
** This method is not bulletproof: different numbers may generate the
** same key (e.g., very large numbers will overflow to 'inf') and for
** floats larger than 2^53 the result is still an integer. At worst,
** this only wastes an entry with a duplicate.
** floats larger than 2^53 the result is still an integer. For those
** cases, just generate a new entry. At worst, this only wastes an entry
** with a duplicate.
*/
static int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o, kv;
@ -625,7 +627,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) {
const lua_Number k = r * (1 + q); /* key */
lua_Integer ik;
setfltvalue(&kv, k); /* key as a TValue */
if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integral value? */
if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integer value? */
int n = k2proto(fs, &kv, &o); /* use key */
if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */
return n;
@ -704,6 +706,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
}
/*
** Get the value of 'var' in a register and generate an opcode to check
** whether that register is nil. 'k' is the index of the variable name
** in the list of constants. If its value cannot be encoded in Bx, a 0
** will use '?' for the name.
*/
void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) {
luaK_exp2anyreg(fs, var);
luaK_fixline(fs, line);
k = (k >= MAXARG_Bx) ? 0 : k + 1;
luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k);
luaK_fixline(fs, line);
freeexp(fs, var);
}
/*
** Convert a constant in 'v' into an expression description 'e'
*/
@ -784,6 +802,15 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
}
}
/*
** Change a vararg parameter into a regular local variable
*/
void luaK_vapar2local (FuncState *fs, expdesc *var) {
needvatab(fs->f); /* function will need a vararg table */
/* now a vararg parameter is equivalent to a regular local variable */
var->k = VLOCAL;
}
/*
** Ensure that expression 'e' is not a variable (nor a <const>).
@ -795,6 +822,9 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
const2exp(const2val(fs, e), e);
break;
}
case VVARGVAR: {
luaK_vapar2local(fs, e); /* turn it into a local variable */
} /* FALLTHROUGH */
case VLOCAL: { /* already in a register */
int temp = e->u.var.ridx;
e->u.info = temp; /* (can't do a direct assignment; values overlap) */
@ -829,6 +859,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
e->k = VRELOC;
break;
}
case VVARGIND: {
freeregs(fs, e->u.ind.t, e->u.ind.idx);
e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx);
e->k = VRELOC;
break;
}
case VVARARG: case VCALL: {
luaK_setoneret(fs, e);
break;
@ -991,11 +1027,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
/*
** Ensures final expression result is either in a register
** or in an upvalue.
** Ensures final expression result is either in a register,
** in an upvalue, or it is the vararg parameter.
*/
void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
if (e->k != VUPVAL || hasjumps(e))
if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e))
luaK_exp2anyreg(fs, e);
}
@ -1090,6 +1126,10 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
break;
}
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
/* now, assignment is to a regular table */
} /* FALLTHROUGH */
case VINDEXED: {
codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
break;
@ -1162,7 +1202,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
/*
** Emit code to go through if 'e' is false, jump otherwise.
*/
void luaK_goiffalse (FuncState *fs, expdesc *e) {
static void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of new jump */
luaK_dischargevars(fs, e);
switch (e->k) {
@ -1223,7 +1263,7 @@ static void codenot (FuncState *fs, expdesc *e) {
** Check whether expression 'e' is a short literal string
*/
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 <= MAXINDEXRK &&
ttisshrstring(&fs->f->k[e->u.info]));
}
@ -1301,6 +1341,13 @@ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
}
/* auxiliary function to define indexing expressions */
static void fillidxk (expdesc *t, int idx, expkind k) {
t->u.ind.idx = cast_byte(idx);
t->k = k;
}
/*
** Create expression 't[k]'. 't' must have its final result already in a
** register or upvalue. Upvalues can only be indexed by literal strings.
@ -1312,31 +1359,32 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (k->k == VKSTR)
keystr = str2K(fs, k);
lua_assert(!hasjumps(t) &&
(t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
(t->k == VLOCAL || t->k == VVARGVAR ||
t->k == VNONRELOC || t->k == VUPVAL));
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) {
lu_byte temp = cast_byte(t->u.info); /* upvalue index */
t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
lua_assert(isKstr(fs, k));
t->u.ind.idx = cast_short(k->u.info); /* literal short string */
t->k = VINDEXUP;
fillidxk(t, k->u.info, VINDEXUP); /* literal short string */
}
else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */
int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */
lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */
lua_assert(vreg == fs->f->numparams);
t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap) */
fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */
}
else {
/* register index of the table */
t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
if (isKstr(fs, k)) {
t->u.ind.idx = cast_short(k->u.info); /* literal short string */
t->k = VINDEXSTR;
}
else if (isCint(k)) { /* int. constant in proper range? */
t->u.ind.idx = cast_short(k->u.ival);
t->k = VINDEXI;
}
else {
t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */
t->k = VINDEXED;
}
if (isKstr(fs, k))
fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */
else if (isCint(k)) /* int. constant in proper range? */
fillidxk(t, cast_int(k->u.ival), VINDEXI);
else
fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */
}
t->u.ind.keystr = keystr; /* string index in 'k' */
t->u.ind.ro = 0; /* by default, not read-only */
@ -1881,6 +1929,8 @@ static int finaltarget (Instruction *code, int i) {
void luaK_finish (FuncState *fs) {
int i;
Proto *p = fs->f;
if (p->flag & PF_VATAB) /* will it use a vararg table? */
p->flag &= cast_byte(~PF_VAHID); /* then it will not use hidden args. */
for (i = 0; i < fs->pc; i++) {
Instruction *pc = &p->code[i];
/* avoid "not used" warnings when assert is off (for 'onelua.c') */
@ -1888,7 +1938,7 @@ void luaK_finish (FuncState *fs) {
lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc));
switch (GET_OPCODE(*pc)) {
case OP_RETURN0: case OP_RETURN1: {
if (!(fs->needclose || (p->flag & PF_ISVARARG)))
if (!(fs->needclose || (p->flag & PF_VAHID)))
break; /* no extra work */
/* else use OP_RETURN to do the extra work */
SET_OPCODE(*pc, OP_RETURN);
@ -1896,13 +1946,23 @@ void luaK_finish (FuncState *fs) {
case OP_RETURN: case OP_TAILCALL: {
if (fs->needclose)
SETARG_k(*pc, 1); /* signal that it needs to close */
if (p->flag & PF_ISVARARG)
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
if (p->flag & PF_VAHID) /* does it use hidden arguments? */
SETARG_C(*pc, p->numparams + 1); /* signal that */
break;
}
case OP_JMP: {
case OP_GETVARG: {
if (p->flag & PF_VATAB) /* function has a vararg table? */
SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */
break;
}
case OP_VARARG: {
if (p->flag & PF_VATAB) /* function has a vararg table? */
SETARG_k(*pc, 1); /* must get vararg there */
break;
}
case OP_JMP: { /* to optimize jumps to jumps */
int target = finaltarget(p->code, i);
fixjump(fs, i, target);
fixjump(fs, i, target); /* jump directly to final target */
break;
}
default: break;

View File

@ -68,9 +68,12 @@ LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C,
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_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k,
int line);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
LUAI_FUNC void luaK_vapar2local (FuncState *fs, expdesc *var);
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
@ -79,7 +82,6 @@ LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
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_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);

View File

@ -189,15 +189,17 @@ static int luaB_close (lua_State *L) {
return 2;
}
}
case COS_RUN: /* running coroutine? */
case COS_NORM:
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
case COS_RUN:
lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */
if (lua_tothread(L, -1) == co)
return luaL_error(L, "cannot close main thread");
lua_closethread(co, L); /* close itself */
lua_assert(0); /* previous call does not return */
/* previous call does not return *//* FALLTHROUGH */
default:
lua_assert(0);
return 0;
default: /* normal or running coroutine */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
}
}

View File

@ -18,7 +18,7 @@
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
/* consider all non-ascii codepoints to be alphabetic */
/* consider all non-ASCII codepoints to be alphabetic */
#define NONA 0x01
#else
#define NONA 0x00 /* default */

View File

@ -184,7 +184,7 @@ static const char *upvalname (const Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func.p))->p->flag & PF_ISVARARG) {
if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) {
int nextra = ci->u.l.nextraargs;
if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func.p - nextra - (n + 1);
@ -304,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
int i;
TValue v;
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
if (!(p->flag & PF_ISVARARG)) /* regular function? */
if (!(isvararg(p))) /* regular function? */
i = 0; /* consider all instructions */
else { /* vararg function */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
@ -348,7 +348,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
ar->nparams = 0;
}
else {
ar->isvararg = (f->l.p->flag & PF_ISVARARG) ? 1 : 0;
ar->isvararg = (isvararg(f->l.p)) ? 1 : 0;
ar->nparams = f->l.p->numparams;
}
break;
@ -814,6 +814,14 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
}
l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) {
const char *globalname = "?"; /* default name if k == 0 */
if (k > 0)
kname(cl->p, k - 1, &globalname);
luaG_runerror(L, "global '%s' already defined", globalname);
}
/* add src:line information to 'msg' */
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
int line) {
@ -904,7 +912,7 @@ int luaG_tracecall (lua_State *L) {
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->flag & PF_ISVARARG)
if (isvararg(p))
return 0; /* hooks will start at VARARGPREP instruction */
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */
luaD_hookcall(L, ci); /* check 'call' hook */

View File

@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k);
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line);

83
ldo.c
View File

@ -57,10 +57,18 @@
** =======================================================
*/
/* chained list of long jump buffers */
typedef struct lua_longjmp {
struct lua_longjmp *previous;
jmp_buf b;
volatile TStatus status; /* error code */
} lua_longjmp;
/*
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
** default, Lua handles errors with exceptions when compiling as
** C++ code, with _longjmp/_setjmp when asked to use them, and with
** C++ code, with _longjmp/_setjmp when available (POSIX), and with
** longjmp/setjmp otherwise.
*/
#if !defined(LUAI_THROW) /* { */
@ -69,38 +77,38 @@
/* C++ exceptions */
#define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,f,ud) \
try { (f)(L, ud); } catch(...) { if ((c)->status == 0) (c)->status = -1; }
#define luai_jmpbuf int /* dummy field */
static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) {
try {
f(L, ud); /* call function protected */
}
catch (lua_longjmp *c1) { /* Lua error */
if (c1 != c) /* not the correct level? */
throw; /* rethrow to upper level */
}
catch (...) { /* non-Lua exception */
c->status = -1; /* create some error code */
}
}
#elif defined(LUA_USE_POSIX) /* }{ */
/* in POSIX, try _longjmp/_setjmp (more efficient) */
/* in POSIX, use _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#else /* }{ */
/* ISO C handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
#define LUAI_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#endif /* } */
#endif /* } */
/* chain list of long jump buffers */
struct lua_longjmp {
struct lua_longjmp *previous;
luai_jmpbuf b;
volatile TStatus status; /* error code */
};
void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
if (errcode == LUA_ERRMEM) { /* memory error? */
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
@ -151,7 +159,7 @@ l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) {
TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
l_uint32 oldnCcalls = L->nCcalls;
struct lua_longjmp lj;
lua_longjmp lj;
lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
@ -174,6 +182,20 @@ TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
#define STACKERRSPACE 200
/*
** LUAI_MAXSTACK limits the size of the Lua stack.
** It must fit into INT_MAX/2.
*/
#if !defined(LUAI_MAXSTACK)
#if 1000000 < (INT_MAX / 2)
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK (INT_MAX / 2u)
#endif
#endif
/* maximum stack size that respects size_t */
#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE)
@ -189,7 +211,7 @@ TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
#define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE)
/* raise an error while running the message handler */
/* raise a stack error while running the message handler */
l_noret luaD_errerr (lua_State *L) {
TString *msg = luaS_newliteral(L, "error in error handling");
setsvalue2s(L, L->top.p, msg);
@ -198,6 +220,25 @@ l_noret luaD_errerr (lua_State *L) {
}
/*
** Check whether stacks have enough space to run a simple function (such
** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two
** available CallInfos, and two "slots" in the C stack.
*/
int luaD_checkminstack (lua_State *L) {
if (getCcalls(L) >= LUAI_MAXCCALLS - 2)
return 0; /* not enough C-stack slots */
if (L->ci->next == NULL && luaE_extendCI(L, 0) == NULL)
return 0; /* unable to allocate first ci */
if (L->ci->next->next == NULL && luaE_extendCI(L, 0) == NULL)
return 0; /* unable to allocate second ci */
if (L->stack_last.p - L->top.p >= BASIC_STACK_SIZE)
return 1; /* enough (BASIC_STACK_SIZE) free slots in the Lua stack */
else /* try to grow stack to a size with enough free slots */
return luaD_growstack(L, BASIC_STACK_SIZE, 0);
}
/*
** In ISO C, any pointer use after the pointer has been deallocated is
** undefined behavior. So, before a stack reallocation, all pointers
@ -325,7 +366,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror)
luaD_errerr(L); /* error inside message handler */
luaD_errerr(L); /* stack error inside message handler */
return 0; /* if not 'raiseerror', just signal it */
}
else if (n < MAXSTACK) { /* avoids arithmetic overflows */
@ -465,7 +506,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
int ftransfer;
if (isLua(ci)) {
Proto *p = ci_func(ci)->p;
if (p->flag & PF_ISVARARG)
if (p->flag & PF_VAHID)
delta = ci->u.l.nextraargs + p->numparams + 1;
}
ci->func.p += delta; /* if vararg, back to virtual 'func' */
@ -583,7 +624,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1))
/*

1
ldo.h
View File

@ -89,6 +89,7 @@ LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
LUAI_FUNC void luaD_inctop (lua_State *L);
LUAI_FUNC int luaD_checkminstack (lua_State *L);
LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode);
LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode);

22
ldump.c
View File

@ -132,27 +132,31 @@ static void dumpInteger (DumpState *D, lua_Integer x) {
/*
** Dump a String. First dump its "size": size==0 means NULL;
** size==1 is followed by an index and means "reuse saved string with
** that index"; size>=2 is followed by the string contents with real
** size==size-2 and means that string, which will be saved with
** the next available index.
** Dump a String. First dump its "size":
** size==0 is followed by an index and means "reuse saved string with
** that index"; index==0 means NULL.
** size>=1 is followed by the string contents with real size==size-1 and
** means that string, which will be saved with the next available index.
** The real size does not include the ending '\0' (which is not dumped),
** so adding 1 to it cannot overflow a size_t.
*/
static void dumpString (DumpState *D, TString *ts) {
if (ts == NULL)
dumpSize(D, 0);
if (ts == NULL) {
dumpVarint(D, 0); /* will "reuse" NULL */
dumpVarint(D, 0); /* special index for NULL */
}
else {
TValue idx;
int tag = luaH_getstr(D->h, ts, &idx);
if (!tagisempty(tag)) { /* string already saved? */
dumpVarint(D, 1); /* reuse a saved string */
dumpVarint(D, 0); /* reuse a saved string */
dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */
}
else { /* must write and save the string */
TValue key, value; /* to save the string in the hash */
size_t size;
const char *s = getlstr(ts, size);
dumpSize(D, size + 2);
dumpSize(D, size + 1);
dumpVector(D, s, size + 1); /* include ending '\0' */
D->nstr++; /* one more saved string */
setsvalue(D->L, &key, ts); /* the string is the key */

View File

@ -196,8 +196,7 @@ void luaF_unlinkupval (UpVal *uv) {
*/
void luaF_closeupval (lua_State *L, StkId level) {
UpVal *uv;
StkId upl; /* stack index pointed by 'uv' */
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top.p);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */

15
lgc.c
View File

@ -594,10 +594,10 @@ static void traversestrongtable (global_State *g, Table *h) {
*/
static int getmode (global_State *g, Table *h) {
const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
if (mode == NULL || !ttisshrstring(mode))
return 0; /* ignore non-(short)string modes */
if (mode == NULL || !ttisstring(mode))
return 0; /* ignore non-string modes */
else {
const char *smode = getshrstr(tsvalue(mode));
const char *smode = getstr(tsvalue(mode));
const char *weakkey = strchr(smode, 'k');
const char *weakvalue = strchr(smode, 'v');
return ((weakkey != NULL) << 1) | (weakvalue != NULL);
@ -624,7 +624,7 @@ static l_mem traversetable (global_State *g, Table *h) {
linkgclist(h, g->allweak); /* must clear collected entries */
break;
}
return 1 + 2*sizenode(h) + h->asize;
return cast(l_mem, 1 + 2*sizenode(h) + h->asize);
}
@ -1293,7 +1293,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
correctgraylists(g);
checkSizes(L, g);
g->gcstate = GCSpropagate; /* skip restart */
if (!g->gcemergency)
if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L))
callallpendingfinalizers(L);
}
@ -1667,12 +1667,13 @@ static l_mem singlestep (lua_State *L, int fast) {
break;
}
case GCScallfin: { /* call finalizers */
if (g->tobefnz && !g->gcemergency) {
if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) {
g->gcstopem = 0; /* ok collections during finalizers */
GCTM(L); /* call one finalizer */
stepresult = CWUFIN;
}
else { /* emergency mode or no more finalizers */
else { /* no more finalizers or emergency mode or not enough stack
to run finalizers */
g->gcstate = GCSpause; /* finish collection */
stepresult = step2pause;
}

View File

@ -114,7 +114,7 @@ static int l_checkmode (const char *mode) {
#if !defined(l_fseek) /* { */
#if defined(LUA_USE_POSIX) /* { */
#if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */
#include <sys/types.h>

View File

@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = {
#if 0
** you can update the following list with this command:
**
** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
**
#endif
@ -57,8 +57,8 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_BANDK,
&&L_OP_BORK,
&&L_OP_BXORK,
&&L_OP_SHRI,
&&L_OP_SHLI,
&&L_OP_SHRI,
&&L_OP_ADD,
&&L_OP_SUB,
&&L_OP_MUL,
@ -106,6 +106,8 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_SETLIST,
&&L_OP_CLOSURE,
&&L_OP_VARARG,
&&L_OP_GETVARG,
&&L_OP_ERRNNIL,
&&L_OP_VARARGPREP,
&&L_OP_EXTRAARG

View File

@ -20,8 +20,8 @@
/*
** 'l_mem' is a signed integer big enough to count the total memory
** used by Lua. (It is signed due to the use of debt in several
** computations.) Usually, 'ptrdiff_t' should work, but we use 'long'
** for 16-bit machines.
** computations.) 'lu_mem' is a corresponding unsigned type. Usually,
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
*/
#if defined(LUAI_MEM) /* { external definitions? */
typedef LUAI_MEM l_mem;
@ -59,13 +59,6 @@ typedef lu_byte TStatus;
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: cast_sizet(LUA_MAXINTEGER))
/*
** floor of the log2 of the maximum signed value for integral type 't'.
** (That is, maximum 'n' such that '2^n' fits in the given signed type.)
*/
#define log2maxs(t) (l_numbits(t) - 2)
/*
** test whether an unsigned value is a power of 2 (or zero)
*/
@ -287,6 +280,55 @@ typedef unsigned long l_uint32;
#endif
/*
** lua_numbertointeger converts a float number with an integral value
** to an integer, or returns 0 if the float is not within the range of
** a lua_Integer. (The range comparisons are tricky because of
** rounding. The tests here assume a two-complement representation,
** where MININTEGER always has an exact representation as a float;
** MAXINTEGER may not have one, and therefore its conversion to float
** may have an ill-defined value.)
*/
#define lua_numbertointeger(n,p) \
((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
(n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
(*(p) = (LUA_INTEGER)(n), 1))
/*
** LUAI_FUNC is a mark for all extern functions that are not to be
** exported to outside modules.
** LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables,
** none of which to be exported to outside modules (LUAI_DDEF for
** definitions and LUAI_DDEC for declarations).
** Elf and MACH/gcc (versions 3.2 and later) mark them as "hidden" to
** optimize access when Lua is compiled as a shared library. Not all elf
** targets support this attribute. Unfortunately, gcc does not offer
** a way to check whether the target offers that support, and those
** without support give a warning about it. To avoid these warnings,
** change to the default definition.
*/
#if !defined(LUAI_FUNC)
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
(defined(__ELF__) || defined(__MACH__))
#define LUAI_FUNC __attribute__((visibility("internal"))) extern
#else
#define LUAI_FUNC extern
#endif
#define LUAI_DDEC(dec) LUAI_FUNC dec
#define LUAI_DDEF /* empty */
#endif
/* Give these macros simpler names for internal use */
#define l_likely(x) luai_likely(x)
#define l_unlikely(x) luai_unlikely(x)
/*
** {==================================================================
** "Abstraction Layer" for basic report of messages and errors

View File

@ -38,31 +38,37 @@ static int math_abs (lua_State *L) {
return 1;
}
static int math_sin (lua_State *L) {
lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
return 1;
}
static int math_cos (lua_State *L) {
lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
return 1;
}
static int math_tan (lua_State *L) {
lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
return 1;
}
static int math_asin (lua_State *L) {
lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
return 1;
}
static int math_acos (lua_State *L) {
lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan (lua_State *L) {
lua_Number y = luaL_checknumber(L, 1);
lua_Number x = luaL_optnumber(L, 2, 1);
@ -167,6 +173,7 @@ static int math_ult (lua_State *L) {
return 1;
}
static int math_log (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1);
lua_Number res;
@ -188,22 +195,42 @@ static int math_log (lua_State *L) {
return 1;
}
static int math_exp (lua_State *L) {
lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
return 1;
}
static int math_deg (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
return 1;
}
static int math_rad (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
return 1;
}
static int math_frexp (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1);
int ep;
lua_pushnumber(L, l_mathop(frexp)(x, &ep));
lua_pushinteger(L, ep);
return 2;
}
static int math_ldexp (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1);
int ep = (int)luaL_checkinteger(L, 2);
lua_pushnumber(L, l_mathop(ldexp)(x, ep));
return 1;
}
static int math_min (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int imin = 1; /* index of current minimum value */
@ -251,7 +278,7 @@ static int math_type (lua_State *L) {
*/
/*
** This code uses lots of shifts. ANSI C does not allow shifts greater
** This code uses lots of shifts. ISO 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
@ -666,20 +693,6 @@ static int math_pow (lua_State *L) {
return 1;
}
static int math_frexp (lua_State *L) {
int e;
lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e));
lua_pushinteger(L, e);
return 2;
}
static int math_ldexp (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1);
int ep = (int)luaL_checkinteger(L, 2);
lua_pushnumber(L, l_mathop(ldexp)(x, ep));
return 1;
}
static int math_log10 (lua_State *L) {
lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
return 1;
@ -702,7 +715,9 @@ static const luaL_Reg mathlib[] = {
{"tointeger", math_toint},
{"floor", math_floor},
{"fmod", math_fmod},
{"frexp", math_frexp},
{"ult", math_ult},
{"ldexp", math_ldexp},
{"log", math_log},
{"max", math_max},
{"min", math_min},
@ -718,8 +733,6 @@ static const luaL_Reg mathlib[] = {
{"sinh", math_sinh},
{"tanh", math_tanh},
{"pow", math_pow},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10},
#endif
/* placeholders */

View File

@ -306,6 +306,16 @@ static void setpath (lua_State *L, const char *fieldname,
/* }================================================================== */
/*
** External strings created by DLLs may need the DLL code to be
** deallocated. This implies that a DLL can only be unloaded after all
** its strings were deallocated. To ensure that, we create a 'library
** string' to represent each DLL, and when this string is deallocated
** it closes its corresponding DLL.
** (The string itself is irrelevant; its userdata is the DLL pointer.)
*/
/*
** return registry.CLIBS[path]
*/
@ -320,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) {
/*
** registry.CLIBS[path] = plib -- for queries
** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
** Deallocate function for library strings.
** Unload the DLL associated with the string being deallocated.
*/
static void addtoclib (lua_State *L, const char *path, void *plib) {
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushlightuserdata(L, plib);
lua_pushvalue(L, -1);
lua_setfield(L, -3, path); /* CLIBS[path] = plib */
lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
lua_pop(L, 1); /* pop CLIBS table */
static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) {
/* string itself is irrelevant and static */
(void)ptr; (void)osize; (void)nsize;
lsys_unloadlib(ud); /* unload library represented by the string */
return NULL;
}
/*
** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
** handles in list CLIBS
** Create a library string that, when deallocated, will unload 'plib'
*/
static int gctm (lua_State *L) {
lua_Integer n = luaL_len(L, 1);
for (; n >= 1; n--) { /* for each handle, in reverse order */
lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
lsys_unloadlib(lua_touserdata(L, -1));
lua_pop(L, 1); /* pop handle */
}
return 0;
static void createlibstr (lua_State *L, void *plib) {
/* common content for all library strings */
static const char dummy[] = "01234567890";
lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib);
}
/*
** registry.CLIBS[path] = plib -- for queries.
** Also create a reference to strlib, so that the library string will
** only be collected when registry.CLIBS is collected.
*/
static void addtoclib (lua_State *L, const char *path, void *plib) {
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushlightuserdata(L, plib);
lua_setfield(L, -2, path); /* CLIBS[path] = plib */
createlibstr(L, plib);
luaL_ref(L, -2); /* keep library string in CLIBS */
lua_pop(L, 1); /* pop CLIBS table */
}
/* error codes for 'lookforfunc' */
#define ERRLIB 1
@ -361,8 +378,8 @@ static int gctm (lua_State *L) {
** Then, if 'sym' is '*', return true (as library has been loaded).
** Otherwise, look for symbol 'sym' in the library and push a
** C function with that symbol.
** Return 0 and 'true' or a function in the stack; in case of
** errors, return an error code and an error message in the stack.
** Return 0 with 'true' or a function in the stack; in case of
** errors, return an error code with an error message in the stack.
*/
static int lookforfunc (lua_State *L, const char *path, const char *sym) {
void *reg = checkclib(L, path); /* check loaded C libraries */
@ -704,21 +721,9 @@ static void createsearcherstable (lua_State *L) {
}
/*
** create table CLIBS to keep track of loaded C libraries,
** setting a finalizer to close all libraries when closing state.
*/
static void createclibstable (lua_State *L) {
luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
lua_createtable(L, 0, 1); /* create metatable for CLIBS */
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
lua_setmetatable(L, -2);
}
LUAMOD_API int luaopen_package (lua_State *L) {
createclibstable(L);
luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
lua_pop(L, 1); /* will not use it now */
luaL_newlib(L, pk_funcs); /* create 'package' table */
createsearcherstable(L);
/* set paths */

View File

@ -31,7 +31,8 @@
/*
** Computes ceil(log2(x))
** Computes ceil(log2(x)), which is the smallest integer n such that
** x <= (1 << n).
*/
lu_byte luaO_ceillog2 (unsigned int x) {
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */
@ -86,7 +87,7 @@ lu_byte luaO_codeparam (unsigned int p) {
** overflow, so we check which order is best.
*/
l_mem luaO_applyparam (lu_byte p, l_mem x) {
unsigned int m = p & 0xF; /* mantissa */
int m = p & 0xF; /* mantissa */
int e = (p >> 4); /* exponent */
if (e > 0) { /* normalized? */
e--; /* correct exponent */
@ -385,7 +386,7 @@ size_t luaO_str2num (const char *s, TValue *o) {
int luaO_utf8esc (char *buff, l_uint32 x) {
int n = 1; /* number of bytes put in buffer (backwards) */
lua_assert(x <= 0x7FFFFFFFu);
if (x < 0x80) /* ascii? */
if (x < 0x80) /* ASCII? */
buff[UTF8BUFFSZ - 1] = cast_char(x);
else { /* need continuation bytes */
unsigned int mfb = 0x3f; /* maximum that fits in first byte */

View File

@ -418,6 +418,7 @@ typedef struct TString {
#define strisshr(ts) ((ts)->shrlen >= 0)
#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG)
/*
@ -582,9 +583,18 @@ typedef struct AbsLineInfo {
/*
** Flags in Prototypes
*/
#define PF_ISVARARG 1
#define PF_FIXED 2 /* prototype has parts in fixed memory */
#define PF_VAHID 1 /* function has hidden vararg arguments */
#define PF_VATAB 2 /* function has vararg table */
#define PF_FIXED 4 /* prototype has parts in fixed memory */
/* a vararg function either has hidden args. or a vararg table */
#define isvararg(p) ((p)->flag & (PF_VAHID | PF_VATAB))
/*
** mark that a function needs a vararg table. (The flag PF_VAHID will
** be cleared later.)
*/
#define needvatab(p) ((p)->flag |= PF_VATAB)
/*
** Function Prototypes

View File

@ -53,8 +53,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
@ -102,6 +102,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */
,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
};

View File

@ -224,8 +224,8 @@ enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ};
/*
** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
** has extra descriptions in the notes after the enumeration.
** Grep "ORDER OP" if you change this enum.
** See "Notes" below for more information about some instructions.
*/
typedef enum {
@ -238,7 +238,7 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
OP_LOADK,/* A Bx R[A] := K[Bx] */
OP_LOADKX,/* A R[A] := K[extra arg] */
OP_LOADFALSE,/* A R[A] := false */
OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */
OP_LFALSESKIP,/*A R[A] := false; pc++ */
OP_LOADTRUE,/* A R[A] := true */
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
@ -272,8 +272,8 @@ OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */
OP_SHRI,/* A B sC R[A] := R[B] >> sC */
OP_SHLI,/* A B sC R[A] := sC << R[B] */
OP_SHRI,/* A B sC R[A] := R[B] >> sC */
OP_ADD,/* A B C R[A] := R[B] + R[C] */
OP_SUB,/* A B C R[A] := R[B] - R[C] */
@ -289,7 +289,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */
OP_SHL,/* A B C R[A] := R[B] << R[C] */
OP_SHR,/* A B C R[A] := R[B] >> R[C] */
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */
OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */
OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
@ -315,12 +315,12 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
OP_TEST,/* A k if (not R[A] == k) then pc++ */
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */
OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] */
OP_RETURN0,/* return */
OP_RETURN1,/* A return R[A] */
@ -336,9 +336,13 @@ OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */
OP_VARARGPREP,/*A (adjust vararg parameters) */
OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */
OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx - 1] is global name)*/
OP_VARARGPREP,/* (adjust varargs) */
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode;
@ -367,7 +371,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
OP_RETURN*, OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
set top (like in OP_CALL with C == 0). 'k' means function has a
vararg table, which is in R[B].
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
@ -382,18 +387,23 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
power of 2) plus 1, or zero for size zero. If not k, the array size
is vC. Otherwise, the array size is EXTRAARG _ vC.
(*) In OP_ERRNNIL, (Bx == 0) means index of global name doesn't
fit in Bx. (So, that name is not available for the error message.)
(*) For comparisons, k specifies what condition the test should accept
(true or false).
(*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
(the constant is the first operand).
(the constant is the first operand).
(*) All 'skips' (pc++) assume that next instruction is a jump.
(*) All comparison and test instructions assume that the instruction
being skipped (pc++) is a jump.
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
function builds upvalues, which may need to be closed. C > 0 means
the function is vararg, so that its 'func' must be corrected before
returning; in this case, (C - 1) is its number of fixed parameters.
the function has hidden vararg arguments, so that its 'func' must be
corrected before returning; in this case, (C - 1) is its number of
fixed parameters.
(*) In comparisons with an immediate operand, C signals whether the
original operand was a float. (It must be corrected in case of

View File

@ -45,8 +45,8 @@ static const char *const opnames[] = {
"BANDK",
"BORK",
"BXORK",
"SHRI",
"SHLI",
"SHRI",
"ADD",
"SUB",
"MUL",
@ -94,6 +94,8 @@ static const char *const opnames[] = {
"SETLIST",
"CLOSURE",
"VARARG",
"GETVARG",
"ERRNNIL",
"VARARGPREP",
"EXTRAARG",
NULL

View File

@ -34,7 +34,7 @@
#if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
#elif defined(LUA_USE_C89) /* C89 (only 1-char options) */
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
#else /* C99 specification */
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
@ -273,7 +273,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
static const char *checkoption (lua_State *L, const char *conv,
ptrdiff_t convlen, char *buff) {
size_t convlen, char *buff) {
const char *option = LUA_STRFTIMEOPTIONS;
unsigned oplen = 1; /* length of options being checked */
for (; *option != '\0' && oplen <= convlen; option += oplen) {
@ -333,7 +333,8 @@ static int os_date (lua_State *L) {
size_t reslen;
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
s++; /* skip '%' */
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
/* copy specifier to 'cc' */
s = checkoption(L, s, ct_diff2sz(se - s), cc + 1);
reslen = strftime(buff, SIZETIMEFMT, cc, stm);
luaL_addsize(&b, reslen);
}

192
lparser.c
View File

@ -30,7 +30,7 @@
/* maximum number of variable declarationss per function (must be
/* maximum number of variable declarations per function (must be
smaller than 250, due to the bytecode format) */
#define MAXVARS 200
@ -197,7 +197,7 @@ static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
Dyndata *dyd = ls->dyd;
Vardesc *var;
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarationss");
dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations");
var = &dyd->actvar.arr[dyd->actvar.n++];
var->vd.kind = kind; /* default */
var->vd.name = name;
@ -279,7 +279,9 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
/*
** Raises an error if variable described by 'e' is read only
** Raises an error if variable described by 'e' is read only; moreover,
** if 'e' is t[exp] where t is the vararg parameter, change it to index
** a real table. (Virtual vararg tables cannot be changed.)
*/
static void check_readonly (LexState *ls, expdesc *e) {
FuncState *fs = ls->fs;
@ -289,7 +291,7 @@ static void check_readonly (LexState *ls, expdesc *e) {
varname = ls->dyd->actvar.arr[e->u.info].vd.name;
break;
}
case VLOCAL: {
case VLOCAL: case VVARGVAR: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
varname = vardesc->vd.name;
@ -301,6 +303,10 @@ static void check_readonly (LexState *ls, expdesc *e) {
varname = up->name;
break;
}
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
e->k = VINDEXED;
} /* FALLTHROUGH */
case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */
if (e->u.ind.ro) /* read-only? */
varname = tsvalue(&fs->f->k[e->u.ind.keystr]);
@ -426,8 +432,11 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) {
else if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i);
else /* local variable */
else { /* local variable */
init_var(fs, var, i);
if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */
var->k = VVARGVAR;
}
return cast_int(var->k);
}
}
@ -467,8 +476,13 @@ static void marktobeclosed (FuncState *fs) {
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
int v = searchvar(fs, n, var); /* look up variables at current level */
if (v >= 0) { /* found? */
if (v == VLOCAL && !base)
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
if (!base) {
if (var->k == VVARGVAR) /* vararg parameter? */
luaK_vapar2local(fs, var); /* change it to a regular local */
if (var->k == VLOCAL)
markupval(fs, var->u.var.vidx); /* will be used as an upvalue */
}
/* else nothing else to be done */
}
else { /* not found at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */
@ -485,6 +499,20 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
}
static void buildglobal (LexState *ls, TString *varname, expdesc *var) {
FuncState *fs = ls->fs;
expdesc key;
init_exp(var, VGLOBAL, -1); /* global by default */
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
if (var->k == VGLOBAL)
luaK_semerror(ls, "%s is global when accessing variable '%s'",
LUA_ENV, getstr(varname));
luaK_exp2anyregup(fs, var); /* _ENV could be a constant */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* 'var' represents _ENV[varname] */
}
/*
** Find a variable with the given name 'n', handling global variables
** too.
@ -494,18 +522,11 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
init_exp(var, VGLOBAL, -1); /* global by default */
singlevaraux(fs, varname, var, 1);
if (var->k == VGLOBAL) { /* global name? */
expdesc key;
int info = var->u.info;
/* global by default in the scope of a global declaration? */
if (info == -2)
luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
if (var->k == VGLOBAL)
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
getstr(varname));
luaK_exp2anyregup(fs, var); /* but could be a constant */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
buildglobal(ls, varname, var);
if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST)
var->u.ind.ro = 1; /* mark variable as read-only */
else /* anyway must be a global */
@ -526,6 +547,7 @@ static void singlevar (LexState *ls, expdesc *var) {
static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
FuncState *fs = ls->fs;
int needed = nvars - nexps; /* extra values needed */
luaK_checkstack(fs, needed);
if (hasmultret(e->k)) { /* last expression has multiple returns? */
int extra = needed + 1; /* discount last expression itself */
if (extra < 0)
@ -665,7 +687,7 @@ static void createlabel (LexState *ls, TString *name, int line, int last) {
/*
** Traverse the pending goto's of the finishing block checking whether
** Traverse the pending gotos of the finishing block checking whether
** each match some label of that block. Those that do not match are
** "exported" to the outer block, to be solved there. In particular,
** its 'nactvar' is updated with the level of the inner block,
@ -898,6 +920,19 @@ typedef struct ConsControl {
} ConsControl;
/*
** Maximum number of elements in a constructor, to control the following:
** * counter overflows;
** * overflows in 'extra' for OP_NEWTABLE and OP_SETLIST;
** * overflows when adding multiple returns in OP_SETLIST.
*/
#define MAX_CNST (INT_MAX/2)
#if MAX_CNST/(MAXARG_vC + 1) > MAXARG_Ax
#undef MAX_CNST
#define MAX_CNST (MAXARG_Ax * (MAXARG_vC + 1))
#endif
static void recfield (LexState *ls, ConsControl *cc) {
/* recfield -> (NAME | '['exp']') = exp */
FuncState *fs = ls->fs;
@ -918,7 +953,7 @@ static void recfield (LexState *ls, ConsControl *cc) {
static void closelistfield (FuncState *fs, ConsControl *cc) {
if (cc->v.k == VVOID) return; /* there is no list item */
lua_assert(cc->tostore > 0);
luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID;
if (cc->tostore >= cc->maxtostore) {
@ -1006,10 +1041,12 @@ static void constructor (LexState *ls, expdesc *t) {
checknext(ls, '{' /*}*/);
cc.maxtostore = maxtostore(fs);
do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
if (ls->t.token == /*{*/ '}') break;
closelistfield(fs, &cc);
if (cc.v.k != VVOID) /* is there a previous list item? */
closelistfield(fs, &cc); /* close it */
field(ls, &cc);
luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST,
"items in a constructor");
} while (testnext(ls, ',') || testnext(ls, ';'));
check_match(ls, /*{*/ '}', '{' /*}*/, line);
lastlistfield(fs, &cc);
@ -1019,9 +1056,9 @@ static void constructor (LexState *ls, expdesc *t) {
/* }====================================================================== */
static void setvararg (FuncState *fs, int nparams) {
fs->f->flag |= PF_ISVARARG;
luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0);
static void setvararg (FuncState *fs) {
fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */
luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0);
}
@ -1030,7 +1067,7 @@ static void parlist (LexState *ls) {
FuncState *fs = ls->fs;
Proto *f = fs->f;
int nparams = 0;
int isvararg = 0;
int varargk = 0;
if (ls->t.token != ')') { /* is 'parlist' not empty? */
do {
switch (ls->t.token) {
@ -1040,19 +1077,26 @@ static void parlist (LexState *ls) {
break;
}
case TK_DOTS: {
luaX_next(ls);
isvararg = 1;
varargk = 1;
luaX_next(ls); /* skip '...' */
if (ls->t.token == TK_NAME)
new_varkind(ls, str_checkname(ls), RDKVAVAR);
else
new_localvarliteral(ls, "(vararg table)");
break;
}
default: luaX_syntaxerror(ls, "<name> or '...' expected");
}
} while (!isvararg && testnext(ls, ','));
} while (!varargk && testnext(ls, ','));
}
adjustlocalvars(ls, nparams);
f->numparams = cast_byte(fs->nactvar);
if (isvararg)
setvararg(fs, f->numparams); /* declared vararg */
luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
if (varargk) {
setvararg(fs); /* declared vararg */
adjustlocalvars(ls, 1); /* vararg parameter */
}
/* reserve registers for parameters (plus vararg parameter, if present) */
luaK_reserveregs(fs, fs->nactvar);
}
@ -1239,9 +1283,9 @@ static void simpleexp (LexState *ls, expdesc *v) {
}
case TK_DOTS: { /* vararg */
FuncState *fs = ls->fs;
check_condition(ls, fs->f->flag & PF_ISVARARG,
check_condition(ls, isvararg(fs->f),
"cannot use '...' outside a vararg function");
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1));
break;
}
case '{' /*}*/: { /* constructor */
@ -1435,6 +1479,15 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
}
}
/* Create code to store the "top" register in 'var' */
static void storevartop (FuncState *fs, expdesc *var) {
expdesc e;
init_exp(&e, VNONRELOC, fs->freereg - 1);
luaK_storevar(fs, var, &e); /* will also free the top register */
}
/*
** Parse and compile a multiple assignment. The first "variable"
** (a 'suffixedexp') was already read by the caller.
@ -1468,8 +1521,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
return; /* avoid default */
}
}
init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
luaK_storevar(ls->fs, &lh->v, &e);
storevartop(ls->fs, &lh->v); /* default assignment */
}
@ -1812,7 +1864,7 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
switch (kind) {
case RDKTOCLOSE:
luaK_semerror(ls, "global variables cannot be to-be-closed");
break; /* to avoid warnings */
return kind; /* to avoid warnings */
case RDKCONST:
return GDKCONST; /* adjust kind for global variable */
default:
@ -1821,25 +1873,74 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
}
static void checkglobal (LexState *ls, TString *varname, int line) {
FuncState *fs = ls->fs;
expdesc var;
int k;
buildglobal(ls, varname, &var); /* create global variable in 'var' */
k = var.u.ind.keystr; /* index of global name in 'k' */
luaK_codecheckglobal(fs, &var, k, line);
}
/*
** Recursively traverse list of globals to be initalized. When
** going, generate table description for the global. In the end,
** after all indices have been generated, read list of initializing
** expressions. When returning, generate the assignment of the value on
** the stack to the corresponding table description. 'n' is the variable
** being handled, range [0, nvars - 1].
*/
static void initglobal (LexState *ls, int nvars, int firstidx, int n,
int line) {
if (n == nvars) { /* traversed all variables? */
expdesc e;
int nexps = explist(ls, &e); /* read list of expressions */
adjust_assign(ls, nvars, nexps, &e);
}
else { /* handle variable 'n' */
FuncState *fs = ls->fs;
expdesc var;
TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name;
buildglobal(ls, varname, &var); /* create global variable in 'var' */
enterlevel(ls); /* control recursion depth */
initglobal(ls, nvars, firstidx, n + 1, line);
leavelevel(ls);
checkglobal(ls, varname, line);
storevartop(fs, &var);
}
}
static void globalnames (LexState *ls, lu_byte defkind) {
FuncState *fs = ls->fs;
int nvars = 0;
int lastidx; /* index of last registered variable */
do { /* for each name */
TString *vname = str_checkname(ls);
lu_byte kind = getglobalattribute(ls, defkind);
lastidx = new_varkind(ls, vname, kind);
nvars++;
} while (testnext(ls, ','));
if (testnext(ls, '=')) /* initialization? */
initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber);
fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */
}
static void globalstat (LexState *ls) {
/* globalstat -> (GLOBAL) attrib '*'
globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */
FuncState *fs = ls->fs;
/* get prefixed attribute (if any); default is regular global variable */
lu_byte defkind = getglobalattribute(ls, GDKREG);
if (testnext(ls, '*')) {
if (!testnext(ls, '*'))
globalnames(ls, defkind);
else {
/* use NULL as name to represent '*' entries */
new_varkind(ls, NULL, defkind);
fs->nactvar++; /* activate declaration */
}
else {
do { /* list of names */
TString *vname = str_checkname(ls);
lu_byte kind = getglobalattribute(ls, defkind);
new_varkind(ls, vname, kind);
fs->nactvar++; /* activate declaration */
} while (testnext(ls, ','));
}
}
@ -1850,8 +1951,9 @@ static void globalfunc (LexState *ls, int line) {
TString *fname = str_checkname(ls);
new_varkind(ls, fname, GDKREG); /* declare global variable */
fs->nactvar++; /* enter its scope */
buildvar(ls, fname, &var);
buildglobal(ls, fname, &var);
body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
checkglobal(ls, fname, line);
luaK_storevar(fs, &var, &b);
luaK_fixline(fs, line); /* definition "happens" in the first line */
}
@ -2049,7 +2151,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl;
Upvaldesc *env;
open_func(ls, fs, &bl);
setvararg(fs, 0); /* main function is always declared vararg */
setvararg(fs); /* main function is always vararg */
env = allocupvalue(fs); /* ...set environment upvalue */
env->instack = 1;
env->idx = 0;

View File

@ -37,6 +37,8 @@ typedef enum {
info = result register */
VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VVARGVAR, /* vararg parameter; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VGLOBAL, /* global variable;
info = relative index in 'actvar.arr' (or -1 for
implicit declaration) */
@ -49,6 +51,8 @@ typedef enum {
ind.ro = true if it represents a read-only global;
ind.keystr = if key is a string, index in 'k' of that string;
-1 if key is not a string */
VVARGIND, /* indexed vararg parameter;
ind.* as in VINDEXED */
VINDEXUP, /* indexed upvalue;
ind.idx = key's K index;
ind.* as in VINDEXED */
@ -97,10 +101,11 @@ typedef struct expdesc {
/* kinds of variables */
#define VDKREG 0 /* regular local */
#define RDKCONST 1 /* local constant */
#define RDKTOCLOSE 2 /* to-be-closed */
#define RDKCTC 3 /* local compile-time constant */
#define GDKREG 4 /* regular global */
#define GDKCONST 5 /* global constant */
#define RDKVAVAR 2 /* vararg parameter */
#define RDKTOCLOSE 3 /* to-be-closed */
#define RDKCTC 4 /* local compile-time constant */
#define GDKREG 5 /* regular global */
#define GDKCONST 6 /* global constant */
/* variables that live in registers */
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)

View File

@ -68,14 +68,19 @@ void luaE_setdebt (global_State *g, l_mem debt) {
}
CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *luaE_extendCI (lua_State *L, int err) {
CallInfo *ci;
lua_assert(L->ci->next == NULL);
ci = luaM_new(L, CallInfo);
lua_assert(L->ci->next == NULL);
L->ci->next = ci;
ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo);
if (l_unlikely(ci == NULL)) { /* allocation failed? */
if (err)
luaM_error(L); /* raise the error */
return NULL; /* else only report it */
}
ci->next = L->ci->next;
ci->previous = L->ci;
ci->next = NULL;
L->ci->next = ci;
if (ci->next)
ci->next->previous = ci;
ci->u.l.trap = 0;
L->nci++;
return ci;

View File

@ -85,7 +85,7 @@ typedef struct CallInfo CallInfo;
** they must be visited again at the end of the cycle), but they are
** marked black because assignments to them must activate barriers (to
** move them back to TOUCHED1).
** - Open upvales are kept gray to avoid barriers, but they stay out
** - Open upvalues are kept gray to avoid barriers, but they stay out
** of gray lists. (They don't even have a 'gclist' field.)
*/
@ -232,7 +232,7 @@ struct CallInfo {
/* call is running a C function (still in first 16 bits) */
#define CIST_C (1u << (CIST_RECST + 3))
/* call is on a fresh "luaV_execute" frame */
#define CIST_FRESH cast(l_uint32, CIST_C << 1)
#define CIST_FRESH (cast(l_uint32, CIST_C) << 1)
/* function is closing tbc variables */
#define CIST_CLSRET (CIST_FRESH << 1)
/* function has tbc variables to close */
@ -249,10 +249,6 @@ struct CallInfo {
#define CIST_HOOKYIELD (CIST_TAIL << 1)
/* function "called" a finalizer */
#define CIST_FIN (CIST_HOOKYIELD << 1)
#if defined(LUA_COMPAT_LT_LE)
/* using __lt for __le */
#define CIST_LEQ (CIST_FIN << 1)
#endif
#define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1)
@ -430,9 +426,9 @@ union GCUnion {
/*
** macro to convert a Lua object into a GCObject
** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.)
*/
#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
#define obj2gco(v) \
check_exp(novariant((v)->tt) >= LUA_TSTRING, &(cast_u(v)->gc))
/* actual number of total memory allocated */
@ -442,7 +438,7 @@ union GCUnion {
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC lu_mem luaE_threadsize (lua_State *L);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err);
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);

View File

@ -39,18 +39,18 @@
/*
** equality for long strings
** generic equality for strings
*/
int luaS_eqlngstr (TString *a, TString *b) {
size_t len = a->u.lnglen;
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
return (a == b) || /* same instance or... */
((len == b->u.lnglen) && /* equal length and ... */
(memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */
int luaS_eqstr (TString *a, TString *b) {
size_t len1, len2;
const char *s1 = getlstr(a, len1);
const char *s2 = getlstr(b, len2);
return ((len1 == len2) && /* equal length and ... */
(memcmp(s1, s2, len1) == 0)); /* equal contents */
}
unsigned luaS_hash (const char *str, size_t l, unsigned seed) {
static unsigned luaS_hash (const char *str, size_t l, unsigned seed) {
unsigned int h = seed ^ cast_uint(l);
for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
@ -315,28 +315,9 @@ static void f_newext (lua_State *L, void *ud) {
}
static void f_pintern (lua_State *L, void *ud) {
struct NewExt *ne = cast(struct NewExt *, ud);
ne->ts = internshrstr(L, ne->s, ne->len);
}
TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud) {
struct NewExt ne;
if (len <= LUAI_MAXSHORTLEN) { /* short string? */
ne.s = s; ne.len = len;
if (!falloc)
f_pintern(L, &ne); /* just internalize string */
else {
TStatus status = luaD_rawrunprotected(L, f_pintern, &ne);
(*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */
if (status != LUA_OK) /* memory error? */
luaM_error(L); /* re-raise memory error */
}
return ne.ts;
}
/* "normal" case: long strings */
if (!falloc) {
ne.kind = LSTRFIX;
f_newext(L, &ne); /* just create header */
@ -357,3 +338,16 @@ TString *luaS_newextlstr (lua_State *L,
}
/*
** Normalize an external string: If it is short, internalize it.
*/
TString *luaS_normstr (lua_State *L, TString *ts) {
size_t len = ts->u.lnglen;
if (len > LUAI_MAXSHORTLEN)
return ts; /* long string; keep the original */
else {
const char *str = getlngstr(ts);
return internshrstr(L, str, len);
}
}

View File

@ -54,9 +54,8 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
LUAI_FUNC unsigned luaS_hash (const char *str, size_t l, unsigned seed);
LUAI_FUNC unsigned luaS_hashlongstr (TString *ts);
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC int luaS_eqstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
LUAI_FUNC void luaS_clearcache (global_State *g);
LUAI_FUNC void luaS_init (lua_State *L);
@ -69,5 +68,6 @@ LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
LUAI_FUNC TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud);
LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind);
LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts);
#endif

View File

@ -132,27 +132,31 @@ static int str_upper (lua_State *L) {
}
/*
** MAX_SIZE is limited both by size_t and lua_Integer.
** When x <= MAX_SIZE, x can be safely cast to size_t or lua_Integer.
*/
static int str_rep (lua_State *L) {
size_t l, lsep;
const char *s = luaL_checklstring(L, 1, &l);
size_t len, lsep;
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer n = luaL_checkinteger(L, 2);
const char *sep = luaL_optlstring(L, 3, "", &lsep);
if (n <= 0)
lua_pushliteral(L, "");
else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n)))
if (n <= 0 || (len | lsep) == 0)
lua_pushliteral(L, ""); /* no repetitions or both strings empty */
else if (l_unlikely(len > MAX_SIZE - lsep ||
cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n))
return luaL_error(L, "resulting string too large");
else {
size_t totallen = ((size_t)n * (l + lsep)) - lsep;
size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep;
luaL_Buffer b;
char *p = luaL_buffinitsize(L, &b, totallen);
while (n-- > 1) { /* first n-1 copies (followed by separator) */
memcpy(p, s, l * sizeof(char)); p += l;
memcpy(p, s, len * sizeof(char)); p += len;
if (lsep > 0) { /* empty 'memcpy' is not that cheap */
memcpy(p, sep, lsep * sizeof(char));
p += lsep;
memcpy(p, sep, lsep * sizeof(char)); p += lsep;
}
}
memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
memcpy(p, s, len * sizeof(char)); /* last copy without separator */
luaL_pushresultsize(&b, totallen);
}
return 1;
@ -265,11 +269,18 @@ static int tonum (lua_State *L, int arg) {
}
static void trymt (lua_State *L, const char *mtname) {
/*
** To be here, either the first operand was a string or the first
** operand didn't have a corresponding metamethod. (Otherwise, that
** other metamethod would have been called.) So, if this metamethod
** doesn't work, the only other option would be for the second
** operand to have a different metamethod.
*/
static void trymt (lua_State *L, const char *mtkey, const char *opname) {
lua_settop(L, 2); /* back to the original arguments */
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
!luaL_getmetafield(L, 2, mtname)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
!luaL_getmetafield(L, 2, mtkey)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", opname,
luaL_typename(L, -2), luaL_typename(L, -1));
lua_insert(L, -3); /* put metamethod before arguments */
lua_call(L, 2, 1); /* call metamethod */
@ -280,7 +291,7 @@ static int arith (lua_State *L, int op, const char *mtname) {
if (tonum(L, 1) && tonum(L, 2))
lua_arith(L, op); /* result will be on the top */
else
trymt(L, mtname);
trymt(L, mtname, mtname + 2);
return 1;
}
@ -957,7 +968,7 @@ static int str_gsub (lua_State *L) {
reprepstate(&ms); /* (re)prepare state for new match */
if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
n++;
changed = add_value(&ms, &b, src, e, tr) | changed;
changed = add_value(&ms, &b, src, e, tr) || changed;
src = lastmatch = e;
}
else if (src < ms.src_end) /* otherwise, skip one character */
@ -1715,7 +1726,7 @@ static int str_packsize (lua_State *L) {
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
"variable-length format");
size += ntoalign; /* total space used by option */
luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size,
luaL_argcheck(L, totalsize <= MAX_SIZE - size,
1, "format result too large");
totalsize += size;
}
@ -1811,8 +1822,8 @@ static int str_unpack (lua_State *L) {
lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos,
h.islittle, cast_int(size), 0);
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
lua_pushlstring(L, data + pos + size, len);
pos += len; /* skip string */
lua_pushlstring(L, data + pos + size, cast_sizet(len));
pos += cast_sizet(len); /* skip string */
break;
}
case Kzstr: {

150
ltable.c
View File

@ -156,7 +156,7 @@ static Node *hashint (const Table *t, lua_Integer i) {
** The main computation should be just
** n = frexp(n, &i); return (n * INT_MAX) + i
** but there are some numerical subtleties.
** In a two-complement representation, INT_MAX does not has an exact
** In a two-complement representation, INT_MAX may not have an exact
** representation as a float, but INT_MIN does; because the absolute
** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the
** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
@ -234,41 +234,51 @@ l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) {
** Check whether key 'k1' is equal to the key in node 'n2'. This
** equality is raw, so there are no metamethods. Floats with integer
** values have been normalized, so integers cannot be equal to
** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
** that short strings are handled in the default case.
** A true 'deadok' means to accept dead keys as equal to their original
** values. All dead keys are compared in the default case, by pointer
** identity. (Only collectable objects can produce dead keys.) Note that
** dead long strings are also compared by identity.
** Once a key is dead, its corresponding value may be collected, and
** then another value can be created with the same address. If this
** other value is given to 'next', 'equalkey' will signal a false
** positive. In a regular traversal, this situation should never happen,
** as all keys given to 'next' came from the table itself, and therefore
** could not have been collected. Outside a regular traversal, we
** have garbage in, garbage out. What is relevant is that this false
** positive does not break anything. (In particular, 'next' will return
** some other valid item on the table or nil.)
** floats. It is assumed that 'eqshrstr' is simply pointer equality,
** so that short strings are handled in the default case. The flag
** 'deadok' means to accept dead keys as equal to their original values.
** (Only collectable objects can produce dead keys.) Note that dead
** long strings are also compared by identity. Once a key is dead,
** its corresponding value may be collected, and then another value
** can be created with the same address. If this other value is given
** to 'next', 'equalkey' will signal a false positive. In a regular
** traversal, this situation should never happen, as all keys given to
** 'next' came from the table itself, and therefore could not have been
** collected. Outside a regular traversal, we have garbage in, garbage
** out. What is relevant is that this false positive does not break
** anything. (In particular, 'next' will return some other valid item
** on the table or nil.)
*/
static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
!(deadok && keyisdead(n2) && iscollectable(k1)))
return 0; /* cannot be same key */
switch (keytt(n2)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1;
case LUA_VNUMINT:
return (ivalue(k1) == keyival(n2));
case LUA_VNUMFLT:
return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
case LUA_VLIGHTUSERDATA:
return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_VLCF:
return fvalue(k1) == fvalueraw(keyval(n2));
case ctb(LUA_VLNGSTR):
return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
default:
if (rawtt(k1) != keytt(n2)) { /* not the same variants? */
if (keyisshrstr(n2) && ttislngstring(k1)) {
/* an external string can be equal to a short-string key */
return luaS_eqstr(tsvalue(k1), keystrval(n2));
}
else if (deadok && keyisdead(n2) && iscollectable(k1)) {
/* a collectable value can be equal to a dead key */
return gcvalue(k1) == gcvalueraw(keyval(n2));
}
else
return 0; /* otherwise, different variants cannot be equal */
}
else { /* equal variants */
switch (keytt(n2)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1;
case LUA_VNUMINT:
return (ivalue(k1) == keyival(n2));
case LUA_VNUMFLT:
return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
case LUA_VLIGHTUSERDATA:
return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_VLCF:
return fvalue(k1) == fvalueraw(keyval(n2));
case ctb(LUA_VLNGSTR):
return luaS_eqstr(tsvalue(k1), keystrval(n2));
default:
return gcvalue(k1) == gcvalueraw(keyval(n2));
}
}
}
@ -641,10 +651,9 @@ static void reinserthash (lua_State *L, Table *ot, Table *t) {
/*
** Exchange the hash part of 't1' and 't2'. (In 'flags', only the
** dummy bit must be exchanged: The 'isrealasize' is not related
** to the hash part, and the metamethod bits do not change during
** a resize, so the "real" table can keep their values.)
** Exchange the hash part of 't1' and 't2'. (In 'flags', only the dummy
** bit must be exchanged: The metamethod bits do not change during a
** resize, so the "real" table can keep their values.)
*/
static void exchangehashpart (Table *t1, Table *t2) {
lu_byte lsizenode = t1->lsizenode;
@ -1146,19 +1155,28 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key,
lua_assert(hres != HOK);
if (hres == HNOTFOUND) {
TValue aux;
const TValue *actk = key; /* actual key to insert */
if (l_unlikely(ttisnil(key)))
luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) {
lua_Number f = fltvalue(key);
lua_Integer k;
if (luaV_flttointeger(f, &k, F2Ieq)) {
setivalue(&aux, k); /* key is equal to an integer */
key = &aux; /* insert it as an integer */
if (luaV_flttointeger(f, &k, F2Ieq)) { /* is key equal to an integer? */
setivalue(&aux, k);
actk = &aux; /* use the integer as the key */
}
else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN");
}
luaH_newkey(L, t, key, value);
else if (isextstr(key)) { /* external string? */
/* If string is short, must internalize it to be used as table key */
TString *ts = luaS_normstr(L, tsvalue(key));
setsvalue2s(L, L->top.p++, ts); /* anchor 'ts' (EXTRA_STACK) */
luaH_newkey(L, t, s2v(L->top.p - 1), value);
L->top.p--;
return;
}
luaH_newkey(L, t, actk, value);
}
else if (hres > 0) { /* regular Node? */
setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value);
@ -1202,24 +1220,36 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
/*
** Try to find a boundary in the hash part of table 't'. From the
** caller, we know that 'j' is zero or present and that 'j + 1' is
** present. We want to find a larger key that is absent from the
** table, so that we can do a binary search between the two keys to
** find a boundary. We keep doubling 'j' until we get an absent index.
** If the doubling would overflow, we try LUA_MAXINTEGER. If it is
** absent, we are ready for the binary search. ('j', being max integer,
** is larger or equal to 'i', but it cannot be equal because it is
** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a
** boundary. ('j + 1' cannot be a present integer key because it is
** not a valid integer in Lua.)
** caller, we know that 'asize + 1' is present. We want to find a larger
** key that is absent from the table, so that we can do a binary search
** between the two keys to find a boundary. We keep doubling 'j' until
** we get an absent index. If the doubling would overflow, we try
** LUA_MAXINTEGER. If it is absent, we are ready for the binary search.
** ('j', being max integer, is larger or equal to 'i', but it cannot be
** equal because it is absent while 'i' is present.) Otherwise, 'j' is a
** boundary. ('j + 1' cannot be a present integer key because it is not
** a valid integer in Lua.)
** About 'rnd': If we used a fixed algorithm, a bad actor could fill
** a table with only the keys that would be probed, in such a way that
** a small table could result in a huge length. To avoid that, we use
** the state's seed as a source of randomness. For the first probe,
** we "randomly double" 'i' by adding to it a random number roughly its
** width.
*/
static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
lua_Unsigned i;
if (j == 0) j++; /* the caller ensures 'j + 1' is present */
do {
static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) {
lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */
unsigned rnd = G(L)->seed;
int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */
unsigned mask = (1u << n) - 1; /* 11...111 with the width of 'asize' */
unsigned incr = (rnd & mask) + 1; /* first increment (at least 1) */
lua_Unsigned j = (incr <= l_castS2U(LUA_MAXINTEGER) - i) ? i + incr : i + 1;
rnd >>= n; /* used 'n' bits from 'rnd' */
while (!hashkeyisempty(t, j)) { /* repeat until an absent t[j] */
i = j; /* 'i' is a present index */
if (j <= l_castS2U(LUA_MAXINTEGER) / 2)
j *= 2;
if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) {
j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */
rnd >>= 1;
}
else {
j = LUA_MAXINTEGER;
if (hashkeyisempty(t, j)) /* t[j] not present? */
@ -1227,7 +1257,7 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
else /* weird case */
return j; /* well, max integer is a boundary... */
}
} while (!hashkeyisempty(t, j)); /* repeat until an absent t[j] */
}
/* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2;
@ -1268,7 +1298,7 @@ static lua_Unsigned newhint (Table *t, unsigned hint) {
** If there is no array part, or its last element is non empty, the
** border may be in the hash part.
*/
lua_Unsigned luaH_getn (Table *t) {
lua_Unsigned luaH_getn (lua_State *L, Table *t) {
unsigned asize = t->asize;
if (asize > 0) { /* is there an array part? */
const unsigned maxvicinity = 4;
@ -1309,7 +1339,7 @@ lua_Unsigned luaH_getn (Table *t) {
if (isdummy(t) || hashkeyisempty(t, asize + 1))
return asize; /* 'asize + 1' is empty */
else /* 'asize + 1' is also non empty */
return hash_search(t, asize);
return hash_search(L, t, asize);
}

View File

@ -173,7 +173,7 @@ LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize);
LUAI_FUNC lu_mem luaH_size (Table *t);
LUAI_FUNC void luaH_free (lua_State *L, Table *t);
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t);
#if defined(LUA_DEBUG)

View File

@ -164,13 +164,13 @@ static void warnf (void *ud, const char *msg, int tocont) {
#define MARK 0x55 /* 01010101 (a nice pattern) */
typedef union Header {
typedef union memHeader {
LUAI_MAXALIGN;
struct {
size_t size;
int type;
} d;
} Header;
} memHeader;
#if !defined(EXTERNMEMCHECK)
@ -193,14 +193,14 @@ Memcontrol l_memcontrol =
{0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
static void freeblock (Memcontrol *mc, Header *block) {
static void freeblock (Memcontrol *mc, memHeader *block) {
if (block) {
size_t size = block->d.size;
int i;
for (i = 0; i < MARKSIZE; i++) /* check marks after block */
lua_assert(*(cast_charp(block + 1) + size + i) == MARK);
mc->objcount[block->d.type]--;
fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */
fillmem(block, sizeof(memHeader) + size + MARKSIZE); /* erase block */
free(block); /* actually free block */
mc->numblocks--; /* update counts */
mc->total -= size;
@ -210,7 +210,7 @@ static void freeblock (Memcontrol *mc, Header *block) {
void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
Memcontrol *mc = cast(Memcontrol *, ud);
Header *block = cast(Header *, b);
memHeader *block = cast(memHeader *, b);
int type;
if (mc->memlimit == 0) { /* first time? */
char *limit = getenv("MEMLIMIT"); /* initialize memory limit */
@ -241,12 +241,12 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
if (size > oldsize && mc->total+size-oldsize > mc->memlimit)
return NULL; /* fake a memory allocation error */
else {
Header *newblock;
memHeader *newblock;
int i;
size_t commonsize = (oldsize < size) ? oldsize : size;
size_t realsize = sizeof(Header) + size + MARKSIZE;
size_t realsize = sizeof(memHeader) + size + MARKSIZE;
if (realsize < size) return NULL; /* arithmetic overflow! */
newblock = cast(Header *, malloc(realsize)); /* alloc a new block */
newblock = cast(memHeader *, malloc(realsize)); /* alloc a new block */
if (newblock == NULL)
return NULL; /* really out of memory? */
if (block) {
@ -480,7 +480,7 @@ static int lua_checkpc (CallInfo *ci) {
}
static void checkstack (global_State *g, lua_State *L1) {
static void check_stack (global_State *g, lua_State *L1) {
StkId o;
CallInfo *ci;
UpVal *uv;
@ -517,7 +517,7 @@ static void checkrefs (global_State *g, GCObject *o) {
break;
}
case LUA_VTHREAD: {
checkstack(g, gco2th(o));
check_stack(g, gco2th(o));
break;
}
case LUA_VLCL: {
@ -908,6 +908,17 @@ static int get_limits (lua_State *L) {
}
static int get_sizes (lua_State *L) {
lua_newtable(L);
setnameval(L, "Lua state", sizeof(lua_State));
setnameval(L, "global state", sizeof(global_State));
setnameval(L, "TValue", sizeof(TValue));
setnameval(L, "Node", sizeof(Node));
setnameval(L, "stack Value", sizeof(StackValue));
return 1;
}
static int mem_query (lua_State *L) {
if (lua_isnone(L, 1)) {
lua_pushinteger(L, cast_Integer(l_memcontrol.total));
@ -1066,8 +1077,12 @@ static int tracegc (lua_State *L) {
static int hash_query (lua_State *L) {
if (lua_isnone(L, 2)) {
TString *ts;
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected");
lua_pushinteger(L, cast_int(tsvalue(obj_at(L, 1))->hash));
ts = tsvalue(obj_at(L, 1));
if (ts->tt == LUA_VLNGSTR)
luaS_hashlongstr(ts); /* make sure long string has a hash */
lua_pushinteger(L, cast_int(ts->hash));
}
else {
TValue *o = obj_at(L, 1);
@ -1091,6 +1106,27 @@ static int stacklevel (lua_State *L) {
}
static int resetCI (lua_State *L) {
CallInfo *ci = L->ci;
while (ci->next != NULL) {
CallInfo *tofree = ci->next;
ci->next = ci->next->next;
luaM_free(L, tofree);
L->nci--;
}
return 0;
}
static int reallocstack (lua_State *L) {
int n = cast_int(luaL_checkinteger(L, 1));
lua_lock(L);
luaD_reallocstack(L, cast_int(L->top.p - L->stack.p) + n, 1);
lua_unlock(L);
return 0;
}
static int table_query (lua_State *L) {
const Table *t;
int i = cast_int(luaL_optinteger(L, 2, -1));
@ -2167,6 +2203,9 @@ static const struct luaL_Reg tests_funcs[] = {
{"s2d", s2d},
{"sethook", sethook},
{"stacklevel", stacklevel},
{"resetCI", resetCI},
{"reallocstack", reallocstack},
{"sizes", get_sizes},
{"testC", testC},
{"makeCfunc", makeCfunc},
{"totalmem", mem_query},

View File

@ -13,7 +13,6 @@
/* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB
#define LUA_COMPAT_LT_LE
#undef LUA_COMPAT_GLOBAL
@ -64,7 +63,7 @@ LUA_API Memcontrol l_memcontrol;
#define luai_tracegc(L,f) luai_tracegctest(L, f)
LUAI_FUNC void luai_tracegctest (lua_State *L, int first);
extern void luai_tracegctest (lua_State *L, int first);
/*
@ -76,26 +75,26 @@ extern void *l_Trick;
/*
** Function to traverse and check all memory used by Lua
*/
LUAI_FUNC int lua_checkmemory (lua_State *L);
extern int lua_checkmemory (lua_State *L);
/*
** Function to print an object GC-friendly
*/
struct GCObject;
LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o);
extern void lua_printobj (lua_State *L, struct GCObject *o);
/*
** Function to print a value
*/
struct TValue;
LUAI_FUNC void lua_printvalue (struct TValue *v);
extern void lua_printvalue (struct TValue *v);
/*
** Function to print the stack
*/
LUAI_FUNC void lua_printstack (lua_State *L);
LUAI_FUNC int lua_printallstack (lua_State *L);
extern void lua_printstack (lua_State *L);
extern int lua_printallstack (lua_State *L);
/* test for lock/unlock */
@ -122,14 +121,14 @@ LUA_API int luaB_opentests (lua_State *L);
LUA_API void *debug_realloc (void *ud, void *block,
size_t osize, size_t nsize);
#if defined(lua_c)
#define luaL_newstate() \
lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL))
#define luai_openlibs(L) \
{ luaL_openlibs(L); \
luaL_requiref(L, "T", luaB_opentests, 1); \
lua_pop(L, 1); }
#endif
@ -143,19 +142,13 @@ LUA_API void *debug_realloc (void *ud, void *block,
#define STRCACHE_N 23
#define STRCACHE_M 5
/*
** This one is not compatible with tests for opcode optimizations,
** as it blocks some optimizations
#define MAXINDEXRK 0
*/
#define MAXINDEXRK 1
/*
** Reduce maximum stack size to make stack-overflow tests run faster.
** (But value is still large enough to overflow smaller integers.)
*/
#undef LUAI_MAXSTACK
#define LUAI_MAXSTACK 68000

156
ltm.c
View File

@ -196,28 +196,12 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
/*
** Calls an order tag method.
** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old
** behavior: if there is no '__le', try '__lt', based on l <= r iff
** !(r < l) (assuming a total order). If the metamethod yields during
** this substitution, the continuation has to know about it (to negate
** the result of r<l); bit CIST_LEQ in the call status keeps that
** information.
*/
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) {
int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */
if (tag >= 0) /* found tag method? */
return !tagisfalse(tag);
#if defined(LUA_COMPAT_LT_LE)
else if (event == TM_LE) {
/* try '!(p2 < p1)' for '(p1 <= p2)' */
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
tag = callbinTM(L, p2, p1, L->top.p, TM_LT);
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
if (tag >= 0) /* found tag method? */
return tagisfalse(tag);
}
#endif
luaG_ordererror(L, p1, p2); /* no metamethod found */
return 0; /* to avoid warnings */
}
@ -240,36 +224,140 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
}
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
const Proto *p) {
/*
** Create a vararg table at the top of the stack, with 'n' elements
** starting at 'f'.
*/
static void createvarargtab (lua_State *L, StkId f, int n) {
int i;
TValue key, value;
Table *t = luaH_new(L);
sethvalue(L, s2v(L->top.p), t);
L->top.p++;
luaH_resize(L, t, cast_uint(n), 1);
setsvalue(L, &key, luaS_new(L, "n")); /* key is "n" */
setivalue(&value, n); /* value is n */
/* No need to anchor the key: Due to the resize, the next operation
cannot trigger a garbage collection */
luaH_set(L, t, &key, &value); /* t.n = n */
for (i = 0; i < n; i++)
luaH_setint(L, t, i + 1, s2v(f + i));
luaC_checkGC(L);
}
/*
** initial stack: func arg1 ... argn extra1 ...
** ^ ci->func ^ L->top
** final stack: func nil ... nil extra1 ... func arg1 ... argn
** ^ ci->func
*/
static void buildhiddenargs (lua_State *L, CallInfo *ci, const Proto *p,
int totalargs, int nfixparams, int nextra) {
int i;
int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra;
luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */
/* copy function to the top of the stack, after extra arguments */
setobjs2s(L, L->top.p++, ci->func.p);
/* move fixed parameters to the top of the stack */
/* move fixed parameters to after the copied function */
for (i = 1; i <= nfixparams; i++) {
setobjs2s(L, L->top.p++, ci->func.p + i);
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
}
ci->func.p += actual + 1;
ci->top.p += actual + 1;
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */
ci->top.p += totalargs + 1;
}
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int i;
int nextra = ci->u.l.nextraargs;
if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */
checkstackp(L, nextra, where); /* ensure stack space */
L->top.p = where + nextra; /* next instruction will need top */
void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) {
int totalargs = cast_int(L->top.p - ci->func.p) - 1;
int nfixparams = p->numparams;
int nextra = totalargs - nfixparams; /* number of extra arguments */
if (p->flag & PF_VATAB) { /* does it need a vararg table? */
lua_assert(!(p->flag & PF_VAHID));
createvarargtab(L, ci->func.p + nfixparams + 1, nextra);
/* move table to proper place (last parameter) */
setobjs2s(L, ci->func.p + nfixparams + 1, L->top.p - 1);
}
else { /* no table */
lua_assert(p->flag & PF_VAHID);
buildhiddenargs(L, ci, p, totalargs, nfixparams, nextra);
/* set vararg parameter to nil */
setnilvalue(s2v(ci->func.p + nfixparams + 1));
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
}
}
void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) {
int nextra = ci->u.l.nextraargs;
lua_Integer n;
if (tointegerns(rc, &n)) { /* integral value? */
if (l_castS2U(n) - 1 < cast_uint(nextra)) {
StkId slot = ci->func.p - nextra + cast_int(n) - 1;
setobjs2s(((lua_State*)NULL), ra, slot);
return;
}
}
else if (ttisstring(rc)) { /* string value? */
size_t len;
const char *s = getlstr(tsvalue(rc), len);
if (len == 1 && s[0] == 'n') { /* key is "n"? */
setivalue(s2v(ra), nextra);
return;
}
}
setnilvalue(s2v(ra)); /* else produce nil */
}
/*
** Get the number of extra arguments in a vararg function. If vararg
** table has been optimized away, that number is in the call info.
** Otherwise, get the field 'n' from the vararg table and check that it
** has a proper value (non-negative integer not larger than the stack
** limit).
*/
static int getnumargs (lua_State *L, CallInfo *ci, Table *h) {
if (h == NULL) /* no vararg table? */
return ci->u.l.nextraargs;
else {
TValue res;
if (luaH_getshortstr(h, luaS_new(L, "n"), &res) != LUA_VNUMINT ||
l_castS2U(ivalue(&res)) > cast_uint(INT_MAX/2))
luaG_runerror(L, "vararg table has no proper 'n'");
return cast_int(ivalue(&res));
}
}
/*
** Get 'wanted' vararg arguments and put them in 'where'. 'vatab' is
** the register of the vararg table or -1 if there is no vararg table.
*/
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted,
int vatab) {
Table *h = (vatab < 0) ? NULL : hvalue(s2v(ci->func.p + vatab + 1));
int nargs = getnumargs(L, ci, h); /* number of available vararg args. */
int i, touse; /* 'touse' is minimum between 'wanted' and 'nargs' */
if (wanted < 0) {
touse = wanted = nargs; /* get all extra arguments available */
checkstackp(L, nargs, where); /* ensure stack space */
L->top.p = where + nargs; /* next instruction will need top */
}
else
touse = (nargs > wanted) ? wanted : nargs;
if (h == NULL) { /* no vararg table? */
for (i = 0; i < touse; i++) /* get vararg values from the stack */
setobjs2s(L, where + i, ci->func.p - nargs + i);
}
else { /* get vararg values from vararg table */
for (i = 0; i < touse; i++) {
lu_byte tag = luaH_getint(h, i + 1, s2v(where + i));
if (tagisempty(tag))
setnilvalue(s2v(where + i));
}
}
for (i = 0; i < wanted && i < nextra; i++)
setobjs2s(L, where + i, ci->func.p - nextra + i);
for (; i < wanted; i++) /* complete required results with nil */
setnilvalue(s2v(where + i));
}

11
ltm.h
View File

@ -49,7 +49,7 @@ typedef enum {
** Mask with 1 in all fast-access methods. A 1 in any of these bits
** in the flag of a (meta)table means the metatable does not have the
** corresponding metamethod field. (Bit 6 of the flag indicates that
** the table is using the dummy node; bit 7 is used for 'isrealasize'.)
** the table is using the dummy node.)
*/
#define maskflags cast_byte(~(~0u << (TM_EQ + 1)))
@ -95,10 +95,11 @@ LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
struct CallInfo *ci, const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
StkId where, int wanted);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci,
const Proto *p);
LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where,
int wanted, int vatab);
#endif

53
lua.c
View File

@ -303,7 +303,8 @@ static int collectargs (char **argv, int *first) {
case '-': /* '--' */
if (argv[i][2] != '\0') /* extra characters after '--'? */
return has_error; /* invalid option */
*first = i + 1;
/* if there is a script name, it comes after '--' */
*first = (argv[i + 1] != NULL) ? i + 1 : 0;
return args;
case '\0': /* '-' */
return args; /* script "name" is '-' */
@ -348,6 +349,7 @@ static int collectargs (char **argv, int *first) {
*/
static int runargs (lua_State *L, char **argv, int n) {
int i;
lua_warning(L, "@off", 0); /* by default, Lua stand-alone has warnings off */
for (i = 1; i < n; i++) {
int option = argv[i][1];
lua_assert(argv[i][0] == '-'); /* already checked */
@ -437,13 +439,24 @@ static int handle_luainit (lua_State *L) {
** the standard input.
** * lua_saveline defines how to "save" a read line in a "history".
** * lua_freeline defines how to free a line read by lua_readline.
**
** If lua_readline is defined, all of them should be defined.
*/
#if !defined(lua_readline) /* { */
/* Otherwise, all previously listed functions should be defined. */
/* Code to use the readline library, either statically or dynamically linked */
#if defined(LUA_USE_READLINE) /* { */
/* Lua will be linked with '-lreadline' */
#include <readline/readline.h>
#include <readline/history.h>
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
#define lua_readline(buff,prompt) ((void)buff, readline(prompt))
#define lua_saveline(line) add_history(line)
#define lua_freeline(line) free(line)
#else /* }{ */
/* use dynamically loaded readline (or nothing) */
/* pointer to 'readline' function (if any) */
typedef char *(*l_readlineT) (const char *prompt);
@ -479,22 +492,9 @@ static void lua_freeline (char *line) {
}
#if defined(LUA_USE_READLINE) /* { */
/* assume Lua will be linked with '-lreadline' */
#include <readline/readline.h>
#include <readline/history.h>
static void lua_initreadline(lua_State *L) {
UNUSED(L);
rl_readline_name = "lua";
l_readline = readline;
l_addhist = add_history;
}
#elif defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* }{ */
#if defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* { */
/* try to load 'readline' dynamically */
#include <dlfcn.h>
static void lua_initreadline (lua_State *L) {
@ -507,15 +507,20 @@ static void lua_initreadline (lua_State *L) {
*name = "lua";
l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline")));
l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history")));
if (l_readline == NULL)
lua_warning(L, "unable to load 'readline'", 0);
}
}
#else /* }{ */
#else /* }{ */
/* no dlopen or LUA_READLINELIB undefined */
/* no readline; leave function pointers as NULL */
#define lua_initreadline(L) cast(void, L)
/* Leave pointers with NULL */
#define lua_initreadline(L) ((void)L)
#endif /* } */
#endif /* } */
#endif /* } */
#endif /* } */
@ -721,7 +726,7 @@ static int pmain (lua_State *L) {
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
return 0; /* error running LUA_INIT */
}
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */
return 0; /* something failed */
if (script > 0) { /* execute main script (if there is one) */
if (handle_script(L, argv + script) != LUA_OK)

13
lua.h
View File

@ -37,10 +37,10 @@
/*
** Pseudo-indices
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
** space after that to help overflow detection)
** (The stack size is limited to INT_MAX/2; we keep some free empty
** space after that to help overflow detection.)
*/
#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000))
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
@ -432,13 +432,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
** compatibility macros
** ===============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)
#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
#endif
#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)

108
luaconf.h
View File

@ -59,7 +59,7 @@
/*
** When Posix DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone
** When POSIX DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone
** application will try to dynamically link a 'readline' facility
** for its REPL. In that case, LUA_READLINELIB is the name of the
** library it will look for those facilities. If lua.c cannot open
@ -76,7 +76,7 @@
#if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
#define LUA_USE_DLOPEN /* macOS does not need -ldl */
#define LUA_READLINELIB "libedit.dylib"
#endif
@ -88,7 +88,7 @@
#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX)
#error "Posix is not compatible with C89"
#error "POSIX is not compatible with C89"
#endif
@ -138,7 +138,7 @@
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
#define LUA_32BITS 0
/* #define LUA_32BITS */
/*
@ -153,7 +153,7 @@
#endif
#if LUA_32BITS /* { */
#if defined(LUA_32BITS) /* { */
/*
** 32-bit integers and 'float'
*/
@ -319,32 +319,13 @@
** More often than not the libs go together with the core.
*/
#define LUALIB_API LUA_API
#if defined(__cplusplus)
/* Lua uses the "C name" when calling open functions */
#define LUAMOD_API extern "C"
#else
#define LUAMOD_API LUA_API
/*
@@ LUAI_FUNC is a mark for all extern functions that are not to be
** exported to outside modules.
@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables,
** none of which to be exported to outside modules (LUAI_DDEF for
** definitions and LUAI_DDEC for declarations).
** CHANGE them if you need to mark them in some special way. Elf/gcc
** (versions 3.2 and later) mark them as "hidden" to optimize access
** when Lua is compiled as a shared library. Not all elf targets support
** this attribute. Unfortunately, gcc does not offer a way to check
** whether the target offers that support, and those without support
** give a warning about it. To avoid these warnings, change to the
** default definition.
*/
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
defined(__ELF__) /* { */
#define LUAI_FUNC __attribute__((visibility("internal"))) extern
#else /* }{ */
#define LUAI_FUNC extern
#endif /* } */
#define LUAI_DDEC(dec) LUAI_FUNC dec
#define LUAI_DDEF /* empty */
#endif
/* }================================================================== */
@ -361,36 +342,13 @@
#define LUA_COMPAT_GLOBAL
/*
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
** You can define it to get all options, or change specific options
** to fit your specific needs.
*/
#if defined(LUA_COMPAT_5_3) /* { */
/*
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
** functions in the mathematical library.
** (These functions were already officially removed in 5.3;
** nevertheless they are still available here.)
*/
#define LUA_COMPAT_MATHLIB
/*
@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
** luaL_checkint, luaL_checklong, etc.)
** (These macros were also officially removed in 5.3, but they are still
** available here.)
*/
#define LUA_COMPAT_APIINTCASTS
/*
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
** using '__lt'.
*/
#define LUA_COMPAT_LT_LE
/* #define LUA_COMPAT_MATHLIB */
/*
@ -407,8 +365,6 @@
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#endif /* } */
/* }================================================================== */
@ -440,26 +396,11 @@
*/
/* The following definitions are good for most cases here */
/* The following definition is good for most cases here */
#define l_floor(x) (l_mathop(floor)(x))
/*
@@ lua_numbertointeger converts a float number with an integral value
** to an integer, or returns 0 if float is not within the range of
** a lua_Integer. (The range comparisons are tricky because of
** rounding. The tests here assume a two-complement representation,
** where MININTEGER always has an exact representation as a float;
** MAXINTEGER may not have one, and therefore its conversion to float
** may have an ill-defined value.)
*/
#define lua_numbertointeger(n,p) \
((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
(n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
(*(p) = (LUA_INTEGER)(n), 1))
/* now the variable definitions */
#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
@ -719,13 +660,6 @@
#endif
#if defined(LUA_CORE) || defined(LUA_LIB)
/* shorter names for Lua's own use */
#define l_likely(x) luai_likely(x)
#define l_unlikely(x) luai_unlikely(x)
#endif
/* }================================================================== */
@ -763,20 +697,6 @@
** =====================================================================
*/
/*
@@ LUAI_MAXSTACK limits the size of the Lua stack.
** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack
** space and to reserve some numbers for pseudo-indices.
** (It must fit into max(int)/2.)
*/
#if 1000000 < (INT_MAX / 2)
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK (INT_MAX / 2u)
#endif
/*
@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
** a Lua state with very fast access.
@ -821,7 +741,5 @@
#endif

View File

@ -109,7 +109,7 @@ static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) {
static size_t loadSize (LoadState *S) {
return loadVarint(S, MAX_SIZE);
return cast_sizet(loadVarint(S, MAX_SIZE));
}
@ -147,20 +147,20 @@ static void loadString (LoadState *S, Proto *p, TString **sl) {
TString *ts;
TValue sv;
size_t size = loadSize(S);
if (size == 0) { /* no string? */
lua_assert(*sl == NULL); /* must be prefilled */
return;
}
else if (size == 1) { /* previously saved string? */
if (size == 0) { /* previously saved string? */
lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */
TValue stv;
if (idx == 0) { /* no string? */
lua_assert(*sl == NULL); /* must be prefilled */
return;
}
if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING)
error(S, "invalid string index");
*sl = ts = tsvalue(&stv); /* get its value */
luaC_objbarrier(L, p, ts);
return; /* do not save it again */
}
else if ((size -= 2) <= LUAI_MAXSHORTLEN) { /* short string? */
else if ((size -= 1) <= LUAI_MAXSHORTLEN) { /* short string? */
char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */
loadVector(S, buff, size + 1); /* load string into buffer */
*sl = ts = luaS_newlstr(L, buff, size); /* create string */
@ -327,7 +327,8 @@ static void loadFunction (LoadState *S, Proto *f) {
f->linedefined = loadInt(S);
f->lastlinedefined = loadInt(S);
f->numparams = loadByte(S);
f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */
/* get only the meaningful flags */
f->flag = cast_byte(loadByte(S) & ~PF_FIXED);
if (S->fixed)
f->flag |= PF_FIXED; /* signal that code is fixed */
f->maxstacksize = loadByte(S);

View File

@ -10,7 +10,6 @@
#include "lprefix.h"
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
@ -47,7 +46,7 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
** Decode one UTF-8 sequence, returning NULL if byte sequence is
** invalid. The array 'limits' stores the minimum value for each
** sequence length, to check for overlong representations. Its first
** entry forces an error for non-ascii bytes with no continuation
** entry forces an error for non-ASCII bytes with no continuation
** bytes (count == 0).
*/
static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
@ -55,7 +54,7 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
{~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
unsigned int c = (unsigned char)s[0];
l_uint32 res = 0; /* final result */
if (c < 0x80) /* ascii? */
if (c < 0x80) /* ASCII? */
res = c;
else {
int count = 0; /* to count number of continuation bytes */
@ -215,9 +214,10 @@ static int byteoffset (lua_State *L) {
}
lua_pushinteger(L, posi + 1); /* initial position */
if ((s[posi] & 0x80) != 0) { /* multi-byte character? */
do {
posi++;
} while (iscontp(s + posi + 1)); /* skip to final byte */
if (iscont(s[posi]))
return luaL_error(L, "initial position is a continuation byte");
while (iscontp(s + posi + 1))
posi++; /* skip to last continuation byte */
}
/* else one-byte character: final position is the initial one */
lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */

236
lvm.c
View File

@ -372,6 +372,14 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
}
/*
** Function to be used for 0-terminated string order comparison
*/
#if !defined(l_strcoll)
#define l_strcoll strcoll
#endif
/*
** Compare two strings 'ts1' x 'ts2', returning an integer less-equal-
** -greater than zero if 'ts1' is less-equal-greater than 'ts2'.
@ -386,7 +394,7 @@ static int l_strcmp (const TString *ts1, const TString *ts2) {
size_t rl2;
const char *s2 = getlstr(ts2, rl2);
for (;;) { /* for each segment */
int temp = strcoll(s1, s2);
int temp = l_strcoll(s1, s2);
if (temp != 0) /* not equal? */
return temp; /* done */
else { /* strings are equal up to a '\0' */
@ -573,52 +581,74 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
*/
int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm;
if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
return 0; /* only numbers can be equal with different variants */
else { /* two numbers with different variants */
/* One of them is an integer. If the other does not have an
integer value, they cannot be equal; otherwise, compare their
integer values. */
lua_Integer i1, i2;
return (luaV_tointegerns(t1, &i1, F2Ieq) &&
luaV_tointegerns(t2, &i2, F2Ieq) &&
i1 == i2);
if (ttype(t1) != ttype(t2)) /* not the same type? */
return 0;
else if (ttypetag(t1) != ttypetag(t2)) {
switch (ttypetag(t1)) {
case LUA_VNUMINT: { /* integer == float? */
/* integer and float can only be equal if float has an integer
value equal to the integer */
lua_Integer i2;
return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) &&
ivalue(t1) == i2);
}
case LUA_VNUMFLT: { /* float == integer? */
lua_Integer i1; /* see comment in previous case */
return (luaV_flttointeger(fltvalue(t1), &i1, F2Ieq) &&
i1 == ivalue(t2));
}
case LUA_VSHRSTR: case LUA_VLNGSTR: {
/* compare two strings with different variants: they can be
equal when one string is a short string and the other is
an external string */
return luaS_eqstr(tsvalue(t1), tsvalue(t2));
}
default:
/* only numbers (integer/float) and strings (long/short) can have
equal values with different variants */
return 0;
}
}
/* values have same type and same variant */
switch (ttypetag(t1)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1;
case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2));
case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_VLCF: return fvalue(t1) == fvalue(t2);
case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
case LUA_VUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1;
else if (L == NULL) return 0;
tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
if (tm == NULL)
tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
else { /* equal variants */
switch (ttypetag(t1)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1;
case LUA_VNUMINT:
return (ivalue(t1) == ivalue(t2));
case LUA_VNUMFLT:
return (fltvalue(t1) == fltvalue(t2));
case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_VSHRSTR:
return eqshrstr(tsvalue(t1), tsvalue(t2));
case LUA_VLNGSTR:
return luaS_eqstr(tsvalue(t1), tsvalue(t2));
case LUA_VUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1;
else if (L == NULL) return 0;
tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
if (tm == NULL)
tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
}
case LUA_VTABLE: {
if (hvalue(t1) == hvalue(t2)) return 1;
else if (L == NULL) return 0;
tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
if (tm == NULL)
tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
}
case LUA_VLCF:
return (fvalue(t1) == fvalue(t2));
default: /* functions and threads */
return (gcvalue(t1) == gcvalue(t2));
}
case LUA_VTABLE: {
if (hvalue(t1) == hvalue(t2)) return 1;
else if (L == NULL) return 0;
tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
if (tm == NULL)
tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
if (tm == NULL) /* no TM? */
return 0; /* objects are different */
else {
int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
return !tagisfalse(tag);
}
default:
return gcvalue(t1) == gcvalue(t2);
}
if (tm == NULL) /* no TM? */
return 0; /* objects are different */
else {
int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
return !tagisfalse(tag);
}
}
@ -627,6 +657,11 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
#define tostring(L,o) \
(ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1)))
/*
** Check whether object is a short empty string to optimize concatenation.
** (External strings can be empty too; they will be concatenated like
** non-empty ones.)
*/
#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0)
/* copy strings in stack from top - n up to top - 1 to buffer */
@ -661,8 +696,8 @@ void luaV_concat (lua_State *L, int total) {
setobjs2s(L, top - 2, top - 1); /* result is second op. */
}
else {
/* at least two non-empty string values; get as many as possible */
size_t tl = tsslen(tsvalue(s2v(top - 1)));
/* at least two string values; get as many as possible */
size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */
TString *ts;
/* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
@ -700,7 +735,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
Table *h = hvalue(rb);
tm = fasttm(L, h->metatable, TM_LEN);
if (tm) break; /* metamethod? break switch to call it */
setivalue(s2v(ra), l_castU2S(luaH_getn(h))); /* else primitive len */
setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */
return;
}
case LUA_VSHRSTR: {
@ -839,12 +874,6 @@ void luaV_finishOp (lua_State *L) {
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
int res = !l_isfalse(s2v(L->top.p - 1));
L->top.p--;
#if defined(LUA_COMPAT_LT_LE)
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
ci->callstatus ^= CIST_LEQ; /* clear mark */
res = !res; /* negate result */
}
#endif
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
if (res != GETARG_k(inst)) /* condition failed? */
ci->u.l.savedpc++; /* skip jump instruction */
@ -888,6 +917,10 @@ void luaV_finishOp (lua_State *L) {
/*
** {==================================================================
** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute'
**
** All these macros are to be used exclusively inside the main
** iterpreter loop (function luaV_execute) and may access directly
** the local variables of that function (L, i, pc, ci, etc.).
** ===================================================================
*/
@ -909,17 +942,17 @@ void luaV_finishOp (lua_State *L) {
** operation, 'fop' is the float operation.
*/
#define op_arithI(L,iop,fop) { \
StkId ra = RA(i); \
TValue *ra = vRA(i); \
TValue *v1 = vRB(i); \
int imm = GETARG_sC(i); \
if (ttisinteger(v1)) { \
lua_Integer iv1 = ivalue(v1); \
pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
pc++; setivalue(ra, iop(L, iv1, imm)); \
} \
else if (ttisfloat(v1)) { \
lua_Number nb = fltvalue(v1); \
lua_Number fimm = cast_num(imm); \
pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
pc++; setfltvalue(ra, fop(L, nb, fimm)); \
}}
@ -930,6 +963,7 @@ void luaV_finishOp (lua_State *L) {
#define op_arithf_aux(L,v1,v2,fop) { \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
StkId ra = RA(i); \
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
}}
@ -938,7 +972,6 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over floats and others with register operands.
*/
#define op_arithf(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
op_arithf_aux(L, v1, v2, fop); }
@ -948,7 +981,6 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations with K operands for floats.
*/
#define op_arithfK(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); }
@ -958,8 +990,8 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over integers and floats.
*/
#define op_arith_aux(L,v1,v2,iop,fop) { \
StkId ra = RA(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \
StkId ra = RA(i); \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
} \
@ -988,12 +1020,12 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with constant operand.
*/
#define op_bitwiseK(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
lua_Integer i1; \
lua_Integer i2 = ivalue(v2); \
if (tointegerns(v1, &i1)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \
}}
@ -1002,11 +1034,11 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with register operands.
*/
#define op_bitwise(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
lua_Integer i1; lua_Integer i2; \
if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \
}}
@ -1017,18 +1049,18 @@ void luaV_finishOp (lua_State *L) {
** integers.
*/
#define op_order(L,opi,opn,other) { \
StkId ra = RA(i); \
TValue *ra = vRA(i); \
int cond; \
TValue *rb = vRB(i); \
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
lua_Integer ia = ivalue(s2v(ra)); \
if (ttisinteger(ra) && ttisinteger(rb)) { \
lua_Integer ia = ivalue(ra); \
lua_Integer ib = ivalue(rb); \
cond = opi(ia, ib); \
} \
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
cond = opn(s2v(ra), rb); \
else if (ttisnumber(ra) && ttisnumber(rb)) \
cond = opn(ra, rb); \
else \
Protect(cond = other(L, s2v(ra), rb)); \
Protect(cond = other(L, ra, rb)); \
docondjump(); }
@ -1037,19 +1069,19 @@ void luaV_finishOp (lua_State *L) {
** always small enough to have an exact representation as a float.)
*/
#define op_orderI(L,opi,opf,inv,tm) { \
StkId ra = RA(i); \
TValue *ra = vRA(i); \
int cond; \
int im = GETARG_sB(i); \
if (ttisinteger(s2v(ra))) \
cond = opi(ivalue(s2v(ra)), im); \
else if (ttisfloat(s2v(ra))) { \
lua_Number fa = fltvalue(s2v(ra)); \
if (ttisinteger(ra)) \
cond = opi(ivalue(ra), im); \
else if (ttisfloat(ra)) { \
lua_Number fa = fltvalue(ra); \
lua_Number fim = cast_num(im); \
cond = opf(fa, fim); \
} \
else { \
int isf = GETARG_C(i); \
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
Protect(cond = luaT_callorderiTM(L, ra, im, inv, isf, tm)); \
} \
docondjump(); }
@ -1068,6 +1100,7 @@ void luaV_finishOp (lua_State *L) {
#define RA(i) (base+GETARG_A(i))
#define vRA(i) s2v(RA(i))
#define RB(i) (base+GETARG_B(i))
#define vRB(i) s2v(RB(i))
#define KB(i) (k+GETARG_B(i))
@ -1108,14 +1141,14 @@ void luaV_finishOp (lua_State *L) {
/*
** Correct global 'pc'.
*/
#define savepc(L) (ci->u.l.savedpc = pc)
#define savepc(ci) (ci->u.l.savedpc = pc)
/*
** Whenever code can raise errors, the global 'pc' and the global
** 'top' must be correct to report occasional errors.
*/
#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
#define savestate(L,ci) (savepc(ci), L->top.p = ci->top.p)
/*
@ -1125,7 +1158,7 @@ void luaV_finishOp (lua_State *L) {
#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci))
/* special version that does not change the top */
#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
#define ProtectNT(exp) (savepc(ci), (exp), updatetrap(ci))
/*
** Protect code that can only raise errors. (That is, it cannot change
@ -1143,7 +1176,7 @@ void luaV_finishOp (lua_State *L) {
/* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \
{ luaC_condGC(L, (savepc(L), L->top.p = (c)), \
{ luaC_condGC(L, (savepc(ci), L->top.p = (c)), \
updatetrap(ci)); \
luai_threadyield(L); }
@ -1450,16 +1483,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwiseK(L, l_bxor);
vmbreak;
}
vmcase(OP_SHRI) {
StkId ra = RA(i);
TValue *rb = vRB(i);
int ic = GETARG_sC(i);
lua_Integer ib;
if (tointegerns(rb, &ib)) {
pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
}
vmbreak;
}
vmcase(OP_SHLI) {
StkId ra = RA(i);
TValue *rb = vRB(i);
@ -1470,6 +1493,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmbreak;
}
vmcase(OP_SHRI) {
StkId ra = RA(i);
TValue *rb = vRB(i);
int ic = GETARG_sC(i);
lua_Integer ib;
if (tointegerns(rb, &ib)) {
pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
}
vmbreak;
}
vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd);
vmbreak;
@ -1512,14 +1545,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwise(L, l_bxor);
vmbreak;
}
vmcase(OP_SHR) {
op_bitwise(L, luaV_shiftr);
vmbreak;
}
vmcase(OP_SHL) {
op_bitwise(L, luaV_shiftl);
vmbreak;
}
vmcase(OP_SHR) {
op_bitwise(L, luaV_shiftr);
vmbreak;
}
vmcase(OP_MMBIN) {
StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */
@ -1692,7 +1725,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
if (b != 0) /* fixed number of arguments? */
L->top.p = ra + b; /* top signals number of arguments */
/* else previous instruction set top */
savepc(L); /* in case of errors */
savepc(ci); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */
@ -1868,7 +1901,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmcase(OP_SETLIST) {
StkId ra = RA(i);
unsigned n = cast_uint(GETARG_vB(i));
unsigned int last = cast_uint(GETARG_vC(i));
unsigned last = cast_uint(GETARG_vC(i));
Table *h = hvalue(s2v(ra));
if (n == 0)
n = cast_uint(L->top.p - ra) - 1; /* get up to the top */
@ -1902,12 +1935,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_VARARG) {
StkId ra = RA(i);
int n = GETARG_C(i) - 1; /* required results */
Protect(luaT_getvarargs(L, ci, ra, n));
int n = GETARG_C(i) - 1; /* required results (-1 means all) */
int vatab = GETARG_k(i) ? GETARG_B(i) : -1;
Protect(luaT_getvarargs(L, ci, ra, n, vatab));
vmbreak;
}
vmcase(OP_GETVARG) {
StkId ra = RA(i);
TValue *rc = vRC(i);
luaT_getvararg(ci, ra, rc);
vmbreak;
}
vmcase(OP_ERRNNIL) {
TValue *ra = vRA(i);
if (!ttisnil(ra))
halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i)));
vmbreak;
}
vmcase(OP_VARARGPREP) {
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
ProtectNT(luaT_adjustvarargs(L, ci, cl->p));
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
luaD_hookcall(L, ci);
L->oldpc = 1; /* next opcode will be seen as a "new" line */

View File

@ -15,9 +15,9 @@ CWARNSCPP= \
-Wdouble-promotion \
-Wmissing-declarations \
-Wconversion \
-Wstrict-overflow=2 \
# the next warnings might be useful sometimes,
# but usually they generate too much noise
# -Wstrict-overflow=2 \
# -Werror \
# -pedantic # warns if we use jump tables \
# -Wformat=2 \
@ -72,7 +72,7 @@ LOCAL = $(TESTS) $(CWARNS)
# For C89, "-std=c89 -DLUA_USE_C89"
# Note that Linux/Posix options are not compatible with C89
MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX
MYLDFLAGS= $(LOCAL) -Wl,-E
MYLDFLAGS= -Wl,-E
MYLIBS= -ldl
@ -166,8 +166,7 @@ ldump.o: ldump.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h \
ltable.h
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.h
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \

View File

@ -8,11 +8,11 @@
---------------------------------------------------------------
header = [[
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<!DOCTYPE html>
<html>
<head>
<title>Lua 5.4 Reference Manual</title>
<title>Lua 5.5 Reference Manual</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link rel="stylesheet" href="lua.css">
<link rel="stylesheet" href="manual.css">
@ -23,7 +23,7 @@ header = [[
<hr>
<h1>
<a href="http://www.lua.org/home.html"><img src="logo.gif" alt="[Lua logo]" border="0"></a>
Lua 5.4 Reference Manual
Lua 5.5 Reference Manual
</h1>
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

View File

@ -107,7 +107,7 @@ for small machines and embedded systems.
Unless stated otherwise,
any overflow when manipulating integer values @def{wrap around},
according to the usual rules of two-complement arithmetic.
according to the usual rules of two's complement arithmetic.
(In other words,
the actual result is the unique representable integer
that is equal modulo @M{2@sp{n}} to the mathematical result,
@ -127,7 +127,8 @@ strings can contain any 8-bit value,
including @x{embedded zeros} (@Char{\0}).
Lua is also encoding-agnostic;
it makes no assumptions about the contents of a string.
The length of any string in Lua must fit in a Lua integer.
The length of any string in Lua must fit in a Lua integer,
and the string plus a small header must fit in @id{size_t}.
Lua can call (and manipulate) functions written in Lua and
functions written in C @see{functioncall}.
@ -229,7 +230,7 @@ as the following example illustrates:
@verbatim{
X = 1 -- Ok, global by default
do
global Y -- voids implicit initial declaration
global Y -- voids the implicit initial declaration
Y = 1 -- Ok, Y declared as global
X = 1 -- ERROR, X not declared
end
@ -269,7 +270,7 @@ print(x) --> 10 (the global one)
Notice that, in a declaration like @T{local x = x},
the new @id{x} being declared is not in scope yet,
and so the @id{x} in the left-hand side refers to the outside variable.
and so the @id{x} on the right-hand side refers to the outside variable.
Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions
@ -1555,7 +1556,8 @@ It has the following syntax:
exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
}
The given identifier (@bnfNter{Name}) defines the control variable,
which is a new read-only variable local to the loop body (@emph{block}).
which is a new read-only (@id{const}) variable local to the loop body
(@emph{block}).
The loop starts by evaluating once the three control expressions.
Their values are called respectively
@ -1610,7 +1612,7 @@ works as follows.
The names @rep{var_i} declare loop variables local to the loop body.
The first of these variables is the @emph{control variable},
which is a read-only variable.
which is a read-only (@id{const}) variable.
The loop starts by evaluating @rep{explist}
to produce four values:
@ -1651,15 +1653,22 @@ Function calls are explained in @See{functioncall}.
@sect3{localvar| @title{Variable Declarations}
Local and global variables can be declared anywhere inside a block.
The declaration for locals can include an initialization:
The declaration can include an initialization:
@Produc{
@producname{stat}@producbody{@Rw{local}
attnamelist @bnfopt{@bnfter{=} explist}}
@producname{stat}@producbody{@Rw{global} attnamelist}
@producname{stat}@producbody{@Rw{global}
attnamelist @bnfopt{@bnfter{=} explist}}
}
If present, an initial assignment has the same semantics
If there is no initialization,
local variables are initialized with @nil;
global variables are left unchanged.
Otherwise, the initialization gets the same adjustment
of a multiple assignment @see{assignment}.
Otherwise, all local variables are initialized with @nil.
Moreover, for global variables,
the initialization will raise a runtime error
if the variable is already defined,
that is, it has a non-nil value.
The list of names may be prefixed by an attribute
(a name between angle brackets)
@ -1712,7 +1721,8 @@ and a program that starts with any other global declaration
(e.g., @T{global none}) can only refer to declared variables.
Note that, for global variables,
the effect of any declaration is only syntactical:
the effect of any declaration is only syntactical
(except for the optional assignment):
@verbatim{
global X <const>, _G
X = 1 -- ERROR
@ -2081,12 +2091,12 @@ Note that keys that are not positive integers
do not interfere with borders.
A table with exactly one border is called a @def{sequence}.
For instance, the table @T{{10, 20, 30, 40, 50}} is a sequence,
For instance, the table @T{{10,20,30,40,50}} is a sequence,
as it has only one border (5).
The table @T{{10, 20, 30, nil, 50}} has two borders (3 and 5),
The table @T{{10,20,30,nil,50}} has two borders (3 and 5),
and therefore it is not a sequence.
(The @nil at index 4 is called a @emphx{hole}.)
The table @T{{nil, 20, 30, nil, nil, 60, nil}}
The table @T{{nil,20,30,nil,nil,60,nil}}
has three borders (0, 3, and 6),
so it is not a sequence, too.
The table @T{{}} is a sequence with border 0.
@ -2211,7 +2221,7 @@ The form
}
can be used to emulate methods.
A call @T{v:name(@rep{args})}
is syntactic sugar for @T{v.name(v,@rep{args})},
is syntactic sugar for @T{v.name(v, @rep{args})},
except that @id{v} is evaluated only once.
Arguments have the following syntax:
@ -2258,7 +2268,7 @@ return x or f(x) -- results adjusted to 1
@sect3{func-def| @title{Function Definitions}
The syntax for function definition is
The syntax for a function definition is
@Produc{
@producname{functiondef}@producbody{@Rw{function} funcbody}
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@ -2308,71 +2318,10 @@ global function f () @rep{body} end
}
translates to
@verbatim{
global f; f = function () @rep{body} end
global f; global f = function () @rep{body} end
}
A function definition is an executable expression,
whose value has type @emph{function}.
When Lua precompiles a chunk,
all its function bodies are precompiled too,
but they are not created yet.
Then, whenever Lua executes the function definition,
the function is @emph{instantiated} (or @emph{closed}).
This function instance, or @emphx{closure},
is the final value of the expression.
Parameters act as local variables that are
initialized with the argument values:
@Produc{
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @Or
@bnfter{...}}
}
When a Lua function is called,
it adjusts its list of @x{arguments} to
the length of its list of parameters @see{multires},
unless the function is a @def{variadic function},
which is indicated by three dots (@Char{...})
at the end of its parameter list.
A variadic function does not adjust its argument list;
instead, it collects all extra arguments and supplies them
to the function through a @def{vararg expression},
which is also written as three dots.
The value of this expression is a list of all actual extra arguments,
similar to a function with multiple results @see{multires}.
As an example, consider the following definitions:
@verbatim{
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
}
Then, we have the following mapping from arguments to parameters and
to the vararg expression:
@verbatim{
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, ... -> (nothing)
g(3, 4) a=3, b=4, ... -> (nothing)
g(3, 4, 5, 8) a=3, b=4, ... -> 5 8
g(5, r()) a=5, b=1, ... -> 2 3
}
Results are returned using the @Rw{return} statement @see{control}.
If control reaches the end of a function
without encountering a @Rw{return} statement,
then the function returns with no results.
@index{multiple return}
There is a system-dependent limit on the number of values
that a function may return.
This limit is guaranteed to be at least 1000.
The second @Rw{global} makes the assignment an initialization,
which will raise an error if that global is already defined.
The @emphx{colon} syntax
is used to emulate @def{methods},
@ -2386,10 +2335,104 @@ is syntactic sugar for
t.a.b.c.f = function (self, @rep{params}) @rep{body} end
}
A function definition is an executable expression,
whose value has type @emph{function}.
When Lua precompiles a chunk,
all its function bodies are precompiled too,
but they are not created yet.
Then, whenever Lua executes the function definition,
the function is @emph{instantiated} (or @emph{closed}).
This function instance, or @emphx{closure},
is the final value of the expression.
Results are returned using the @Rw{return} statement @see{control}.
If control reaches the end of a function
without encountering a @Rw{return} statement,
then the function returns with no results.
@index{multiple return}
There is a system-dependent limit on the number of values
that a function may return.
This limit is guaranteed to be at least 1000.
@sect4{@title{Parameters}
Parameters act as local variables that are
initialized with the argument values:
@Produc{
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or
varargparam}
@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}}
}
When a Lua function is called,
it adjusts its list of @x{arguments} to
the length of its list of parameters @see{multires},
unless the function is a @def{variadic function},
which is indicated by three dots (@Char{...})
at the end of its parameter list.
A variadic function does not adjust its argument list;
instead, it collects all extra arguments and supplies them
to the function through a @def{vararg table}.
In that table,
the values at indices 1, 2, etc. are the extra arguments,
and the value at index @St{n} is the number of extra arguments.
As an example, consider the following definitions:
@verbatim{
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
}
Then, we have the following mapping from arguments to parameters and
to the vararg table:
@verbatim{
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, va. table -> {n = 0}
g(3, 4) a=3, b=4, va. table -> {n = 0}
g(3, 4, 5, 8) a=3, b=4, va. table -> {5, 8, n = 2}
g(5, r()) a=5, b=1, va. table -> {2, 3, n = 2}
}
@sect3{multires| @title{Lists of expressions, multiple results,
and adjustment}
A vararg table in a variadic function can have an optional name,
given after the three dots.
When present,
that name denotes a read-only local variable that
refers to the vararg table.
If the vararg table does not have a name,
it can only be accessed through a vararg expression.
A vararg expression is also written as three dots,
and its value is a list of the values in the vararg table,
from 1 to the integer value at index @St{n}.
(Therefore, if the code does not modify the vararg table,
this list corresponds to the extra arguments in the function call.)
This list behaves like the results from a
function with multiple results @see{multires}.
As an optimization,
if the vararg table satisfies some conditions,
the code does not create an actual table and instead translates
the indexing expressions and the vararg expressions
into accesses to the internal vararg data.
The conditions are as follows:
If the vararg table has a name,
that name is not an upvalue in a nested function
and it is used only as the base table
in the syntactic constructions @T{t[exp]} or @T{t.id}.
Note that an anonymous vararg table always satisfy these conditions.
}
}
@sect3{multires| @title{Lists of Expressions, Multiple Results, and Adjustment}
Both function calls and vararg expressions can result in multiple values.
These expressions are called @def{multires expressions}.
@ -2406,22 +2449,22 @@ These are the places where Lua expects a list of expressions:
@description{
@item{A @rw{return} statement,
for instance @T{return e1, e2, e3} @see{control}.}
for instance @T{return e1,e2,e3} @see{control}.}
@item{A table constructor,
for instance @T{{e1, e2, e3}} @see{tableconstructor}.}
for instance @T{{e1,e2,e3}} @see{tableconstructor}.}
@item{The arguments of a function call,
for instance @T{foo(e1, e2, e3)} @see{functioncall}.}
for instance @T{foo(e1,e2,e3)} @see{functioncall}.}
@item{A multiple assignment,
for instance @T{a , b, c = e1, e2, e3} @see{assignment}.}
for instance @T{a,b,c = e1,e2,e3} @see{assignment}.}
@item{A local declaration,
for instance @T{local a , b, c = e1, e2, e3} @see{localvar}.}
@item{A local or global declaration,
which is similar to a multiple assignment.}
@item{The initial values in a generic @rw{for} loop,
for instance @T{for k in e1, e2, e3 do ... end} @see{for}.}
for instance @T{for k in e1,e2,e3 do ... end} @see{for}.}
}
In the last four cases,
@ -2429,8 +2472,7 @@ the list of values from the list of expressions
must be @emph{adjusted} to a specific length:
the number of parameters in a call to a non-variadic function
@see{func-def},
the number of variables in a multiple assignment or
a local declaration,
the number of variables in a multiple assignment or a declaration,
and exactly four values for a generic @rw{for} loop.
The @def{adjustment} follows these rules:
If there are more values than needed,
@ -2459,7 +2501,7 @@ we recommend assigning the vararg expression
to a single variable and using that variable
in its place.
Here are some examples of uses of mutlres expressions.
Here are some examples of uses of multires expressions.
In all cases, when the construction needs
@Q{the n-th result} and there is no such result,
it uses a @nil.
@ -2648,7 +2690,7 @@ which behaves like a nil value.
}
@sect3{constchar|@title{Pointers to strings}
@sect3{constchar|@title{Pointers to Strings}
Several functions in the API return pointers (@T{const char*})
to Lua strings in the stack.
@ -2823,7 +2865,16 @@ status codes to indicate different kinds of errors or other conditions:
For such errors, Lua does not call the @x{message handler}.
}
@item{@defid{LUA_ERRERR}| error while running the @x{message handler}.}
@item{@defid{LUA_ERRERR}|
stack overflow while running the @x{message handler}
due to another stack overflow.
More often than not,
this error is the result of some other error while running
a message handler.
An error in a message handler will call the handler again,
which will generate the error again, and so on,
until this loop exhausts the stack and cause this error.
}
@item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation
or format error in a binary chunk.}
@ -3004,7 +3055,7 @@ typedef void * (*lua_Alloc) (void *ud,
size_t osize,
size_t nsize);|
The type of the @x{memory-allocation function} used by Lua states.
The type of the @x{memory-allocator function} used by Lua states.
The allocator function must provide a
functionality similar to @id{realloc},
but not exactly the same.
@ -3039,11 +3090,12 @@ the allocator must behave like @id{realloc}.
In particular, the allocator returns @id{NULL}
if and only if it cannot fulfill the request.
Here is a simple implementation for the @x{allocator function}.
It is used in the auxiliary library by @Lid{luaL_newstate}.
Here is a simple implementation for the @x{allocator function},
corresponding to the function @Lid{luaL_alloc} from the
auxiliary library.
@verbatim{
static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
void *luaL_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
free(ptr);
@ -3470,7 +3522,7 @@ This function should not be called by a finalizer.
@APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);|
@apii{0,0,-}
Returns the @x{memory-allocation function} of a given state.
Returns the @x{memory-allocator function} of a given state.
If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the
opaque pointer given when the memory-allocator function was set.
@ -3588,9 +3640,9 @@ because a pseudo-index is not an actual stack position.
The type of integers in Lua.
By default this type is @id{long long},
(usually a 64-bit two-complement integer),
(usually a 64-bit two's complement integer),
but that can be changed to @id{long} or @id{int}
(usually a 32-bit two-complement integer).
(usually a 32-bit two's complement integer).
(See @id{LUA_INT_TYPE} in @id{luaconf.h}.)
Lua also defines the constants
@ -3827,7 +3879,7 @@ is a seed for the hashing of strings.
@apii{0,1,m}
Creates a new empty table and pushes it onto the stack.
It is equivalent to @T{lua_createtable(L, 0, 0)}.
It is equivalent to @T{lua_createtable(L,0,0)}.
}
@ -3924,8 +3976,8 @@ This macro may evaluate its arguments more than once.
}
@APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx,
char *buff);|
@APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx,
char *buff);|
@apii{0,0,-}
Converts the number at acceptable index @id{idx} to a string
@ -4050,7 +4102,7 @@ This function is equivalent to @Lid{lua_pushcclosure} with no upvalues.
}
@APIEntry{const char *(lua_pushexternalstring) (lua_State *L,
@APIEntry{const char *lua_pushexternalstring (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud);|
@apii{0,1,m}
@ -4073,21 +4125,18 @@ the string @id{s} as the block,
the length plus one (to account for the ending zero) as the old size,
and 0 as the new size.
Lua always @x{internalizes} strings with lengths up to 40 characters.
So, for strings in that range,
this function will immediately internalize the string
and call @id{falloc} to free the buffer.
Even when using an external buffer,
Lua still has to allocate a header for the string.
In case of a memory-allocation error,
Lua will call @id{falloc} before raising the error.
The function returns a pointer to the string (that is, @id{s}).
}
@APIEntry{const char *lua_pushfstring (lua_State *L, const char *fmt, ...);|
@apii{0,1,m}
@apii{0,1,v}
Pushes onto the stack a formatted string
and returns a pointer to this string @see{constchar}.
@ -4107,6 +4156,9 @@ A conversion specifier (and its corresponding extra argument) can be
Every occurrence of @Char{%} in the string @id{fmt}
must form a valid conversion specifier.
Besides memory allocation errors,
this function may raise an error if the resulting string is too large.
}
@APIEntry{void lua_pushglobaltable (lua_State *L);|
@ -4139,7 +4191,7 @@ light userdata with the same @N{C address}.
}
@APIEntry{const char *lua_pushliteral (lua_State *L, const char *s);|
@apii{0,1,m}
@apii{0,1,v}
This macro is equivalent to @Lid{lua_pushstring},
but should be used only when @id{s} is a literal string.
@ -4148,7 +4200,7 @@ but should be used only when @id{s} is a literal string.
}
@APIEntry{const char *lua_pushlstring (lua_State *L, const char *s, size_t len);|
@apii{0,1,m}
@apii{0,1,v}
Pushes the string pointed to by @id{s} with size @id{len}
onto the stack.
@ -4160,6 +4212,9 @@ including @x{embedded zeros}.
Returns a pointer to the internal copy of the string @see{constchar}.
Besides memory allocation errors,
this function may raise an error if the string is too large.
}
@APIEntry{void lua_pushnil (lua_State *L);|
@ -5019,8 +5074,8 @@ then @id{name} is set to @id{NULL}.
@item{@id{namewhat}|
explains the @T{name} field.
The value of @T{namewhat} can be
@T{"global"}, @T{"local"}, @T{"method"},
@T{"field"}, @T{"upvalue"}, or @T{""} (the empty string),
@T{"global"}, @T{"local"}, @T{"upvalue"},
@T{"field"}, @T{""} (the empty string), plus some other options,
according to how the function was called.
(Lua uses the empty string when no other option seems to apply.)
}
@ -5384,7 +5439,7 @@ the auxiliary library provides higher-level functions for some
common tasks.
All functions and types from the auxiliary library
are defined in header file @id{lauxlib.h} and
are defined in the header file @id{lauxlib.h} and
have a prefix @id{luaL_}.
All functions in the auxiliary library are built on
@ -5528,7 +5583,7 @@ Its pattern of use is as follows:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.}
@item{Then initialize it with a call @T{luaL_buffinit(L, &b)}.}
@item{Then initialize it with a call @T{luaL_buffinit(L,&b)}.}
@item{
Then add string pieces to the buffer calling any of
@ -5549,12 +5604,12 @@ you can use the buffer like this:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.}
@item{Then initialize it and preallocate a space of
size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.}
size @id{sz} with a call @T{luaL_buffinitsize(L,&b,sz)}.}
@item{Then produce the string into that space.}
@item{
Finish by calling @T{luaL_pushresultsize(&b, sz)},
Finish by calling @T{luaL_pushresultsize(&b,sz)},
where @id{sz} is the total size of the resulting string
copied into that space (which may be less than or
equal to the preallocated size).
@ -5947,9 +6002,8 @@ it does not run it.
@apii{0,0,-}
Returns a value with a weak attempt for randomness.
(It produces that value based on the current date and time
and the address of an internal variable,
in case the machine has Address Space Layout Randomization.)
The parameter @id{L} can be @id{NULL}
if there is no Lua state available.
}
@ -6005,8 +6059,9 @@ with @id{tname} in the registry.
@apii{0,0,-}
Creates a new Lua state.
It calls @Lid{lua_newstate} with an
allocator based on the @N{ISO C} allocation functions
It calls @Lid{lua_newstate} with @Lid{luaL_alloc} as
the allocator function and the result of @T{luaL_makeseed(NULL)}
as the seed,
and then sets a warning function and a panic function @see{C-error}
that print messages to the standard error output.
@ -6159,7 +6214,7 @@ You should not manually set integer keys in the table
after the first use of @Lid{luaL_ref}.
You can retrieve an object referred by the reference @id{r}
by calling @T{lua_rawgeti(L, t, r)} or @T{lua_geti(L, t, r)}.
by calling @T{lua_rawgeti(L,t,r)} or @T{lua_geti(L,t,r)}.
The function @Lid{luaL_unref} frees a reference.
If the object on the top of the stack is @nil,
@ -6230,6 +6285,15 @@ in the registry @seeC{luaL_newmetatable}.
}
@APIEntry{
void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize);|
A standard allocator function for Lua @seeF{lua_Alloc},
built on top of the C functions @id{realloc} and @id{free}.
}
@APIEntry{
typedef struct luaL_Stream {
FILE *f;
@ -6428,7 +6492,7 @@ the host program can call the function @Lid{luaL_openlibs}.
Alternatively,
the host can select which libraries to open,
by using @Lid{luaL_openselectedlibs}.
Both functions are defined in the header file @id{lualib.h}.
Both functions are declared in the header file @id{lualib.h}.
@index{lualib.h}
The stand-alone interpreter @id{lua} @see{lua-sa}
@ -6575,7 +6639,7 @@ The call always returns the previous value of the parameter.
If the call does not give a new value,
the value is left unchanged.
Lua rounds these values before storing them;
Lua stores these values in a compressed format,
so, the value returned as the previous value may not be
exactly the last value set.
}
@ -6589,10 +6653,10 @@ This function should not be called by a finalizer.
}
@LibEntry{dofile ([filename])|
Opens the named file and executes its content as a Lua chunk.
Opens the named file and executes its content as a Lua chunk,
returning all values returned by the chunk.
When called without arguments,
@id{dofile} executes the content of the standard input (@id{stdin}).
Returns all values returned by the chunk.
In case of errors, @id{dofile} propagates the error
to its caller.
(That is, @id{dofile} does not run in protected mode.)
@ -6741,11 +6805,11 @@ In particular, you may set existing fields to nil.
@LibEntry{pairs (t)|
If @id{t} has a metamethod @idx{__pairs},
calls it with @id{t} as argument and returns the first three
calls it with @id{t} as argument and returns the first four
results from the call.
Otherwise,
returns three values: the @Lid{next} function, the table @id{t}, and @nil,
returns the @Lid{next} function, the table @id{t}, plus two @nil values,
so that the construction
@verbatim{
for k,v in pairs(t) do @rep{body} end
@ -6908,7 +6972,7 @@ and @St{userdata}.
A global variable (not a function) that
holds a string containing the running Lua version.
The current value of this variable is @St{Lua 5.4}.
The current value of this variable is @St{Lua 5.5}.
}
@ -6964,7 +7028,7 @@ in case of error
(either the original error that stopped the coroutine or
errors in closing methods),
this function returns @false plus the error object;
otherwise ir returns @true.
otherwise it returns @true.
}
@ -7154,7 +7218,7 @@ to search for a @N{C loader}.
Lua initializes the @N{C path} @Lid{package.cpath} in the same way
it initializes the Lua path @Lid{package.path},
using the environment variable @defid{LUA_CPATH_5_4},
using the environment variable @defid{LUA_CPATH_5_5},
or the environment variable @defid{LUA_CPATH},
or a default path defined in @id{luaconf.h}.
@ -7223,7 +7287,7 @@ A string with the path used by @Lid{require}
to search for a Lua loader.
At start-up, Lua initializes this variable with
the value of the environment variable @defid{LUA_PATH_5_4} or
the value of the environment variable @defid{LUA_PATH_5_5} or
the environment variable @defid{LUA_PATH} or
with a default path defined in @id{luaconf.h},
if those environment variables are not defined.
@ -7594,9 +7658,9 @@ x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
end)
-- x="4+5 = 9"
local t = {name="lua", version="5.4"}
local t = {name="lua", version="5.5"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
-- x="lua-5.4.tar.gz"
-- x="lua-5.5.tar.gz"
}
}
@ -7680,7 +7744,7 @@ If @id{j} is absent, then it is assumed to be equal to @num{-1}
In particular,
the call @T{string.sub(s,1,j)} returns a prefix of @id{s}
with length @id{j},
and @T{string.sub(s, -i)} (for a positive @id{i})
and @T{string.sub(s,-i)} (for a positive @id{i})
returns a suffix of @id{s}
with length @id{i}.
@ -8116,7 +8180,7 @@ the function returns @fail.
A negative @id{n} gets characters before position @id{i}.
The default for @id{i} is 1 when @id{n} is non-negative
and @T{#s + 1} otherwise,
so that @T{utf8.offset(s, -n)} gets the offset of the
so that @T{utf8.offset(s,-n)} gets the offset of the
@id{n}-th character from the end of the string.
As a special case,
@ -8169,7 +8233,7 @@ the table will have; its default is zero.
Inserts element @id{value} at position @id{pos} in @id{list},
shifting up the elements
@T{list[pos], list[pos+1], @Cdots, list[#list]}.
@T{list[pos],list[pos+1],@Cdots,list[#list]}.
The default value for @id{pos} is @T{#list+1},
so that a call @T{table.insert(t,x)} inserts @id{x} at the end
of the list @id{t}.
@ -8207,7 +8271,7 @@ Removes from @id{list} the element at position @id{pos},
returning the value of the removed element.
When @id{pos} is an integer between 1 and @T{#list},
it shifts down the elements
@T{list[pos+1], list[pos+2], @Cdots, list[#list]}
@T{list[pos+1],list[pos+2],@Cdots,list[#list]}
and erases element @T{list[#list]};
The index @id{pos} can also be 0 when @T{#list} is 0,
or @T{#list + 1}.
@ -8337,6 +8401,17 @@ that rounds the quotient towards zero. (integer/float)
}
@LibEntry{math.frexp (x)|
Returns two numbers @id{m} and @id{e} such that @M{x = m2@sp{e}},
where @id{e} is an integer.
When @id{x} is zero, NaN, +inf, or -inf,
@id{m} is equal to @id{x};
otherwise, the absolute value of @id{m}
is in the range @C{(} @M{[0.5, 1)} @C{]}.
}
@LibEntry{math.huge|
The float value @idx{HUGE_VAL},
@ -8344,6 +8419,12 @@ a value greater than any other numeric value.
}
@LibEntry{math.ldexp (m, e)|
Returns @M{m2@sp{e}}, where @id{e} is an integer.
}
@LibEntry{math.log (x [, base])|
Returns the logarithm of @id{x} in the given base.
@ -8399,7 +8480,7 @@ Converts the angle @id{x} from degrees to radians.
When called without arguments,
returns a pseudo-random float with uniform distribution
in the range @C{(} @M{[0,1)}. @C{]}
in the range @C{(} @M{[0, 1)}. @C{]}
When called with two integers @id{m} and @id{n},
@id{math.random} returns a pseudo-random integer
with uniform distribution in the range @M{[m, n]}.
@ -9120,6 +9201,10 @@ Compile-time constants may not appear in this listing,
if they were optimized away by the compiler.
Negative indices refer to vararg arguments;
@num{-1} is the first vararg argument.
These negative indices are only available when the vararg table
has been optimized away;
otherwise, the vararg arguments are available in the vararg table.
The function returns @fail
if there is no variable with the given index,
and raises an error when called with a level out of range.
@ -9332,7 +9417,7 @@ when the standard input (@id{stdin}) is a terminal,
and as @T{lua -} otherwise.
When called without the option @T{-E},
the interpreter checks for an environment variable @defid{LUA_INIT_5_4}
the interpreter checks for an environment variable @defid{LUA_INIT_5_5}
(or @defid{LUA_INIT} if the versioned name is not defined)
before running any argument.
If the variable content has the format @T{@At@rep{filename}},
@ -9702,8 +9787,10 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}}
@Or @bnfter{...}}
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or
varargparam}
@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}}
@producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}}

View File

@ -5,10 +5,14 @@
**
** $ gcc -O2 -std=c99 -o lua onelua.c -lm
**
** or
** or (for C89)
**
** $ gcc -O2 -std=c89 -DLUA_USE_C89 -o lua onelua.c -lm
**
** or (for Linux)
**
** gcc -O2 -o lua -DLUA_USE_LINUX -Wl,-E onelua.c -lm -ldl
**
*/
/* default is to build the full interpreter */
@ -30,7 +34,15 @@
#define LUA_USE_LINUX
#define LUA_USE_MACOSX
#define LUA_USE_POSIX
#define LUA_ANSI
#endif
/*
** Other specific features
*/
#if 0
#define LUA_32BITS
#define LUA_USE_C89
#endif
@ -54,12 +66,10 @@
#include <string.h>
#include <time.h>
/* setup for luaconf.h */
#define LUA_CORE
#define LUA_LIB
#define ltable_c
#define lvm_c
#include "luaconf.h"
/* do not export internal symbols */
@ -110,6 +120,11 @@
#include "linit.c"
#endif
/* test library -- used only for internal development */
#if defined(LUA_DEBUG)
#include "ltests.c"
#endif
/* lua */
#ifdef MAKE_LUA
#include "lua.c"

View File

@ -246,7 +246,8 @@ assert(not T.testC("compare LT 1 4, return 1"))
assert(not T.testC("compare LE 9 1, return 1"))
assert(not T.testC("compare EQ 9 9, return 1"))
local b = {__lt = function (a,b) return a[1] < b[1] end}
local b = {__lt = function (a,b) return a[1] < b[1] end,
__le = function (a,b) return a[1] <= b[1] end}
local a1,a3,a4 = setmetatable({1}, b),
setmetatable({3}, b),
setmetatable({4}, b)

View File

@ -308,11 +308,11 @@ else
_ENV.x, _ENV.y = nil
end
_ENV = _G
-- testing preload
do
local p = package
package = {}
@ -331,6 +331,26 @@ do
assert(type(package.path) == "string")
end
do print("testing external strings")
package.cpath = DC"?"
local lib2 = require"lib2-v2"
local t = {}
for _, len in ipairs{0, 10, 39, 40, 41, 1000} do
local str = string.rep("a", len)
local str1 = lib2.newstr(str)
assert(str == str1)
assert(not T or T.hash(str) == T.hash(str1))
t[str1] = 20; assert(t[str] == 20 and t[str1] == 20)
t[str] = 10; assert(t[str1] == 10)
local tt = {[str1] = str1}
assert(next(tt) == str1 and next(tt, str1) == nil)
assert(tt[str] == str)
local str2 = lib2.newstr(str1)
assert(str == str2 and t[str2] == 10 and tt[str2] == str)
end
end
print('+')
end --]
@ -447,7 +467,7 @@ do
end
-- test of large float/integer indices
-- test of large float/integer indices
-- compute maximum integer where all bits fit in a float
local maxint = math.maxinteger

View File

@ -4,7 +4,7 @@ local strsub = string.sub
local print = print
_ENV = nil
global none
-- Try to convert a value to an integer, without assuming any coercion.
local function toint (x)

View File

@ -24,7 +24,7 @@ assert(not pcall(type))
-- testing local-function recursion
global fact; fact = false
global fact = false
do
local res = 1
local function fact (n)
@ -65,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+')
global t; t = nil -- 'declare' t
global t = nil -- 'declare' t
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f( -- this line change must be valid

View File

@ -702,7 +702,9 @@ else
assert(t.currentline == t.linedefined + 2)
assert(not debug.getinfo(c, 1)) -- no other level
assert(coroutine.resume(c)) -- run next line
local n,v = debug.getlocal(c, 0, 2) -- check next local
local n,v = debug.getlocal(c, 0, 2) -- check vararg table
assert(n == "(vararg table)" and v == nil)
local n,v = debug.getlocal(c, 0, 3) -- check next local
assert(n == "b" and v == 10)
v = {coroutine.resume(c)} -- finish coroutine
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)

View File

@ -356,8 +356,8 @@ function f(a,b)
global assert, g, string
local _, y = debug.getlocal(1, 2)
assert(x == a and y == b)
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
assert(debug.setlocal(2, 4, "manga") == "B")
assert(debug.setlocal(2, 4, "pera") == "AA".."AA")
assert(debug.setlocal(2, 5, "manga") == "B")
x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
@ -392,7 +392,7 @@ function g (...)
global *
local B = 13
global<const> assert
local x,y = debug.getlocal(1,5)
local x,y = debug.getlocal(1,6)
assert(x == 'B' and y == 13)
end
end
@ -458,7 +458,8 @@ local function collectlocals (level)
local tab = {}
for i = 1, math.huge do
local n, v = debug.getlocal(level + 1, i)
if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
if not (n and string.find(n, "^[a-zA-Z0-9_]+$") or
n == "(vararg table)") then
break -- consider only real variables
end
tab[n] = v
@ -725,6 +726,9 @@ assert(t.isvararg == false and t.nparams == 3 and t.nups == 0)
t = debug.getinfo(function (a,b,...) return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1)
t = debug.getinfo(function (a,b,...t) t.n = 2; return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 0)
t = debug.getinfo(1) -- main
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
debug.getupvalue(t.func, 1) == "_ENV")

View File

@ -418,28 +418,28 @@ end
-- testing line error
local function lineerror (s, l)
local function lineerror (s, l, w)
local err,msg = pcall(load(s))
local line = tonumber(string.match(msg, ":(%d+):"))
assert(line == l or (not line and not l))
assert((line == l or (not line and not l)) and string.find(msg, w))
end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3)
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4)
lineerror("function a.x.y ()\na=a+1\nend", 1)
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2, "limit")
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3, "to call")
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4, "to call")
lineerror("function a.x.y ()\na=a+1\nend", 1, "index")
lineerror("a = \na\n+\n{}", 3)
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6)
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3)
lineerror("a = \na\n+\n{}", 3, "arithmetic")
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6, "arithmetic")
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3, "arithmetic")
lineerror("a\n=\n-\n\nprint\n;", 3)
lineerror("a\n=\n-\n\nprint\n;", 3, "arithmetic")
lineerror([[
a
( -- <<
23)
]], 2)
]], 2, "call")
lineerror([[
local a = {x = 13}
@ -449,7 +449,7 @@ x
( -- <<
23
)
]], 5)
]], 5, "call")
lineerror([[
local a = {x = 13}
@ -459,17 +459,17 @@ x
(
23 + a
)
]], 6)
]], 6, "arithmetic")
local p = [[
function g() f() end
function f(x) error('a', XX) end
g()
]]
XX=3;lineerror((p), 3)
XX=0;lineerror((p), false)
XX=1;lineerror((p), 2)
XX=2;lineerror((p), 1)
XX=3;lineerror((p), 3, "a")
XX=0;lineerror((p), false, "a")
XX=1;lineerror((p), 2, "a")
XX=2;lineerror((p), 1, "a")
_G.XX, _G.g, _G.f = nil
@ -477,7 +477,7 @@ lineerror([[
local b = false
if not b then
error 'test'
end]], 3)
end]], 3, "test")
lineerror([[
local b = false
@ -487,7 +487,7 @@ if not b then
error 'test'
end
end
end]], 5)
end]], 5, "test")
lineerror([[
_ENV = 1
@ -495,7 +495,7 @@ global function foo ()
local a = 10
return a
end
]], 2)
]], 2, "index")
-- bug in 5.4.0
@ -503,17 +503,37 @@ lineerror([[
local a = 0
local b = 1
local c = b % a
]], 3)
]], 3, "perform")
do
-- Force a negative estimate for base line. Error in instruction 2
-- (after VARARGPREP, GETGLOBAL), with first absolute line information
-- (forced by too many lines) in instruction 0.
local s = string.format("%s return __A.x", string.rep("\n", 300))
lineerror(s, 301)
lineerror(s, 301, "index")
end
local function stxlineerror (s, l, w)
local err,msg = load(s)
local line = tonumber(string.match(msg, ":(%d+):"))
assert((line == l or (not line and not l)) and string.find(msg, w, 1, true))
end
stxlineerror([[
::L1::
::L1::
]], 2, "already defined")
stxlineerror([[
global none
local x = b
]], 2, "not declared")
stxlineerror([[
local <close> a, b
]], 1, "multiple")
if not _soft then
-- several tests that exhaust the Lua stack
collectgarbage()
@ -689,21 +709,26 @@ end
-- testing syntax limits
local function testrep (init, rep, close, repc, finalresult)
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
local res, msg = load(s)
assert(res) -- 100 levels is OK
local function gencode (n)
return init .. string.rep(rep, n) .. close .. string.rep(repc, n)
end
local res, msg = load(gencode(100)) -- 100 levels is OK
assert(res)
if (finalresult) then
assert(res() == finalresult)
end
s = init .. string.rep(rep, 500)
local res, msg = load(s) -- 500 levels not ok
local res, msg = load(gencode(500)) -- 500 levels not ok
assert(not res and (string.find(msg, "too many") or
string.find(msg, "overflow")))
end
testrep("local a", ",a", ";", "") -- local variables
testrep("local a", ",a", "= 1", ",1") -- local variables initialized
testrep("local a", ",a", "= f()", "") -- local variables initialized
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}")
testrep("return ", "(", "2", ")", 2)
testrep("local a; a=", "{", "0", "}") -- constructors
testrep("return ", "(", "2", ")", 2) -- parentheses
-- nested calls (a(a(a(a(...)))))
testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
testrep("", "do ", "", " end")
testrep("", "while a do ", "", " end")

View File

@ -715,7 +715,7 @@ do
end
if T and T.nonblock then
if T and T.nonblock and not _port then
print("testing failed write")
-- unable to write anything to /dev/full
@ -840,7 +840,7 @@ assert(os.date("!\0\0") == "\0\0")
local x = string.rep("a", 10000)
assert(os.date(x) == x)
local t = os.time()
global D; D = os.date("*t", t)
global D = os.date("*t", t)
assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))

View File

@ -707,4 +707,46 @@ end
collectgarbage(oldmode)
if T then
print("testing stack issues when calling finalizers")
local X
local obj
local function initobj ()
X = false
obj = setmetatable({}, {__gc = function () X = true end})
end
local function loop (n)
if n > 0 then loop(n - 1) end
end
-- should not try to call finalizer without a CallInfo available
initobj()
loop(20) -- ensure stack space
T.resetCI() -- remove extra CallInfos
T.alloccount(0) -- cannot allocate more CallInfos
obj = nil
collectgarbage() -- will not call finalizer
T.alloccount()
assert(X == false)
collectgarbage() -- now will call finalizer (it was still pending)
assert(X == true)
-- should not try to call finalizer without stack space available
initobj()
loop(5) -- ensure enough CallInfos
T.reallocstack(0) -- remove extra stack slots
T.alloccount(0) -- cannot reallocate stack
obj = nil
collectgarbage() -- will not call finalizer
T.alloccount()
assert(X == false)
collectgarbage() -- now will call finalizer (it was still pending)
assert(X == true)
end
print('OK')

View File

@ -176,6 +176,20 @@ do print"testing stop-the-world collection"
assert(collectgarbage("param", "stepsize") == step)
end
if T then -- test GC parameter codification
for _, percentage in ipairs{5, 10, 12, 20, 50, 100, 200, 500} do
local param = T.codeparam(percentage) -- codify percentage
for _, value in ipairs{1, 2, 10, 100, 257, 1023, 6500, 100000} do
local exact = value*percentage // 100
local aprox = T.applyparam(param, value) -- apply percentage
-- difference is at most 10% (+1 compensates difference due to
-- rounding to integers)
assert(math.abs(aprox - exact) <= exact/10 + 1)
end
end
end
collectgarbage(oldmode)
print('OK')

View File

@ -293,6 +293,7 @@ end
foo()
--------------------------------------------------------------------------
-- check for compilation errors
local function checkerr (code, err)
local st, msg = load(code)
assert(not st and string.find(msg, err))
@ -380,7 +381,7 @@ do
global *
Y = x + Y
assert(_ENV.Y == 20)
Y = nil
end
@ -411,5 +412,66 @@ do -- mixing lots of global/local declarations
_ENV.x200 = nil
end
do print "testing initialization in global declarations"
global<const> a, b, c = 10, 20, 30
assert(_ENV.a == 10 and b == 20 and c == 30)
_ENV.a = nil; _ENV.b = nil; _ENV.c = nil;
global<const> a, b, c = 10
assert(_ENV.a == 10 and b == nil and c == nil)
_ENV.a = nil; _ENV.b = nil; _ENV.c = nil;
global table
global a, b, c, d = table.unpack{1, 2, 3, 6, 5}
assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6)
a = nil; b = nil; c = nil; d = nil
local a, b = 100, 200
do
global a, b = a, b
end
assert(_ENV.a == 100 and _ENV.b == 200)
_ENV.a = nil; _ENV.b = nil
assert(_ENV.a == nil and _ENV.b == nil and _ENV.c == nil and _ENV.d == nil)
end
do
global table, string
-- global initialization when names don't fit in K
-- to fill constant table
local code = {}
for i = 1, 300 do code[i] = "'" .. i .. "'" end
code = table.concat(code, ",")
code = string.format([[
return function (_ENV)
local dummy = {%s} -- fill initial positions in constant table,
-- so that initialization must use registers for global names
global a, b, c = 10, 20, 30
end]], code)
local fun = assert(load(code))()
local env = {}
fun(env)
assert(env.a == 10 and env.b == 20 and env.c == 30)
end
do -- testing global redefinitions
-- cannot use 'checkerr' as errors are not compile time
global pcall
local f = assert(load("global print = 10"))
local st, msg = pcall(f)
assert(string.find(msg, "global 'print' already defined"))
local f = assert(load("local _ENV = {AA = false}; global AA = 10"))
local st, msg = pcall(f)
assert(string.find(msg, "global 'AA' already defined"))
end
print'OK'

View File

@ -1,6 +1,8 @@
-- $Id: testes/heavy.lua,v $
-- See Copyright Notice in file lua.h
global <const> *
local function teststring ()
print("creating a string too long")
do
@ -47,9 +49,9 @@ local function loadrep (x, what)
end
function controlstruct ()
local function controlstruct ()
print("control structure too long")
local lim = ((1 << 24) - 2) // 3
local lim = ((1 << 24) - 2) // 4
local s = string.rep("a = a + 1\n", lim)
s = "while true do " .. s .. "end"
assert(load(s))
@ -63,7 +65,7 @@ function controlstruct ()
end
function manylines ()
local function manylines ()
print("loading chunk with too many lines")
local st, msg = loadrep("\n", "lines")
assert(not st and string.find(msg, "too many lines"))
@ -71,7 +73,7 @@ function manylines ()
end
function hugeid ()
local function hugeid ()
print("loading chunk with huge identifier")
local st, msg = loadrep("a", "chars")
assert(not st and
@ -80,7 +82,7 @@ function hugeid ()
print('+')
end
function toomanyinst ()
local function toomanyinst ()
print("loading chunk with too many instructions")
local st, msg = loadrep("a = 10; ", "instructions")
print('+')
@ -107,7 +109,7 @@ local function loadrepfunc (prefix, f)
end
function toomanyconst ()
local function toomanyconst ()
print("loading function with too many constants")
loadrepfunc("function foo () return {0,",
function (n)
@ -126,7 +128,7 @@ function toomanyconst ()
end
function toomanystr ()
local function toomanystr ()
local a = {}
local st, msg = pcall(function ()
for i = 1, math.huge do
@ -144,7 +146,7 @@ function toomanystr ()
end
function toomanyidx ()
local function toomanyidx ()
local a = {}
local st, msg = pcall(function ()
for i = 1, math.huge do

View File

@ -1,7 +1,7 @@
#include "lua.h"
/* function from lib1.c */
int lib1_export (lua_State *L);
LUAMOD_API int lib1_export (lua_State *L);
LUAMOD_API int luaopen_lib11 (lua_State *L) {
return lib1_export(L);

View File

@ -1,3 +1,7 @@
/* implementation for lib2-v2 */
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
@ -8,8 +12,54 @@ static int id (lua_State *L) {
}
struct STR {
void *ud;
lua_Alloc allocf;
};
static void *t_freestr (void *ud, void *ptr, size_t osize, size_t nsize) {
struct STR *blk = (struct STR*)ptr - 1;
blk->allocf(blk->ud, blk, sizeof(struct STR) + osize, 0);
return NULL;
}
static int newstr (lua_State *L) {
size_t len;
const char *str = luaL_checklstring(L, 1, &len);
void *ud;
lua_Alloc allocf = lua_getallocf(L, &ud);
struct STR *blk = (struct STR*)allocf(ud, NULL, 0,
len + 1 + sizeof(struct STR));
if (blk == NULL) { /* allocation error? */
lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
blk->ud = ud; blk->allocf = allocf;
memcpy(blk + 1, str, len + 1);
lua_pushexternalstring(L, (char *)(blk + 1), len, t_freestr, L);
return 1;
}
/*
** Create an external string and keep it in the registry, so that it
** will test that the library code is still available (to deallocate
** this string) when closing the state.
*/
static void initstr (lua_State *L) {
lua_pushcfunction(L, newstr);
lua_pushstring(L,
"012345678901234567890123456789012345678901234567890123456789");
lua_call(L, 1, 1); /* call newstr("0123...") */
luaL_ref(L, LUA_REGISTRYINDEX); /* keep string in the registry */
}
static const struct luaL_Reg funcs[] = {
{"id", id},
{"newstr", newstr},
{NULL, NULL}
};
@ -18,6 +68,7 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) {
lua_settop(L, 2);
lua_setglobal(L, "y"); /* y gets 2nd parameter */
lua_setglobal(L, "x"); /* x gets 1st parameter */
initstr(L);
luaL_newlib(L, funcs);
return 1;
}

View File

@ -5,7 +5,7 @@ LUA_DIR = ../../
CC = gcc
# compilation should generate Dynamic-Link Libraries
CFLAGS = -Wall -std=c99 -O2 -I$(LUA_DIR) -fPIC -shared
CFLAGS = -Wall -O2 -I$(LUA_DIR) -fPIC -shared
# libraries used by the tests
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so

View File

@ -310,8 +310,7 @@ do -- testing presence of second argument
local function foo (howtoclose, obj, n)
local ca -- copy of 'a' visible inside its close metamethod
do
local a <close> = func2close(function (...)
local t = table.pack(...)
local a <close> = func2close(function (...t)
assert(select("#", ...) == n)
assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj))
ca = 15 -- final value to be returned if howtoclose=="scope"
@ -911,8 +910,7 @@ do
local extrares -- result from extra yield (if any)
local function check (body, extra, ...)
local t = table.pack(...) -- expected returns
local function check (body, extra, ...t)
local co = coroutine.wrap(body)
if extra then
extrares = co() -- runs until first (extra) yield

View File

@ -90,7 +90,7 @@ prepfile[[
1, a
)
]]
RUN('lua - < %s > %s', prog, out)
RUN('lua - -- < %s > %s', prog, out)
checkout("1\tnil\n")
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
@ -133,7 +133,7 @@ checkout("-h\n")
prepfile("print(package.path)")
-- test LUA_PATH
RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out)
RUN('env LUA_INIT= LUA_PATH=x lua -- %s > %s', prog, out)
checkout("x\n")
-- test LUA_PATH_version
@ -358,7 +358,7 @@ RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("6\n10\n10\n\n")
prepfile("a = [[b\nc\nd\ne]]\na")
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i -- < %s > %s]], prog, out)
checkprogout("b\nc\nd\ne\n\n")
-- input interrupted in continuation line
@ -488,12 +488,13 @@ assert(not os.remove(out))
-- invalid options
NoRun("unrecognized option '-h'", "lua -h")
NoRun("unrecognized option '---'", "lua ---")
NoRun("unrecognized option '-Ex'", "lua -Ex")
NoRun("unrecognized option '-Ex'", "lua -Ex --")
NoRun("unrecognized option '-vv'", "lua -vv")
NoRun("unrecognized option '-iv'", "lua -iv")
NoRun("'-e' needs argument", "lua -e")
NoRun("syntax error", "lua -e a")
NoRun("'-l' needs argument", "lua -l")
NoRun("-i", "lua -- -i") -- handles -i as a script name
if T then -- test library?

View File

@ -685,6 +685,18 @@ assert(eq(math.exp(0), 1))
assert(eq(math.sin(10), math.sin(10%(2*math.pi))))
do print("testing ldexp/frexp")
global ipairs
for _, x in ipairs{0, 10, 32, -math.pi, 1e10, 1e-10, math.huge, -math.huge} do
local m, p = math.frexp(x)
assert(math.ldexp(m, p) == x)
local am = math.abs(m)
assert(m == x or (0.5 <= am and am < 1))
end
end
assert(tonumber(' 1.3e-2 ') == 1.3e-2)
assert(tonumber(' -1.00000000000001 ') == -1.00000000000001)

View File

@ -42,8 +42,12 @@ checkerr(MEMERRMSG, f)
T.alloccount() -- remove limit
-- preallocate stack space
local function deep (n) if n > 0 then deep(n - 1) end end
-- test memory errors; increase limit for maximum memory by steps,
-- o that we get memory errors in all allocations of a given
-- so that we get memory errors in all allocations of a given
-- task, until there is enough memory to complete the task without
-- errors.
local function testbytes (s, f)
@ -53,6 +57,7 @@ local function testbytes (s, f)
local a,b = nil
while true do
collectgarbage(); collectgarbage()
deep(4)
T.totalmem(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.totalmem(0) -- remove limit
@ -77,6 +82,7 @@ local function testalloc (s, f)
local a,b = nil
while true do
collectgarbage(); collectgarbage()
deep(4)
T.alloccount(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.alloccount() -- remove limit
@ -87,21 +93,19 @@ local function testalloc (s, f)
M = M + 1 -- increase allocation limit
end
print(string.format("minimum allocations for %s: %d allocations", s, M))
return a
return M
end
local function testamem (s, f)
testalloc(s, f)
return testbytes(s, f)
local aloc = testalloc(s, f)
local res = testbytes(s, f)
return {aloc = aloc, res = res}
end
-- doing nothing
b = testamem("doing nothing", function () return 10 end)
assert(b == 10)
-- testing memory errors when creating a new state
local b = testamem("function call", function () return 10 end)
assert(b.res == 10 and b.aloc == 0)
testamem("state creation", function ()
local st = T.newstate()
@ -121,6 +125,18 @@ testamem("coroutine creation", function()
return coroutine.create(print)
end)
do -- vararg tables
local function pack (...t) return t end
local b = testamem("vararg table", function ()
return pack(10, 20, 30, 40, "hello")
end)
assert(b.aloc == 3) -- new table uses three memory blocks
-- table optimized away
local function sel (n, ...arg) return arg[n] + arg.n end
local b = testamem("optimized vararg table",
function () return sel(2.0, 20, 30) end)
assert(b.res == 32 and b.aloc == 0) -- no memory needed for this case
end
-- testing to-be-closed variables
testamem("to-be-closed variables", function()
@ -150,9 +166,9 @@ local function expand (n,s)
e, s, expand(n-1,s), e)
end
G=0; collectgarbage(); a =collectgarbage("count")
G=0; collectgarbage()
load(expand(20,"G=G+1"))()
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
assert(G==20); collectgarbage()
G = nil
testamem("running code on new thread", function ()
@ -160,6 +176,14 @@ testamem("running code on new thread", function ()
end)
do -- external strings
local str = string.rep("a", 100)
testamem("creating external strings", function ()
return T.externstr(str)
end)
end
-- testing memory x compiler
testamem("loadstring", function ()
@ -258,6 +282,25 @@ testamem("growing stack", function ()
return foo(100)
end)
collectgarbage()
collectgarbage()
global io, T, setmetatable, collectgarbage, print
local Count = 0
testamem("finalizers", function ()
local X = false
local obj = setmetatable({}, {__gc = function () X = true end})
obj = nil
T.resetCI() -- remove extra CallInfos
T.reallocstack(18) -- remove extra stack slots
Count = Count + 1
io.stderr:write(Count, "\n")
T.trick(io)
collectgarbage()
return X
end)
-- }==================================================================

View File

@ -345,6 +345,18 @@ do
end
end
do print("testing attack on table length")
local t = {}
local lim = math.floor(math.log(math.maxinteger, 2)) - 1
for i = lim, 0, -1 do
t[2^i] = true
end
assert(t[1 << lim])
-- next loop should not take forever
for i = 1, #t do end
end
local nofind = {}
@ -893,13 +905,18 @@ local function foo1 (e,i)
if i <= e.n then return i,a[i] end
end
setmetatable(a, {__pairs = function (x) return foo, x, 0 end})
local closed = false
setmetatable(a, {__pairs = function (x)
local tbc = setmetatable({}, {__close = function () closed = true end})
return foo, x, 0, tbc
end})
local i = 0
for k,v in pairs(a) do
i = i + 1
assert(k == i and v == k+1)
end
assert(closed) -- 'tbc' has been closed
a.n = 5
a[3] = 30

View File

@ -540,6 +540,23 @@ else
assert(y == x)
local z = T.externstr(x) -- external allocated long string
assert(z == y)
local e = T.externstr("") -- empty external string
assert(e .. "x" == "x" and "x" .. e == "x")
assert(e .. e == "" and #e == 0)
-- external string as the "n" key in vararg table
local n = T.externstr("n")
local n0 = T.externstr("n\0")
local function aux (...t) assert(t[n0] == nil); return t[n] end
assert(aux(10, 20, 30) == 3)
-- external string as mode in weak table
local t = setmetatable({}, {__mode = T.externstr("kv")})
t[{}] = {}
assert(next(t))
collectgarbage()
assert(next(t) == nil)
end
print('OK')

View File

@ -1,12 +1,17 @@
-- track collections
local M = {}
-- import list
local setmetatable, stderr, collectgarbage =
setmetatable, io.stderr, collectgarbage
local stderr, collectgarbage = io.stderr, collectgarbage
_ENV = nil
-- the debug version of setmetatable does not create any object (such as
-- a '__metatable' string), and so it is more appropriate to be used in
-- a finalizer
local setmetatable = require"debug".setmetatable
global none
local active = false

View File

@ -152,11 +152,20 @@ checkerror("position out of bounds", utf8.offset, "", 1, -1)
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "\x80", 1)
checkerror("continuation byte", utf8.offset, "\x9c", -1)
-- error in indices for len
checkerror("out of bounds", utf8.len, "abc", 0, 2)
checkerror("out of bounds", utf8.len, "abc", 1, 4)
do -- missing continuation bytes
-- get what is available
local p, e = utf8.offset("\xE0", 1)
assert(p == 1 and e == 1)
local p, e = utf8.offset("\xE0\x9e", -1)
assert(p == 1 and e == 2)
end
local s = "hello World"
local t = {string.byte(s, 1, -1)}

View File

@ -3,9 +3,12 @@
print('testing vararg')
local function f (a, ...)
local function f (a, ...t)
local x = {n = select('#', ...), ...}
for i = 1, x.n do assert(a[i] == x[i]) end
assert(x.n == t.n)
for i = 1, x.n do
assert(a[i] == x[i] and x[i] == t[i])
end
return x.n
end
@ -17,7 +20,7 @@ local function c12 (...)
return res, 2
end
local function vararg (...) return {n = select('#', ...), ...} end
local function vararg (... t) return t end
local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
@ -98,8 +101,40 @@ a,b,c,d,e = f(4)
assert(a==nil and b==nil and c==nil and d==nil and e==nil)
do -- vararg expressions using unpack
local function aux (a, v, ...t)
for k, val in pairs(v) do t[k] = val end
return ...
end
local t = table.pack(aux(10, {11, [5] = 24}, 1, 2, 3, nil, 4))
assert(t.n == 5 and t[1] == 11 and t[2] == 2 and t[3] == 3
and t[4] == nil and t[5] == 24)
local t = table.pack(aux(nil, {1, [20] = "a", [30] = "b", n = 30}))
assert(t.n == 30 and t[1] == 1 and t[20] == "a" and t[30] == "b")
-- table has only those four elements
assert(next(t, next(t, next(t, next(t, next(t, nil))))) == nil)
local a, b, c, d = aux(nil, {}, 10, 20, 30)
assert(a == 10 and b == 20 and c == 30 and d == nil)
local function aux (a, b, n, ...t) t.n = n; return b, ... end
local t = table.pack(aux(10, 1, 10000))
assert(t.n == 10001 and t[1] == 1 and #t == 1)
local function checkerr (emsg, f, ...)
local st, msg = pcall(f, ...)
assert(not st and string.find(msg, emsg))
end
checkerr("no proper 'n'", aux, 1, 1, -1)
checkerr("no proper 'n'", aux, 1, 1, math.maxinteger)
checkerr("no proper 'n'", aux, 1, 1, math.mininteger)
checkerr("no proper 'n'", aux, 1, 1, 1.0)
end
-- varargs for main chunks
local f = load[[ return {...} ]]
local f = assert(load[[ return {...} ]])
local x = f(2,3)
assert(x[1] == 2 and x[2] == 3 and x[3] == undef)
@ -147,5 +182,79 @@ do
local a, b = g()
assert(a == nil and b == 2)
end
do -- vararg parameter used in nested functions
local function foo (...tab1)
return function (...tab2)
return {tab1, tab2}
end
end
local f = foo(10, 20, 30)
local t = f("a", "b")
assert(t[1].n == 3 and t[1][1] == 10)
assert(t[2].n == 2 and t[2][1] == "a")
end
do -- vararg parameter is read-only
local st, msg = load("return function (... t) t = 10 end")
assert(string.find(msg, "const variable 't'"))
local st, msg = load[[
local function foo (...extra)
return function (...) extra = nil end
end
]]
assert(string.find(msg, "const variable 'extra'"))
end
do -- _ENV as vararg parameter
local st, msg = load[[
local function aux (... _ENV)
global <const> a
a = 10
end ]]
assert(string.find(msg, "const variable 'a'"))
local function aux (..._ENV)
global a; a = 10
return a
end
assert(aux() == 10)
local function aux (... _ENV)
global a = 10
return a
end
assert(aux() == 10)
end
do -- access to vararg parameter
local function notab (keys, t, ...v)
for _, k in pairs(keys) do
assert(t[k] == v[k])
end
assert(t.n == v.n)
return ...
end
local t = table.pack(10, 20, 30)
local keys = {-1, 0, 1, t.n, t.n + 1, 1.0, 1.1, "n", print, "k", "1"}
notab(keys, t, 10, 20, 30) -- ensure stack space
local m = collectgarbage"count"
notab(keys, t, 10, 20, 30)
-- 'notab' does not create any table/object
assert(m == collectgarbage"count")
-- writing to the vararg table
local function foo (...t)
t[1] = t[1] + 10
return t[1]
end
assert(foo(10, 30) == 20)
end
print('OK')