1
0
mirror of https://github.com/lua/lua synced 2026-06-08 15:34:49 +00:00

Compare commits

..

92 Commits

Author SHA1 Message Date
Roberto I 40b76de2d7 Removed unused function 'luaD_inctop'
Commit 3228a97 removed all its uses.
2026-06-03 11:58:39 -03:00
Roberto I 0465c23b3e Cleaning 'luaP_isIT' and 'luaP_isOT'
- 'luaP_isOT' is only used for tests, so it is defined as a macro to avoid
wasting space with an unused function.
- 'luaP_isIT' must include OP_VARARGPREP.
2026-05-28 15:10:17 -03:00
Roberto I 53b41d0cdd Avoid warning in some compilers
In function 'funcargs', some compilers can complain that 'args' can be
used unitialized, due to the default case (syntax error).
2026-05-08 15:01:59 -03:00
Roberto I 36c1f6d949 Small correction in luaP_opmodes
OP_VARARGPREP neither sets nor uses L->top.
2026-04-29 15:17:55 -03:00
Roberto Ierusalimschy 0da6d320f7 Details 2026-04-28 13:44:29 -03:00
Roberto I ae23e72601 new macro 'setnilvalue2s' 2026-04-23 18:00:23 -03:00
Roberto I 4c5d5063a5 'load' reader function doesn't need to preserve stack 2026-04-23 17:58:55 -03:00
Roberto I 3228a97c6a Bug: 'lua_load' does not preserve the stack
'lua_load' does not preserve the stack through the calls to the
reader function, as it should. Immediately after the first call (to
detect whether chunk is binary) it adds stuff, and it also adds a new
table when starting the compilation of each new function.
2026-04-23 17:57:42 -03:00
Roberto I 0c16a42d61 Details
Added compiler option LUA_NODEBUGLIB to make Lua with no open debug
library + small improvements in the manual.
2026-04-21 15:27:22 -03:00
Roberto I d0bd25d2e7 Better error messages for vararg-table fields 2026-04-13 14:06:23 -03:00
Roberto I 29cf284089 Avoid macros luaL_loadbuffer and luaL_loadfile
Use luaL_loadbufferx and luaL_loadfilex instead, being explicit about
whether to accept binary chunks.
2026-04-07 13:42:34 -03:00
Roberto I c037162a1a Stricter test for use of '__builtin_expect'
GCC introduced this macro in version 3.
2026-04-01 15:09:07 -03:00
Roberto Ierusalimschy efddc2309c Bug: wrong initialization in result from 'gmatch'
Function returned by 'string.gmatch' can be left in an inconsistent
state after an error.
2026-04-01 15:01:58 -03:00
Roberto I f1bb2773bb Bug: Loading a binary chunk does not run the GC 2026-04-01 14:59:41 -03:00
Roberto I 51269bd783 Adjustment in useless parameter L in macros luai_num* 2026-03-15 15:14:14 -03:00
Roberto I 377cbea61b 'table.tunpack' using 'aux_getn' like the others
'table.tunpack' was not checking its first argument, which could
result in error messages generated inside the API, without location
information.
2026-03-09 16:24:49 -03:00
Roberto I 36d5d2b284 Details 2026-03-09 16:24:06 -03:00
Roberto I 9e501d9855 Slightly better documentation for LUAI_MAXALIGN 2026-03-09 16:23:03 -03:00
Roberto I 10eb89d114 BUG: shift overflow in utf-8 decode
An initial byte \xFF will ask for 7 continuation bytes, and then the
shift by (count * 5) will try to shift 35 bits.
2026-02-18 13:24:04 -03:00
Roberto Ierusalimschy 7c40c5edb2 Details
Spaces + added initialization to the documentation of global
declarations.
2026-02-10 16:41:02 -03:00
Roberto Ierusalimschy b60e2bcd7c Avoid an assignment of values that overlap
The original code was like this, where t->u.ind.t and t->u.info overlap:

  t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
2026-02-09 13:44:27 -03:00
Roberto I c6b4848238 Environment variable for readline library name
The name of the readline library can be changed from its default value
through environment variable LUA_READLINELIB.
2026-01-30 16:47:33 -03:00
Roberto I efbc297545 New year and (eventual) new release 2026-01-29 14:24:25 -03:00
Roberto I cfcaa9493b Explanation about char* parameters in the C API 2026-01-23 16:25:18 -03:00
Roberto I 3360710bd3 Another way to handle option -E
A pointer to function 'l_getenv' can point to the regular 'getenv' or
to a dumb function (that always returns NULL) to ignore environment
variables.
2026-01-22 13:47:10 -03:00
Roberto Ierusalimschy e992c6a959 Some compilation options configurable from makefile
Compilation options LUA_COMPAT_GLOBAL, LUA_COMPAT_LOOPVAR, and
LUA_READLINELIB do not affect the API, so they can be changed
through the make file.
2026-01-20 13:06:16 -03:00
Roberto I f5d1e8639b New compile option LUA_COMPAT_LOOPVAR
When on, this option makes for-loop control variables not read only.
2026-01-16 16:38:44 -03:00
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
79 changed files with 2268 additions and 1085 deletions
+24 -12
View File
@@ -187,7 +187,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top.p; diff = ((func + 1) + idx) - L->top.p;
for (; diff > 0; diff--) for (; diff > 0; diff--)
setnilvalue(s2v(L->top.p++)); /* clear new slots */ setnilvalue2s(L->top.p++); /* clear new slots */
} }
else { else {
api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
@@ -210,7 +210,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level), api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level),
"no variable to close at given level"); "no variable to close at given level");
level = luaF_close(L, level, CLOSEKTOP, 0); level = luaF_close(L, level, CLOSEKTOP, 0);
setnilvalue(s2v(level)); setnilvalue2s(level);
lua_unlock(L); lua_unlock(L);
} }
@@ -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); const TValue *o = index2value(L, idx);
if (ttisnumber(o)) { if (ttisnumber(o)) {
unsigned len = luaO_tostringbuff(o, buff); 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_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen);
case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen); case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen);
case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len); 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; 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. ** 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 ** function to a 'void*', so the conversion here goes through
** a 'size_t'. (As the returned pointer is only informative, this ** a 'size_t'. (As the returned pointer is only informative, this
** conversion should not be a problem.) ** conversion should not be a problem.)
@@ -507,7 +513,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) {
LUA_API void lua_pushnil (lua_State *L) { LUA_API void lua_pushnil (lua_State *L) {
lua_lock(L); lua_lock(L);
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@@ -564,7 +570,7 @@ LUA_API const char *lua_pushexternalstring (lua_State *L,
LUA_API const char *lua_pushstring (lua_State *L, const char *s) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
lua_lock(L); lua_lock(L);
if (s == NULL) if (s == NULL)
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
else { else {
TString *ts; TString *ts;
ts = luaS_new(L, s); ts = luaS_new(L, s);
@@ -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 ** 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. ** cannot collect it.
*/ */
static void getGlobalTable (lua_State *L, TValue *gt) { static void getGlobalTable (lua_State *L, TValue *gt) {
@@ -737,7 +743,7 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
static int finishrawget (lua_State *L, lu_byte tag) { static int finishrawget (lua_State *L, lu_byte tag) {
if (tagisempty(tag)) /* avoid copying empty items to the stack */ if (tagisempty(tag)) /* avoid copying empty items to the stack */
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return novariant(tag); return novariant(tag);
@@ -830,7 +836,7 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
o = index2value(L, idx); o = index2value(L, idx);
api_check(L, ttisfulluserdata(o), "full userdata expected"); api_check(L, ttisfulluserdata(o), "full userdata expected");
if (n <= 0 || n > uvalue(o)->nuvalue) { if (n <= 0 || n > uvalue(o)->nuvalue) {
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
t = LUA_TNONE; t = LUA_TNONE;
} }
else { else {
@@ -1116,6 +1122,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
ZIO z; ZIO z;
TStatus status; TStatus status;
lua_lock(L); lua_lock(L);
luaC_checkGC(L);
if (!chunkname) chunkname = "?"; if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data); luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname, mode); status = luaD_protectedparser(L, &z, chunkname, mode);
@@ -1195,11 +1202,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
case LUA_GCSTEP: { case LUA_GCSTEP: {
lu_byte oldstp = g->gcstp; lu_byte oldstp = g->gcstp;
l_mem n = cast(l_mem, va_arg(argp, size_t)); l_mem n = cast(l_mem, va_arg(argp, size_t));
l_mem newdebt;
int work = 0; /* true if GC did some work */ int work = 0; /* true if GC did some work */
g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ g->gcstp = 0; /* allow GC to run (other bits must be zero here) */
if (n <= 0) if (n <= 0)
n = g->GCdebt; /* force to run one basic step */ newdebt = 0; /* force to run one basic step */
luaE_setdebt(g, g->GCdebt - n); 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); luaC_condGC(L, (void)0, work = 1);
if (work && g->gcstate == GCSpause) /* end of cycle? */ if (work && g->gcstate == GCSpause) /* end of cycle? */
res = 1; /* signal it */ res = 1; /* signal it */
+13 -9
View File
@@ -742,7 +742,7 @@ typedef struct LoadF {
static const char *getF (lua_State *L, void *ud, size_t *size) { static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud; LoadF *lf = (LoadF *)ud;
(void)L; /* not used */ UNUSED(L);
if (lf->n > 0) { /* are there pre-read characters to be read? */ if (lf->n > 0) { /* are there pre-read characters to be read? */
*size = lf->n; /* return them (chars already in buffer) */ *size = lf->n; /* return them (chars already in buffer) */
lf->n = 0; /* no more pre-read characters */ 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) { static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud; LoadS *ls = (LoadS *)ud;
(void)L; /* not used */ UNUSED(L);
if (ls->size == 0) return NULL; if (ls->size == 0) return NULL;
*size = ls->size; *size = ls->size;
ls->size = 0; ls->size = 0;
@@ -874,7 +874,7 @@ LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
return luaL_loadbuffer(L, s, strlen(s), s); return luaL_loadbufferx(L, s, strlen(s), s, "t");
} }
/* }====================================================== */ /* }====================================================== */
@@ -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 *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */ UNUSED(ud); UNUSED(osize);
if (nsize == 0) { if (nsize == 0) {
free(ptr); free(ptr);
return NULL; return NULL;
@@ -1172,16 +1172,20 @@ static unsigned int luai_makeseed (void) {
LUALIB_API unsigned int luaL_makeseed (lua_State *L) { LUALIB_API unsigned int luaL_makeseed (lua_State *L) {
(void)L; /* unused */ UNUSED(L);
return luai_makeseed(); 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)) { if (l_likely(L)) {
lua_atpanic(L, &panic); lua_atpanic(L, &panic);
lua_setwarnf(L, warnfoff, L); /* default is warnings off */ lua_setwarnf(L, warnfon, L);
} }
return L; return L;
} }
+4 -1
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_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat); 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 */ /* predefined references */
#define LUA_NOREF (-2) #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 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); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
+21 -27
View File
@@ -279,21 +279,22 @@ static int luaB_next (lua_State *L) {
static int pairscont (lua_State *L, int status, lua_KContext k) { static int pairscont (lua_State *L, int status, lua_KContext k) {
(void)L; (void)status; (void)k; /* unused */ (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) { static int luaB_pairs (lua_State *L) {
luaL_checkany(L, 1); luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, luaB_next); /* will return generator, */ lua_pushcfunction(L, luaB_next); /* will return generator and */
lua_pushvalue(L, 1); /* state, */ lua_pushvalue(L, 1); /* state */
lua_pushnil(L); /* and initial value */ lua_pushnil(L); /* initial value */
lua_pushnil(L); /* to-be-closed object */
} }
else { else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */ 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;
} }
@@ -339,9 +340,11 @@ static int load_aux (lua_State *L, int status, int envidx) {
static const char *getMode (lua_State *L, int idx) { static const char *getMode (lua_State *L, int idx) {
const char *mode = luaL_optstring(L, idx, "bt"); const char *mode = luaL_optstring(L, idx, NULL);
if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */ if (mode != NULL && strchr(mode, 'B') != NULL) {
/* Lua code cannot use fixed buffers */
luaL_argerror(L, idx, "invalid mode"); luaL_argerror(L, idx, "invalid mode");
}
return mode; return mode;
} }
@@ -363,33 +366,24 @@ static int luaB_loadfile (lua_State *L) {
/* /*
** reserved slot, above all arguments, to hold a copy of the returned ** Reader for generic 'load' function.
** string to avoid it being collected while parsed. 'load' has four
** optional arguments (chunk, source name, mode, and environment).
*/
#define RESERVEDSLOT 5
/*
** Reader for generic 'load' function: 'lua_load' uses the
** stack for internal stuff, so the reader cannot change the
** stack top. Instead, it keeps its resulting string in a
** reserved slot inside the stack.
*/ */
static const char *generic_reader (lua_State *L, void *ud, size_t *size) { static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
(void)(ud); /* not used */ int *firstcall = cast(int *, ud);
luaL_checkstack(L, 2, "too many nested functions"); luaL_checkstack(L, 2, "too many nested functions");
if (*firstcall)
*firstcall = 0;
else
lua_pop(L, 1); /* remove previous result */
lua_pushvalue(L, 1); /* get function */ lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */ lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* pop result */
*size = 0; *size = 0;
return NULL; return NULL;
} }
else if (l_unlikely(!lua_isstring(L, -1))) else if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "reader function must return a string"); luaL_error(L, "reader function must return a string");
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lua_tolstring(L, -1, size);
return lua_tolstring(L, RESERVEDSLOT, size);
} }
@@ -404,10 +398,10 @@ static int luaB_load (lua_State *L) {
status = luaL_loadbufferx(L, s, l, chunkname, mode); status = luaL_loadbufferx(L, s, l, chunkname, mode);
} }
else { /* loading from a reader function */ else { /* loading from a reader function */
int firstcall = 1; /* userdata for generic_reader */
const char *chunkname = luaL_optstring(L, 2, "=(load)"); const char *chunkname = luaL_optstring(L, 2, "=(load)");
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, RESERVEDSLOT); /* create reserved slot */ status = lua_load(L, generic_reader, &firstcall, chunkname, mode);
status = lua_load(L, generic_reader, NULL, chunkname, mode);
} }
return load_aux(L, status, env); return load_aux(L, status, env);
} }
@@ -424,7 +418,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
static int luaB_dofile (lua_State *L) { static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL); const char *fname = luaL_optstring(L, 1, NULL);
lua_settop(L, 1); lua_settop(L, 1);
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) if (l_unlikely(luaL_loadfilex(L, fname, "bt") != LUA_OK))
return lua_error(L); return lua_error(L);
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
return dofilecont(L, 0, 0); return dofilecont(L, 0, 0);
+107 -48
View File
@@ -45,6 +45,7 @@ l_noret luaK_semerror (LexState *ls, const char *fmt, ...) {
va_list argp; va_list argp;
pushvfstring(ls->L, argp, fmt, msg); pushvfstring(ls->L, argp, fmt, msg);
ls->t.token = 0; /* remove "near <token>" from final message */ 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); luaX_syntaxerror(ls, msg);
} }
@@ -565,20 +566,20 @@ static int k2proto (FuncState *fs, TValue *key, TValue *v) {
TValue val; TValue val;
Proto *f = fs->f; Proto *f = fs->f;
int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */ int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */
int k;
if (!tagisempty(tag)) { /* is there an index there? */ 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 */ /* collisions can happen only for float keys */
lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v)); lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v));
return k; /* reuse index */ return k; /* reuse index */
} }
/* constant not found; create a new entry */ else { /* constant not found; create a new entry */
k = addk(fs, f, v); int k = addk(fs, f, v);
/* cache it for reuse; numerical value does not need GC barrier; /* cache it for reuse; numerical value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */ table is not a metatable, so it does not need to invalidate cache */
setivalue(&val, k); setivalue(&val, k);
luaH_set(fs->ls->L, fs->kcache, key, &val); luaH_set(fs->ls->L, fs->kcache, key, &val);
return k; 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 ** Add a float to list of constants and return its index. Floats
** with integral values need a different key, to avoid collision ** 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. ** 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 ** This method is not bulletproof: different numbers may generate the
** same key (e.g., very large numbers will overflow to 'inf') and for ** 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, ** floats larger than 2^53 the result is still an integer. For those
** this only wastes an entry with a duplicate. ** 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) { static int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o, kv; TValue o, kv;
@@ -625,7 +627,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) {
const lua_Number k = r * (1 + q); /* key */ const lua_Number k = r * (1 + q); /* key */
lua_Integer ik; lua_Integer ik;
setfltvalue(&kv, k); /* key as a TValue */ 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 */ int n = k2proto(fs, &kv, &o); /* use key */
if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */ if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */
return n; return n;
@@ -661,11 +663,11 @@ static int boolT (FuncState *fs) {
** Add nil to list of constants and return its index. ** Add nil to list of constants and return its index.
*/ */
static int nilK (FuncState *fs) { static int nilK (FuncState *fs) {
TValue k, v; lua_State *L = fs->ls->L;
setnilvalue(&v); TValue k;
/* cannot use nil as key; instead use table itself */ /* cannot use nil as key; instead use table itself */
sethvalue(fs->ls->L, &k, fs->kcache); sethvalue(L, &k, fs->kcache);
return k2proto(fs, &k, &v); return k2proto(fs, &k, &G(L)->nilvalue);
} }
@@ -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' ** 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>). ** Ensure that expression 'e' is not a variable (nor a <const>).
@@ -795,9 +822,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
const2exp(const2val(fs, e), e); const2exp(const2val(fs, e), e);
break; break;
} }
case VVARGVAR: {
luaK_vapar2local(fs, e); /* turn it into a local variable */
} /* FALLTHROUGH */
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
int temp = e->u.var.ridx; int temp = e->u.var.ridx;
e->u.info = temp; /* (can't do a direct assignment; values overlap) */ e->u.info = temp; /* (avoid a direct assignment; values overlap) */
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
break; break;
} }
@@ -829,6 +859,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
e->k = VRELOC; e->k = VRELOC;
break; 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: { case VVARARG: case VCALL: {
luaK_setoneret(fs, e); luaK_setoneret(fs, e);
break; break;
@@ -991,11 +1027,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
/* /*
** Ensures final expression result is either in a register ** Ensures final expression result is either in a register,
** or in an upvalue. ** in an upvalue, or it is the vararg parameter.
*/ */
void luaK_exp2anyregup (FuncState *fs, expdesc *e) { 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); 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); codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
break; break;
} }
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
/* now, assignment is to a regular table */
} /* FALLTHROUGH */
case VINDEXED: { case VINDEXED: {
codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
break; break;
@@ -1162,7 +1202,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
/* /*
** Emit code to go through if 'e' is false, jump otherwise. ** 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 */ int pc; /* pc of new jump */
luaK_dischargevars(fs, e); luaK_dischargevars(fs, e);
switch (e->k) { switch (e->k) {
@@ -1223,7 +1263,7 @@ static void codenot (FuncState *fs, expdesc *e) {
** Check whether expression 'e' is a short literal string ** Check whether expression 'e' is a short literal string
*/ */
static int isKstr (FuncState *fs, expdesc *e) { static int isKstr (FuncState *fs, expdesc *e) {
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK &&
ttisshrstring(&fs->f->k[e->u.info])); 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 ** Create expression 't[k]'. 't' must have its final result already in a
** register or upvalue. Upvalues can only be indexed by literal strings. ** register or upvalue. Upvalues can only be indexed by literal strings.
@@ -1312,31 +1359,33 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (k->k == VKSTR) if (k->k == VKSTR)
keystr = str2K(fs, k); keystr = str2K(fs, k);
lua_assert(!hasjumps(t) && 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'? */ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */ luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) { if (t->k == VUPVAL) {
lu_byte temp = cast_byte(t->u.info); /* upvalue index */ lu_byte temp = cast_byte(t->u.info); /* upvalue index */
t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ t->u.ind.t = temp; /* (avoid a direct assignment; values overlap) */
lua_assert(isKstr(fs, k)); lua_assert(isKstr(fs, k));
t->u.ind.idx = cast_short(k->u.info); /* literal short string */ fillidxk(t, k->u.info, VINDEXUP); /* literal short string */
t->k = VINDEXUP; }
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 { else {
/* register index of the table */ /* register index of the table */
t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); lu_byte temp = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
if (isKstr(fs, k)) { t->u.ind.t = temp; /* (avoid a direct assignment; values may overlap?) */
t->u.ind.idx = cast_short(k->u.info); /* literal short string */ if (isKstr(fs, k))
t->k = VINDEXSTR; fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */
} else if (isCint(k)) /* int. constant in proper range? */
else if (isCint(k)) { /* int. constant in proper range? */ fillidxk(t, cast_int(k->u.ival), VINDEXI);
t->u.ind.idx = cast_short(k->u.ival); else
t->k = VINDEXI; fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */
}
else {
t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */
t->k = VINDEXED;
}
} }
t->u.ind.keystr = keystr; /* string index in 'k' */ t->u.ind.keystr = keystr; /* string index in 'k' */
t->u.ind.ro = 0; /* by default, not read-only */ t->u.ind.ro = 0; /* by default, not read-only */
@@ -1881,14 +1930,14 @@ static int finaltarget (Instruction *code, int i) {
void luaK_finish (FuncState *fs) { void luaK_finish (FuncState *fs) {
int i; int i;
Proto *p = fs->f; 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++) { for (i = 0; i < fs->pc; i++) {
Instruction *pc = &p->code[i]; Instruction *pc = &p->code[i];
/* avoid "not used" warnings when assert is off (for 'onelua.c') */
(void)luaP_isOT; (void)luaP_isIT;
lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc));
switch (GET_OPCODE(*pc)) { switch (GET_OPCODE(*pc)) {
case OP_RETURN0: case OP_RETURN1: { case OP_RETURN0: case OP_RETURN1: {
if (!(fs->needclose || (p->flag & PF_ISVARARG))) if (!(fs->needclose || (p->flag & PF_VAHID)))
break; /* no extra work */ break; /* no extra work */
/* else use OP_RETURN to do the extra work */ /* else use OP_RETURN to do the extra work */
SET_OPCODE(*pc, OP_RETURN); SET_OPCODE(*pc, OP_RETURN);
@@ -1896,13 +1945,23 @@ void luaK_finish (FuncState *fs) {
case OP_RETURN: case OP_TAILCALL: { case OP_RETURN: case OP_TAILCALL: {
if (fs->needclose) if (fs->needclose)
SETARG_k(*pc, 1); /* signal that it needs to close */ SETARG_k(*pc, 1); /* signal that it needs to close */
if (p->flag & PF_ISVARARG) if (p->flag & PF_VAHID) /* does it use hidden arguments? */
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ SETARG_C(*pc, p->numparams + 1); /* signal that */
break; 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); int target = finaltarget(p->code, i);
fixjump(fs, i, target); fixjump(fs, i, target); /* jump directly to final target */
break; break;
} }
default: break; default: break;
+3 -1
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 int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
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_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (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_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 void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
@@ -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_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, 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_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
+6 -4
View File
@@ -189,15 +189,17 @@ static int luaB_close (lua_State *L) {
return 2; 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 */ lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */
if (lua_tothread(L, -1) == co) if (lua_tothread(L, -1) == co)
return luaL_error(L, "cannot close main thread"); return luaL_error(L, "cannot close main thread");
lua_closethread(co, L); /* close itself */ 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; return 0;
default: /* normal or running coroutine */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
} }
} }
+1 -1
View File
@@ -18,7 +18,7 @@
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ #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 #define NONA 0x01
#else #else
#define NONA 0x00 /* default */ #define NONA 0x00 /* default */
+1 -1
View File
@@ -427,7 +427,7 @@ static int db_debug (lua_State *L) {
if (fgets(buffer, sizeof(buffer), stdin) == NULL || if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
strcmp(buffer, "cont\n") == 0) strcmp(buffer, "cont\n") == 0)
return 0; return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || if (luaL_loadbufferx(L, buffer, strlen(buffer), "=(debug command)", "t") ||
lua_pcall(L, 0, 0, 0)) lua_pcall(L, 0, 0, 0))
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
lua_settop(L, 0); /* remove eventual returns */ lua_settop(L, 0); /* remove eventual returns */
+14 -6
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) { 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; int nextra = ci->u.l.nextraargs;
if (n >= -nextra) { /* 'n' is negative */ if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func.p - nextra - (n + 1); *pos = ci->func.p - nextra - (n + 1);
@@ -291,7 +291,7 @@ static int nextline (const Proto *p, int currentline, int pc) {
static void collectvalidlines (lua_State *L, Closure *f) { static void collectvalidlines (lua_State *L, Closure *f) {
if (!LuaClosure(f)) { if (!LuaClosure(f)) {
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
api_incr_top(L); api_incr_top(L);
} }
else { else {
@@ -304,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
int i; int i;
TValue v; TValue v;
setbtvalue(&v); /* boolean 'true' to be the value of all indices */ 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 */ i = 0; /* consider all instructions */
else { /* vararg function */ else { /* vararg function */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); 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; ar->nparams = 0;
} }
else { 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; ar->nparams = f->l.p->numparams;
} }
break; break;
@@ -580,7 +580,7 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
kname(p, k, name); kname(p, k, name);
return isEnv(p, lastpc, i, 1); return isEnv(p, lastpc, i, 1);
} }
case OP_GETTABLE: { case OP_GETTABLE: case OP_GETVARG: {
int k = GETARG_C(i); /* key index */ int k = GETARG_C(i); /* key index */
rname(p, lastpc, k, name); rname(p, lastpc, k, name);
return isEnv(p, lastpc, i, 0); return isEnv(p, lastpc, i, 0);
@@ -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' */ /* add src:line information to 'msg' */
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
int line) { int line) {
@@ -904,7 +912,7 @@ int luaG_tracecall (lua_State *L) {
Proto *p = ci_func(ci)->p; Proto *p = ci_func(ci)->p;
ci->u.l.trap = 1; /* ensure hooks will be checked */ ci->u.l.trap = 1; /* ensure hooks will be checked */
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ 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 */ return 0; /* hooks will start at VARARGPREP instruction */
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */
luaD_hookcall(L, ci); /* check 'call' hook */ luaD_hookcall(L, ci); /* check 'call' hook */
+1
View File
@@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
const TValue *p2); const TValue *p2);
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2); 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 l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line); TString *src, int line);
+96 -35
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 ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
** default, Lua handles errors with exceptions when compiling as ** 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. ** longjmp/setjmp otherwise.
*/ */
#if !defined(LUAI_THROW) /* { */ #if !defined(LUAI_THROW) /* { */
@@ -69,38 +77,38 @@
/* C++ exceptions */ /* C++ exceptions */
#define LUAI_THROW(L,c) throw(c) #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; } static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) {
#define luai_jmpbuf int /* dummy field */ 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) /* }{ */ #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_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud)) #define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#else /* }{ */ #else /* }{ */
/* ISO C handling with long jumps */ /* ISO C handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1) #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_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#endif /* } */ #endif /* } */
#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) { void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
if (errcode == LUA_ERRMEM) { /* memory error? */ if (errcode == LUA_ERRMEM) { /* memory error? */
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ 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) { TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
l_uint32 oldnCcalls = L->nCcalls; l_uint32 oldnCcalls = L->nCcalls;
struct lua_longjmp lj; lua_longjmp lj;
lj.status = LUA_OK; lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */ lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj; L->errorJmp = &lj;
@@ -174,6 +182,20 @@ TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
#define STACKERRSPACE 200 #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 */ /* maximum stack size that respects size_t */
#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) #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) #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) { l_noret luaD_errerr (lua_State *L) {
TString *msg = luaS_newliteral(L, "error in error handling"); TString *msg = luaS_newliteral(L, "error in error handling");
setsvalue2s(L, L->top.p, msg); 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 ** In ISO C, any pointer use after the pointer has been deallocated is
** undefined behavior. So, before a stack reallocation, all pointers ** undefined behavior. So, before a stack reallocation, all pointers
@@ -308,7 +349,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
correctstack(L, oldstack); /* change offsets back to pointers */ correctstack(L, oldstack); /* change offsets back to pointers */
L->stack_last.p = L->stack.p + newsize; L->stack_last.p = L->stack.p + newsize;
for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
setnilvalue(s2v(newstack + i)); /* erase new segment */ setnilvalue2s(newstack + i); /* erase new segment */
return 1; return 1;
} }
@@ -325,7 +366,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
a stack error; cannot grow further than that. */ a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE); lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror) 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 */ return 0; /* if not 'raiseerror', just signal it */
} }
else if (n < MAXSTACK) { /* avoids arithmetic overflows */ else if (n < MAXSTACK) { /* avoids arithmetic overflows */
@@ -389,12 +430,6 @@ void luaD_shrinkstack (lua_State *L) {
luaE_shrinkCI(L); /* shrink CI list */ luaE_shrinkCI(L); /* shrink CI list */
} }
void luaD_inctop (lua_State *L) {
L->top.p++;
luaD_checkstack(L, 1);
}
/* }================================================================== */ /* }================================================================== */
@@ -465,7 +500,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
int ftransfer; int ftransfer;
if (isLua(ci)) { if (isLua(ci)) {
Proto *p = ci_func(ci)->p; Proto *p = ci_func(ci)->p;
if (p->flag & PF_ISVARARG) if (p->flag & PF_VAHID)
delta = ci->u.l.nextraargs + p->numparams + 1; delta = ci->u.l.nextraargs + p->numparams + 1;
} }
ci->func.p += delta; /* if vararg, back to virtual 'func' */ ci->func.p += delta; /* if vararg, back to virtual 'func' */
@@ -513,7 +548,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres,
for (i = 0; i < nres; i++) /* move all results to correct place */ for (i = 0; i < nres; i++) /* move all results to correct place */
setobjs2s(L, res + i, firstresult + i); setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */ for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i)); setnilvalue2s(res + i);
L->top.p = res + wanted; /* top points after the last result */ L->top.p = res + wanted; /* top points after the last result */
} }
@@ -533,7 +568,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
return; return;
case 1 + 1: /* one value needed */ case 1 + 1: /* one value needed */
if (nres == 0) /* no results? */ if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */ setnilvalue2s(res); /* adjust with nil */
else /* at least one result */ else /* at least one result */
setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
L->top.p = res + 1; L->top.p = res + 1;
@@ -583,7 +618,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))
/* /*
@@ -653,7 +688,7 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
setobjs2s(L, ci->func.p + i, func + i); setobjs2s(L, ci->func.p + i, func + i);
func = ci->func.p; /* moved-down function */ func = ci->func.p; /* moved-down function */
for (; narg1 <= nfixparams; narg1++) for (; narg1 <= nfixparams; narg1++)
setnilvalue(s2v(func + narg1)); /* complete missing arguments */ setnilvalue2s(func + narg1); /* complete missing arguments */
ci->top.p = func + 1 + fsize; /* top for new function */ ci->top.p = func + 1 + fsize; /* top for new function */
lua_assert(ci->top.p <= L->stack_last.p); lua_assert(ci->top.p <= L->stack_last.p);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
@@ -700,7 +735,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize); L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
for (; narg < nfixparams; narg++) for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ setnilvalue2s(L->top.p++); /* complete missing arguments */
lua_assert(ci->top.p <= L->stack_last.p); lua_assert(ci->top.p <= L->stack_last.p);
return ci; return ci;
} }
@@ -1087,28 +1122,54 @@ static void checkmode (lua_State *L, const char *mode, const char *x) {
} }
/*
** Before the first call to the reader function, Lua reserves a slot
** with a table for anchoring stuff.
*/
static void f_parser (lua_State *L, void *ud) { static void f_parser (lua_State *L, void *ud) {
LClosure *cl; LClosure *cl;
struct SParser *p = cast(struct SParser *, ud); struct SParser *p = cast(struct SParser *, ud);
const char *mode = p->mode ? p->mode : "bt"; const char *mode = p->mode ? p->mode : "bt";
int c = zgetc(p->z); /* read first character */ int c;
Table *anchor;
ptrdiff_t otop = savestack(L, L->top.p); /* original top */
luaD_checkstack(L, 2);
anchor = luaH_new(L); /* create the anchor table */
sethvalue2s(L, L->top.p++, anchor); /* anchor the anchor table */
c = zgetc(p->z); /* read first character */
if (c == LUA_SIGNATURE[0]) { if (c == LUA_SIGNATURE[0]) {
int fixed = 0; int fixed = 0;
if (strchr(mode, 'B') != NULL) if (strchr(mode, 'B') != NULL)
fixed = 1; fixed = 1;
else else
checkmode(L, mode, "binary"); checkmode(L, mode, "binary");
cl = luaU_undump(L, p->z, p->name, fixed); cl = luaU_undump(L, p->z, anchor, p->name, fixed);
} }
else { else {
checkmode(L, mode, "text"); checkmode(L, mode, "text");
cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); cl = luaY_parser(L, p->z, anchor, &p->buff, &p->dyd, p->name, c);
} }
L->top.p = restorestack(L, otop); /* restore stack */
setclLvalue2s(L, L->top.p++, cl); /* push closure */
lua_assert(cl->nupvalues == cl->p->sizeupvalues); lua_assert(cl->nupvalues == cl->p->sizeupvalues);
luaF_initupvals(L, cl); luaF_initupvals(L, cl);
} }
/*
** Anchor an object in a table in the stack. First, anchor the object
** temporarily in the stack, as luaH_set may call an emergency GC.
** Then, add it in the table with itself as its key.
*/
void luaD_anchorobj (lua_State *L, Table *anchor, GCObject *obj) {
setgcovalue(L, s2v(L->top.p++), obj); /* temporary anchor in the stack */
luaH_set(L, anchor, s2v(L->top.p - 1), s2v(L->top.p - 1));
/* Because this is a new key, luaH_set will call the GC barrier, so
we don't need to call the barrier again here */
L->top.p--;
}
TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name, TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode) { const char *mode) {
struct SParser p; struct SParser p;
+2 -1
View File
@@ -88,7 +88,8 @@ LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); 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 int luaD_growstack (lua_State *L, int n, int raiseerror);
LUAI_FUNC void luaD_shrinkstack (lua_State *L); 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 void luaD_anchorobj (lua_State *L, Table *anchor, GCObject *obj);
LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode);
LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode); LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode);
+13 -9
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; ** Dump a String. First dump its "size":
** size==1 is followed by an index and means "reuse saved string with ** size==0 is followed by an index and means "reuse saved string with
** that index"; size>=2 is followed by the string contents with real ** that index"; index==0 means NULL.
** size==size-2 and means that string, which will be saved with ** size>=1 is followed by the string contents with real size==size-1 and
** the next available index. ** 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) { static void dumpString (DumpState *D, TString *ts) {
if (ts == NULL) if (ts == NULL) {
dumpSize(D, 0); dumpVarint(D, 0); /* will "reuse" NULL */
dumpVarint(D, 0); /* special index for NULL */
}
else { else {
TValue idx; TValue idx;
int tag = luaH_getstr(D->h, ts, &idx); int tag = luaH_getstr(D->h, ts, &idx);
if (!tagisempty(tag)) { /* string already saved? */ 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 */ dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */
} }
else { /* must write and save the string */ else { /* must write and save the string */
TValue key, value; /* to save the string in the hash */ TValue key, value; /* to save the string in the hash */
size_t size; size_t size;
const char *s = getlstr(ts, size); const char *s = getlstr(ts, size);
dumpSize(D, size + 2); dumpSize(D, size + 1);
dumpVector(D, s, size + 1); /* include ending '\0' */ dumpVector(D, s, size + 1); /* include ending '\0' */
D->nstr++; /* one more saved string */ D->nstr++; /* one more saved string */
setsvalue(D->L, &key, ts); /* the string is the key */ setsvalue(D->L, &key, ts); /* the string is the key */
+1 -2
View File
@@ -196,8 +196,7 @@ void luaF_unlinkupval (UpVal *uv) {
*/ */
void luaF_closeupval (lua_State *L, StkId level) { void luaF_closeupval (lua_State *L, StkId level) {
UpVal *uv; UpVal *uv;
StkId upl; /* stack index pointed by 'uv' */ while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
TValue *slot = &uv->u.value; /* new position for value */ TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top.p); lua_assert(uplevel(uv) < L->top.p);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
+9 -8
View File
@@ -594,10 +594,10 @@ static void traversestrongtable (global_State *g, Table *h) {
*/ */
static int getmode (global_State *g, Table *h) { static int getmode (global_State *g, Table *h) {
const TValue *mode = gfasttm(g, h->metatable, TM_MODE); const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
if (mode == NULL || !ttisshrstring(mode)) if (mode == NULL || !ttisstring(mode))
return 0; /* ignore non-(short)string modes */ return 0; /* ignore non-string modes */
else { else {
const char *smode = getshrstr(tsvalue(mode)); const char *smode = getstr(tsvalue(mode));
const char *weakkey = strchr(smode, 'k'); const char *weakkey = strchr(smode, 'k');
const char *weakvalue = strchr(smode, 'v'); const char *weakvalue = strchr(smode, 'v');
return ((weakkey != NULL) << 1) | (weakvalue != NULL); 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 */ linkgclist(h, g->allweak); /* must clear collected entries */
break; break;
} }
return 1 + 2*sizenode(h) + h->asize; return cast(l_mem, 1 + 2*sizenode(h) + h->asize);
} }
@@ -709,7 +709,7 @@ static l_mem traversethread (global_State *g, lua_State *th) {
if (!g->gcemergency) if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++)
setnilvalue(s2v(o)); /* clear dead stack slice */ setnilvalue2s(o); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
th->twups = g->twups; /* link it back to the list */ th->twups = g->twups; /* link it back to the list */
@@ -1293,7 +1293,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
correctgraylists(g); correctgraylists(g);
checkSizes(L, g); checkSizes(L, g);
g->gcstate = GCSpropagate; /* skip restart */ g->gcstate = GCSpropagate; /* skip restart */
if (!g->gcemergency) if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L))
callallpendingfinalizers(L); callallpendingfinalizers(L);
} }
@@ -1667,12 +1667,13 @@ static l_mem singlestep (lua_State *L, int fast) {
break; break;
} }
case GCScallfin: { /* call finalizers */ case GCScallfin: { /* call finalizers */
if (g->tobefnz && !g->gcemergency) { if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) {
g->gcstopem = 0; /* ok collections during finalizers */ g->gcstopem = 0; /* ok collections during finalizers */
GCTM(L); /* call one finalizer */ GCTM(L); /* call one finalizer */
stepresult = CWUFIN; 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 */ g->gcstate = GCSpause; /* finish collection */
stepresult = step2pause; stepresult = step2pause;
} }
+1 -1
View File
@@ -114,7 +114,7 @@ static int l_checkmode (const char *mode) {
#if !defined(l_fseek) /* { */ #if !defined(l_fseek) /* { */
#if defined(LUA_USE_POSIX) /* { */ #if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */
#include <sys/types.h> #include <sys/types.h>
+4 -2
View File
@@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = {
#if 0 #if 0
** you can update the following list with this command: ** 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 #endif
@@ -57,8 +57,8 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_BANDK, &&L_OP_BANDK,
&&L_OP_BORK, &&L_OP_BORK,
&&L_OP_BXORK, &&L_OP_BXORK,
&&L_OP_SHRI,
&&L_OP_SHLI, &&L_OP_SHLI,
&&L_OP_SHRI,
&&L_OP_ADD, &&L_OP_ADD,
&&L_OP_SUB, &&L_OP_SUB,
&&L_OP_MUL, &&L_OP_MUL,
@@ -106,6 +106,8 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_SETLIST, &&L_OP_SETLIST,
&&L_OP_CLOSURE, &&L_OP_CLOSURE,
&&L_OP_VARARG, &&L_OP_VARARG,
&&L_OP_GETVARG,
&&L_OP_ERRNNIL,
&&L_OP_VARARGPREP, &&L_OP_VARARGPREP,
&&L_OP_EXTRAARG &&L_OP_EXTRAARG
+1 -1
View File
@@ -188,7 +188,7 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
so they cannot be collected */ so they cannot be collected */
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */ ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */
ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */ ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */
#if defined(LUA_COMPAT_GLOBAL) #if LUA_COMPAT_GLOBAL
/* compatibility mode: "global" is not a reserved word */ /* compatibility mode: "global" is not a reserved word */
ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */ ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */
ls->glbn->extra = 0; /* mark it as not reserved */ ls->glbn->extra = 0; /* mark it as not reserved */
+57 -15
View File
@@ -20,8 +20,8 @@
/* /*
** 'l_mem' is a signed integer big enough to count the total memory ** '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 ** used by Lua. (It is signed due to the use of debt in several
** computations.) Usually, 'ptrdiff_t' should work, but we use 'long' ** computations.) 'lu_mem' is a corresponding unsigned type. Usually,
** for 16-bit machines. ** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
*/ */
#if defined(LUAI_MEM) /* { external definitions? */ #if defined(LUAI_MEM) /* { external definitions? */
typedef LUAI_MEM l_mem; typedef LUAI_MEM l_mem;
@@ -59,13 +59,6 @@ typedef lu_byte TStatus;
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: cast_sizet(LUA_MAXINTEGER)) : 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) ** test whether an unsigned value is a power of 2 (or zero)
*/ */
@@ -241,12 +234,12 @@ typedef unsigned long l_uint32;
/* floor division (defined as 'floor(a/b)') */ /* floor division (defined as 'floor(a/b)') */
#if !defined(luai_numidiv) #if !defined(luai_numidiv)
#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) #define luai_numidiv(L,a,b) l_floor(luai_numdiv(L,a,b))
#endif #endif
/* float division */ /* float division */
#if !defined(luai_numdiv) #if !defined(luai_numdiv)
#define luai_numdiv(L,a,b) ((a)/(b)) #define luai_numdiv(L,a,b) ((void)L, (a)/(b))
#endif #endif
/* /*
@@ -274,10 +267,10 @@ typedef unsigned long l_uint32;
/* the others are quite standard operations */ /* the others are quite standard operations */
#if !defined(luai_numadd) #if !defined(luai_numadd)
#define luai_numadd(L,a,b) ((a)+(b)) #define luai_numadd(L,a,b) ((void)L, (a)+(b))
#define luai_numsub(L,a,b) ((a)-(b)) #define luai_numsub(L,a,b) ((void)L, (a)-(b))
#define luai_nummul(L,a,b) ((a)*(b)) #define luai_nummul(L,a,b) ((void)L, (a)*(b))
#define luai_numunm(L,a) (-(a)) #define luai_numunm(L,a) ((void)L, -(a))
#define luai_numeq(a,b) ((a)==(b)) #define luai_numeq(a,b) ((a)==(b))
#define luai_numlt(a,b) ((a)<(b)) #define luai_numlt(a,b) ((a)<(b))
#define luai_numle(a,b) ((a)<=(b)) #define luai_numle(a,b) ((a)<=(b))
@@ -287,6 +280,55 @@ typedef unsigned long l_uint32;
#endif #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 ** "Abstraction Layer" for basic report of messages and errors
+30 -17
View File
@@ -38,31 +38,37 @@ static int math_abs (lua_State *L) {
return 1; return 1;
} }
static int math_sin (lua_State *L) { static int math_sin (lua_State *L) {
lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_cos (lua_State *L) { static int math_cos (lua_State *L) {
lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_tan (lua_State *L) { static int math_tan (lua_State *L) {
lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_asin (lua_State *L) { static int math_asin (lua_State *L) {
lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_acos (lua_State *L) { static int math_acos (lua_State *L) {
lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_atan (lua_State *L) { static int math_atan (lua_State *L) {
lua_Number y = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 1);
lua_Number x = luaL_optnumber(L, 2, 1); lua_Number x = luaL_optnumber(L, 2, 1);
@@ -167,6 +173,7 @@ static int math_ult (lua_State *L) {
return 1; return 1;
} }
static int math_log (lua_State *L) { static int math_log (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1); lua_Number x = luaL_checknumber(L, 1);
lua_Number res; lua_Number res;
@@ -188,22 +195,42 @@ static int math_log (lua_State *L) {
return 1; return 1;
} }
static int math_exp (lua_State *L) { static int math_exp (lua_State *L) {
lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_deg (lua_State *L) { static int math_deg (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
return 1; return 1;
} }
static int math_rad (lua_State *L) { static int math_rad (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
return 1; 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) { static int math_min (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); /* number of arguments */
int imin = 1; /* index of current minimum value */ 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 ** than or equal to the width of the type being shifted, so some shifts
** are written in convoluted ways to match that restriction. For ** are written in convoluted ways to match that restriction. For
** preprocessor tests, it assumes a width of 32 bits, so the maximum ** 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; 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) { static int math_log10 (lua_State *L) {
lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
return 1; return 1;
@@ -702,7 +715,9 @@ static const luaL_Reg mathlib[] = {
{"tointeger", math_toint}, {"tointeger", math_toint},
{"floor", math_floor}, {"floor", math_floor},
{"fmod", math_fmod}, {"fmod", math_fmod},
{"frexp", math_frexp},
{"ult", math_ult}, {"ult", math_ult},
{"ldexp", math_ldexp},
{"log", math_log}, {"log", math_log},
{"max", math_max}, {"max", math_max},
{"min", math_min}, {"min", math_min},
@@ -718,8 +733,6 @@ static const luaL_Reg mathlib[] = {
{"sinh", math_sinh}, {"sinh", math_sinh},
{"tanh", math_tanh}, {"tanh", math_tanh},
{"pow", math_pow}, {"pow", math_pow},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10}, {"log10", math_log10},
#endif #endif
/* placeholders */ /* placeholders */
+41 -36
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] ** return registry.CLIBS[path]
*/ */
@@ -320,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) {
/* /*
** registry.CLIBS[path] = plib -- for queries ** Deallocate function for library strings.
** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries ** Unload the DLL associated with the string being deallocated.
*/ */
static void addtoclib (lua_State *L, const char *path, void *plib) { static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) {
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); /* string itself is irrelevant and static */
lua_pushlightuserdata(L, plib); (void)ptr; (void)osize; (void)nsize;
lua_pushvalue(L, -1); lsys_unloadlib(ud); /* unload library represented by the string */
lua_setfield(L, -3, path); /* CLIBS[path] = plib */ return NULL;
lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
lua_pop(L, 1); /* pop CLIBS table */
} }
/* /*
** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib ** Create a library string that, when deallocated, will unload 'plib'
** handles in list CLIBS
*/ */
static int gctm (lua_State *L) { static void createlibstr (lua_State *L, void *plib) {
lua_Integer n = luaL_len(L, 1); /* common content for all library strings */
for (; n >= 1; n--) { /* for each handle, in reverse order */ static const char dummy[] = "01234567890";
lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib);
lsys_unloadlib(lua_touserdata(L, -1));
lua_pop(L, 1); /* pop handle */
}
return 0;
} }
/*
** 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' */ /* error codes for 'lookforfunc' */
#define ERRLIB 1 #define ERRLIB 1
@@ -361,8 +378,8 @@ static int gctm (lua_State *L) {
** Then, if 'sym' is '*', return true (as library has been loaded). ** Then, if 'sym' is '*', return true (as library has been loaded).
** Otherwise, look for symbol 'sym' in the library and push a ** Otherwise, look for symbol 'sym' in the library and push a
** C function with that symbol. ** C function with that symbol.
** Return 0 and 'true' or a function in the stack; in case of ** Return 0 with 'true' or a function in the stack; in case of
** errors, return an error code and an error message in the stack. ** errors, return an error code with an error message in the stack.
*/ */
static int lookforfunc (lua_State *L, const char *path, const char *sym) { static int lookforfunc (lua_State *L, const char *path, const char *sym) {
void *reg = checkclib(L, path); /* check loaded C libraries */ void *reg = checkclib(L, path); /* check loaded C libraries */
@@ -524,7 +541,7 @@ static int searcher_Lua (lua_State *L) {
const char *name = luaL_checkstring(L, 1); const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path", LUA_LSUBSEP); filename = findfile(L, name, "path", LUA_LSUBSEP);
if (filename == NULL) return 1; /* module not found in this path */ if (filename == NULL) return 1; /* module not found in this path */
return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); return checkload(L, (luaL_loadfilex(L, filename, "bt") == LUA_OK), filename);
} }
@@ -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) { 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 */ luaL_newlib(L, pk_funcs); /* create 'package' table */
createsearcherstable(L); createsearcherstable(L);
/* set paths */ /* set paths */
+4 -3
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) { lu_byte luaO_ceillog2 (unsigned int x) {
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */ 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. ** overflow, so we check which order is best.
*/ */
l_mem luaO_applyparam (lu_byte p, l_mem x) { 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 */ int e = (p >> 4); /* exponent */
if (e > 0) { /* normalized? */ if (e > 0) { /* normalized? */
e--; /* correct exponent */ 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 luaO_utf8esc (char *buff, l_uint32 x) {
int n = 1; /* number of bytes put in buffer (backwards) */ int n = 1; /* number of bytes put in buffer (backwards) */
lua_assert(x <= 0x7FFFFFFFu); lua_assert(x <= 0x7FFFFFFFu);
if (x < 0x80) /* ascii? */ if (x < 0x80) /* ASCII? */
buff[UTF8BUFFSZ - 1] = cast_char(x); buff[UTF8BUFFSZ - 1] = cast_char(x);
else { /* need continuation bytes */ else { /* need continuation bytes */
unsigned int mfb = 0x3f; /* maximum that fits in first byte */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */
+14 -3
View File
@@ -208,7 +208,8 @@ typedef union {
#define ttisstrictnil(o) checktag((o), LUA_VNIL) #define ttisstrictnil(o) checktag((o), LUA_VNIL)
#define setnilvalue(obj) settt_(obj, LUA_VNIL) #define setnilvalue(obj) settt_(obj, LUA_VNIL)
#define setnilvalue2s(stk) setnilvalue(s2v(stk))
#define isabstkey(v) checktag((v), LUA_VABSTKEY) #define isabstkey(v) checktag((v), LUA_VABSTKEY)
@@ -418,6 +419,7 @@ typedef struct TString {
#define strisshr(ts) ((ts)->shrlen >= 0) #define strisshr(ts) ((ts)->shrlen >= 0)
#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG)
/* /*
@@ -582,9 +584,18 @@ typedef struct AbsLineInfo {
/* /*
** Flags in Prototypes ** Flags in Prototypes
*/ */
#define PF_ISVARARG 1 #define PF_VAHID 1 /* function has hidden vararg arguments */
#define PF_FIXED 2 /* prototype has parts in fixed memory */ #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 ** Function Prototypes
+11 -18
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_BANDK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ ,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_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_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_ADD */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
@@ -102,35 +102,28 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */
,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_VARARGPREP */
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
}; };
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
/*
** Check whether instruction sets top for next instruction, that is,
** it results in multiple values.
*/
int luaP_isOT (Instruction i) {
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_TAILCALL: return 1;
default:
return testOTMode(op) && GETARG_C(i) == 0;
}
}
/* /*
** Check whether instruction uses top from previous instruction, that is, ** Check whether instruction uses top. That happens for OP_VARARGPREP
** it accepts multiple results. ** and for instructions that use multiple values set by the previous
** instruction.
*/ */
int luaP_isIT (Instruction i) { int luaP_isIT (Instruction i) {
OpCode op = GET_OPCODE(i); OpCode op = GET_OPCODE(i);
switch (op) { switch (op) {
case OP_SETLIST: case OP_SETLIST:
return testITMode(GET_OPCODE(i)) && GETARG_vB(i) == 0; return GETARG_vB(i) == 0;
case OP_VARARGPREP:
return 1;
default: default:
return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0; return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0;
} }
+34 -19
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 (*) ** Grep "ORDER OP" if you change this enum.
** has extra descriptions in the notes after the enumeration. ** See "Notes" below for more information about some instructions.
*/ */
typedef enum { typedef enum {
@@ -238,7 +238,7 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
OP_LOADK,/* A Bx R[A] := K[Bx] */ OP_LOADK,/* A Bx R[A] := K[Bx] */
OP_LOADKX,/* A R[A] := K[extra arg] */ OP_LOADKX,/* A R[A] := K[extra arg] */
OP_LOADFALSE,/* A R[A] := false */ 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_LOADTRUE,/* A R[A] := true */
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */ OP_GETUPVAL,/* A B R[A] := UpValue[B] */
@@ -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_BORK,/* A B C R[A] := R[B] | K[C]:integer */
OP_BXORK,/* 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_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_ADD,/* A B C R[A] := R[B] + R[C] */
OP_SUB,/* 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_SHL,/* A B C R[A] := R[B] << R[C] */
OP_SHR,/* 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_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] */ 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_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
OP_TEST,/* A k if (not R[A] == 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_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_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_RETURN0,/* return */
OP_RETURN1,/* A return R[A] */ 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_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 */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode; } OpCode;
@@ -367,7 +371,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
OP_RETURN*, OP_SETLIST) may use 'top'. OP_RETURN*, OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and (*) 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'. (*) 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 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. 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 (*) For comparisons, k specifies what condition the test should accept
(true or false). (true or false).
(*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped (*) 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 (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
function builds upvalues, which may need to be closed. C > 0 means function builds upvalues, which may need to be closed. C > 0 means
the function is vararg, so that its 'func' must be corrected before the function has hidden vararg arguments, so that its 'func' must be
returning; in this case, (C - 1) is its number of fixed parameters. corrected before returning; in this case, (C - 1) is its number of
fixed parameters.
(*) In comparisons with an immediate operand, C signals whether the (*) In comparisons with an immediate operand, C signals whether the
original operand was a float. (It must be corrected in case of original operand was a float. (It must be corrected in case of
@@ -407,8 +417,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
** bits 0-2: op mode ** bits 0-2: op mode
** bit 3: instruction set register A ** bit 3: instruction set register A
** bit 4: operator is a test (next instruction must be a jump) ** bit 4: operator is a test (next instruction must be a jump)
** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) ** bit 5: used by 'luaP_isIT'
** bit 6: instruction sets 'L->top' for next instruction (when C == 0) ** bit 6: used by 'luaP_isOT'
** bit 7: instruction is an MM instruction (call a metamethod) ** bit 7: instruction is an MM instruction (call a metamethod)
*/ */
@@ -417,12 +427,17 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) #define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) #define testAMode(m) (luaP_opmodes[m] & (1 << 3))
#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) #define testTMode(m) (luaP_opmodes[m] & (1 << 4))
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) #define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
LUAI_FUNC int luaP_isOT (Instruction i); /* Check whether instruction sets top for next instruction, that is,
** it results in multiple values. Used only for tests.
*/
#define luaP_isOT(i) \
(GET_OPCODE(i) == OP_TAILCALL || \
((luaP_opmodes[GET_OPCODE(i)] & (1 << 6)) && GETARG_C(i) == 0))
LUAI_FUNC int luaP_isIT (Instruction i); LUAI_FUNC int luaP_isIT (Instruction i);
+3 -1
View File
@@ -45,8 +45,8 @@ static const char *const opnames[] = {
"BANDK", "BANDK",
"BORK", "BORK",
"BXORK", "BXORK",
"SHRI",
"SHLI", "SHLI",
"SHRI",
"ADD", "ADD",
"SUB", "SUB",
"MUL", "MUL",
@@ -94,6 +94,8 @@ static const char *const opnames[] = {
"SETLIST", "SETLIST",
"CLOSURE", "CLOSURE",
"VARARG", "VARARG",
"GETVARG",
"ERRNNIL",
"VARARGPREP", "VARARGPREP",
"EXTRAARG", "EXTRAARG",
NULL NULL
+1 -1
View File
@@ -34,7 +34,7 @@
#if defined(LUA_USE_WINDOWS) #if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ "||" "#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%" #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
#else /* C99 specification */ #else /* C99 specification */
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ #define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
+171 -60
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) */ smaller than 250, due to the bytecode format) */
#define MAXVARS 200 #define MAXVARS 200
@@ -197,7 +197,7 @@ static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
Dyndata *dyd = ls->dyd; Dyndata *dyd = ls->dyd;
Vardesc *var; Vardesc *var;
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, 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 = &dyd->actvar.arr[dyd->actvar.n++];
var->vd.kind = kind; /* default */ var->vd.kind = kind; /* default */
var->vd.name = name; 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) { static void check_readonly (LexState *ls, expdesc *e) {
FuncState *fs = ls->fs; 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; varname = ls->dyd->actvar.arr[e->u.info].vd.name;
break; break;
} }
case VLOCAL: { case VLOCAL: case VVARGVAR: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
varname = vardesc->vd.name; varname = vardesc->vd.name;
@@ -301,6 +303,10 @@ static void check_readonly (LexState *ls, expdesc *e) {
varname = up->name; varname = up->name;
break; break;
} }
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
e->k = VINDEXED;
} /* FALLTHROUGH */
case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */
if (e->u.ind.ro) /* read-only? */ if (e->u.ind.ro) /* read-only? */
varname = tsvalue(&fs->f->k[e->u.ind.keystr]); 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? */ else if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */ if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i); init_exp(var, VCONST, fs->firstlocal + i);
else /* local variable */ else { /* local variable */
init_var(fs, var, i); init_var(fs, var, i);
if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */
var->k = VVARGVAR;
}
return cast_int(var->k); 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) { static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
int v = searchvar(fs, n, var); /* look up variables at current level */ int v = searchvar(fs, n, var); /* look up variables at current level */
if (v >= 0) { /* found? */ if (v >= 0) { /* found? */
if (v == VLOCAL && !base) if (!base) {
markupval(fs, var->u.var.vidx); /* local will be used as an upval */ 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 */ else { /* not found at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing 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 ** Find a variable with the given name 'n', handling global variables
** too. ** too.
@@ -494,18 +522,11 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
init_exp(var, VGLOBAL, -1); /* global by default */ init_exp(var, VGLOBAL, -1); /* global by default */
singlevaraux(fs, varname, var, 1); singlevaraux(fs, varname, var, 1);
if (var->k == VGLOBAL) { /* global name? */ if (var->k == VGLOBAL) { /* global name? */
expdesc key;
int info = var->u.info; int info = var->u.info;
/* global by default in the scope of a global declaration? */ /* global by default in the scope of a global declaration? */
if (info == -2) if (info == -2)
luaK_semerror(ls, "variable '%s' not declared", getstr(varname)); luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ buildglobal(ls, varname, var);
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] */
if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST) if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST)
var->u.ind.ro = 1; /* mark variable as read-only */ var->u.ind.ro = 1; /* mark variable as read-only */
else /* anyway must be a global */ 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) { static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int needed = nvars - nexps; /* extra values needed */ int needed = nvars - nexps; /* extra values needed */
luaK_checkstack(fs, needed);
if (hasmultret(e->k)) { /* last expression has multiple returns? */ if (hasmultret(e->k)) { /* last expression has multiple returns? */
int extra = needed + 1; /* discount last expression itself */ int extra = needed + 1; /* discount last expression itself */
if (extra < 0) 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 ** each match some label of that block. Those that do not match are
** "exported" to the outer block, to be solved there. In particular, ** "exported" to the outer block, to be solved there. In particular,
** its 'nactvar' is updated with the level of the inner block, ** its 'nactvar' is updated with the level of the inner block,
@@ -799,8 +821,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
luaC_objbarrier(L, f, f->source); luaC_objbarrier(L, f, f->source);
f->maxstacksize = 2; /* registers 0/1 are always valid */ f->maxstacksize = 2; /* registers 0/1 are always valid */
fs->kcache = luaH_new(L); /* create table for function */ fs->kcache = luaH_new(L); /* create table for function */
sethvalue2s(L, L->top.p, fs->kcache); /* anchor it */ luaD_anchorobj(L, ls->h, obj2gco(fs->kcache)); /* anchor it */
luaD_inctop(L);
enterblock(fs, bl, 0); enterblock(fs, bl, 0);
} }
@@ -809,6 +830,7 @@ static void close_func (LexState *ls) {
lua_State *L = ls->L; lua_State *L = ls->L;
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
TValue temp;
luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
leaveblock(fs); leaveblock(fs);
lua_assert(fs->bl == NULL); lua_assert(fs->bl == NULL);
@@ -821,8 +843,10 @@ static void close_func (LexState *ls) {
luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
/* remove kcache table from scanner table ("weigh" its anchor) */
sethvalue(L, &temp, fs->kcache); /* key to be set to nil */
luaH_set(L, ls->h, &temp, &G(L)->nilvalue);
ls->fs = fs->prev; ls->fs = fs->prev;
L->top.p--; /* pop kcache table */
luaC_checkGC(L); luaC_checkGC(L);
} }
@@ -898,6 +922,19 @@ typedef struct ConsControl {
} 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) { static void recfield (LexState *ls, ConsControl *cc) {
/* recfield -> (NAME | '['exp']') = exp */ /* recfield -> (NAME | '['exp']') = exp */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
@@ -918,7 +955,7 @@ static void recfield (LexState *ls, ConsControl *cc) {
static void closelistfield (FuncState *fs, 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); luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID; cc->v.k = VVOID;
if (cc->tostore >= cc->maxtostore) { if (cc->tostore >= cc->maxtostore) {
@@ -1006,10 +1043,12 @@ static void constructor (LexState *ls, expdesc *t) {
checknext(ls, '{' /*}*/); checknext(ls, '{' /*}*/);
cc.maxtostore = maxtostore(fs); cc.maxtostore = maxtostore(fs);
do { do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
if (ls->t.token == /*{*/ '}') break; 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); field(ls, &cc);
luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST,
"items in a constructor");
} while (testnext(ls, ',') || testnext(ls, ';')); } while (testnext(ls, ',') || testnext(ls, ';'));
check_match(ls, /*{*/ '}', '{' /*}*/, line); check_match(ls, /*{*/ '}', '{' /*}*/, line);
lastlistfield(fs, &cc); lastlistfield(fs, &cc);
@@ -1019,9 +1058,9 @@ static void constructor (LexState *ls, expdesc *t) {
/* }====================================================================== */ /* }====================================================================== */
static void setvararg (FuncState *fs, int nparams) { static void setvararg (FuncState *fs) {
fs->f->flag |= PF_ISVARARG; fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */
luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0);
} }
@@ -1030,7 +1069,7 @@ static void parlist (LexState *ls) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
int nparams = 0; int nparams = 0;
int isvararg = 0; int varargk = 0;
if (ls->t.token != ')') { /* is 'parlist' not empty? */ if (ls->t.token != ')') { /* is 'parlist' not empty? */
do { do {
switch (ls->t.token) { switch (ls->t.token) {
@@ -1040,19 +1079,26 @@ static void parlist (LexState *ls) {
break; break;
} }
case TK_DOTS: { case TK_DOTS: {
luaX_next(ls); varargk = 1;
isvararg = 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; break;
} }
default: luaX_syntaxerror(ls, "<name> or '...' expected"); default: luaX_syntaxerror(ls, "<name> or '...' expected");
} }
} while (!isvararg && testnext(ls, ',')); } while (!varargk && testnext(ls, ','));
} }
adjustlocalvars(ls, nparams); adjustlocalvars(ls, nparams);
f->numparams = cast_byte(fs->nactvar); f->numparams = cast_byte(fs->nactvar);
if (isvararg) if (varargk) {
setvararg(fs, f->numparams); /* declared vararg */ setvararg(fs); /* declared vararg */
luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ adjustlocalvars(ls, 1); /* vararg parameter */
}
/* reserve registers for parameters (plus vararg parameter, if present) */
luaK_reserveregs(fs, fs->nactvar);
} }
@@ -1120,6 +1166,7 @@ static void funcargs (LexState *ls, expdesc *f) {
} }
default: { default: {
luaX_syntaxerror(ls, "function arguments expected"); luaX_syntaxerror(ls, "function arguments expected");
return; /* to avoid warnings */
} }
} }
lua_assert(f->k == VNONRELOC); lua_assert(f->k == VNONRELOC);
@@ -1239,9 +1286,9 @@ static void simpleexp (LexState *ls, expdesc *v) {
} }
case TK_DOTS: { /* vararg */ case TK_DOTS: { /* vararg */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
check_condition(ls, fs->f->flag & PF_ISVARARG, check_condition(ls, isvararg(fs->f),
"cannot use '...' outside a vararg function"); "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; break;
} }
case '{' /*}*/: { /* constructor */ case '{' /*}*/: { /* constructor */
@@ -1435,6 +1482,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" ** Parse and compile a multiple assignment. The first "variable"
** (a 'suffixedexp') was already read by the caller. ** (a 'suffixedexp') was already read by the caller.
@@ -1468,8 +1524,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
return; /* avoid default */ return; /* avoid default */
} }
} }
init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ storevartop(ls->fs, &lh->v); /* default assignment */
luaK_storevar(ls->fs, &lh->v, &e);
} }
@@ -1630,13 +1685,22 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
} }
/*
** Control whether for-loop control variables are read-only
*/
#if LUA_COMPAT_LOOPVAR
#define LOOPVARKIND VDKREG
#else /* by default, these variables are read only */
#define LOOPVARKIND RDKCONST
#endif
static void fornum (LexState *ls, TString *varname, int line) { static void fornum (LexState *ls, TString *varname, int line) {
/* fornum -> NAME = exp,exp[,exp] forbody */ /* fornum -> NAME = exp,exp[,exp] forbody */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int base = fs->freereg; int base = fs->freereg;
new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)");
new_varkind(ls, varname, RDKCONST); /* control variable */ new_varkind(ls, varname, LOOPVARKIND); /* control variable */
checknext(ls, '='); checknext(ls, '=');
exp1(ls); /* initial value */ exp1(ls); /* initial value */
checknext(ls, ','); checknext(ls, ',');
@@ -1663,7 +1727,7 @@ static void forlist (LexState *ls, TString *indexname) {
new_localvarliteral(ls, "(for state)"); /* iterator function */ new_localvarliteral(ls, "(for state)"); /* iterator function */
new_localvarliteral(ls, "(for state)"); /* state */ new_localvarliteral(ls, "(for state)"); /* state */
new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */ new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */
new_varkind(ls, indexname, RDKCONST); /* control variable */ new_varkind(ls, indexname, LOOPVARKIND); /* control variable */
/* other declared variables */ /* other declared variables */
while (testnext(ls, ',')) { while (testnext(ls, ',')) {
new_localvar(ls, str_checkname(ls)); new_localvar(ls, str_checkname(ls));
@@ -1812,7 +1876,7 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
switch (kind) { switch (kind) {
case RDKTOCLOSE: case RDKTOCLOSE:
luaK_semerror(ls, "global variables cannot be to-be-closed"); luaK_semerror(ls, "global variables cannot be to-be-closed");
break; /* to avoid warnings */ return kind; /* to avoid warnings */
case RDKCONST: case RDKCONST:
return GDKCONST; /* adjust kind for global variable */ return GDKCONST; /* adjust kind for global variable */
default: default:
@@ -1821,25 +1885,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) { static void globalstat (LexState *ls) {
/* globalstat -> (GLOBAL) attrib '*' /* globalstat -> (GLOBAL) attrib '*'
globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */ globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
/* get prefixed attribute (if any); default is regular global variable */ /* get prefixed attribute (if any); default is regular global variable */
lu_byte defkind = getglobalattribute(ls, GDKREG); lu_byte defkind = getglobalattribute(ls, GDKREG);
if (testnext(ls, '*')) { if (!testnext(ls, '*'))
globalnames(ls, defkind);
else {
/* use NULL as name to represent '*' entries */ /* use NULL as name to represent '*' entries */
new_varkind(ls, NULL, defkind); new_varkind(ls, NULL, defkind);
fs->nactvar++; /* activate declaration */ 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 +1963,9 @@ static void globalfunc (LexState *ls, int line) {
TString *fname = str_checkname(ls); TString *fname = str_checkname(ls);
new_varkind(ls, fname, GDKREG); /* declare global variable */ new_varkind(ls, fname, GDKREG); /* declare global variable */
fs->nactvar++; /* enter its scope */ 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' */ body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
checkglobal(ls, fname, line);
luaK_storevar(fs, &var, &b); luaK_storevar(fs, &var, &b);
luaK_fixline(fs, line); /* definition "happens" in the first line */ luaK_fixline(fs, line); /* definition "happens" in the first line */
} }
@@ -2009,7 +2123,7 @@ static void statement (LexState *ls) {
gotostat(ls, line); gotostat(ls, line);
break; break;
} }
#if defined(LUA_COMPAT_GLOBAL) #if LUA_COMPAT_GLOBAL
case TK_NAME: { case TK_NAME: {
/* compatibility code to parse global keyword when "global" /* compatibility code to parse global keyword when "global"
is not reserved */ is not reserved */
@@ -2049,7 +2163,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl; BlockCnt bl;
Upvaldesc *env; Upvaldesc *env;
open_func(ls, fs, &bl); 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 = allocupvalue(fs); /* ...set environment upvalue */
env->instack = 1; env->instack = 1;
env->idx = 0; env->idx = 0;
@@ -2063,16 +2177,14 @@ static void mainfunc (LexState *ls, FuncState *fs) {
} }
LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) { Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate; LexState lexstate;
FuncState funcstate; FuncState funcstate;
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ LClosure *cl;
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ lexstate.h = anchor; /* table for scanner */
luaD_inctop(L); cl = luaF_newLclosure(L, 1); /* create main closure */
lexstate.h = luaH_new(L); /* create table for scanner */ luaD_anchorobj(L, anchor, obj2gco(cl)); /* anchor it in scanner table */
sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
luaD_inctop(L);
funcstate.f = cl->p = luaF_newproto(L); funcstate.f = cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
@@ -2085,7 +2197,6 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
L->top.p--; /* remove scanner's table */ return cl;
return cl; /* closure is on the stack, too */
} }
+12 -6
View File
@@ -37,6 +37,8 @@ typedef enum {
info = result register */ info = result register */
VLOCAL, /* local variable; var.ridx = register index; VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */ var.vidx = relative index in 'actvar.arr' */
VVARGVAR, /* vararg parameter; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VGLOBAL, /* global variable; VGLOBAL, /* global variable;
info = relative index in 'actvar.arr' (or -1 for info = relative index in 'actvar.arr' (or -1 for
implicit declaration) */ implicit declaration) */
@@ -49,6 +51,8 @@ typedef enum {
ind.ro = true if it represents a read-only global; ind.ro = true if it represents a read-only global;
ind.keystr = if key is a string, index in 'k' of that string; ind.keystr = if key is a string, index in 'k' of that string;
-1 if key is not a string */ -1 if key is not a string */
VVARGIND, /* indexed vararg parameter;
ind.* as in VINDEXED */
VINDEXUP, /* indexed upvalue; VINDEXUP, /* indexed upvalue;
ind.idx = key's K index; ind.idx = key's K index;
ind.* as in VINDEXED */ ind.* as in VINDEXED */
@@ -97,10 +101,11 @@ typedef struct expdesc {
/* kinds of variables */ /* kinds of variables */
#define VDKREG 0 /* regular local */ #define VDKREG 0 /* regular local */
#define RDKCONST 1 /* local constant */ #define RDKCONST 1 /* local constant */
#define RDKTOCLOSE 2 /* to-be-closed */ #define RDKVAVAR 2 /* vararg parameter */
#define RDKCTC 3 /* local compile-time constant */ #define RDKTOCLOSE 3 /* to-be-closed */
#define GDKREG 4 /* regular global */ #define RDKCTC 4 /* local compile-time constant */
#define GDKCONST 5 /* global constant */ #define GDKREG 5 /* regular global */
#define GDKCONST 6 /* global constant */
/* variables that live in registers */ /* variables that live in registers */
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE) #define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)
@@ -184,8 +189,9 @@ typedef struct FuncState {
LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs); LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs);
LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l, LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l,
const char *what); const char *what);
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor,
Dyndata *dyd, const char *name, int firstchar); Mbuffer *buff, Dyndata *dyd,
const char *name, int firstchar);
#endif #endif
+13 -8
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; CallInfo *ci;
lua_assert(L->ci->next == NULL); ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo);
ci = luaM_new(L, CallInfo); if (l_unlikely(ci == NULL)) { /* allocation failed? */
lua_assert(L->ci->next == NULL); if (err)
L->ci->next = ci; luaM_error(L); /* raise the error */
return NULL; /* else only report it */
}
ci->next = L->ci->next;
ci->previous = L->ci; ci->previous = L->ci;
ci->next = NULL; L->ci->next = ci;
if (ci->next)
ci->next->previous = ci;
ci->u.l.trap = 0; ci->u.l.trap = 0;
L->nci++; L->nci++;
return ci; return ci;
@@ -146,7 +151,7 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
static void resetCI (lua_State *L) { static void resetCI (lua_State *L) {
CallInfo *ci = L->ci = &L->base_ci; CallInfo *ci = L->ci = &L->base_ci;
ci->func.p = L->stack.p; ci->func.p = L->stack.p;
setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */ setnilvalue2s(ci->func.p); /* 'function' entry for basic 'ci' */
ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */
ci->u.c.k = NULL; ci->u.c.k = NULL;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
@@ -161,7 +166,7 @@ static void stack_init (lua_State *L1, lua_State *L) {
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist.p = L1->stack.p; L1->tbclist.p = L1->stack.p;
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ setnilvalue2s(L1->stack.p + i); /* erase new stack */
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
/* initialize first ci */ /* initialize first ci */
resetCI(L1); resetCI(L1);
+5 -9
View File
@@ -85,7 +85,7 @@ typedef struct CallInfo CallInfo;
** they must be visited again at the end of the cycle), but they are ** they must be visited again at the end of the cycle), but they are
** marked black because assignments to them must activate barriers (to ** marked black because assignments to them must activate barriers (to
** move them back to TOUCHED1). ** 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.) ** 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) */ /* call is running a C function (still in first 16 bits) */
#define CIST_C (1u << (CIST_RECST + 3)) #define CIST_C (1u << (CIST_RECST + 3))
/* call is on a fresh "luaV_execute" frame */ /* 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 */ /* function is closing tbc variables */
#define CIST_CLSRET (CIST_FRESH << 1) #define CIST_CLSRET (CIST_FRESH << 1)
/* function has tbc variables to close */ /* function has tbc variables to close */
@@ -249,10 +249,6 @@ struct CallInfo {
#define CIST_HOOKYIELD (CIST_TAIL << 1) #define CIST_HOOKYIELD (CIST_TAIL << 1)
/* function "called" a finalizer */ /* function "called" a finalizer */
#define CIST_FIN (CIST_HOOKYIELD << 1) #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) #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 ** 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 */ /* 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_setdebt (global_State *g, l_mem debt);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); 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_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L);
+21 -27
View File
@@ -39,18 +39,18 @@
/* /*
** equality for long strings ** generic equality for strings
*/ */
int luaS_eqlngstr (TString *a, TString *b) { int luaS_eqstr (TString *a, TString *b) {
size_t len = a->u.lnglen; size_t len1, len2;
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); const char *s1 = getlstr(a, len1);
return (a == b) || /* same instance or... */ const char *s2 = getlstr(b, len2);
((len == b->u.lnglen) && /* equal length and ... */ return ((len1 == len2) && /* equal length and ... */
(memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */ (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); unsigned int h = seed ^ cast_uint(l);
for (; l > 0; l--) for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); 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, TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud) { const char *s, size_t len, lua_Alloc falloc, void *ud) {
struct NewExt ne; 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) { if (!falloc) {
ne.kind = LSTRFIX; ne.kind = LSTRFIX;
f_newext(L, &ne); /* just create header */ 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);
}
}
+2 -2
View File
@@ -54,9 +54,8 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) #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 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_resize (lua_State *L, int newsize);
LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_clearcache (global_State *g);
LUAI_FUNC void luaS_init (lua_State *L); 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, LUAI_FUNC TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud); const char *s, size_t len, lua_Alloc falloc, void *ud);
LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind); LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind);
LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts);
#endif #endif
+37 -20
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) { static int str_rep (lua_State *L) {
size_t l, lsep; size_t len, lsep;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, 1, &len);
lua_Integer n = luaL_checkinteger(L, 2); lua_Integer n = luaL_checkinteger(L, 2);
const char *sep = luaL_optlstring(L, 3, "", &lsep); const char *sep = luaL_optlstring(L, 3, "", &lsep);
if (n <= 0) if (n <= 0 || (len | lsep) == 0)
lua_pushliteral(L, ""); lua_pushliteral(L, ""); /* no repetitions or both strings empty */
else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n))) 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"); return luaL_error(L, "resulting string too large");
else { else {
size_t totallen = ((size_t)n * (l + lsep)) - lsep; size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep;
luaL_Buffer b; luaL_Buffer b;
char *p = luaL_buffinitsize(L, &b, totallen); char *p = luaL_buffinitsize(L, &b, totallen);
while (n-- > 1) { /* first n-1 copies (followed by separator) */ 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 */ if (lsep > 0) { /* empty 'memcpy' is not that cheap */
memcpy(p, sep, lsep * sizeof(char)); memcpy(p, sep, lsep * sizeof(char)); p += lsep;
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); luaL_pushresultsize(&b, totallen);
} }
return 1; 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 */ lua_settop(L, 2); /* back to the original arguments */
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
!luaL_getmetafield(L, 2, mtname))) !luaL_getmetafield(L, 2, mtkey)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, luaL_error(L, "attempt to %s a '%s' with a '%s'", opname,
luaL_typename(L, -2), luaL_typename(L, -1)); luaL_typename(L, -2), luaL_typename(L, -1));
lua_insert(L, -3); /* put metamethod before arguments */ lua_insert(L, -3); /* put metamethod before arguments */
lua_call(L, 2, 1); /* call metamethod */ 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)) if (tonum(L, 1) && tonum(L, 2))
lua_arith(L, op); /* result will be on the top */ lua_arith(L, op); /* result will be on the top */
else else
trymt(L, mtname); trymt(L, mtname, mtname + 2);
return 1; return 1;
} }
@@ -746,19 +757,25 @@ static int nospecials (const char *p, size_t l) {
} }
/*
** Prepare state for matches. These fields are not affected by each match.
*/
static void prepstate (MatchState *ms, lua_State *L, static void prepstate (MatchState *ms, lua_State *L,
const char *s, size_t ls, const char *p, size_t lp) { const char *s, size_t ls, const char *p, size_t lp) {
ms->L = L; ms->L = L;
ms->matchdepth = MAXCCALLS;
ms->src_init = s; ms->src_init = s;
ms->src_end = s + ls; ms->src_end = s + ls;
ms->p_end = p + lp; ms->p_end = p + lp;
} }
/*
** (Re)prepare state for a match, setting fields that change during
** each match.
*/
static void reprepstate (MatchState *ms) { static void reprepstate (MatchState *ms) {
ms->matchdepth = MAXCCALLS;
ms->level = 0; ms->level = 0;
lua_assert(ms->matchdepth == MAXCCALLS);
} }
@@ -957,7 +974,7 @@ static int str_gsub (lua_State *L) {
reprepstate(&ms); /* (re)prepare state for new match */ reprepstate(&ms); /* (re)prepare state for new match */
if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
n++; n++;
changed = add_value(&ms, &b, src, e, tr) | changed; changed = add_value(&ms, &b, src, e, tr) || changed;
src = lastmatch = e; src = lastmatch = e;
} }
else if (src < ms.src_end) /* otherwise, skip one character */ else if (src < ms.src_end) /* otherwise, skip one character */
@@ -1715,7 +1732,7 @@ static int str_packsize (lua_State *L) {
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
"variable-length format"); "variable-length format");
size += ntoalign; /* total space used by option */ 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"); 1, "format result too large");
totalsize += size; totalsize += size;
} }
@@ -1811,8 +1828,8 @@ static int str_unpack (lua_State *L) {
lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos, lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos,
h.islittle, cast_int(size), 0); h.islittle, cast_int(size), 0);
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
lua_pushlstring(L, data + pos + size, len); lua_pushlstring(L, data + pos + size, cast_sizet(len));
pos += len; /* skip string */ pos += cast_sizet(len); /* skip string */
break; break;
} }
case Kzstr: { case Kzstr: {
+90 -60
View File
@@ -156,7 +156,7 @@ static Node *hashint (const Table *t, lua_Integer i) {
** The main computation should be just ** The main computation should be just
** n = frexp(n, &i); return (n * INT_MAX) + i ** n = frexp(n, &i); return (n * INT_MAX) + i
** but there are some numerical subtleties. ** 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 ** representation as a float, but INT_MIN does; because the absolute
** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** 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 ** 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 ** Check whether key 'k1' is equal to the key in node 'n2'. This
** equality is raw, so there are no metamethods. Floats with integer ** equality is raw, so there are no metamethods. Floats with integer
** values have been normalized, so integers cannot be equal to ** values have been normalized, so integers cannot be equal to
** floats. It is assumed that 'eqshrstr' is simply pointer equality, so ** floats. It is assumed that 'eqshrstr' is simply pointer equality,
** that short strings are handled in the default case. ** so that short strings are handled in the default case. The flag
** A true 'deadok' means to accept dead keys as equal to their original ** 'deadok' means to accept dead keys as equal to their original values.
** values. All dead keys are compared in the default case, by pointer ** (Only collectable objects can produce dead keys.) Note that dead
** identity. (Only collectable objects can produce dead keys.) Note that ** long strings are also compared by identity. Once a key is dead,
** dead long strings are also compared by identity. ** its corresponding value may be collected, and then another value
** Once a key is dead, its corresponding value may be collected, and ** can be created with the same address. If this other value is given
** then another value can be created with the same address. If this ** to 'next', 'equalkey' will signal a false positive. In a regular
** other value is given to 'next', 'equalkey' will signal a false ** traversal, this situation should never happen, as all keys given to
** positive. In a regular traversal, this situation should never happen, ** 'next' came from the table itself, and therefore could not have been
** as all keys given to 'next' came from the table itself, and therefore ** collected. Outside a regular traversal, we have garbage in, garbage
** could not have been collected. Outside a regular traversal, we ** out. What is relevant is that this false positive does not break
** have garbage in, garbage out. What is relevant is that this false ** anything. (In particular, 'next' will return some other valid item
** positive does not break anything. (In particular, 'next' will return ** on the table or nil.)
** some other valid item on the table or nil.)
*/ */
static int equalkey (const TValue *k1, const Node *n2, int deadok) { static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ if (rawtt(k1) != keytt(n2)) { /* not the same variants? */
!(deadok && keyisdead(n2) && iscollectable(k1))) if (keyisshrstr(n2) && ttislngstring(k1)) {
return 0; /* cannot be same key */ /* an external string can be equal to a short-string key */
switch (keytt(n2)) { return luaS_eqstr(tsvalue(k1), keystrval(n2));
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: }
return 1; else if (deadok && keyisdead(n2) && iscollectable(k1)) {
case LUA_VNUMINT: /* a collectable value can be equal to a dead key */
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:
return gcvalue(k1) == gcvalueraw(keyval(n2)); 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 ** Exchange the hash part of 't1' and 't2'. (In 'flags', only the dummy
** dummy bit must be exchanged: The 'isrealasize' is not related ** bit must be exchanged: The metamethod bits do not change during a
** to the hash part, and the metamethod bits do not change during ** resize, so the "real" table can keep their values.)
** a resize, so the "real" table can keep their values.)
*/ */
static void exchangehashpart (Table *t1, Table *t2) { static void exchangehashpart (Table *t1, Table *t2) {
lu_byte lsizenode = t1->lsizenode; lu_byte lsizenode = t1->lsizenode;
@@ -1146,19 +1155,28 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key,
lua_assert(hres != HOK); lua_assert(hres != HOK);
if (hres == HNOTFOUND) { if (hres == HNOTFOUND) {
TValue aux; TValue aux;
const TValue *actk = key; /* actual key to insert */
if (l_unlikely(ttisnil(key))) if (l_unlikely(ttisnil(key)))
luaG_runerror(L, "table index is nil"); luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) { else if (ttisfloat(key)) {
lua_Number f = fltvalue(key); lua_Number f = fltvalue(key);
lua_Integer k; lua_Integer k;
if (luaV_flttointeger(f, &k, F2Ieq)) { if (luaV_flttointeger(f, &k, F2Ieq)) { /* is key equal to an integer? */
setivalue(&aux, k); /* key is equal to an integer */ setivalue(&aux, k);
key = &aux; /* insert it as an integer */ actk = &aux; /* use the integer as the key */
} }
else if (l_unlikely(luai_numisnan(f))) else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN"); 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? */ else if (hres > 0) { /* regular Node? */
setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value); 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 ** 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 ** caller, we know that 'asize + 1' is present. We want to find a larger
** present. We want to find a larger key that is absent from the ** key that is absent from the table, so that we can do a binary search
** table, so that we can do a binary search between the two keys to ** between the two keys to find a boundary. We keep doubling 'j' until
** find a boundary. We keep doubling 'j' until we get an absent index. ** we get an absent index. If the doubling would overflow, we try
** If the doubling would overflow, we try LUA_MAXINTEGER. If it is ** LUA_MAXINTEGER. If it is absent, we are ready for the binary search.
** absent, we are ready for the binary search. ('j', being max integer, ** ('j', being max integer, is larger or equal to 'i', but it cannot be
** is larger or equal to 'i', but it cannot be equal because it is ** equal because it is absent while 'i' is present.) Otherwise, 'j' is a
** 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
** boundary. ('j + 1' cannot be a present integer key because it is ** a valid integer in Lua.)
** 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) { static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) {
lua_Unsigned i; lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */
if (j == 0) j++; /* the caller ensures 'j + 1' is present */ unsigned rnd = G(L)->seed;
do { 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 */ i = j; /* 'i' is a present index */
if (j <= l_castS2U(LUA_MAXINTEGER) / 2) if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) {
j *= 2; j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */
rnd >>= 1;
}
else { else {
j = LUA_MAXINTEGER; j = LUA_MAXINTEGER;
if (hashkeyisempty(t, j)) /* t[j] not present? */ 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 */ else /* weird case */
return j; /* well, max integer is a boundary... */ 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 */ /* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */ while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2; 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 ** If there is no array part, or its last element is non empty, the
** border may be in the hash part. ** 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; unsigned asize = t->asize;
if (asize > 0) { /* is there an array part? */ if (asize > 0) { /* is there an array part? */
const unsigned maxvicinity = 4; const unsigned maxvicinity = 4;
@@ -1309,7 +1339,7 @@ lua_Unsigned luaH_getn (Table *t) {
if (isdummy(t) || hashkeyisempty(t, asize + 1)) if (isdummy(t) || hashkeyisempty(t, asize + 1))
return asize; /* 'asize + 1' is empty */ return asize; /* 'asize + 1' is empty */
else /* 'asize + 1' is also non empty */ else /* 'asize + 1' is also non empty */
return hash_search(t, asize); return hash_search(L, t, asize);
} }
+1 -1
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 lu_mem luaH_size (Table *t);
LUAI_FUNC void luaH_free (lua_State *L, 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 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) #if defined(LUA_DEBUG)
+7 -4
View File
@@ -42,15 +42,17 @@ static int checkfield (lua_State *L, const char *key, int n) {
/* /*
** Check that 'arg' either is a table or can behave like one (that is, ** Check that 'arg' either is a table or can behave like one (that is,
** has a metatable with the required metamethods) ** has a metatable with the required metamethods).
*/ */
static void checktab (lua_State *L, int arg, int what) { static void checktab (lua_State *L, int arg, int what) {
if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ int tp = lua_type(L, arg);
if (tp != LUA_TTABLE) { /* is it not a table? */
int n = 1; /* number of elements to pop */ int n = 1; /* number of elements to pop */
if (lua_getmetatable(L, arg) && /* must have metatable */ if (lua_getmetatable(L, arg) && /* must have metatable */
(!(what & TAB_R) || checkfield(L, "__index", ++n)) && (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
(!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
(!(what & TAB_L) || checkfield(L, "__len", ++n))) { (!(what & TAB_L) || /* strings don't need '__len' to have a length */
tp == LUA_TSTRING || checkfield(L, "__len", ++n))) {
lua_pop(L, n); /* pop metatable and tested metamethods */ lua_pop(L, n); /* pop metatable and tested metamethods */
} }
else else
@@ -204,8 +206,9 @@ static int tpack (lua_State *L) {
static int tunpack (lua_State *L) { static int tunpack (lua_State *L) {
lua_Unsigned n; lua_Unsigned n;
lua_Integer len = aux_getn(L, 1, TAB_R);
lua_Integer i = luaL_optinteger(L, 2, 1); lua_Integer i = luaL_optinteger(L, 2, 1);
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, len);
if (i > e) return 0; /* empty range */ if (i > e) return 0; /* empty range */
n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */ n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */
if (l_unlikely(n >= (unsigned int)INT_MAX || if (l_unlikely(n >= (unsigned int)INT_MAX ||
+54 -15
View File
@@ -164,13 +164,13 @@ static void warnf (void *ud, const char *msg, int tocont) {
#define MARK 0x55 /* 01010101 (a nice pattern) */ #define MARK 0x55 /* 01010101 (a nice pattern) */
typedef union Header { typedef union memHeader {
LUAI_MAXALIGN; LUAI_MAXALIGN;
struct { struct {
size_t size; size_t size;
int type; int type;
} d; } d;
} Header; } memHeader;
#if !defined(EXTERNMEMCHECK) #if !defined(EXTERNMEMCHECK)
@@ -193,14 +193,14 @@ Memcontrol l_memcontrol =
{0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; {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) { if (block) {
size_t size = block->d.size; size_t size = block->d.size;
int i; int i;
for (i = 0; i < MARKSIZE; i++) /* check marks after block */ for (i = 0; i < MARKSIZE; i++) /* check marks after block */
lua_assert(*(cast_charp(block + 1) + size + i) == MARK); lua_assert(*(cast_charp(block + 1) + size + i) == MARK);
mc->objcount[block->d.type]--; 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 */ free(block); /* actually free block */
mc->numblocks--; /* update counts */ mc->numblocks--; /* update counts */
mc->total -= size; 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) { void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
Memcontrol *mc = cast(Memcontrol *, ud); Memcontrol *mc = cast(Memcontrol *, ud);
Header *block = cast(Header *, b); memHeader *block = cast(memHeader *, b);
int type; int type;
if (mc->memlimit == 0) { /* first time? */ if (mc->memlimit == 0) { /* first time? */
char *limit = getenv("MEMLIMIT"); /* initialize memory limit */ 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) if (size > oldsize && mc->total+size-oldsize > mc->memlimit)
return NULL; /* fake a memory allocation error */ return NULL; /* fake a memory allocation error */
else { else {
Header *newblock; memHeader *newblock;
int i; int i;
size_t commonsize = (oldsize < size) ? oldsize : size; 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! */ 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) if (newblock == NULL)
return NULL; /* really out of memory? */ return NULL; /* really out of memory? */
if (block) { 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; StkId o;
CallInfo *ci; CallInfo *ci;
UpVal *uv; UpVal *uv;
@@ -517,7 +517,7 @@ static void checkrefs (global_State *g, GCObject *o) {
break; break;
} }
case LUA_VTHREAD: { case LUA_VTHREAD: {
checkstack(g, gco2th(o)); check_stack(g, gco2th(o));
break; break;
} }
case LUA_VLCL: { 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) { static int mem_query (lua_State *L) {
if (lua_isnone(L, 1)) { if (lua_isnone(L, 1)) {
lua_pushinteger(L, cast_Integer(l_memcontrol.total)); 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) { static int hash_query (lua_State *L) {
if (lua_isnone(L, 2)) { if (lua_isnone(L, 2)) {
TString *ts;
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected"); 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 { else {
TValue *o = obj_at(L, 1); 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) { static int table_query (lua_State *L) {
const Table *t; const Table *t;
int i = cast_int(luaL_optinteger(L, 2, -1)); int i = cast_int(luaL_optinteger(L, 2, -1));
@@ -1109,7 +1145,7 @@ static int table_query (lua_State *L) {
if (!tagisempty(*getArrTag(t, i))) if (!tagisempty(*getArrTag(t, i)))
arr2obj(t, cast_uint(i), s2v(L->top.p)); arr2obj(t, cast_uint(i), s2v(L->top.p));
else else
setnilvalue(s2v(L->top.p)); setnilvalue2s(L->top.p);
api_incr_top(L); api_incr_top(L);
lua_pushnil(L); lua_pushnil(L);
} }
@@ -1266,7 +1302,7 @@ static int doonnewstack (lua_State *L) {
lua_State *L1 = lua_newthread(L); lua_State *L1 = lua_newthread(L);
size_t l; size_t l;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, 1, &l);
int status = luaL_loadbuffer(L1, s, l, s); int status = luaL_loadbufferx(L1, s, l, s, "t");
if (status == LUA_OK) if (status == LUA_OK)
status = lua_pcall(L1, 0, 0, 0); status = lua_pcall(L1, 0, 0, 0);
lua_pushinteger(L, status); lua_pushinteger(L, status);
@@ -1346,7 +1382,7 @@ static int doremote (lua_State *L) {
const char *code = luaL_checklstring(L, 2, &lcode); const char *code = luaL_checklstring(L, 2, &lcode);
int status; int status;
lua_settop(L1, 0); lua_settop(L1, 0);
status = luaL_loadbuffer(L1, code, lcode, code); status = luaL_loadbufferx(L1, code, lcode, code, "t");
if (status == LUA_OK) if (status == LUA_OK)
status = lua_pcall(L1, 0, LUA_MULTRET, 0); status = lua_pcall(L1, 0, LUA_MULTRET, 0);
if (status != LUA_OK) { if (status != LUA_OK) {
@@ -1702,7 +1738,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
lua_pushinteger(L1, luaL_len(L1, getindex)); lua_pushinteger(L1, luaL_len(L1, getindex));
} }
else if EQ("loadfile") { else if EQ("loadfile") {
luaL_loadfile(L1, luaL_checkstring(L1, getnum)); luaL_loadfilex(L1, luaL_checkstring(L1, getnum), "t");
} }
else if EQ("loadstring") { else if EQ("loadstring") {
size_t slen; size_t slen;
@@ -2167,6 +2203,9 @@ static const struct luaL_Reg tests_funcs[] = {
{"s2d", s2d}, {"s2d", s2d},
{"sethook", sethook}, {"sethook", sethook},
{"stacklevel", stacklevel}, {"stacklevel", stacklevel},
{"resetCI", resetCI},
{"reallocstack", reallocstack},
{"sizes", get_sizes},
{"testC", testC}, {"testC", testC},
{"makeCfunc", makeCfunc}, {"makeCfunc", makeCfunc},
{"totalmem", mem_query}, {"totalmem", mem_query},
+10 -16
View File
@@ -13,8 +13,8 @@
/* test Lua with compatibility code */ /* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB #define LUA_COMPAT_MATHLIB
#define LUA_COMPAT_LT_LE
#undef LUA_COMPAT_GLOBAL #undef LUA_COMPAT_GLOBAL
#define LUA_COMPAT_GLOBAL 0
#define LUA_DEBUG #define LUA_DEBUG
@@ -64,7 +64,7 @@ LUA_API Memcontrol l_memcontrol;
#define luai_tracegc(L,f) luai_tracegctest(L, f) #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 +76,26 @@ extern void *l_Trick;
/* /*
** Function to traverse and check all memory used by Lua ** 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 ** Function to print an object GC-friendly
*/ */
struct GCObject; 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 ** Function to print a value
*/ */
struct TValue; struct TValue;
LUAI_FUNC void lua_printvalue (struct TValue *v); extern void lua_printvalue (struct TValue *v);
/* /*
** Function to print the stack ** Function to print the stack
*/ */
LUAI_FUNC void lua_printstack (lua_State *L); extern void lua_printstack (lua_State *L);
LUAI_FUNC int lua_printallstack (lua_State *L); extern int lua_printallstack (lua_State *L);
/* test for lock/unlock */ /* test for lock/unlock */
@@ -122,14 +122,14 @@ LUA_API int luaB_opentests (lua_State *L);
LUA_API void *debug_realloc (void *ud, void *block, LUA_API void *debug_realloc (void *ud, void *block,
size_t osize, size_t nsize); size_t osize, size_t nsize);
#if defined(lua_c)
#define luaL_newstate() \ #define luaL_newstate() \
lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL)) lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL))
#define luai_openlibs(L) \ #define luai_openlibs(L) \
{ luaL_openlibs(L); \ { luaL_openlibs(L); \
luaL_requiref(L, "T", luaB_opentests, 1); \ luaL_requiref(L, "T", luaB_opentests, 1); \
lua_pop(L, 1); } lua_pop(L, 1); }
#endif
@@ -143,19 +143,13 @@ LUA_API void *debug_realloc (void *ud, void *block,
#define STRCACHE_N 23 #define STRCACHE_N 23
#define STRCACHE_M 5 #define STRCACHE_M 5
#define MAXINDEXRK 1
/*
** This one is not compatible with tests for opcode optimizations,
** as it blocks some optimizations
#define MAXINDEXRK 0
*/
/* /*
** Reduce maximum stack size to make stack-overflow tests run faster. ** Reduce maximum stack size to make stack-overflow tests run faster.
** (But value is still large enough to overflow smaller integers.) ** (But value is still large enough to overflow smaller integers.)
*/ */
#undef LUAI_MAXSTACK
#define LUAI_MAXSTACK 68000 #define LUAI_MAXSTACK 68000
+124 -36
View File
@@ -196,28 +196,12 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
/* /*
** Calls an order tag method. ** 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, int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) { TMS event) {
int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */ int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */
if (tag >= 0) /* found tag method? */ if (tag >= 0) /* found tag method? */
return !tagisfalse(tag); 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 */ luaG_ordererror(L, p1, p2); /* no metamethod found */
return 0; /* to avoid warnings */ return 0; /* to avoid warnings */
} }
@@ -240,37 +224,141 @@ 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 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; ci->u.l.nextraargs = nextra;
luaD_checkstack(L, p->maxstacksize + 1); 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); 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++) { for (i = 1; i <= nfixparams; i++) {
setobjs2s(L, L->top.p++, ci->func.p + i); setobjs2s(L, L->top.p++, ci->func.p + i);
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ setnilvalue2s(ci->func.p + i); /* erase original parameter (for GC) */
} }
ci->func.p += actual + 1; ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */
ci->top.p += actual + 1; ci->top.p += totalargs + 1;
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
} }
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) {
int i; 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 */
setnilvalue2s(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; int nextra = ci->u.l.nextraargs;
if (wanted < 0) { lua_Integer n;
wanted = nextra; /* get all extra arguments available */ if (tointegerns(rc, &n)) { /* integral value? */
checkstackp(L, nextra, where); /* ensure stack space */ if (l_castS2U(n) - 1 < cast_uint(nextra)) {
L->top.p = where + nextra; /* next instruction will need top */ StkId slot = ci->func.p - nextra + cast_int(n) - 1;
setobjs2s(((lua_State*)NULL), ra, slot);
return;
}
} }
for (i = 0; i < wanted && i < nextra; i++) else if (ttisstring(rc)) { /* string value? */
setobjs2s(L, where + i, ci->func.p - nextra + i); size_t len;
for (; i < wanted; i++) /* complete required results with nil */ const char *s = getlstr(tsvalue(rc), len);
setnilvalue(s2v(where + i)); if (len == 1 && s[0] == 'n') { /* key is "n"? */
setivalue(s2v(ra), nextra);
return;
}
}
setnilvalue2s(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))
setnilvalue2s(where + i);
}
}
for (; i < wanted; i++) /* complete required results with nil */
setnilvalue2s(where + i);
} }
+6 -5
View File
@@ -49,7 +49,7 @@ typedef enum {
** Mask with 1 in all fast-access methods. A 1 in any of these bits ** 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 ** in the flag of a (meta)table means the metatable does not have the
** corresponding metamethod field. (Bit 6 of the flag indicates that ** 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))) #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, LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event); int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci,
struct CallInfo *ci, const Proto *p); const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc);
StkId where, int wanted); LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where,
int wanted, int vatab);
#endif #endif
+74 -41
View File
@@ -30,6 +30,12 @@
#define LUA_INIT_VAR "LUA_INIT" #define LUA_INIT_VAR "LUA_INIT"
#endif #endif
/* Name of the environment variable with the name of the readline library */
#if !defined(LUA_RLLIB_VAR)
#define LUA_RLLIB_VAR "LUA_READLINELIB"
#endif
#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
@@ -201,12 +207,12 @@ static int dochunk (lua_State *L, int status) {
static int dofile (lua_State *L, const char *name) { static int dofile (lua_State *L, const char *name) {
return dochunk(L, luaL_loadfile(L, name)); return dochunk(L, luaL_loadfilex(L, name, "bt"));
} }
static int dostring (lua_State *L, const char *s, const char *name) { static int dostring (lua_State *L, const char *s, const char *name) {
return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); return dochunk(L, luaL_loadbufferx(L, s, strlen(s), name, "t"));
} }
@@ -260,7 +266,7 @@ static int handle_script (lua_State *L, char **argv) {
const char *fname = argv[0]; const char *fname = argv[0];
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
fname = NULL; /* stdin */ fname = NULL; /* stdin */
status = luaL_loadfile(L, fname); status = luaL_loadfilex(L, fname, "bt");
if (status == LUA_OK) { if (status == LUA_OK) {
int n = pushargs(L); /* push arguments to script */ int n = pushargs(L); /* push arguments to script */
status = docall(L, n, LUA_MULTRET); status = docall(L, n, LUA_MULTRET);
@@ -303,7 +309,8 @@ static int collectargs (char **argv, int *first) {
case '-': /* '--' */ case '-': /* '--' */
if (argv[i][2] != '\0') /* extra characters after '--'? */ if (argv[i][2] != '\0') /* extra characters after '--'? */
return has_error; /* invalid option */ 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; return args;
case '\0': /* '-' */ case '\0': /* '-' */
return args; /* script "name" is '-' */ return args; /* script "name" is '-' */
@@ -348,6 +355,7 @@ static int collectargs (char **argv, int *first) {
*/ */
static int runargs (lua_State *L, char **argv, int n) { static int runargs (lua_State *L, char **argv, int n) {
int i; int i;
lua_warning(L, "@off", 0); /* by default, Lua stand-alone has warnings off */
for (i = 1; i < n; i++) { for (i = 1; i < n; i++) {
int option = argv[i][1]; int option = argv[i][1];
lua_assert(argv[i][0] == '-'); /* already checked */ lua_assert(argv[i][0] == '-'); /* already checked */
@@ -372,12 +380,21 @@ static int runargs (lua_State *L, char **argv, int n) {
} }
static char *(*l_getenv)(const char *name);
/* Function to ignore environment variables, used by option -E */
static char *no_getenv (const char *name) {
UNUSED(name);
return NULL;
}
static int handle_luainit (lua_State *L) { static int handle_luainit (lua_State *L) {
const char *name = "=" LUA_INITVARVERSION; const char *name = "=" LUA_INITVARVERSION;
const char *init = getenv(name + 1); const char *init = l_getenv(name + 1);
if (init == NULL) { if (init == NULL) {
name = "=" LUA_INIT_VAR; name = "=" LUA_INIT_VAR;
init = getenv(name + 1); /* try alternative name */ init = l_getenv(name + 1); /* try alternative name */
} }
if (init == NULL) return LUA_OK; if (init == NULL) return LUA_OK;
else if (init[0] == '@') else if (init[0] == '@')
@@ -437,13 +454,24 @@ static int handle_luainit (lua_State *L) {
** the standard input. ** the standard input.
** * lua_saveline defines how to "save" a read line in a "history". ** * lua_saveline defines how to "save" a read line in a "history".
** * lua_freeline defines how to free a line read by lua_readline. ** * 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) /* { */ #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) */ /* pointer to 'readline' function (if any) */
typedef char *(*l_readlineT) (const char *prompt); typedef char *(*l_readlineT) (const char *prompt);
@@ -479,43 +507,41 @@ static void lua_freeline (char *line) {
} }
#if defined(LUA_USE_READLINE) /* { */ #if defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* { */
/* 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 = cast(l_readlineT, readline);
l_addhist = cast(l_addhistT, add_history);
}
#elif defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* }{ */
/* try to load 'readline' dynamically */ /* try to load 'readline' dynamically */
#include <dlfcn.h> #include <dlfcn.h>
static void lua_initreadline (lua_State *L) { static void lua_initreadline (lua_State *L) {
void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL); const char *rllib = l_getenv(LUA_RLLIB_VAR); /* name of readline library */
if (lib == NULL) void *lib; /* library handle */
lua_warning(L, "library '" LUA_READLINELIB "' not found", 0); if (rllib == NULL) /* no environment variable? */
else { rllib = LUA_READLINELIB; /* use default name */
lib = dlopen(rllib, RTLD_NOW | RTLD_LOCAL);
if (lib != NULL) {
const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); const char **name = cast(const char**, dlsym(lib, "rl_readline_name"));
if (name != NULL) if (name != NULL)
*name = "lua"; *name = "lua";
l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline"))); l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline")));
l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history"))); l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history")));
if (l_readline != NULL) /* could load readline function? */
return; /* everything ok */
/* else emit a warning */
} }
lua_warning(L, "unable to load readline library '", 1);
lua_warning(L, rllib, 1);
lua_warning(L, "'", 0);
} }
#else /* }{ */ #else /* }{ */
/* no dlopen or LUA_READLINELIB undefined */
/* no readline; leave function pointers as NULL */ /* Leave pointers with NULL */
#define lua_initreadline(L) cast(void, L) #define lua_initreadline(L) ((void)L)
#endif /* } */ #endif /* } */
#endif /* } */
#endif /* } */ #endif /* } */
@@ -583,11 +609,11 @@ static int pushline (lua_State *L, int firstline) {
static int addreturn (lua_State *L) { static int addreturn (lua_State *L) {
const char *line = lua_tostring(L, -1); /* original line */ const char *line = lua_tostring(L, -1); /* original line */
const char *retline = lua_pushfstring(L, "return %s;", line); const char *retline = lua_pushfstring(L, "return %s;", line);
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); int status = luaL_loadbufferx(L, retline, strlen(retline), "=stdin", "t");
if (status == LUA_OK) if (status == LUA_OK)
lua_remove(L, -2); /* remove modified line */ lua_remove(L, -2); /* remove modified line */
else else
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ lua_pop(L, 2); /* pop result from 'luaL_loadbufferx' and modified line */
return status; return status;
} }
@@ -614,7 +640,7 @@ static int multiline (lua_State *L) {
const char *line = lua_tolstring(L, 1, &len); /* get first line */ const char *line = lua_tolstring(L, 1, &len); /* get first line */
checklocal(line); checklocal(line);
for (;;) { /* repeat until gets a complete statement */ for (;;) { /* repeat until gets a complete statement */
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ int status = luaL_loadbufferx(L, line, len, "=stdin", "t"); /* try it */
if (!incomplete(L, status) || !pushline(L, 0)) if (!incomplete(L, status) || !pushline(L, 0))
return status; /* should not or cannot try to add continuation line */ return status; /* should not or cannot try to add continuation line */
lua_remove(L, -2); /* remove error message (from incomplete line) */ lua_remove(L, -2); /* remove error message (from incomplete line) */
@@ -688,7 +714,13 @@ static void doREPL (lua_State *L) {
/* }================================================================== */ /* }================================================================== */
#if !defined(luai_openlibs) #if !defined(luai_openlibs)
#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0) #if defined(LUA_NODEBUGLIB)
/* With this option, code must require the debug library before using it */
#define luai_openlibs(L) luaL_openselectedlibs(L, ~LUA_DBLIBK, LUA_DBLIBK)
#else
/* The default is to open all standard libraries */
#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif
#endif #endif
@@ -710,18 +742,19 @@ static int pmain (lua_State *L) {
if (args & has_v) /* option '-v'? */ if (args & has_v) /* option '-v'? */
print_version(); print_version();
if (args & has_E) { /* option '-E'? */ if (args & has_E) { /* option '-E'? */
l_getenv = &no_getenv; /* program will ignore environment variables */
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
} }
else
l_getenv = &getenv;
luai_openlibs(L); /* open standard libraries */ luai_openlibs(L); /* open standard libraries */
createargtable(L, argv, argc, script); /* create table 'arg' */ createargtable(L, argv, argc, script); /* create table 'arg' */
lua_gc(L, LUA_GCRESTART); /* start GC... */ lua_gc(L, LUA_GCRESTART); /* start GC... */
lua_gc(L, LUA_GCGEN); /* ...in generational mode */ lua_gc(L, LUA_GCGEN); /* ...in generational mode */
if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */
return 0; /* error running LUA_INIT */ if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */
}
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
return 0; /* something failed */ return 0; /* something failed */
if (script > 0) { /* execute main script (if there is one) */ if (script > 0) { /* execute main script (if there is one) */
if (handle_script(L, argv + script) != LUA_OK) if (handle_script(L, argv + script) != LUA_OK)
+6 -13
View File
@@ -13,13 +13,13 @@
#include <stddef.h> #include <stddef.h>
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2026 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
#define LUA_VERSION_MAJOR_N 5 #define LUA_VERSION_MAJOR_N 5
#define LUA_VERSION_MINOR_N 5 #define LUA_VERSION_MINOR_N 5
#define LUA_VERSION_RELEASE_N 0 #define LUA_VERSION_RELEASE_N 1
#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) #define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N)
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N)
@@ -37,10 +37,10 @@
/* /*
** Pseudo-indices ** Pseudo-indices
** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty ** (The stack size is limited to INT_MAX/2; we keep some free empty
** space after that to help overflow detection) ** 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)) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
@@ -432,13 +432,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
** compatibility macros ** 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_newuserdata(L,s) lua_newuserdatauv(L,s,1)
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) #define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
@@ -528,7 +521,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2025 Lua.org, PUC-Rio. * Copyright (C) 1994-2026 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
+45 -105
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 ** application will try to dynamically link a 'readline' facility
** for its REPL. In that case, LUA_READLINELIB is the name of the ** 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 ** library it will look for those facilities. If lua.c cannot open
@@ -70,15 +70,19 @@
#if defined(LUA_USE_LINUX) #if defined(LUA_USE_LINUX)
#define LUA_USE_POSIX #define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_DLOPEN /* needs an extra library: -ldl */
#if !defined(LUA_READLINELIB)
#define LUA_READLINELIB "libreadline.so" #define LUA_READLINELIB "libreadline.so"
#endif #endif
#endif
#if defined(LUA_USE_MACOSX) #if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX #define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ #define LUA_USE_DLOPEN /* macOS does not need -ldl */
#if !defined(LUA_READLINELIB)
#define LUA_READLINELIB "libedit.dylib" #define LUA_READLINELIB "libedit.dylib"
#endif #endif
#endif
#if defined(LUA_USE_IOS) #if defined(LUA_USE_IOS)
@@ -88,7 +92,7 @@
#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) #if defined(LUA_USE_C89) && defined(LUA_USE_POSIX)
#error "Posix is not compatible with C89" #error "POSIX is not compatible with C89"
#endif #endif
@@ -138,7 +142,7 @@
/* /*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/ */
#define LUA_32BITS 0 /* #define LUA_32BITS */
/* /*
@@ -153,7 +157,7 @@
#endif #endif
#if LUA_32BITS /* { */ #if defined(LUA_32BITS) /* { */
/* /*
** 32-bit integers and 'float' ** 32-bit integers and 'float'
*/ */
@@ -224,17 +228,17 @@
#if !defined(LUA_PATH_DEFAULT) #if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \ #define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_LDIR "?.lua;" LUA_LDIR "?\\init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ LUA_CDIR "?.lua;" LUA_CDIR "?\\init.lua;" \
LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ LUA_SHRDIR "?.lua;" LUA_SHRDIR "?\\init.lua;" \
".\\?.lua;" ".\\?\\init.lua" ".\\?.lua;" ".\\?\\init.lua"
#endif #endif
#if !defined(LUA_CPATH_DEFAULT) #if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \ #define LUA_CPATH_DEFAULT \
LUA_CDIR"?.dll;" \ LUA_CDIR "?.dll;" \
LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ LUA_CDIR "..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
LUA_CDIR"loadall.dll;" ".\\?.dll" LUA_CDIR "loadall.dll;" ".\\?.dll"
#endif #endif
#else /* }{ */ #else /* }{ */
@@ -245,14 +249,14 @@
#if !defined(LUA_PATH_DEFAULT) #if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \ #define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_LDIR "?.lua;" LUA_LDIR "?/init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ LUA_CDIR "?.lua;" LUA_CDIR "?/init.lua;" \
"./?.lua;" "./?/init.lua" "./?.lua;" "./?/init.lua"
#endif #endif
#if !defined(LUA_CPATH_DEFAULT) #if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \ #define LUA_CPATH_DEFAULT \
LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" LUA_CDIR "?.so;" LUA_CDIR "loadall.so;" "./?.so"
#endif #endif
#endif /* } */ #endif /* } */
@@ -319,32 +323,13 @@
** More often than not the libs go together with the core. ** More often than not the libs go together with the core.
*/ */
#define LUALIB_API LUA_API #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 #define LUAMOD_API LUA_API
#endif
/*
@@ 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 */
/* }================================================================== */ /* }================================================================== */
@@ -358,15 +343,19 @@
/* /*
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word @@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
*/ */
#define LUA_COMPAT_GLOBAL #if !defined(LUA_COMPAT_GLOBAL)
#define LUA_COMPAT_GLOBAL 1
#endif
/* /*
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. @@ LUA_COMPAT_LOOPVAR makes for-loop control variables not read-only,
** You can define it to get all options, or change specific options ** as they were in previous versions.
** to fit your specific needs.
*/ */
#if defined(LUA_COMPAT_5_3) /* { */ #if !defined(LUA_COMPAT_LOOPVAR)
#define LUA_COMPAT_LOOPVAR 0
#endif
/* /*
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated @@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
@@ -374,23 +363,7 @@
** (These functions were already officially removed in 5.3; ** (These functions were already officially removed in 5.3;
** nevertheless they are still available here.) ** nevertheless they are still available here.)
*/ */
#define LUA_COMPAT_MATHLIB /* #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
/* /*
@@ -407,8 +380,6 @@
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #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) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#endif /* } */
/* }================================================================== */ /* }================================================================== */
@@ -440,26 +411,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)) #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 */ /* now the variable definitions */
#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
@@ -708,7 +664,7 @@
*/ */
#if !defined(luai_likely) #if !defined(luai_likely)
#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) #if !defined(LUA_NOBUILTIN) && defined(__GNUC__) && (__GNUC__ >= 3)
#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) #define luai_likely(x) (__builtin_expect(((x) != 0), 1))
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) #define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else #else
@@ -719,13 +675,6 @@
#endif #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 +712,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 @@ LUA_EXTRASPACE defines the size of a raw memory area associated with
** a Lua state with very fast access. ** a Lua state with very fast access.
@@ -801,10 +736,17 @@
/* /*
@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure @@ LUAI_MAXALIGN defines fields that ensure proper alignment for
** maximum alignment for the other items in that union. ** memory areas offered by Lua (e.g., userdata memory).
** Add fields to it if you need alignment for non-ISO objects.
*/ */
#if defined(LLONG_MAX)
/* use ISO C99 stuff */
#define LUAI_MAXALIGN long double u; void *s; long long l
#else
/* use only C89 stuff */
#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l #define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
#endif
/* }================================================================== */ /* }================================================================== */
@@ -821,7 +763,5 @@
#endif #endif
+14 -16
View File
@@ -109,7 +109,7 @@ static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) {
static size_t loadSize (LoadState *S) { 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; TString *ts;
TValue sv; TValue sv;
size_t size = loadSize(S); size_t size = loadSize(S);
if (size == 0) { /* no string? */ if (size == 0) { /* previously saved string? */
lua_assert(*sl == NULL); /* must be prefilled */
return;
}
else if (size == 1) { /* previously saved string? */
lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */ lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */
TValue stv; 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) if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING)
error(S, "invalid string index"); error(S, "invalid string index");
*sl = ts = tsvalue(&stv); /* get its value */ *sl = ts = tsvalue(&stv); /* get its value */
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);
return; /* do not save it again */ 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' */ char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */
loadVector(S, buff, size + 1); /* load string into buffer */ loadVector(S, buff, size + 1); /* load string into buffer */
*sl = ts = luaS_newlstr(L, buff, size); /* create string */ *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->linedefined = loadInt(S);
f->lastlinedefined = loadInt(S); f->lastlinedefined = loadInt(S);
f->numparams = loadByte(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) if (S->fixed)
f->flag |= PF_FIXED; /* signal that code is fixed */ f->flag |= PF_FIXED; /* signal that code is fixed */
f->maxstacksize = loadByte(S); f->maxstacksize = loadByte(S);
@@ -391,7 +392,8 @@ static void checkHeader (LoadState *S) {
/* /*
** Load precompiled chunk. ** Load precompiled chunk.
*/ */
LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) { LClosure *luaU_undump (lua_State *L, ZIO *Z, Table *anchor, const char *name,
int fixed) {
LoadState S; LoadState S;
LClosure *cl; LClosure *cl;
if (*name == '@' || *name == '=') if (*name == '@' || *name == '=')
@@ -404,20 +406,16 @@ LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) {
S.fixed = cast_byte(fixed); S.fixed = cast_byte(fixed);
S.offset = 1; /* fist byte was already read */ S.offset = 1; /* fist byte was already read */
checkHeader(&S); checkHeader(&S);
cl = luaF_newLclosure(L, loadByte(&S)); S.h = anchor;
setclLvalue2s(L, L->top.p, cl);
luaD_inctop(L);
S.h = luaH_new(L); /* create list of saved strings */
S.nstr = 0; S.nstr = 0;
sethvalue2s(L, L->top.p, S.h); /* anchor it */ cl = luaF_newLclosure(L, loadByte(&S));
luaD_inctop(L); luaD_anchorobj(L, anchor, obj2gco(cl));
cl->p = luaF_newproto(L); cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);
loadFunction(&S, cl->p); loadFunction(&S, cl->p);
if (cl->nupvalues != cl->p->sizeupvalues) if (cl->nupvalues != cl->p->sizeupvalues)
error(&S, "corrupted chunk"); error(&S, "corrupted chunk");
luai_verifycode(L, cl->p); luai_verifycode(L, cl->p);
L->top.p--; /* pop table */
return cl; return cl;
} }
+2 -2
View File
@@ -30,8 +30,8 @@
/* load one chunk; from lundump.c */ /* load one chunk; from lundump.c */
LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, Table *anchor,
int fixed); const char* name, int fixed);
/* dump one chunk; from ldump.c */ /* dump one chunk; from ldump.c */
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
+11 -8
View File
@@ -10,7 +10,6 @@
#include "lprefix.h" #include "lprefix.h"
#include <assert.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.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 ** Decode one UTF-8 sequence, returning NULL if byte sequence is
** invalid. The array 'limits' stores the minimum value for each ** invalid. The array 'limits' stores the minimum value for each
** sequence length, to check for overlong representations. Its first ** 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). ** bytes (count == 0).
*/ */
static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
@@ -55,8 +54,10 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
{~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; {~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
unsigned int c = (unsigned char)s[0]; unsigned int c = (unsigned char)s[0];
l_uint32 res = 0; /* final result */ l_uint32 res = 0; /* final result */
if (c < 0x80) /* ascii? */ if (c < 0x80) /* ASCII? */
res = c; res = c;
else if (c >= 0xfe) /* c >= 1111 1110b ? */
return NULL; /* would need six or more continuation bytes */
else { else {
int count = 0; /* to count number of continuation bytes */ int count = 0; /* to count number of continuation bytes */
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
@@ -65,8 +66,9 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
} }
lua_assert(count <= 5);
res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */ res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */
if (count > 5 || res > MAXUTF || res < limits[count]) if (res > MAXUTF || res < limits[count])
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */ s += count; /* skip continuation bytes read */
} }
@@ -147,7 +149,7 @@ static int codepoint (lua_State *L) {
static void pushutfchar (lua_State *L, int arg) { static void pushutfchar (lua_State *L, int arg) {
lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
lua_pushfstring(L, "%U", (long)code); lua_pushfstring(L, "%U", cast(unsigned long, code));
} }
@@ -215,9 +217,10 @@ static int byteoffset (lua_State *L) {
} }
lua_pushinteger(L, posi + 1); /* initial position */ lua_pushinteger(L, posi + 1); /* initial position */
if ((s[posi] & 0x80) != 0) { /* multi-byte character? */ if ((s[posi] & 0x80) != 0) { /* multi-byte character? */
do { if (iscont(s[posi]))
posi++; return luaL_error(L, "initial position is a continuation byte");
} while (iscontp(s + posi + 1)); /* skip to final byte */ while (iscontp(s + posi + 1))
posi++; /* skip to last continuation byte */
} }
/* else one-byte character: final position is the initial one */ /* else one-byte character: final position is the initial one */
lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */ lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */
+145 -99
View File
@@ -268,9 +268,9 @@ static int forprep (lua_State *L, StkId ra) {
/* /*
** Execute a step of a float numerical for loop, returning ** Execute a step of a float numerical for loop, returning
** true iff the loop must continue. (The integer case is ** true iff the loop must continue. (The integer case is
** written online with opcode OP_FORLOOP, for performance.) ** written inline with opcode OP_FORLOOP, for performance.)
*/ */
static int floatforloop (StkId ra) { static int floatforloop (lua_State *L, StkId ra) {
lua_Number step = fltvalue(s2v(ra + 1)); lua_Number step = fltvalue(s2v(ra + 1));
lua_Number limit = fltvalue(s2v(ra)); lua_Number limit = fltvalue(s2v(ra));
lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */ lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */
@@ -303,7 +303,7 @@ lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key,
else { /* 't' is a table */ else { /* 't' is a table */
tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
if (tm == NULL) { /* no metamethod? */ if (tm == NULL) { /* no metamethod? */
setnilvalue(s2v(val)); /* result is nil */ setnilvalue2s(val); /* result is nil */
return LUA_VNIL; return LUA_VNIL;
} }
/* else will try the metamethod */ /* else will try the metamethod */
@@ -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- ** Compare two strings 'ts1' x 'ts2', returning an integer less-equal-
** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. ** -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; size_t rl2;
const char *s2 = getlstr(ts2, rl2); const char *s2 = getlstr(ts2, rl2);
for (;;) { /* for each segment */ for (;;) { /* for each segment */
int temp = strcoll(s1, s2); int temp = l_strcoll(s1, s2);
if (temp != 0) /* not equal? */ if (temp != 0) /* not equal? */
return temp; /* done */ return temp; /* done */
else { /* strings are equal up to a '\0' */ else { /* strings are equal up to a '\0' */
@@ -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) { int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm; const TValue *tm;
if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ if (ttype(t1) != ttype(t2)) /* not the same type? */
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) return 0;
return 0; /* only numbers can be equal with different variants */ else if (ttypetag(t1) != ttypetag(t2)) {
else { /* two numbers with different variants */ switch (ttypetag(t1)) {
/* One of them is an integer. If the other does not have an case LUA_VNUMINT: { /* integer == float? */
integer value, they cannot be equal; otherwise, compare their /* integer and float can only be equal if float has an integer
integer values. */ value equal to the integer */
lua_Integer i1, i2; lua_Integer i2;
return (luaV_tointegerns(t1, &i1, F2Ieq) && return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) &&
luaV_tointegerns(t2, &i2, F2Ieq) && ivalue(t1) == i2);
i1 == 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 */ else { /* equal variants */
switch (ttypetag(t1)) { switch (ttypetag(t1)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); return 1;
case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); case LUA_VNUMINT:
case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); return (ivalue(t1) == ivalue(t2));
case LUA_VLCF: return fvalue(t1) == fvalue(t2); case LUA_VNUMFLT:
case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); return (fltvalue(t1) == fltvalue(t2));
case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_VUSERDATA: { case LUA_VSHRSTR:
if (uvalue(t1) == uvalue(t2)) return 1; return eqshrstr(tsvalue(t1), tsvalue(t2));
else if (L == NULL) return 0; case LUA_VLNGSTR:
tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); return luaS_eqstr(tsvalue(t1), tsvalue(t2));
if (tm == NULL) case LUA_VUSERDATA: {
tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); if (uvalue(t1) == uvalue(t2)) return 1;
break; /* will try TM */ 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 (tm == NULL) /* no TM? */
if (hvalue(t1) == hvalue(t2)) return 1; return 0; /* objects are different */
else if (L == NULL) return 0; else {
tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
if (tm == NULL) return !tagisfalse(tag);
tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
} }
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) \ #define tostring(L,o) \
(ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) (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) #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0)
/* copy strings in stack from top - n up to top - 1 to buffer */ /* 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. */ setobjs2s(L, top - 2, top - 1); /* result is second op. */
} }
else { else {
/* at least two non-empty string values; get as many as possible */ /* at least two string values; get as many as possible */
size_t tl = tsslen(tsvalue(s2v(top - 1))); size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */
TString *ts; TString *ts;
/* collect total length and number of strings */ /* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
@@ -700,7 +735,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
Table *h = hvalue(rb); Table *h = hvalue(rb);
tm = fasttm(L, h->metatable, TM_LEN); tm = fasttm(L, h->metatable, TM_LEN);
if (tm) break; /* metamethod? break switch to call it */ 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; return;
} }
case LUA_VSHRSTR: { case LUA_VSHRSTR: {
@@ -839,12 +874,6 @@ void luaV_finishOp (lua_State *L) {
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
int res = !l_isfalse(s2v(L->top.p - 1)); int res = !l_isfalse(s2v(L->top.p - 1));
L->top.p--; 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); lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
if (res != GETARG_k(inst)) /* condition failed? */ if (res != GETARG_k(inst)) /* condition failed? */
ci->u.l.savedpc++; /* skip jump instruction */ 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' ** 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. ** operation, 'fop' is the float operation.
*/ */
#define op_arithI(L,iop,fop) { \ #define op_arithI(L,iop,fop) { \
StkId ra = RA(i); \ TValue *ra = vRA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
int imm = GETARG_sC(i); \ int imm = GETARG_sC(i); \
if (ttisinteger(v1)) { \ if (ttisinteger(v1)) { \
lua_Integer iv1 = ivalue(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)) { \ else if (ttisfloat(v1)) { \
lua_Number nb = fltvalue(v1); \ lua_Number nb = fltvalue(v1); \
lua_Number fimm = cast_num(imm); \ 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) { \ #define op_arithf_aux(L,v1,v2,fop) { \
lua_Number n1; lua_Number n2; \ lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
StkId ra = RA(i); \
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ 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. ** Arithmetic operations over floats and others with register operands.
*/ */
#define op_arithf(L,fop) { \ #define op_arithf(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@@ -948,7 +981,6 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations with K operands for floats. ** Arithmetic operations with K operands for floats.
*/ */
#define op_arithfK(L,fop) { \ #define op_arithfK(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@@ -958,8 +990,8 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over integers and floats. ** Arithmetic operations over integers and floats.
*/ */
#define op_arith_aux(L,v1,v2,iop,fop) { \ #define op_arith_aux(L,v1,v2,iop,fop) { \
StkId ra = RA(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \ if (ttisinteger(v1) && ttisinteger(v2)) { \
StkId ra = RA(i); \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
} \ } \
@@ -988,12 +1020,12 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with constant operand. ** Bitwise operations with constant operand.
*/ */
#define op_bitwiseK(L,op) { \ #define op_bitwiseK(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \ TValue *v2 = KC(i); \
lua_Integer i1; \ lua_Integer i1; \
lua_Integer i2 = ivalue(v2); \ lua_Integer i2 = ivalue(v2); \
if (tointegerns(v1, &i1)) { \ if (tointegerns(v1, &i1)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \ pc++; setivalue(s2v(ra), op(i1, i2)); \
}} }}
@@ -1002,11 +1034,11 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with register operands. ** Bitwise operations with register operands.
*/ */
#define op_bitwise(L,op) { \ #define op_bitwise(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
lua_Integer i1; lua_Integer i2; \ lua_Integer i1; lua_Integer i2; \
if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \ pc++; setivalue(s2v(ra), op(i1, i2)); \
}} }}
@@ -1017,18 +1049,18 @@ void luaV_finishOp (lua_State *L) {
** integers. ** integers.
*/ */
#define op_order(L,opi,opn,other) { \ #define op_order(L,opi,opn,other) { \
StkId ra = RA(i); \ TValue *ra = vRA(i); \
int cond; \ int cond; \
TValue *rb = vRB(i); \ TValue *rb = vRB(i); \
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ if (ttisinteger(ra) && ttisinteger(rb)) { \
lua_Integer ia = ivalue(s2v(ra)); \ lua_Integer ia = ivalue(ra); \
lua_Integer ib = ivalue(rb); \ lua_Integer ib = ivalue(rb); \
cond = opi(ia, ib); \ cond = opi(ia, ib); \
} \ } \
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ else if (ttisnumber(ra) && ttisnumber(rb)) \
cond = opn(s2v(ra), rb); \ cond = opn(ra, rb); \
else \ else \
Protect(cond = other(L, s2v(ra), rb)); \ Protect(cond = other(L, ra, rb)); \
docondjump(); } docondjump(); }
@@ -1037,19 +1069,19 @@ void luaV_finishOp (lua_State *L) {
** always small enough to have an exact representation as a float.) ** always small enough to have an exact representation as a float.)
*/ */
#define op_orderI(L,opi,opf,inv,tm) { \ #define op_orderI(L,opi,opf,inv,tm) { \
StkId ra = RA(i); \ TValue *ra = vRA(i); \
int cond; \ int cond; \
int im = GETARG_sB(i); \ int im = GETARG_sB(i); \
if (ttisinteger(s2v(ra))) \ if (ttisinteger(ra)) \
cond = opi(ivalue(s2v(ra)), im); \ cond = opi(ivalue(ra), im); \
else if (ttisfloat(s2v(ra))) { \ else if (ttisfloat(ra)) { \
lua_Number fa = fltvalue(s2v(ra)); \ lua_Number fa = fltvalue(ra); \
lua_Number fim = cast_num(im); \ lua_Number fim = cast_num(im); \
cond = opf(fa, fim); \ cond = opf(fa, fim); \
} \ } \
else { \ else { \
int isf = GETARG_C(i); \ 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(); } docondjump(); }
@@ -1068,6 +1100,7 @@ void luaV_finishOp (lua_State *L) {
#define RA(i) (base+GETARG_A(i)) #define RA(i) (base+GETARG_A(i))
#define vRA(i) s2v(RA(i))
#define RB(i) (base+GETARG_B(i)) #define RB(i) (base+GETARG_B(i))
#define vRB(i) s2v(RB(i)) #define vRB(i) s2v(RB(i))
#define KB(i) (k+GETARG_B(i)) #define KB(i) (k+GETARG_B(i))
@@ -1108,14 +1141,14 @@ void luaV_finishOp (lua_State *L) {
/* /*
** Correct global 'pc'. ** 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 ** Whenever code can raise errors, the global 'pc' and the global
** 'top' must be correct to report occasional errors. ** '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)) #define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci))
/* special version that does not change the top */ /* 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 ** 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 */ /* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, (savepc(L), L->top.p = (c)), \ { luaC_condGC(L, (savepc(ci), L->top.p = (c)), \
updatetrap(ci)); \ updatetrap(ci)); \
luai_threadyield(L); } luai_threadyield(L); }
@@ -1450,16 +1483,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwiseK(L, l_bxor); op_bitwiseK(L, l_bxor);
vmbreak; 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) { vmcase(OP_SHLI) {
StkId ra = RA(i); StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
@@ -1470,6 +1493,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
vmbreak; 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) { vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd); op_arith(L, l_addi, luai_numadd);
vmbreak; vmbreak;
@@ -1512,14 +1545,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwise(L, l_bxor); op_bitwise(L, l_bxor);
vmbreak; vmbreak;
} }
vmcase(OP_SHR) {
op_bitwise(L, luaV_shiftr);
vmbreak;
}
vmcase(OP_SHL) { vmcase(OP_SHL) {
op_bitwise(L, luaV_shiftl); op_bitwise(L, luaV_shiftl);
vmbreak; vmbreak;
} }
vmcase(OP_SHR) {
op_bitwise(L, luaV_shiftr);
vmbreak;
}
vmcase(OP_MMBIN) { vmcase(OP_MMBIN) {
StkId ra = RA(i); StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */ 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? */ if (b != 0) /* fixed number of arguments? */
L->top.p = ra + b; /* top signals number of arguments */ L->top.p = ra + b; /* top signals number of arguments */
/* else previous instruction set top */ /* 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) if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */ updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */ else { /* Lua call: run function in this same C frame */
@@ -1808,7 +1841,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
} }
} }
else if (floatforloop(ra)) /* float loop */ else if (floatforloop(L, ra)) /* float loop */
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
updatetrap(ci); /* allows a signal to break the loop */ updatetrap(ci); /* allows a signal to break the loop */
vmbreak; vmbreak;
@@ -1868,7 +1901,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmcase(OP_SETLIST) { vmcase(OP_SETLIST) {
StkId ra = RA(i); StkId ra = RA(i);
unsigned n = cast_uint(GETARG_vB(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)); Table *h = hvalue(s2v(ra));
if (n == 0) if (n == 0)
n = cast_uint(L->top.p - ra) - 1; /* get up to the top */ 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) { vmcase(OP_VARARG) {
StkId ra = RA(i); StkId ra = RA(i);
int n = GETARG_C(i) - 1; /* required results */ int n = GETARG_C(i) - 1; /* required results (-1 means all) */
Protect(luaT_getvarargs(L, ci, ra, n)); 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; vmbreak;
} }
vmcase(OP_VARARGPREP) { 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 */ if (l_unlikely(trap)) { /* previous "Protect" updated trap */
luaD_hookcall(L, ci); luaD_hookcall(L, ci);
L->oldpc = 1; /* next opcode will be seen as a "new" line */ L->oldpc = 1; /* next opcode will be seen as a "new" line */
+11 -7
View File
@@ -15,9 +15,9 @@ CWARNSCPP= \
-Wdouble-promotion \ -Wdouble-promotion \
-Wmissing-declarations \ -Wmissing-declarations \
-Wconversion \ -Wconversion \
-Wstrict-overflow=2 \
# the next warnings might be useful sometimes, # the next warnings might be useful sometimes,
# but usually they generate too much noise # but usually they generate too much noise
# -Wstrict-overflow=2 \
# -Werror \ # -Werror \
# -pedantic # warns if we use jump tables \ # -pedantic # warns if we use jump tables \
# -Wformat=2 \ # -Wformat=2 \
@@ -60,10 +60,13 @@ CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
# create problems; some are only available in newer gcc versions. To # create problems; some are only available in newer gcc versions. To
# use some of them, we also have to define an environment variable # use some of them, we also have to define an environment variable
# ASAN_OPTIONS="detect_invalid_pointer_pairs=2". # ASAN_OPTIONS="detect_invalid_pointer_pairs=2".
# -fsanitize=undefined # -fsanitize=undefined (you may need to add "-lubsan" to libs)
# -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare # -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare
# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g
# Test mode: Add test library, turn on asserts, redefine several
# constants ("to give some bugs a chance"), track memory use, and add
# debug information.
# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g
LOCAL = $(TESTS) $(CWARNS) LOCAL = $(TESTS) $(CWARNS)
@@ -71,13 +74,15 @@ LOCAL = $(TESTS) $(CWARNS)
# To enable Linux goodies, -DLUA_USE_LINUX # To enable Linux goodies, -DLUA_USE_LINUX
# For C89, "-std=c89 -DLUA_USE_C89" # For C89, "-std=c89 -DLUA_USE_C89"
# Note that Linux/Posix options are not compatible with C89 # Note that Linux/Posix options are not compatible with C89
# (For 32-bit, add option "-m32" to MYCFLAGS and MYLDFLAGS.)
MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX
MYLDFLAGS= $(LOCAL) -Wl,-E MYLDFLAGS= -Wl,-E
MYLIBS= -ldl MYLIBS= -ldl
CC= gcc CC= gcc
CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -march=native # (Optionally we can use -march=native -mno-avx512f.)
CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common
AR= ar rc AR= ar rc
RANLIB= ranlib RANLIB= ranlib
RM= rm -f RM= rm -f
@@ -166,8 +171,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 \ 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 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 \ 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 \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
ltable.h
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.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 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 \ llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
+2 -2
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env lua5.3 #!/usr/bin/env lua
-- special marks: -- special marks:
@@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<p> <p>
<small> <small>
<a href="http://www.lua.org/copyright.html">Copyright</a> <a href="http://www.lua.org/copyright.html">Copyright</a>
&copy; 2025 Lua.org, PUC-Rio. All rights reserved. &copy; 2026 Lua.org, PUC-Rio. All rights reserved.
</small> </small>
<hr> <hr>
+267 -154
View File
@@ -107,7 +107,7 @@ for small machines and embedded systems.
Unless stated otherwise, Unless stated otherwise,
any overflow when manipulating integer values @def{wrap around}, 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, (In other words,
the actual result is the unique representable integer the actual result is the unique representable integer
that is equal modulo @M{2@sp{n}} to the mathematical result, 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}). including @x{embedded zeros} (@Char{\0}).
Lua is also encoding-agnostic; Lua is also encoding-agnostic;
it makes no assumptions about the contents of a string. 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 Lua can call (and manipulate) functions written in Lua and
functions written in C @see{functioncall}. functions written in C @see{functioncall}.
@@ -229,7 +230,7 @@ as the following example illustrates:
@verbatim{ @verbatim{
X = 1 -- Ok, global by default X = 1 -- Ok, global by default
do do
global Y -- voids implicit initial declaration global Y -- voids the implicit initial declaration
Y = 1 -- Ok, Y declared as global Y = 1 -- Ok, Y declared as global
X = 1 -- ERROR, X not declared X = 1 -- ERROR, X not declared
end end
@@ -269,7 +270,7 @@ print(x) --> 10 (the global one)
Notice that, in a declaration like @T{local x = x}, Notice that, in a declaration like @T{local x = x},
the new @id{x} being declared is not in scope yet, 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, Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions 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}} exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
} }
The given identifier (@bnfNter{Name}) defines the control variable, 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. The loop starts by evaluating once the three control expressions.
Their values are called respectively 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 names @rep{var_i} declare loop variables local to the loop body.
The first of these variables is the @emph{control variable}, 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} The loop starts by evaluating @rep{explist}
to produce four values: to produce four values:
@@ -1651,15 +1653,22 @@ Function calls are explained in @See{functioncall}.
@sect3{localvar| @title{Variable Declarations} @sect3{localvar| @title{Variable Declarations}
Local and global variables can be declared anywhere inside a block. 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{ @Produc{
@producname{stat}@producbody{@Rw{local} @producname{stat}@producbody{@Rw{local}
attnamelist @bnfopt{@bnfter{=} explist}} 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}. 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 The list of names may be prefixed by an attribute
(a name between angle brackets) (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. (e.g., @T{global none}) can only refer to declared variables.
Note that, for global 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{ @verbatim{
global X <const>, _G global X <const>, _G
X = 1 -- ERROR X = 1 -- ERROR
@@ -2081,12 +2091,12 @@ Note that keys that are not positive integers
do not interfere with borders. do not interfere with borders.
A table with exactly one border is called a @def{sequence}. 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). 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. and therefore it is not a sequence.
(The @nil at index 4 is called a @emphx{hole}.) (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), has three borders (0, 3, and 6),
so it is not a sequence, too. so it is not a sequence, too.
The table @T{{}} is a sequence with border 0. The table @T{{}} is a sequence with border 0.
@@ -2211,7 +2221,7 @@ The form
} }
can be used to emulate methods. can be used to emulate methods.
A call @T{v:name(@rep{args})} 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. except that @id{v} is evaluated only once.
Arguments have the following syntax: Arguments have the following syntax:
@@ -2258,7 +2268,7 @@ return x or f(x) -- results adjusted to 1
@sect3{func-def| @title{Function Definitions} @sect3{func-def| @title{Function Definitions}
The syntax for function definition is The syntax for a function definition is
@Produc{ @Produc{
@producname{functiondef}@producbody{@Rw{function} funcbody} @producname{functiondef}@producbody{@Rw{function} funcbody}
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@@ -2308,71 +2318,10 @@ global function f () @rep{body} end
} }
translates to translates to
@verbatim{ @verbatim{
global f; f = function () @rep{body} end global f; global f = function () @rep{body} end
} }
The second @Rw{global} makes the assignment an initialization,
A function definition is an executable expression, which will raise an error if that global is already defined.
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 @emphx{colon} syntax The @emphx{colon} syntax
is used to emulate @def{methods}, 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 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, A vararg table in a variadic function can have an optional name,
and adjustment} 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. Both function calls and vararg expressions can result in multiple values.
These expressions are called @def{multires expressions}. These expressions are called @def{multires expressions}.
@@ -2406,22 +2449,22 @@ These are the places where Lua expects a list of expressions:
@description{ @description{
@item{A @rw{return} statement, @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, @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, @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, @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, @item{A local or global declaration,
for instance @T{local a , b, c = e1, e2, e3} @see{localvar}.} which is similar to a multiple assignment.}
@item{The initial values in a generic @rw{for} loop, @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, 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: must be @emph{adjusted} to a specific length:
the number of parameters in a call to a non-variadic function the number of parameters in a call to a non-variadic function
@see{func-def}, @see{func-def},
the number of variables in a multiple assignment or the number of variables in a multiple assignment or a declaration,
a local declaration,
and exactly four values for a generic @rw{for} loop. and exactly four values for a generic @rw{for} loop.
The @def{adjustment} follows these rules: The @def{adjustment} follows these rules:
If there are more values than needed, 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 to a single variable and using that variable
in its place. 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 In all cases, when the construction needs
@Q{the n-th result} and there is no such result, @Q{the n-th result} and there is no such result,
it uses a @nil. it uses a @nil.
@@ -2648,9 +2690,21 @@ 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*}) Several functions in the API accept pointers (@T{const char*})
to C strings.
Some of there parameters have an associated length (@T{size_t}).
Unless stated otherwise,
when there is an associated length,
the string can contain embedded zeros;
moreover, the pointer can be @id{NULL} if the length is zero.
When there is no associated length,
the pointer must point to a zero-terminated string.
In any case, the string contents should remain unchanged
until the function returns.
Several functions in the API also return pointers (@T{const char*})
to Lua strings in the stack. to Lua strings in the stack.
(See @Lid{lua_pushfstring}, @Lid{lua_pushlstring}, (See @Lid{lua_pushfstring}, @Lid{lua_pushlstring},
@Lid{lua_pushstring}, and @Lid{lua_tolstring}. @Lid{lua_pushstring}, and @Lid{lua_tolstring}.
@@ -2823,7 +2877,16 @@ status codes to indicate different kinds of errors or other conditions:
For such errors, Lua does not call the @x{message handler}. 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 @item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation
or format error in a binary chunk.} or format error in a binary chunk.}
@@ -3004,7 +3067,7 @@ typedef void * (*lua_Alloc) (void *ud,
size_t osize, size_t osize,
size_t nsize);| 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 The allocator function must provide a
functionality similar to @id{realloc}, functionality similar to @id{realloc},
but not exactly the same. but not exactly the same.
@@ -3039,11 +3102,12 @@ the allocator must behave like @id{realloc}.
In particular, the allocator returns @id{NULL} In particular, the allocator returns @id{NULL}
if and only if it cannot fulfill the request. if and only if it cannot fulfill the request.
Here is a simple implementation for the @x{allocator function}. Here is a simple implementation for the @x{allocator function},
It is used in the auxiliary library by @Lid{luaL_newstate}. corresponding to the function @Lid{luaL_alloc} from the
auxiliary library.
@verbatim{ @verbatim{
static void *l_alloc (void *ud, void *ptr, size_t osize, void *luaL_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) { size_t nsize) {
(void)ud; (void)osize; /* not used */ (void)ud; (void)osize; /* not used */
if (nsize == 0) { if (nsize == 0) {
free(ptr); free(ptr);
@@ -3470,7 +3534,7 @@ This function should not be called by a finalizer.
@APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| @APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);|
@apii{0,0,-} @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 If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the
opaque pointer given when the memory-allocator function was set. opaque pointer given when the memory-allocator function was set.
@@ -3588,9 +3652,9 @@ because a pseudo-index is not an actual stack position.
The type of integers in Lua. The type of integers in Lua.
By default this type is @id{long long}, 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} 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}.) (See @id{LUA_INT_TYPE} in @id{luaconf.h}.)
Lua also defines the constants Lua also defines the constants
@@ -3827,7 +3891,7 @@ is a seed for the hashing of strings.
@apii{0,1,m} @apii{0,1,m}
Creates a new empty table and pushes it onto the stack. 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)}.
} }
@@ -3851,8 +3915,12 @@ like any Lua object.
This function creates and pushes on the stack a new full userdata, This function creates and pushes on the stack a new full userdata,
with @id{nuvalue} associated Lua values, called @id{user values}, with @id{nuvalue} associated Lua values, called @id{user values},
plus an associated block of raw memory with @id{size} bytes. plus an associated block of raw memory with @id{size} bytes.
(The user values can be set and read with the functions
@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.) The user values can be set and read with the functions
@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.
The block of memory is suitably aligned for any @N{ISO C} object.
(See macro @id{LUAI_MAXALIGN} in file @id{luaconf.h} for other
alignment requirements.)
The function returns the address of the block of memory. The function returns the address of the block of memory.
Lua ensures that this address is valid as long as Lua ensures that this address is valid as long as
@@ -3894,8 +3962,8 @@ this confuses the next call to @Lid{lua_next}.
This function may raise an error if the given key This function may raise an error if the given key
is neither @nil nor present in the table. is neither @nil nor present in the table.
See function @Lid{next} for the caveats of modifying
the table during its traversal. See function @Lid{next} for more details about the traversal.
} }
@@ -3924,8 +3992,8 @@ This macro may evaluate its arguments more than once.
} }
@APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx, @APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx,
char *buff);| char *buff);|
@apii{0,0,-} @apii{0,0,-}
Converts the number at acceptable index @id{idx} to a string Converts the number at acceptable index @id{idx} to a string
@@ -4050,7 +4118,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);| const char *s, size_t len, lua_Alloc falloc, void *ud);|
@apii{0,1,m} @apii{0,1,m}
@@ -4073,21 +4141,18 @@ the string @id{s} as the block,
the length plus one (to account for the ending zero) as the old size, the length plus one (to account for the ending zero) as the old size,
and 0 as the new 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, Even when using an external buffer,
Lua still has to allocate a header for the string. Lua still has to allocate a header for the string.
In case of a memory-allocation error, In case of a memory-allocation error,
Lua will call @id{falloc} before raising the 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, ...);| @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 Pushes onto the stack a formatted string
and returns a pointer to this string @see{constchar}. and returns a pointer to this string @see{constchar}.
@@ -4107,6 +4172,9 @@ A conversion specifier (and its corresponding extra argument) can be
Every occurrence of @Char{%} in the string @id{fmt} Every occurrence of @Char{%} in the string @id{fmt}
must form a valid conversion specifier. 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);| @APIEntry{void lua_pushglobaltable (lua_State *L);|
@@ -4139,7 +4207,7 @@ light userdata with the same @N{C address}.
} }
@APIEntry{const char *lua_pushliteral (lua_State *L, const char *s);| @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}, This macro is equivalent to @Lid{lua_pushstring},
but should be used only when @id{s} is a literal string. but should be used only when @id{s} is a literal string.
@@ -4148,7 +4216,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);| @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} Pushes the string pointed to by @id{s} with size @id{len}
onto the stack. onto the stack.
@@ -4160,6 +4228,9 @@ including @x{embedded zeros}.
Returns a pointer to the internal copy of the string @see{constchar}. 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);| @APIEntry{void lua_pushnil (lua_State *L);|
@@ -4377,7 +4448,7 @@ Starts and resumes a coroutine in the given thread @id{L}.
To start a coroutine, To start a coroutine,
you push the main function plus any arguments you push the main function plus any arguments
onto the empty stack of the thread. onto the empty stack of the thread.
then you call @Lid{lua_resume}, Then you call @Lid{lua_resume},
with @id{nargs} being the number of arguments. with @id{nargs} being the number of arguments.
The function returns when the coroutine suspends, The function returns when the coroutine suspends,
finishes its execution, or raises an unprotected error. finishes its execution, or raises an unprotected error.
@@ -4557,7 +4628,7 @@ You can resume threads with status @Lid{LUA_OK}
} }
@APIEntry{size_t lua_stringtonumber (lua_State *L, const char *s);| @APIEntry{size_t lua_stringtonumber (lua_State *L, const char *s);|
@apii{0,1,-} @apii{0,0|1,-}
Converts the zero-terminated string @id{s} to a number, Converts the zero-terminated string @id{s} to a number,
pushes that number into the stack, pushes that number into the stack,
@@ -4887,7 +4958,7 @@ Lua calls the given @x{continuation function} @id{k} to continue
the execution of the @N{C function} that yielded @see{continuations}. the execution of the @N{C function} that yielded @see{continuations}.
This continuation function receives the same stack This continuation function receives the same stack
from the previous function, from the previous function,
with the @id{n} results removed and with all the results (@id{nresults}) removed and
replaced by the arguments passed to @Lid{lua_resume}. replaced by the arguments passed to @Lid{lua_resume}.
Moreover, Moreover,
the continuation function receives the value @id{ctx} the continuation function receives the value @id{ctx}
@@ -4977,7 +5048,7 @@ the function was defined in a string where
} }
@item{@id{srclen}| @item{@id{srclen}|
The length of the string @id{source}. the length of the string @id{source}.
} }
@item{@id{short_src}| @item{@id{short_src}|
@@ -5019,8 +5090,8 @@ then @id{name} is set to @id{NULL}.
@item{@id{namewhat}| @item{@id{namewhat}|
explains the @T{name} field. explains the @T{name} field.
The value of @T{namewhat} can be The value of @T{namewhat} can be
@T{"global"}, @T{"local"}, @T{"method"}, @T{"global"}, @T{"local"}, @T{"upvalue"},
@T{"field"}, @T{"upvalue"}, or @T{""} (the empty string), @T{"field"}, @T{""} (the empty string), plus some other options,
according to how the function was called. according to how the function was called.
(Lua uses the empty string when no other option seems to apply.) (Lua uses the empty string when no other option seems to apply.)
} }
@@ -5141,7 +5212,7 @@ running at the given level;
} }
@item{@Char{S}| @item{@Char{S}|
fills in the fields @id{source}, @id{short_src}, fills in the fields @id{source}, @id{srclen}, @id{short_src},
@id{linedefined}, @id{lastlinedefined}, and @id{what}; @id{linedefined}, @id{lastlinedefined}, and @id{what};
} }
@@ -5317,7 +5388,9 @@ Returns @id{NULL} (and pops nothing)
when the index is greater than when the index is greater than
the number of active local variables. the number of active local variables.
Parameters @id{ar} and @id{n} are as in the function @Lid{lua_getlocal}. Parameters @id{ar} and @id{n} are as in the function @Lid{lua_getlocal},
except that @id{ar} cannot be @id{NULL},
as @id{lua_setlocal} only operates on activation records.
} }
@@ -5384,7 +5457,7 @@ the auxiliary library provides higher-level functions for some
common tasks. common tasks.
All functions and types from the auxiliary library 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_}. have a prefix @id{luaL_}.
All functions in the auxiliary library are built on All functions in the auxiliary library are built on
@@ -5528,7 +5601,7 @@ Its pattern of use is as follows:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} @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{ @item{
Then add string pieces to the buffer calling any of Then add string pieces to the buffer calling any of
@@ -5549,12 +5622,12 @@ you can use the buffer like this:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} @item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.}
@item{Then initialize it and preallocate a space of @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{Then produce the string into that space.}
@item{ @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 where @id{sz} is the total size of the resulting string
copied into that space (which may be less than or copied into that space (which may be less than or
equal to the preallocated size). equal to the preallocated size).
@@ -5947,9 +6020,8 @@ it does not run it.
@apii{0,0,-} @apii{0,0,-}
Returns a value with a weak attempt for randomness. Returns a value with a weak attempt for randomness.
(It produces that value based on the current date and time The parameter @id{L} can be @id{NULL}
and the address of an internal variable, if there is no Lua state available.
in case the machine has Address Space Layout Randomization.)
} }
@@ -6005,8 +6077,9 @@ with @id{tname} in the registry.
@apii{0,0,-} @apii{0,0,-}
Creates a new Lua state. Creates a new Lua state.
It calls @Lid{lua_newstate} with an It calls @Lid{lua_newstate} with @Lid{luaL_alloc} as
allocator based on the @N{ISO C} allocation functions 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} and then sets a warning function and a panic function @see{C-error}
that print messages to the standard error output. that print messages to the standard error output.
@@ -6159,7 +6232,7 @@ You should not manually set integer keys in the table
after the first use of @Lid{luaL_ref}. after the first use of @Lid{luaL_ref}.
You can retrieve an object referred by the reference @id{r} 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. The function @Lid{luaL_unref} frees a reference.
If the object on the top of the stack is @nil, If the object on the top of the stack is @nil,
@@ -6230,6 +6303,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{ @APIEntry{
typedef struct luaL_Stream { typedef struct luaL_Stream {
FILE *f; FILE *f;
@@ -6428,7 +6510,7 @@ the host program can call the function @Lid{luaL_openlibs}.
Alternatively, Alternatively,
the host can select which libraries to open, the host can select which libraries to open,
by using @Lid{luaL_openselectedlibs}. 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} @index{lualib.h}
The stand-alone interpreter @id{lua} @see{lua-sa} The stand-alone interpreter @id{lua} @see{lua-sa}
@@ -6575,7 +6657,7 @@ The call always returns the previous value of the parameter.
If the call does not give a new value, If the call does not give a new value,
the value is left unchanged. 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 so, the value returned as the previous value may not be
exactly the last value set. exactly the last value set.
} }
@@ -6589,10 +6671,10 @@ This function should not be called by a finalizer.
} }
@LibEntry{dofile ([filename])| @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, When called without arguments,
@id{dofile} executes the content of the standard input (@id{stdin}). @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 In case of errors, @id{dofile} propagates the error
to its caller. to its caller.
(That is, @id{dofile} does not run in protected mode.) (That is, @id{dofile} does not run in protected mode.)
@@ -6741,19 +6823,18 @@ In particular, you may set existing fields to nil.
@LibEntry{pairs (t)| @LibEntry{pairs (t)|
If @id{t} has a metamethod @idx{__pairs}, 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. results from the call.
Otherwise, 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 so that the construction
@verbatim{ @verbatim{
for k,v in pairs(t) do @rep{body} end for k,v in pairs(t) do @rep{body} end
} }
will iterate over all key@En{}value pairs of table @id{t}. will iterate over all key@En{}value pairs of table @id{t}.
See function @Lid{next} for the caveats of modifying See function @Lid{next} for more details about the traversal.
the table during its traversal.
} }
@@ -6964,7 +7045,7 @@ in case of error
(either the original error that stopped the coroutine or (either the original error that stopped the coroutine or
errors in closing methods), errors in closing methods),
this function returns @false plus the error object; this function returns @false plus the error object;
otherwise ir returns @true. otherwise it returns @true.
} }
@@ -7680,7 +7761,7 @@ If @id{j} is absent, then it is assumed to be equal to @num{-1}
In particular, In particular,
the call @T{string.sub(s,1,j)} returns a prefix of @id{s} the call @T{string.sub(s,1,j)} returns a prefix of @id{s}
with length @id{j}, 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} returns a suffix of @id{s}
with length @id{i}. with length @id{i}.
@@ -8116,7 +8197,7 @@ the function returns @fail.
A negative @id{n} gets characters before position @id{i}. A negative @id{n} gets characters before position @id{i}.
The default for @id{i} is 1 when @id{n} is non-negative The default for @id{i} is 1 when @id{n} is non-negative
and @T{#s + 1} otherwise, 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. @id{n}-th character from the end of the string.
As a special case, As a special case,
@@ -8169,7 +8250,7 @@ the table will have; its default is zero.
Inserts element @id{value} at position @id{pos} in @id{list}, Inserts element @id{value} at position @id{pos} in @id{list},
shifting up the elements 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}, 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 so that a call @T{table.insert(t,x)} inserts @id{x} at the end
of the list @id{t}. of the list @id{t}.
@@ -8207,7 +8288,7 @@ Removes from @id{list} the element at position @id{pos},
returning the value of the removed element. returning the value of the removed element.
When @id{pos} is an integer between 1 and @T{#list}, When @id{pos} is an integer between 1 and @T{#list},
it shifts down the elements 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]}; and erases element @T{list[#list]};
The index @id{pos} can also be 0 when @T{#list} is 0, The index @id{pos} can also be 0 when @T{#list} is 0,
or @T{#list + 1}. or @T{#list + 1}.
@@ -8337,6 +8418,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| @LibEntry{math.huge|
The float value @idx{HUGE_VAL}, The float value @idx{HUGE_VAL},
@@ -8344,6 +8436,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])| @LibEntry{math.log (x [, base])|
Returns the logarithm of @id{x} in the given base. Returns the logarithm of @id{x} in the given base.
@@ -8399,7 +8497,7 @@ Converts the angle @id{x} from degrees to radians.
When called without arguments, When called without arguments,
returns a pseudo-random float with uniform distribution 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}, When called with two integers @id{m} and @id{n},
@id{math.random} returns a pseudo-random integer @id{math.random} returns a pseudo-random integer
with uniform distribution in the range @M{[m, n]}. with uniform distribution in the range @M{[m, n]}.
@@ -9026,6 +9124,7 @@ which automatically removes the file when the program ends.
This library provides This library provides
the functionality of the @link{debugI|debug interface} to Lua programs. the functionality of the @link{debugI|debug interface} to Lua programs.
You should exert care when using this library. You should exert care when using this library.
Several of its functions Several of its functions
violate basic assumptions about Lua code violate basic assumptions about Lua code
@@ -9035,6 +9134,8 @@ that userdata metatables cannot be changed by Lua code;
that Lua programs do not crash) that Lua programs do not crash)
and therefore can compromise otherwise secure code. and therefore can compromise otherwise secure code.
Moreover, some functions in this library may be slow. Moreover, some functions in this library may be slow.
It is good practice to always require this library explicitly
before using it.
All functions in this library are provided All functions in this library are provided
inside the @defid{debug} table. inside the @defid{debug} table.
@@ -9120,6 +9221,10 @@ Compile-time constants may not appear in this listing,
if they were optimized away by the compiler. if they were optimized away by the compiler.
Negative indices refer to vararg arguments; Negative indices refer to vararg arguments;
@num{-1} is the first vararg argument. @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 The function returns @fail
if there is no variable with the given index, if there is no variable with the given index,
and raises an error when called with a level out of range. and raises an error when called with a level out of range.
@@ -9509,12 +9614,18 @@ change between versions.
@item{ @item{
The word @Rw{global} is a reserved word. The word @Rw{global} is a reserved word.
Do not use it as a regular name. Do not use it as a regular name.
The compilation option @id{LUA_COMPAT_GLOBAL} (see @id{luaconf.h})
makes @id{global} a regular word.
} }
@item{ @item{
The control variable in @Rw{for} loops is read only. The control variable in @Rw{for} loops is read only.
If you need to change it, If you need to change it,
declare a local variable with the same name in the loop body. declare a local variable with the same name in the loop body.
The compilation option @id{LUA_COMPAT_LOOPVAR} (see @id{luaconf.h})
makes these variables regular (writable).
} }
@item{ @item{
@@ -9640,7 +9751,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist} @OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{global} attnamelist @OrNL @Rw{global} attnamelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{global} @bnfopt{attrib} @bnfter{*} @OrNL @Rw{global} @bnfopt{attrib} @bnfter{*}
} }
@@ -9702,8 +9813,10 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or
@Or @bnfter{...}} varargparam}
@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}}
@producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} @producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}}
+20 -5
View File
@@ -5,10 +5,14 @@
** **
** $ gcc -O2 -std=c99 -o lua onelua.c -lm ** $ 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 ** $ 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 */ /* default is to build the full interpreter */
@@ -30,7 +34,15 @@
#define LUA_USE_LINUX #define LUA_USE_LINUX
#define LUA_USE_MACOSX #define LUA_USE_MACOSX
#define LUA_USE_POSIX #define LUA_USE_POSIX
#define LUA_ANSI #endif
/*
** Other specific features
*/
#if 0
#define LUA_32BITS
#define LUA_USE_C89
#endif #endif
@@ -54,12 +66,10 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
/* setup for luaconf.h */ /* setup for luaconf.h */
#define LUA_CORE #define LUA_CORE
#define LUA_LIB #define LUA_LIB
#define ltable_c
#define lvm_c
#include "luaconf.h" #include "luaconf.h"
/* do not export internal symbols */ /* do not export internal symbols */
@@ -110,6 +120,11 @@
#include "linit.c" #include "linit.c"
#endif #endif
/* test library -- used only for internal development */
#if defined(LUA_DEBUG)
#include "ltests.c"
#endif
/* lua */ /* lua */
#ifdef MAKE_LUA #ifdef MAKE_LUA
#include "lua.c" #include "lua.c"
+2 -1
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 LE 9 1, return 1"))
assert(not T.testC("compare EQ 9 9, 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), local a1,a3,a4 = setmetatable({1}, b),
setmetatable({3}, b), setmetatable({3}, b),
setmetatable({4}, b) setmetatable({4}, b)
+22 -2
View File
@@ -308,11 +308,11 @@ else
_ENV.x, _ENV.y = nil _ENV.x, _ENV.y = nil
end end
_ENV = _G _ENV = _G
-- testing preload -- testing preload
do do
local p = package local p = package
package = {} package = {}
@@ -331,6 +331,26 @@ do
assert(type(package.path) == "string") assert(type(package.path) == "string")
end 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('+') print('+')
end --] end --]
@@ -447,7 +467,7 @@ do
end end
-- test of large float/integer indices -- test of large float/integer indices
-- compute maximum integer where all bits fit in a float -- compute maximum integer where all bits fit in a float
local maxint = math.maxinteger local maxint = math.maxinteger
+1 -1
View File
@@ -4,7 +4,7 @@ local strsub = string.sub
local print = print local print = print
_ENV = nil global none
-- Try to convert a value to an integer, without assuming any coercion. -- Try to convert a value to an integer, without assuming any coercion.
local function toint (x) local function toint (x)
+28 -2
View File
@@ -24,7 +24,7 @@ assert(not pcall(type))
-- testing local-function recursion -- testing local-function recursion
global fact; fact = false global fact = false
do do
local res = 1 local res = 1
local function fact (n) local function fact (n)
@@ -65,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+') 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 function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f( -- this line change must be valid f( -- this line change must be valid
@@ -372,6 +372,32 @@ do -- another bug (in 5.4.0)
end end
if T then
-- check stack level when calling reader function
local function get (str)
local pos = 0
local level = nil
return function ()
pos = pos + 1
local c = string.sub(str, pos, pos)
local newlevel = T.stacklevel()
if not level then
level = newlevel
else
assert(level == newlevel)
end
return #c > 0 and c or nil
end
end
local str = "local function foo () end; return 121"
assert(assert(load(get(str)))() == 121)
str = string.dump(load(str))
assert(assert(load(get(str)))() == 121)
end
x = string.dump(load("x = 1; return x")) x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b")) a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1) assert(a() == 1 and _G.x == 1)
+3 -1
View File
@@ -702,7 +702,9 @@ else
assert(t.currentline == t.linedefined + 2) assert(t.currentline == t.linedefined + 2)
assert(not debug.getinfo(c, 1)) -- no other level assert(not debug.getinfo(c, 1)) -- no other level
assert(coroutine.resume(c)) -- run next line assert(coroutine.resume(c)) -- run next line
local n,v = debug.getlocal(c, 0, 2) -- check next local 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) assert(n == "b" and v == 10)
v = {coroutine.resume(c)} -- finish coroutine v = {coroutine.resume(c)} -- finish coroutine
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
+8 -4
View File
@@ -356,8 +356,8 @@ function f(a,b)
global assert, g, string global assert, g, string
local _, y = debug.getlocal(1, 2) local _, y = debug.getlocal(1, 2)
assert(x == a and y == b) assert(x == a and y == b)
assert(debug.setlocal(2, 3, "pera") == "AA".."AA") assert(debug.setlocal(2, 4, "pera") == "AA".."AA")
assert(debug.setlocal(2, 4, "manga") == "B") assert(debug.setlocal(2, 5, "manga") == "B")
x = debug.getinfo(2) x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
@@ -392,7 +392,7 @@ function g (...)
global * global *
local B = 13 local B = 13
global<const> assert global<const> assert
local x,y = debug.getlocal(1,5) local x,y = debug.getlocal(1,6)
assert(x == 'B' and y == 13) assert(x == 'B' and y == 13)
end end
end end
@@ -458,7 +458,8 @@ local function collectlocals (level)
local tab = {} local tab = {}
for i = 1, math.huge do for i = 1, math.huge do
local n, v = debug.getlocal(level + 1, i) 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 break -- consider only real variables
end end
tab[n] = v 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") t = debug.getinfo(function (a,b,...) return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) 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 t = debug.getinfo(1) -- main
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
debug.getupvalue(t.func, 1) == "_ENV") debug.getupvalue(t.func, 1) == "_ENV")
+57 -29
View File
@@ -159,6 +159,9 @@ assert(not string.find(doit"aaa={13}; local bbbb=1; aaa[bbbb](3)", "'bbbb'"))
checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number") checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number")
checkmessage("aaa=(1)..{}", "a table value") checkmessage("aaa=(1)..{}", "a table value")
checkmessage("local function foo (...t) return t.xx + 1 end; foo()",
"field 'xx'")
-- bug in 5.4.6 -- bug in 5.4.6
checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'") checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'")
@@ -418,28 +421,28 @@ end
-- testing line error -- testing line error
local function lineerror (s, l) local function lineerror (s, l, w)
local err,msg = pcall(load(s)) local err,msg = pcall(load(s))
local line = tonumber(string.match(msg, ":(%d+):")) 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 end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2) 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) 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) 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) lineerror("function a.x.y ()\na=a+1\nend", 1, "index")
lineerror("a = \na\n+\n{}", 3) lineerror("a = \na\n+\n{}", 3, "arithmetic")
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6) lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6, "arithmetic")
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3) 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([[ lineerror([[
a a
( -- << ( -- <<
23) 23)
]], 2) ]], 2, "call")
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}
@@ -449,7 +452,7 @@ x
( -- << ( -- <<
23 23
) )
]], 5) ]], 5, "call")
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}
@@ -459,17 +462,17 @@ x
( (
23 + a 23 + a
) )
]], 6) ]], 6, "arithmetic")
local p = [[ local p = [[
function g() f() end function g() f() end
function f(x) error('a', XX) end function f(x) error('a', XX) end
g() g()
]] ]]
XX=3;lineerror((p), 3) XX=3;lineerror((p), 3, "a")
XX=0;lineerror((p), false) XX=0;lineerror((p), false, "a")
XX=1;lineerror((p), 2) XX=1;lineerror((p), 2, "a")
XX=2;lineerror((p), 1) XX=2;lineerror((p), 1, "a")
_G.XX, _G.g, _G.f = nil _G.XX, _G.g, _G.f = nil
@@ -477,7 +480,7 @@ lineerror([[
local b = false local b = false
if not b then if not b then
error 'test' error 'test'
end]], 3) end]], 3, "test")
lineerror([[ lineerror([[
local b = false local b = false
@@ -487,7 +490,7 @@ if not b then
error 'test' error 'test'
end end
end end
end]], 5) end]], 5, "test")
lineerror([[ lineerror([[
_ENV = 1 _ENV = 1
@@ -495,7 +498,7 @@ global function foo ()
local a = 10 local a = 10
return a return a
end end
]], 2) ]], 2, "index")
-- bug in 5.4.0 -- bug in 5.4.0
@@ -503,17 +506,37 @@ lineerror([[
local a = 0 local a = 0
local b = 1 local b = 1
local c = b % a local c = b % a
]], 3) ]], 3, "perform")
do do
-- Force a negative estimate for base line. Error in instruction 2 -- Force a negative estimate for base line. Error in instruction 2
-- (after VARARGPREP, GETGLOBAL), with first absolute line information -- (after VARARGPREP, GETGLOBAL), with first absolute line information
-- (forced by too many lines) in instruction 0. -- (forced by too many lines) in instruction 0.
local s = string.format("%s return __A.x", string.rep("\n", 300)) local s = string.format("%s return __A.x", string.rep("\n", 300))
lineerror(s, 301) lineerror(s, 301, "index")
end 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 if not _soft then
-- several tests that exhaust the Lua stack -- several tests that exhaust the Lua stack
collectgarbage() collectgarbage()
@@ -689,21 +712,26 @@ end
-- testing syntax limits -- testing syntax limits
local function testrep (init, rep, close, repc, finalresult) local function testrep (init, rep, close, repc, finalresult)
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100) local function gencode (n)
local res, msg = load(s) return init .. string.rep(rep, n) .. close .. string.rep(repc, n)
assert(res) -- 100 levels is OK end
local res, msg = load(gencode(100)) -- 100 levels is OK
assert(res)
if (finalresult) then if (finalresult) then
assert(res() == finalresult) assert(res() == finalresult)
end end
s = init .. string.rep(rep, 500) local res, msg = load(gencode(500)) -- 500 levels not ok
local res, msg = load(s) -- 500 levels not ok
assert(not res and (string.find(msg, "too many") or assert(not res and (string.find(msg, "too many") or
string.find(msg, "overflow"))) string.find(msg, "overflow")))
end 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", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}") testrep("local a; a=", "{", "0", "}") -- constructors
testrep("return ", "(", "2", ")", 2) 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("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
testrep("", "do ", "", " end") testrep("", "do ", "", " end")
testrep("", "while a do ", "", " end") testrep("", "while a do ", "", " end")
+2 -2
View File
@@ -715,7 +715,7 @@ do
end end
if T and T.nonblock then if T and T.nonblock and not _port then
print("testing failed write") print("testing failed write")
-- unable to write anything to /dev/full -- unable to write anything to /dev/full
@@ -840,7 +840,7 @@ assert(os.date("!\0\0") == "\0\0")
local x = string.rep("a", 10000) local x = string.rep("a", 10000)
assert(os.date(x) == x) assert(os.date(x) == x)
local t = os.time() 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) == assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000)) string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
+42
View File
@@ -707,4 +707,46 @@ end
collectgarbage(oldmode) 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') print('OK')
+14
View File
@@ -176,6 +176,20 @@ do print"testing stop-the-world collection"
assert(collectgarbage("param", "stepsize") == step) assert(collectgarbage("param", "stepsize") == step)
end 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) collectgarbage(oldmode)
print('OK') print('OK')
+63 -1
View File
@@ -293,6 +293,7 @@ end
foo() foo()
-------------------------------------------------------------------------- --------------------------------------------------------------------------
-- check for compilation errors
local function checkerr (code, err) local function checkerr (code, err)
local st, msg = load(code) local st, msg = load(code)
assert(not st and string.find(msg, err)) assert(not st and string.find(msg, err))
@@ -380,7 +381,7 @@ do
global * global *
Y = x + Y Y = x + Y
assert(_ENV.Y == 20) assert(_ENV.Y == 20)
Y = nil
end end
@@ -411,5 +412,66 @@ do -- mixing lots of global/local declarations
_ENV.x200 = nil _ENV.x200 = nil
end 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' print'OK'
+10 -8
View File
@@ -1,6 +1,8 @@
-- $Id: testes/heavy.lua,v $ -- $Id: testes/heavy.lua,v $
-- See Copyright Notice in file lua.h -- See Copyright Notice in file lua.h
global <const> *
local function teststring () local function teststring ()
print("creating a string too long") print("creating a string too long")
do do
@@ -47,9 +49,9 @@ local function loadrep (x, what)
end end
function controlstruct () local function controlstruct ()
print("control structure too long") 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) local s = string.rep("a = a + 1\n", lim)
s = "while true do " .. s .. "end" s = "while true do " .. s .. "end"
assert(load(s)) assert(load(s))
@@ -63,7 +65,7 @@ function controlstruct ()
end end
function manylines () local function manylines ()
print("loading chunk with too many lines") print("loading chunk with too many lines")
local st, msg = loadrep("\n", "lines") local st, msg = loadrep("\n", "lines")
assert(not st and string.find(msg, "too many lines")) assert(not st and string.find(msg, "too many lines"))
@@ -71,7 +73,7 @@ function manylines ()
end end
function hugeid () local function hugeid ()
print("loading chunk with huge identifier") print("loading chunk with huge identifier")
local st, msg = loadrep("a", "chars") local st, msg = loadrep("a", "chars")
assert(not st and assert(not st and
@@ -80,7 +82,7 @@ function hugeid ()
print('+') print('+')
end end
function toomanyinst () local function toomanyinst ()
print("loading chunk with too many instructions") print("loading chunk with too many instructions")
local st, msg = loadrep("a = 10; ", "instructions") local st, msg = loadrep("a = 10; ", "instructions")
print('+') print('+')
@@ -107,7 +109,7 @@ local function loadrepfunc (prefix, f)
end end
function toomanyconst () local function toomanyconst ()
print("loading function with too many constants") print("loading function with too many constants")
loadrepfunc("function foo () return {0,", loadrepfunc("function foo () return {0,",
function (n) function (n)
@@ -126,7 +128,7 @@ function toomanyconst ()
end end
function toomanystr () local function toomanystr ()
local a = {} local a = {}
local st, msg = pcall(function () local st, msg = pcall(function ()
for i = 1, math.huge do for i = 1, math.huge do
@@ -144,7 +146,7 @@ function toomanystr ()
end end
function toomanyidx () local function toomanyidx ()
local a = {} local a = {}
local st, msg = pcall(function () local st, msg = pcall(function ()
for i = 1, math.huge do for i = 1, math.huge do
+1 -1
View File
@@ -1,7 +1,7 @@
#include "lua.h" #include "lua.h"
/* function from lib1.c */ /* 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) { LUAMOD_API int luaopen_lib11 (lua_State *L) {
return lib1_export(L); return lib1_export(L);
+51
View File
@@ -1,3 +1,7 @@
/* implementation for lib2-v2 */
#include <string.h>
#include "lua.h" #include "lua.h"
#include "lauxlib.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[] = { static const struct luaL_Reg funcs[] = {
{"id", id}, {"id", id},
{"newstr", newstr},
{NULL, NULL} {NULL, NULL}
}; };
@@ -18,6 +68,7 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) {
lua_settop(L, 2); lua_settop(L, 2);
lua_setglobal(L, "y"); /* y gets 2nd parameter */ lua_setglobal(L, "y"); /* y gets 2nd parameter */
lua_setglobal(L, "x"); /* x gets 1st parameter */ lua_setglobal(L, "x"); /* x gets 1st parameter */
initstr(L);
luaL_newlib(L, funcs); luaL_newlib(L, funcs);
return 1; return 1;
} }
+1 -1
View File
@@ -5,7 +5,7 @@ LUA_DIR = ../../
CC = gcc CC = gcc
# compilation should generate Dynamic-Link Libraries # 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 # libraries used by the tests
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
+2 -4
View File
@@ -310,8 +310,7 @@ do -- testing presence of second argument
local function foo (howtoclose, obj, n) local function foo (howtoclose, obj, n)
local ca -- copy of 'a' visible inside its close metamethod local ca -- copy of 'a' visible inside its close metamethod
do do
local a <close> = func2close(function (...) local a <close> = func2close(function (...t)
local t = table.pack(...)
assert(select("#", ...) == n) assert(select("#", ...) == n)
assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) 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" ca = 15 -- final value to be returned if howtoclose=="scope"
@@ -911,8 +910,7 @@ do
local extrares -- result from extra yield (if any) local extrares -- result from extra yield (if any)
local function check (body, extra, ...) local function check (body, extra, ...t)
local t = table.pack(...) -- expected returns
local co = coroutine.wrap(body) local co = coroutine.wrap(body)
if extra then if extra then
extrares = co() -- runs until first (extra) yield extrares = co() -- runs until first (extra) yield
+27 -5
View File
@@ -78,6 +78,9 @@ end
RUN('lua -v') RUN('lua -v')
RUN('lua -v > %s', out)
local release = string.match(getoutput(), "Lua (%d+%.%d+%.%d+)")
print(string.format("(temporary program file used in these tests: %s)", prog)) print(string.format("(temporary program file used in these tests: %s)", prog))
-- running stdin as a file -- running stdin as a file
@@ -90,7 +93,7 @@ prepfile[[
1, a 1, a
) )
]] ]]
RUN('lua - < %s > %s', prog, out) RUN('lua - -- < %s > %s', prog, out)
checkout("1\tnil\n") checkout("1\tnil\n")
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out) RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
@@ -133,7 +136,7 @@ checkout("-h\n")
prepfile("print(package.path)") prepfile("print(package.path)")
-- test LUA_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") checkout("x\n")
-- test LUA_PATH_version -- test LUA_PATH_version
@@ -167,7 +170,9 @@ checkout("10\n11\n")
-- test errors in LUA_INIT -- test errors in LUA_INIT
NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua') NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
-- test option '-E'
print("testing option '-E'")
local defaultpath, defaultCpath local defaultpath, defaultCpath
do do
@@ -192,6 +197,22 @@ assert(not string.find(defaultpath, "xxx") and
string.find(defaultCpath, "lua")) string.find(defaultCpath, "lua"))
-- (LUA_READLINELIB was introduced in 5.5.1)
if release >= "5.5.1" then
print"testing readline library name"
-- should generate a warning when trying to load inexistent library "xuxu"
local env = [[LUA_READLINELIB=xuxu LUA_INIT="warn('@allow')"]]
local code = 'echo " " | env %s lua %s -W -i >%s 2>&1'
RUN(code, env, "", out) -- run code with no extra options
assert(string.find(getoutput(),
"warning: unable to load readline library 'xuxu'"))
RUN(code, env, "-E", out) -- run again with option -E
-- no warning when LUA_READLINELIB is to be ignored
assert(not string.find(getoutput(), "warning"))
end
-- test replacement of ';;' to default path -- test replacement of ';;' to default path
local function convert (p) local function convert (p)
prepfile("print(package.path)") prepfile("print(package.path)")
@@ -358,7 +379,7 @@ RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("6\n10\n10\n\n") checkprogout("6\n10\n10\n\n")
prepfile("a = [[b\nc\nd\ne]]\na") 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") checkprogout("b\nc\nd\ne\n\n")
-- input interrupted in continuation line -- input interrupted in continuation line
@@ -488,12 +509,13 @@ assert(not os.remove(out))
-- invalid options -- invalid options
NoRun("unrecognized option '-h'", "lua -h") NoRun("unrecognized option '-h'", "lua -h")
NoRun("unrecognized option '---'", "lua ---") 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 '-vv'", "lua -vv")
NoRun("unrecognized option '-iv'", "lua -iv") NoRun("unrecognized option '-iv'", "lua -iv")
NoRun("'-e' needs argument", "lua -e") NoRun("'-e' needs argument", "lua -e")
NoRun("syntax error", "lua -e a") NoRun("syntax error", "lua -e a")
NoRun("'-l' needs argument", "lua -l") NoRun("'-l' needs argument", "lua -l")
NoRun("-i", "lua -- -i") -- handles -i as a script name
if T then -- test library? if T then -- test library?
+12
View File
@@ -685,6 +685,18 @@ assert(eq(math.exp(0), 1))
assert(eq(math.sin(10), math.sin(10%(2*math.pi)))) 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.3e-2 ') == 1.3e-2)
assert(tonumber(' -1.00000000000001 ') == -1.00000000000001) assert(tonumber(' -1.00000000000001 ') == -1.00000000000001)
+54 -11
View File
@@ -42,8 +42,12 @@ checkerr(MEMERRMSG, f)
T.alloccount() -- remove limit 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, -- 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 -- task, until there is enough memory to complete the task without
-- errors. -- errors.
local function testbytes (s, f) local function testbytes (s, f)
@@ -53,6 +57,7 @@ local function testbytes (s, f)
local a,b = nil local a,b = nil
while true do while true do
collectgarbage(); collectgarbage() collectgarbage(); collectgarbage()
deep(4)
T.totalmem(M) T.totalmem(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.totalmem(0) -- remove limit T.totalmem(0) -- remove limit
@@ -77,6 +82,7 @@ local function testalloc (s, f)
local a,b = nil local a,b = nil
while true do while true do
collectgarbage(); collectgarbage() collectgarbage(); collectgarbage()
deep(4)
T.alloccount(M) T.alloccount(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.alloccount() -- remove limit T.alloccount() -- remove limit
@@ -87,21 +93,19 @@ local function testalloc (s, f)
M = M + 1 -- increase allocation limit M = M + 1 -- increase allocation limit
end end
print(string.format("minimum allocations for %s: %d allocations", s, M)) print(string.format("minimum allocations for %s: %d allocations", s, M))
return a return M
end end
local function testamem (s, f) local function testamem (s, f)
testalloc(s, f) local aloc = testalloc(s, f)
return testbytes(s, f) local res = testbytes(s, f)
return {aloc = aloc, res = res}
end end
-- doing nothing local b = testamem("function call", function () return 10 end)
b = testamem("doing nothing", function () return 10 end) assert(b.res == 10 and b.aloc == 0)
assert(b == 10)
-- testing memory errors when creating a new state
testamem("state creation", function () testamem("state creation", function ()
local st = T.newstate() local st = T.newstate()
@@ -121,6 +125,18 @@ testamem("coroutine creation", function()
return coroutine.create(print) return coroutine.create(print)
end) 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 -- testing to-be-closed variables
testamem("to-be-closed variables", function() testamem("to-be-closed variables", function()
@@ -150,9 +166,9 @@ local function expand (n,s)
e, s, expand(n-1,s), e) e, s, expand(n-1,s), e)
end end
G=0; collectgarbage(); a =collectgarbage("count") G=0; collectgarbage()
load(expand(20,"G=G+1"))() load(expand(20,"G=G+1"))()
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1) assert(G==20); collectgarbage()
G = nil G = nil
testamem("running code on new thread", function () testamem("running code on new thread", function ()
@@ -160,6 +176,14 @@ testamem("running code on new thread", function ()
end) 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 -- testing memory x compiler
testamem("loadstring", function () testamem("loadstring", function ()
@@ -258,6 +282,25 @@ testamem("growing stack", function ()
return foo(100) return foo(100)
end) 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)
-- }================================================================== -- }==================================================================
+18 -1
View File
@@ -345,6 +345,18 @@ do
end end
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 = {} local nofind = {}
@@ -893,13 +905,18 @@ local function foo1 (e,i)
if i <= e.n then return i,a[i] end if i <= e.n then return i,a[i] end
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 local i = 0
for k,v in pairs(a) do for k,v in pairs(a) do
i = i + 1 i = i + 1
assert(k == i and v == k+1) assert(k == i and v == k+1)
end end
assert(closed) -- 'tbc' has been closed
a.n = 5 a.n = 5
a[3] = 30 a[3] = 30
+10
View File
@@ -347,6 +347,16 @@ do -- init parameter in gmatch
end end
do -- bug since 5.3
local N = 20000
local iter = string.gmatch(string.rep("a", N), string.rep("a?", N))
pcall(iter) -- error for pattern too complex
-- calling function again found recursion count ('matchdepth') equal
-- to -1, so it did not detect next C-stack overflow
pcall(iter)
end
-- tests for `%f' (`frontiers') -- tests for `%f' (`frontiers')
assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x") assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x")
+13
View File
@@ -72,6 +72,19 @@ assert(a==1 and x==nil)
a,x = unpack({1,2}, 1, 1) a,x = unpack({1,2}, 1, 1)
assert(a==1 and x==nil) assert(a==1 and x==nil)
do -- unpack with non-tables
local debug = require"debug"
local oldmt = debug.getmetatable(0)
local str = "hello"
debug.setmetatable(0,
{ __len = function () return #str end,
__index = function (_, i) return string.sub(str, i, i) end})
assert(table.concat({table.unpack(0)}) == str)
debug.setmetatable(0, oldmt) -- restore original metatable for numbers
end
do do
local maxi = (1 << 31) - 1 -- maximum value for an int (usually) local maxi = (1 << 31) - 1 -- maximum value for an int (usually)
local mini = -(1 << 31) -- minimum value for an int (usually) local mini = -(1 << 31) -- minimum value for an int (usually)
+17
View File
@@ -540,6 +540,23 @@ else
assert(y == x) assert(y == x)
local z = T.externstr(x) -- external allocated long string local z = T.externstr(x) -- external allocated long string
assert(z == y) 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 end
print('OK') print('OK')
+8 -3
View File
@@ -1,12 +1,17 @@
-- track collections -- track collections
local M = {} local M = {}
-- import list -- import list
local setmetatable, stderr, collectgarbage = local stderr, collectgarbage = io.stderr, collectgarbage
setmetatable, 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 local active = false
+22 -1
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, "𦧺", 1, 2) checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "\x80", 1) checkerror("continuation byte", utf8.offset, "\x80", 1)
checkerror("continuation byte", utf8.offset, "\x9c", -1)
-- error in indices for len -- error in indices for len
checkerror("out of bounds", utf8.len, "abc", 0, 2) checkerror("out of bounds", utf8.len, "abc", 0, 2)
checkerror("out of bounds", utf8.len, "abc", 1, 4) 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 s = "hello World"
local t = {string.byte(s, 1, -1)} local t = {string.byte(s, 1, -1)}
@@ -229,10 +238,18 @@ s = "\0 \x7F\z
s = string.gsub(s, " ", "") s = string.gsub(s, " ", "")
check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF}) check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF})
-- again, without strictness
s = "\xF0\x90\x80\x80 \xF7\xBF\xBF\xBF\z
\xF8\x88\x80\x80\x80 \xFB\xBF\xBF\xBF\xBF\z
\xFC\x84\x80\x80\x80\x80 \xFD\xBF\xBF\xBF\xBF\xBF"
s = string.gsub(s, " ", "")
check(s, {0x10000,0x1FFFFF, 0x200000,0x3FFFFFF, 0x4000000,0x7FFFFFFF}, true)
do do
-- original UTF-8 values -- original UTF-8 values
local s = "\u{4000000}\u{7FFFFFFF}" local s = "\u{4000000}\u{7FFFFFFF}"
assert(#s == 12) assert(s == "\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF")
check(s, {0x4000000, 0x7FFFFFFF}, true) check(s, {0x4000000, 0x7FFFFFFF}, true)
s = "\u{200000}\u{3FFFFFF}" s = "\u{200000}\u{3FFFFFF}"
@@ -248,6 +265,10 @@ local x = "日本語a-4\0éó"
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243}) check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
-- more than 5 continuation bytes
assert(not utf8.len("\xff\x8f\x8f\x8f\x8f\x8f\x8f\x8f"))
-- Supplementary Characters -- Supplementary Characters
check("𣲷𠜎𠱓𡁻𠵼ab𠺢", check("𣲷𠜎𠱓𡁻𠵼ab𠺢",
{0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,}) {0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,})
+113 -4
View File
@@ -3,9 +3,12 @@
print('testing vararg') print('testing vararg')
local function f (a, ...) local function f (a, ...t)
local x = {n = select('#', ...), ...} 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 return x.n
end end
@@ -17,7 +20,7 @@ local function c12 (...)
return res, 2 return res, 2
end 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 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) 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 -- varargs for main chunks
local f = load[[ return {...} ]] local f = assert(load[[ return {...} ]])
local x = f(2,3) local x = f(2,3)
assert(x[1] == 2 and x[2] == 3 and x[3] == undef) assert(x[1] == 2 and x[2] == 3 and x[3] == undef)
@@ -147,5 +182,79 @@ do
local a, b = g() local a, b = g()
assert(a == nil and b == 2) assert(a == nil and b == 2)
end 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') print('OK')