Compare commits

..

81 Commits

Author SHA1 Message Date
Roberto Ierusalimschy
eadd8c7178 Added option LUA_NOBUILTIN
This option allows external code to avoid the use of gcc builtin macro
'__builtin_expect' in the Lua API.
2021-03-12 15:03:33 -03:00
Roberto Ierusalimschy
014daf43cb Details
Comments and order of hashing macros in 'ltable.c'.
2021-03-12 11:29:34 -03:00
Roberto Ierusalimschy
05b13651f9 File 'tracegc.lua' added to 'packtests' 2021-03-10 10:35:57 -03:00
Roberto Ierusalimschy
81c6021fb4 New implementation for 'tbclist'
- Fixes a bug, by removing dummy nodes together with the node
itself. (The previous implementation could leave dummy nodes in frames
which otherwise had no tbc variables, and therefore would not close
variables; that could leave 'tbclist' pointing higher than 'top', which
could dangle if the stack shrank.)

- Computes MAXDELTA based on the type of delta, to ease changing its
type if needed.

- Instead of 'isdummy', uses 'delta==0' to signal dummy nodes. (Dummy
nodes always have MAXDELTA for their real delta.)
2021-03-10 10:27:19 -03:00
Roberto Ierusalimschy
a7b8b27dd3 Uses of "likely" in macros active to all users
The use of 'l_likely' in auxlib macros 'luaL_argcheck' and
'luaL_argexpected' should not be restricted to Lua's own code.
For that, 'l_likely' was renamed to 'luai_likely' to be exported
to external code.
2021-03-09 12:50:59 -03:00
Roberto Ierusalimschy
511d53a826 lua_settop/lua_pop closes to-be-closed variables
The existence of 'lua_closeslot' is no reason for lua_pop not to close
to-be-closed variables too.  It is too error-prone for lua_pop not to
close tbc variables being popped from the stack.
2021-03-09 11:42:45 -03:00
Roberto Ierusalimschy
f5df7f91f7 Wrong assertion in 'getbaseline'
The assertion cannot compute 'f->abslineinfo[i]' when the initial
estimate 'i' is -1.
2021-03-05 12:10:34 -03:00
Roberto Ierusalimschy
e7803f7dbc New release number (5.4.3) 2021-03-03 09:44:20 -03:00
Roberto Ierusalimschy
b7eb21c1ef Normalization of metamethod typography in the manual 2021-03-02 13:50:00 -03:00
Roberto Ierusalimschy
cf23a93d82 Added assertions for proper use of string buffers 2021-03-02 11:39:42 -03:00
Roberto Ierusalimschy
9a2de786de Stack check in warning function for tests
The warning function using for tests need to check the stack before
pushing anything. (Warning functions are not expected to access a
Lua state, therefore they have no preallocated stack space.)
2021-03-02 11:35:40 -03:00
Roberto Ierusalimschy
5276973224 New test module 'tracegc'
New module easies the inclusion of GC tracing in individual test files.
2021-03-01 13:54:29 -03:00
Roberto Ierusalimschy
f9d857a81b Stack reallocation done in two phases
$he stack reallocation is done in two steps (allocation + free) because
the correction of the pointers pointing into the stack must be done
while both addresses (the old stack and the new one) are valid.  In ISO
C, any pointer use after the pointer has been deallocated is undefined
behavior. The compiler option '-fsanitize=pointer-subtract' (plus what
it needs to work) complained about the old implementation.
2021-02-27 12:56:09 -03:00
Roberto Ierusalimschy
1537d6680b New control for reentrancy of emergency collections
Instead of assuming that shrinking a block may be an emergency
collection, use an explicit field ('gcstopem') to stop emergency
collections while GC is working.
2021-02-26 11:41:02 -03:00
Roberto Ierusalimschy
e0260eb2d4 Bug (kind of) in 'isinstack'
The function 'isinstack' tried to work around the undefined behavior
of subtracting two pointers that do not point to the same object,
but the compiler killed to trick. (It optimizes out the safety check,
because in a correct execution it will be always true.)
2021-02-25 13:39:36 -03:00
Roberto Ierusalimschy
5205f073c5 Don't use tointegerns when luaV_tointegerns will do
Some places don't need the "fast path" macro tointegerns, either
because speed is not essential (lcode.c) or because the value is not
supposed to be an integer already (luaV_equalobj and luaG_tointerror).
Moreover, luaV_equalobj should always use F2Ieq, even if Lua is
compiled to "round to floor".
2021-02-24 12:24:42 -03:00
Roberto Ierusalimschy
31925e4cc2 Details
Added documentation and asserts that constants for arithmetic opcodes
must be numbers.
2021-02-24 11:30:46 -03:00
Roberto Ierusalimschy
59c88f846d Broadening the use of branch hints
More uses of macros 'likely'/'unlikely' (renamed to
'l_likely'/'l_unlikely'), both in range (extended to the
libraries) and in scope (extended to hooks, stack growth).
2021-02-24 11:14:44 -03:00
Roberto Ierusalimschy
c03c527fd2 Bug: 'string.concat' error message uses wrong format 2021-02-15 13:31:45 -03:00
Roberto Ierusalimschy
38cc7d40a4 Bug: cannot allow the call 'debug.getinfo(0, ">")'
A 'what' argument starting with '>' indicates that there is a function
in the C stack, which won't be there if the first argument is not a
function.
2021-02-15 10:38:09 -03:00
Roberto Ierusalimschy
bc970005ce '__close' methods can yield in the return of a C function
When, inside a coroutine, a C function with to-be-closed slots return,
the corresponding metamethods can yield. ('__close' metamethods called
through 'lua_closeslot' still cannot yield, as there is no continuation
to go when resuming.)
2021-02-12 13:36:30 -03:00
Roberto Ierusalimschy
f79ccdca9b Eases the use of clang in the makefile
New definition in the makefile for warnings that are valid for gcc but
not for clang (CWARNGCC).
2021-02-10 14:11:51 -03:00
Roberto Ierusalimschy
4e47f81188 New implementation for to-be-closed variables
To-be-closed variables are linked in their own list, embedded into the
stack elements. (Due to alignment, this information does not change
the size of the stack elements in most architectures.)  This new list
does not produce garbage and avoids memory errors when creating tbc
variables.
2021-02-09 14:00:05 -03:00
Roberto Ierusalimschy
c63e5d212b New macro 'completestate' 2021-02-05 17:51:25 -03:00
Roberto Ierusalimschy
dee6433a89 Forbid changing numerical types through compiler options
'luaconf.h' always defines options LUA_32BITS, LUA_C89_NUMBERS,
LUA_INT_TYPE, and LUA_FLOAT_TYPE (using 0/1 for the first two),
to avoid they being set through compiler options. (It is too easy
to forget these options when compiling other software that
interoperates with Lua.)
2021-02-05 15:30:34 -03:00
Roberto Ierusalimschy
2bfa13e520 Fixed some bugs around stack reallocation
Long time without using HARDSTACKTESTS...
2021-02-05 11:00:28 -03:00
Roberto Ierusalimschy
e500892e18 Optimization/simplification of 'getbaseline'
By producing absolute line information at regular intervals, a simple
division can compute the correct entry for a given instruction.
2021-02-02 14:43:55 -03:00
Roberto Ierusalimschy
949187b049 Optimizations for line hook
The function 'changedline' tries harder to avoid calling
'luaG_getfuncline' plus small changes in the use of 'L->oldpc'.
2021-01-28 14:40:29 -03:00
Roberto Ierusalimschy
58aa09a0b9 Small improvements in hooks
- 'L->top' is set once in 'luaD_hook', instead of being set in
'luaD_hookcall' and 'rethook';

- resume discard arguments when returning after an yield inside a hook
(arguments may interfere with the coroutine stack);

- yield inside a hook asserts it has no arguments.
2021-01-26 16:53:51 -03:00
Roberto Ierusalimschy
1f81baffad Janitorial work
Comments, code details, identation.
2021-01-25 10:39:18 -03:00
Roberto Ierusalimschy
0e9254dfa0 Correct order of return hooks vs. close metamethods
The return hook should be called only after closing variables (which
are still part of the function). C functions were calling the hook
before the metamethods.
2021-01-21 10:27:22 -03:00
Roberto Ierusalimschy
6ccd24eff5 Simpler handling of errors when creating tbc variables
New field 'lua_State.ptbc' keeps to-be-closed variable until its
upvalue is created, so that it can be closed in case of a
memory-allocation error.
2021-01-19 10:03:13 -03:00
Roberto Ierusalimschy
d0f34d9137 Allow yields in '__close' metamethods ater errors
Completes commit b07fc10e91. '__close' metamethods can yield even
when they are being called due to an error. '__close' metamethods from
C functions are still not allowed to yield.
2021-01-18 11:40:45 -03:00
Roberto Ierusalimschy
825ac8eca8 Corrected documentation for 'table.sort'
The sort function must define a (strict) weak order for a correct
sorting. A partial order is not enough.
2021-01-14 13:26:55 -03:00
Roberto Ierusalimschy
b07fc10e91 Allow yields inside '__close' metamethods
Initial implementation to allow yields inside '__close' metamethods.
This current version still does not allow a '__close' metamethod
to yield when called due to an error. '__close' metamethods from
C functions also are not allowed to yield.
2021-01-13 13:54:10 -03:00
Roberto Ierusalimschy
cc1692515e New API function 'lua_closeslot'
Closing a to-be-closed variable with 'lua_settop' is too restrictive,
as it erases all slots above the variable. Moreover, it adds side
effects to 'lua_settop', which should be a fairly basic function.
2021-01-11 15:03:01 -03:00
Roberto Ierusalimschy
ce101dcaf7 Handles '__close' errors in coroutines in "coroutine style"
Errors in '__close' metamethods in coroutines are handled by the same
logic that handles other errors, through 'recover'.
2020-12-30 11:20:22 -03:00
Roberto Ierusalimschy
553b37ce4f Do not insert nil values into tables 2020-12-29 13:38:47 -03:00
Roberto Ierusalimschy
4bd10b6fe8 Better error messages for calling non-callable objects
When available, use the calling code to find a suitable name for what
was being called; this is particularly useful for errors of non-callable
metamethods. This commit also improved the debug information for
order metamethods.
2020-12-29 13:15:54 -03:00
Roberto Ierusalimschy
59e565d955 No need to recheck close method before calling it
A to-be-closed variable is constant and it must have a close metamethod
when it is created. A program has to go out of its way (e.g., by
changing the variable's metamethod) to invalidate that check. So,
it is not worth to test that again. If the program tampers with the
metamethod, Lua will raise a regular error when attempting to call it.
2020-12-29 10:23:02 -03:00
Roberto Ierusalimschy
6188f3a654 Reset thread before panicking
Before panicking, it is simpler to reset the thread instead of closing
its variables and adjust the top manually.
2020-12-28 16:34:07 -03:00
Roberto Ierusalimschy
7af27ef59d Cleaner handling of errors in '__close' metamethods
Instead of protecting each individual metamethod call, protect the
entire call to 'luaF_close'.
2020-12-28 11:40:30 -03:00
Roberto Ierusalimschy
0ceada8da9 Report last error in closing methods
When there are multiple errors around closing methods, report the
last error instead of the original.
2020-12-22 10:54:25 -03:00
Roberto Ierusalimschy
f9d29b0c44 Upvalues removed from 'openupval' before being closed
Undo commit c220b0a5d0: '__close' is not called again in case of
errors. (Upvalue is removed from the list before the call.) The
common error that justified that change was C stack overflows, which
are much rarer with the stackless implementation.
2020-12-21 15:21:45 -03:00
Roberto Ierusalimschy
409256b784 'coroutine.close'/'lua_resetthread' report original errors
Besides errors in closing methods, 'coroutine.close' and
'lua_resetthread' also consider the original error that stopped the
thread, if any.
2020-12-18 11:22:42 -03:00
Roberto Ierusalimschy
b17178b27a Cleaner handling of floats in pack/unpack 2020-12-16 11:23:51 -03:00
Roberto Ierusalimschy
e1ceea5674 Cleaner definition for macro 'ttisclosure' 2020-12-16 11:02:40 -03:00
Roberto Ierusalimschy
748d6d4e7a Review of asserts in 'ltests.c'
The module 'ltests.c' must work correctly with asserts off, too.
2020-12-08 11:54:21 -03:00
Roberto Ierusalimschy
e2ea3b31c9 Details (do not affect regular code)
* Avoids multiple definitions of 'lua_assert' in test file.
* Smaller C-stack limit in test mode.
* Note in the manual about the use of false
* Extra test for constant reuse.
2020-12-07 11:17:30 -03:00
Roberto Ierusalimschy
23051e830a Changes in the API of 'luaH_set' and related functions
Functions to set values in a table (luaH_set, luaH_newkey, etc.) receive
the new value, instead of returning a slot where to put the value.
2020-12-04 11:08:42 -03:00
Roberto Ierusalimschy
f15589f3b0 Added test cases for error messages about goto/label 2020-12-03 12:11:15 -03:00
Roberto Ierusalimschy
754ca0060f n Windows, 'popen' accepts "[rw][bt]?" as valid modes
Added the modifiers 'b' and 't' to valid modes for 'popen' in Windows.
2020-12-03 12:09:50 -03:00
Roberto Ierusalimschy
c36ced53c9 Avoid "bad programming habits" in the reference system
References were using both 0 indices and nils as values in arrays.
Both do not fit in the concept of a sequence, which is the kind
of use that guides all Lua optimizations.
2020-12-03 12:00:32 -03:00
Roberto Ierusalimschy
d41c36bf67 'lua_assert' moved from 'lualib.h' to 'lauxlib.h'
The macro is useful also in 'lauxlib.c', which does not include
'lualib.h'. Also, the definition was corrected to be "on" when
LUAI_ASSERT is defined.
2020-12-03 10:39:38 -03:00
Roberto Ierusalimschy
d9d2904f09 Details
Names in the parser and other details that do not change actual code.
2020-12-02 15:13:13 -03:00
Roberto Ierusalimschy
65d2294454 Changed access to global table in the registry
The global table is always in the array part of the registry; we can
use this fact to make its access slightly more efficient.
2020-11-26 18:23:40 -03:00
Roberto Ierusalimschy
131e3fd814 Avoid using 'signal' when 'sigaction' is available
The semantics of 'signal' varies a lot among different implementations;
'sigaction' ensures a more consistent behavior.
2020-11-24 14:41:50 -03:00
Roberto Ierusalimschy
9d067ab73b Optimization for 'n^2'
Squares are much more common than other exponentiations, and 'n*n' is
much more efficient than 'pow'.
2020-11-13 09:59:07 -03:00
Roberto Ierusalimschy
2f4162bc47 Compiler optimization back to '-O2'
Undo commit 6a10f03ff. Compiler performance is important, too.
2020-11-11 15:10:51 -03:00
Roberto Ierusalimschy
ab1aca94e8 Removed optimization for «if ... then goto»
That optimization was too complex and caused some weird traces when
debugging. The more common case «if ... then break» was kept.
2020-11-11 14:41:41 -03:00
Roberto Ierusalimschy
d282652561 Bug when growing a stack
When a stack grows, its extra area can be in use, and it becomes part
of the common area. So, the extra area must be kept correct all the
times. (Bug introduced by commit 5aa36e894f5.)
2020-11-08 11:52:26 -03:00
Roberto Ierusalimschy
58216600eb 'luaL_newstate' should not allocate extra memory
The allocation of a userdata for the state of the warn system can
cause a panic if it fails; 'luaL_ref' also can fail. This commit
re-implements the warn system so that it does not need an explicit
state. Instead, the system uses different functions to represent
the different states.
2020-11-03 16:34:36 -03:00
Roberto Ierusalimschy
94cbe46511 Details
- small corrections in the manual
- ldo.c: 'docall' -> 'ccall' ('docall' already used in 'lua.c')
- comments
2020-10-30 10:18:54 -03:00
Roberto Ierusalimschy
69b71a6919 _PROMPT can have non-string values
'get_prompt' uses 'luaL_tolstring' to convert _PROMPT or _PROMPT2
value to a string. That conversion may invoke a '__tostring'
metamethod.
2020-10-26 11:15:51 -03:00
Roberto Ierusalimschy
d742a193e5 Comments 2020-10-22 15:54:46 -03:00
Roberto Ierusalimschy
e4a38eb0e8 Fixed wrong trace of vararg functions
Trace of vararg functions was skipping an instruction when returning
from a call. (Bug introduced by commit 5d8ce05b3.)
2020-10-19 15:55:25 -03:00
Roberto Ierusalimschy
f07de22576 Fixed compiler option -DHARDSTACKTESTS to commit 5aa36e8 2020-10-19 15:43:59 -03:00
Roberto Ierusalimschy
52c8679760 Fixed bug of keys removed from tables vs 'next'
Fixed the bug that a key removed from a table might not be found
again by 'next'. (This is needed to allow keys to be removed during a
traversal.) This bug was introduced in commit 73ec04fc.
2020-10-14 15:46:58 -03:00
Roberto Ierusalimschy
849b2ecbd2 New release number (5.4.2) 2020-10-12 14:52:39 -03:00
Roberto Ierusalimschy
30528049f1 'lua_upvalueid' returns NULL on invalid upvalue index 2020-10-12 14:51:28 -03:00
Roberto Ierusalimschy
9a89fb1c9d Hash always use all characters in a long string
Hashes for long strings are computed only when they are used as keys
in a table, not a too common case. And, in that case, it is to easy to
force collisions changing only the characters which are not part of the
hash.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
c23cc86c54 Details
- After converting a generic GCObject to a specific type ('gco2*'),
  avoid using the original GCObject (to reduce aliasing).
- Small corrections in comments in 'lopcodes.h'
- Added tests about who calls __close metamethods
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
171dcd7d74 'recover' finish of 'luaD_pcall' should follow the original 2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
5aa36e894f No more field 'lua_State.stacksize'
The stack size is derived from 'stack_last', when needed. Moreover,
the handling of stack sizes is more consistent, always excluding the
extra space except when allocating/deallocating the array.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
9ecd446141 Avoid shrinking stacks to often
Shrink a stack only when the final stack size can be at most 2/3 the
previous size with half of its entries empty. This commit also
improves the clarity of 'luaD_growstack'.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
fb172d0a92 No need for 'volatile' in string.pack/unpack
Type punning an address to 'char *' should be always safe.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
0085db4596 Avoid GCs when testing stack overflow
A GC step may invoke some finalizer, which may error and emit
a warning due to stack overflfow.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
490d42b5f8 Correct handling of 'luaV_execute' invocations
The previous stackless implementations marked all 'luaV_execute'
invocations as fresh. However, re-entering 'luaV_execute' when
resuming a coroutine should not be a fresh invocation. (It works
because 'unroll' called 'luaV_execute' for each call entry, but
it was slower than letting 'luaV_execute' finish all non-fresh
invocations.)
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
287b302acb Revision of stackless implementation
- more organized handling of 'nCcalls'
- comments
- deprecation of 'setcstacklimit'
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
5d8ce05b3f Back to a stackless implementation
A "with stack" implementation gains too little in performance to be
worth all the noise from C-stack overflows.

This commit is almost a sketch, to test performance. There are several
pending stuff:

- review control of C-stack overflow and error messages;
- what to do with setcstacklimit;
- review comments;
- review unroll of Lua calls.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
f83de8e34e Wrong cast in 'str_unpack' 2020-09-30 15:10:12 -03:00
65 changed files with 2496 additions and 1570 deletions

3
.gitignore vendored
View File

@@ -10,3 +10,6 @@ testes/time.txt
testes/time-debug.txt
testes/libs/all
temp
lua

View File

@@ -2,6 +2,6 @@
This is the repository of Lua development code, as seen by the Lua team. It contains the full history of all commits but is mirrored irregularly. For complete information about Lua, visit [Lua.org](https://www.lua.org/).
Please **do not** send pull requests. To report issues and send patches, post a message to the [Lua mailing list](https://www.lua.org/lua-l.html).
Please **do not** send pull requests. To report issues, post a message to the [Lua mailing list](https://www.lua.org/lua-l.html).
Download official Lua releases from [Lua.org](https://www.lua.org/download.html).

2
all
View File

@@ -1,7 +1,7 @@
make -s -j
cd testes/libs; make -s
cd .. # back to directory 'testes'
ulimit -S -s 2000
ulimit -S -s 1000
if { ../lua -W all.lua; } then
echo -e "\n\n final OK!!!!\n\n"
else

81
lapi.c
View File

@@ -39,7 +39,7 @@ const char lua_ident[] =
/*
** Test for a valid index.
** Test for a valid index (one that is not the 'nilvalue').
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
** However, it covers the most common cases in a faster way.
*/
@@ -74,7 +74,8 @@ static TValue *index2value (lua_State *L, int idx) {
return &G(L)->nilvalue; /* it has no upvalues */
else {
CClosure *func = clCvalue(s2v(ci->func));
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue;
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
: &G(L)->nilvalue;
}
}
}
@@ -172,7 +173,7 @@ LUA_API int lua_gettop (lua_State *L) {
LUA_API void lua_settop (lua_State *L, int idx) {
CallInfo *ci;
StkId func;
StkId func, newtop;
ptrdiff_t diff; /* difference for new top */
lua_lock(L);
ci = L->ci;
@@ -187,9 +188,26 @@ LUA_API void lua_settop (lua_State *L, int idx) {
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
diff = idx + 1; /* will "subtract" index (as it is negative) */
}
if (diff < 0 && hastocloseCfunc(ci->nresults))
luaF_close(L, L->top + diff, LUA_OK);
L->top += diff; /* correct top only after closing any upvalue */
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
newtop = L->top + diff;
if (diff < 0 && L->tbclist >= newtop) {
lua_assert(hastocloseCfunc(ci->nresults));
luaF_close(L, newtop, CLOSEKTOP, 0);
}
L->top = newtop; /* correct top only after closing any upvalue */
lua_unlock(L);
}
LUA_API void lua_closeslot (lua_State *L, int idx) {
StkId level;
lua_lock(L);
level = index2stack(L, idx);
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level,
"no variable to close at given level");
luaF_close(L, level, CLOSEKTOP, 0);
level = index2stack(L, idx); /* stack may be moved */
setnilvalue(s2v(level));
lua_unlock(L);
}
@@ -629,11 +647,21 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
}
/*
** Get the global table in the registry. Since all predefined
** indices in the registry were inserted right when the registry
** was created and never removed, they must always be in the array
** part of the registry.
*/
#define getGtable(L) \
(&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg;
const TValue *G;
lua_lock(L);
reg = hvalue(&G(L)->l_registry);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
G = getGtable(L);
return auxgetstr(L, G, name);
}
@@ -811,10 +839,10 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API void lua_setglobal (lua_State *L, const char *name) {
Table *reg;
const TValue *G;
lua_lock(L); /* unlock done in 'auxsetstr' */
reg = hvalue(&G(L)->l_registry);
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
G = getGtable(L);
auxsetstr(L, G, name);
}
@@ -861,12 +889,10 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
Table *t;
TValue *slot;
lua_lock(L);
api_checknelems(L, n);
t = gettable(L, idx);
slot = luaH_set(L, t, key);
setobj2t(L, slot, s2v(L->top - 1));
luaH_set(L, t, key, s2v(L->top - 1));
invalidateTMcache(t);
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
L->top -= n;
@@ -1063,8 +1089,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
Table *reg = hvalue(&G(L)->l_registry);
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
const TValue *gt = getGtable(L);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt);
luaC_barrier(L, f->upvals[0], gt);
@@ -1240,8 +1265,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
lua_lock(L);
o = index2stack(L, idx);
nresults = L->ci->nresults;
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
"marked index below or equal new one");
api_check(L, L->tbclist < o, "given index below or equal a marked one");
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
L->ci->nresults = codeNresults(nresults); /* mark it */
@@ -1383,13 +1407,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
static const UpVal *const nullup = NULL;
LClosure *f;
TValue *fi = index2value(L, fidx);
api_check(L, ttisLclosure(fi), "Lua function expected");
f = clLvalue(fi);
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
if (pf) *pf = f;
return &f->upvals[n - 1]; /* get its upvalue pointer */
if (1 <= n && n <= f->p->sizeupvalues)
return &f->upvals[n - 1]; /* get its upvalue pointer */
else
return (UpVal**)&nullup;
}
@@ -1401,11 +1428,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
}
case LUA_VCCL: { /* C closure */
CClosure *f = clCvalue(fi);
api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index");
return &f->upvalue[n - 1];
}
if (1 <= n && n <= f->nupvalues)
return &f->upvalue[n - 1];
/* else */
} /* FALLTHROUGH */
case LUA_VLCF:
return NULL; /* light C functions have no upvalues */
default: {
api_check(L, 0, "closure expected");
api_check(L, 0, "function expected");
return NULL;
}
}
@@ -1417,6 +1447,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
LClosure *f1;
UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
*up1 = *up2;
luaC_objbarrier(L, f1, *up1);
}

2
lapi.h
View File

@@ -42,6 +42,8 @@
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
#define codeNresults(n) (-(n) - 3)
#define decodeNresults(n) (-(n) - 3)
#endif

146
lauxlib.c
View File

@@ -190,7 +190,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
}
int luaL_typeerror (lua_State *L, int arg, const char *tname) {
LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
const char *msg;
const char *typearg; /* name for the type of the actual argument */
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
@@ -283,10 +283,10 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
LUALIB_API int luaL_execresult (lua_State *L, int stat) {
const char *what = "exit"; /* type of termination */
if (stat != 0 && errno != 0) /* error with an 'errno'? */
return luaL_fileresult(L, 0, NULL);
else {
const char *what = "exit"; /* type of termination */
l_inspectstat(stat, what); /* interpret result */
if (*what == 'e' && stat == 0) /* successful termination? */
lua_pushboolean(L, 1);
@@ -378,7 +378,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
** but without 'msg'.)
*/
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
if (!lua_checkstack(L, space)) {
if (l_unlikely(!lua_checkstack(L, space))) {
if (msg)
luaL_error(L, "stack overflow (%s)", msg);
else
@@ -388,20 +388,20 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
if (lua_type(L, arg) != t)
if (l_unlikely(lua_type(L, arg) != t))
tag_error(L, arg, t);
}
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
if (lua_type(L, arg) == LUA_TNONE)
if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
luaL_argerror(L, arg, "value expected");
}
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
const char *s = lua_tolstring(L, arg, len);
if (!s) tag_error(L, arg, LUA_TSTRING);
if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
return s;
}
@@ -420,7 +420,7 @@ LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
int isnum;
lua_Number d = lua_tonumberx(L, arg, &isnum);
if (!isnum)
if (l_unlikely(!isnum))
tag_error(L, arg, LUA_TNUMBER);
return d;
}
@@ -442,7 +442,7 @@ static void interror (lua_State *L, int arg) {
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
int isnum;
lua_Integer d = lua_tointegerx(L, arg, &isnum);
if (!isnum) {
if (l_unlikely(!isnum)) {
interror(L, arg);
}
return d;
@@ -475,7 +475,7 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
lua_Alloc allocf = lua_getallocf(L, &ud);
UBox *box = (UBox *)lua_touserdata(L, idx);
void *temp = allocf(ud, box->box, box->bsize, newsize);
if (temp == NULL && newsize > 0) { /* allocation error? */
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
@@ -515,13 +515,22 @@ static void newbox (lua_State *L) {
#define buffonstack(B) ((B)->b != (B)->init.b)
/*
** Whenever buffer is accessed, slot 'idx' must either be a box (which
** cannot be NULL) or it is a placeholder for the buffer.
*/
#define checkbufferlevel(B,idx) \
lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
: lua_touserdata(B->L, idx) == (void*)B)
/*
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
** bytes.
*/
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
size_t newsize = B->size * 2; /* double buffer size */
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
return luaL_error(B->L, "buffer too large");
if (newsize < B->n + sz) /* double is not big enough? */
newsize = B->n + sz;
@@ -531,10 +540,11 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
/*
** Returns a pointer to a free area with at least 'sz' bytes in buffer
** 'B'. 'boxidx' is the relative position in the stack where the
** buffer's box is or should be.
** 'B'. 'boxidx' is the relative position in the stack where is the
** buffer's box or its placeholder.
*/
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
checkbufferlevel(B, boxidx);
if (B->size - B->n >= sz) /* enough space? */
return B->b + B->n;
else {
@@ -545,10 +555,9 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
if (buffonstack(B)) /* buffer already has a box? */
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
else { /* no box yet */
lua_pushnil(L); /* reserve slot for final result */
lua_remove(L, boxidx); /* remove placeholder */
newbox(L); /* create a new box */
/* move box (and slot) to its intended position */
lua_rotate(L, boxidx - 1, 2);
lua_insert(L, boxidx); /* move box to its intended position */
lua_toclose(L, boxidx);
newbuff = (char *)resizebox(L, boxidx, newsize);
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
@@ -583,11 +592,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
lua_State *L = B->L;
checkbufferlevel(B, -1);
lua_pushlstring(L, B->b, B->n);
if (buffonstack(B)) {
lua_copy(L, -1, -3); /* move string to reserved slot */
lua_pop(L, 2); /* pop string and box (closing the box) */
}
if (buffonstack(B))
lua_closeslot(L, -2); /* close the box */
lua_remove(L, -2); /* remove box or placeholder from the stack */
}
@@ -622,6 +631,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
B->b = B->init.b;
B->n = 0;
B->size = LUAL_BUFFERSIZE;
lua_pushlightuserdata(L, (void*)B); /* push placeholder */
}
@@ -639,10 +649,14 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
** =======================================================
*/
/* index of free-list header */
#define freelist 0
/* index of free-list header (after the predefined values) */
#define freelist (LUA_RIDX_LAST + 1)
/*
** The previously freed references form a linked list:
** t[freelist] is the index of a first free index, or zero if list is
** empty; t[t[freelist]] is the index of the second element; etc.
*/
LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref;
if (lua_isnil(L, -1)) {
@@ -650,9 +664,16 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
}
t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
lua_pop(L, 1); /* remove it from stack */
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
ref = 0; /* list is empty */
lua_pushinteger(L, 0); /* initialize as an empty list */
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
}
else { /* already initialized */
lua_assert(lua_isinteger(L, -1));
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
}
lua_pop(L, 1); /* remove element from stack */
if (ref != 0) { /* any free element? */
lua_rawgeti(L, t, ref); /* remove it from list */
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
@@ -668,6 +689,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
if (ref >= 0) {
t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist);
lua_assert(lua_isinteger(L, -1));
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
lua_pushinteger(L, ref);
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
@@ -851,7 +873,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
int isnum;
lua_len(L, idx);
l = lua_tointegerx(L, -1, &isnum);
if (!isnum)
if (l_unlikely(!isnum))
luaL_error(L, "object length is not an integer");
lua_pop(L, 1); /* remove object */
return l;
@@ -1006,43 +1028,67 @@ static int panic (lua_State *L) {
/*
** Emit a warning. '*warnstate' means:
** 0 - warning system is off;
** 1 - ready to start a new message;
** 2 - previous message is to be continued.
** Warning functions:
** warnfoff: warning system is off
** warnfon: ready to start a new message
** warnfcont: previous message is to be continued
*/
static void warnf (void *ud, const char *message, int tocont) {
int *warnstate = (int *)ud;
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */
if (strcmp(message, "@off") == 0)
*warnstate = 0;
else if (strcmp(message, "@on") == 0)
*warnstate = 1;
return;
static void warnfoff (void *ud, const char *message, int tocont);
static void warnfon (void *ud, const char *message, int tocont);
static void warnfcont (void *ud, const char *message, int tocont);
/*
** Check whether message is a control message. If so, execute the
** control or ignore it if unknown.
*/
static int checkcontrol (lua_State *L, const char *message, int tocont) {
if (tocont || *(message++) != '@') /* not a control message? */
return 0;
else {
if (strcmp(message, "off") == 0)
lua_setwarnf(L, warnfoff, L); /* turn warnings off */
else if (strcmp(message, "on") == 0)
lua_setwarnf(L, warnfon, L); /* turn warnings on */
return 1; /* it was a control message */
}
else if (*warnstate == 0) /* warnings off? */
return;
if (*warnstate == 1) /* previous message was the last? */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
}
static void warnfoff (void *ud, const char *message, int tocont) {
checkcontrol((lua_State *)ud, message, tocont);
}
/*
** Writes the message and handle 'tocont', finishing the message
** if needed and setting the next warn function.
*/
static void warnfcont (void *ud, const char *message, int tocont) {
lua_State *L = (lua_State *)ud;
lua_writestringerror("%s", message); /* write message */
if (tocont) /* not the last part? */
*warnstate = 2; /* to be continued */
lua_setwarnf(L, warnfcont, L); /* to be continued */
else { /* last part */
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
*warnstate = 1; /* ready to start a new message */
lua_setwarnf(L, warnfon, L); /* next call is a new message */
}
}
static void warnfon (void *ud, const char *message, int tocont) {
if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
return; /* nothing else to be done */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
warnfcont(ud, message, tocont); /* finish processing */
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) {
int *warnstate; /* space for warning state */
if (l_likely(L)) {
lua_atpanic(L, &panic);
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*warnstate = 0; /* default is warnings off */
lua_setwarnf(L, warnf, warnstate);
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
}
return L;
}

View File

@@ -12,6 +12,7 @@
#include <stddef.h>
#include <stdio.h>
#include "luaconf.h"
#include "lua.h"
@@ -130,10 +131,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
#define luaL_argcheck(L, cond,arg,extramsg) \
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
#define luaL_argexpected(L,cond,arg,tname) \
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
@@ -157,6 +158,22 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
#define luaL_pushfail(L) lua_pushnil(L)
/*
** Internal assertions for in-house debugging
*/
#if !defined(lua_assert)
#if defined LUAI_ASSERT
#include <assert.h>
#define lua_assert(c) assert(c)
#else
#define lua_assert(c) ((void)0)
#endif
#endif
/*
** {======================================================
** Generic Buffer manipulation

View File

@@ -138,7 +138,7 @@ static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
return luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
lua_setmetatable(L, 1);
@@ -182,7 +182,8 @@ static int luaB_rawset (lua_State *L) {
static int pushmode (lua_State *L, int oldmode) {
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
: "generational");
return 1;
}
@@ -299,7 +300,7 @@ static int luaB_ipairs (lua_State *L) {
static int load_aux (lua_State *L, int status, int envidx) {
if (status == LUA_OK) {
if (l_likely(status == LUA_OK)) {
if (envidx != 0) { /* 'env' parameter? */
lua_pushvalue(L, envidx); /* environment for loaded function */
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
@@ -355,7 +356,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
*size = 0;
return NULL;
}
else if (!lua_isstring(L, -1))
else if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "reader function must return a string");
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
return lua_tolstring(L, RESERVEDSLOT, size);
@@ -393,7 +394,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
lua_settop(L, 1);
if (luaL_loadfile(L, fname) != LUA_OK)
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
return lua_error(L);
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
return dofilecont(L, 0, 0);
@@ -401,7 +402,7 @@ static int luaB_dofile (lua_State *L) {
static int luaB_assert (lua_State *L) {
if (lua_toboolean(L, 1)) /* condition is true? */
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
return lua_gettop(L); /* return all arguments */
else { /* error */
luaL_checkany(L, 1); /* there must be a condition */
@@ -437,7 +438,7 @@ static int luaB_select (lua_State *L) {
** ignored).
*/
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
lua_pushboolean(L, 0); /* first result (false) */
lua_pushvalue(L, -2); /* error message */
return 2; /* return false, msg */

44
lcode.c
View File

@@ -314,15 +314,6 @@ void luaK_patchtohere (FuncState *fs, int list) {
}
/*
** MAXimum number of successive Instructions WiTHout ABSolute line
** information.
*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 120
#endif
/* limit for difference between lines in relative line info. */
#define LIMLINEDIFF 0x80
@@ -337,13 +328,13 @@ void luaK_patchtohere (FuncState *fs, int list) {
static void savelineinfo (FuncState *fs, Proto *f, int line) {
int linedif = line - fs->previousline;
int pc = fs->pc - 1; /* last instruction coded */
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
f->abslineinfo[fs->nabslineinfo].pc = pc;
f->abslineinfo[fs->nabslineinfo++].line = line;
linedif = ABSLINEINFO; /* signal that there is absolute information */
fs->iwthabs = 0; /* restart counter */
fs->iwthabs = 1; /* restart counter */
}
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
MAX_INT, "opcodes");
@@ -545,11 +536,14 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
** and try to reuse constants. Because some values should not be used
** as keys (nil cannot be a key, integer keys can collapse with float
** keys), the caller must provide a useful 'key' for indexing the cache.
** Note that all functions share the same table, so entering or exiting
** a function can make some indices wrong.
*/
static int addk (FuncState *fs, TValue *key, TValue *v) {
TValue val;
lua_State *L = fs->ls->L;
Proto *f = fs->f;
TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */
const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
int k, oldsize;
if (ttisinteger(idx)) { /* is there an index there? */
k = cast_int(ivalue(idx));
@@ -563,7 +557,8 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
k = fs->nk;
/* numerical value does not need GC barrier;
table has no metatable, so it does not need to invalidate cache */
setivalue(idx, k);
setivalue(&val, k);
luaH_finishset(L, fs->ls->h, key, idx, &val);
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[k], v);
@@ -753,7 +748,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
/*
** Ensure that expression 'e' is not a variable (nor a constant).
** Ensure that expression 'e' is not a variable (nor a <const>).
** (Expression still may have jump lists.)
*/
void luaK_dischargevars (FuncState *fs, expdesc *e) {
@@ -763,7 +758,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
break;
}
case VLOCAL: { /* already in a register */
e->u.info = e->u.var.sidx;
e->u.info = e->u.var.ridx;
e->k = VNONRELOC; /* becomes a non-relocatable value */
break;
}
@@ -805,8 +800,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
/*
** Ensures expression value is in register 'reg' (and therefore
** 'e' will become a non-relocatable expression).
** Ensure expression value is in register 'reg', making 'e' a
** non-relocatable expression.
** (Expression still may have jump lists.)
*/
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
@@ -860,7 +855,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
/*
** Ensures expression value is in any register.
** Ensure expression value is in a register, making 'e' a
** non-relocatable expression.
** (Expression still may have jump lists.)
*/
static void discharge2anyreg (FuncState *fs, expdesc *e) {
@@ -946,8 +942,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
exp2reg(fs, e, e->u.info); /* put final result in it */
return e->u.info;
}
/* else expression has jumps and cannot change its register
to hold the jump values, because it is a local variable.
Go through to the default case. */
}
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */
luaK_exp2nextreg(fs, e); /* default: use next available register */
return e->u.info;
}
@@ -1032,7 +1031,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
return;
}
case VUPVAL: {
@@ -1272,7 +1271,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
}
else {
/* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXSTR;
@@ -1299,7 +1298,8 @@ static int validop (int op, TValue *v1, TValue *v2) {
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
lua_Integer i;
return (tointegerns(v1, &i) && tointegerns(v2, &i));
return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
luaV_tointegerns(v2, &i, LUA_FLOORN2I));
}
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
return (nvalue(v2) != 0);

View File

@@ -31,14 +31,14 @@ static lua_State *getco (lua_State *L) {
*/
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status, nres;
if (!lua_checkstack(co, narg)) {
if (l_unlikely(!lua_checkstack(co, narg))) {
lua_pushliteral(L, "too many arguments to resume");
return -1; /* error flag */
}
lua_xmove(L, co, narg);
status = lua_resume(co, L, narg, &nres);
if (status == LUA_OK || status == LUA_YIELD) {
if (!lua_checkstack(L, nres + 1)) {
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
lua_pop(co, nres); /* remove results anyway */
lua_pushliteral(L, "too many results to resume");
return -1; /* error flag */
@@ -57,7 +57,7 @@ static int luaB_coresume (lua_State *L) {
lua_State *co = getco(L);
int r;
r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) {
if (l_unlikely(r < 0)) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
@@ -73,10 +73,13 @@ static int luaB_coresume (lua_State *L) {
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) { /* error? */
if (l_unlikely(r < 0)) { /* error? */
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
lua_resetthread(co); /* close its tbc variables */
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_resetthread(co); /* close its tbc variables */
lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* copy error message */
}
if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
luaL_where(L, 1); /* add extra info, if available */

View File

@@ -33,7 +33,7 @@ static const char *const HOOKKEY = "_HOOKKEY";
** checked.
*/
static void checkstack (lua_State *L, lua_State *L1, int n) {
if (L != L1 && !lua_checkstack(L1, n))
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
luaL_error(L, "stack overflow");
}
@@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
checkstack(L, L1, 3);
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
@@ -212,7 +213,7 @@ static int db_getlocal (lua_State *L) {
lua_Debug ar;
const char *name;
int level = (int)luaL_checkinteger(L, arg + 1);
if (!lua_getstack(L1, level, &ar)) /* out of range? */
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
checkstack(L, L1, 1);
name = lua_getlocal(L1, &ar, nvar);
@@ -237,7 +238,7 @@ static int db_setlocal (lua_State *L) {
lua_Debug ar;
int level = (int)luaL_checkinteger(L, arg + 1);
int nvar = (int)luaL_checkinteger(L, arg + 2);
if (!lua_getstack(L1, level, &ar)) /* out of range? */
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
@@ -281,25 +282,33 @@ static int db_setupvalue (lua_State *L) {
** Check whether a given upvalue from a given closure exists and
** returns its index
*/
static int checkupval (lua_State *L, int argf, int argnup) {
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
void *id;
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup,
"invalid upvalue index");
return nup;
id = lua_upvalueid(L, argf, nup);
if (pnup) {
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
*pnup = nup;
}
return id;
}
static int db_upvalueid (lua_State *L) {
int n = checkupval(L, 1, 2);
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
void *id = checkupval(L, 1, 2, NULL);
if (id != NULL)
lua_pushlightuserdata(L, id);
else
luaL_pushfail(L);
return 1;
}
static int db_upvaluejoin (lua_State *L) {
int n1 = checkupval(L, 1, 2);
int n2 = checkupval(L, 3, 4);
int n1, n2;
checkupval(L, 1, 2, &n1);
checkupval(L, 3, 4, &n2);
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
lua_upvaluejoin(L, 1, n1, 3, n2);
@@ -369,7 +378,7 @@ static int db_sethook (lua_State *L) {
}
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
/* table just created; initialize it */
lua_pushstring(L, "k");
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
@@ -412,7 +421,7 @@ static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
lua_writestringerror("%s", "lua_debug> ");
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
@@ -440,10 +449,7 @@ static int db_traceback (lua_State *L) {
static int db_setcstacklimit (lua_State *L) {
int limit = (int)luaL_checkinteger(L, 1);
int res = lua_setcstacklimit(L, limit);
if (res == 0)
lua_pushboolean(L, 0);
else
lua_pushinteger(L, res);
lua_pushinteger(L, res);
return 1;
}

131
ldebug.c
View File

@@ -33,8 +33,6 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
/* inverse of 'pcRel' */
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name);
@@ -48,10 +46,16 @@ static int currentpc (CallInfo *ci) {
/*
** Get a "base line" to find the line corresponding to an instruction.
** For that, search the array of absolute line info for the largest saved
** instruction smaller or equal to the wanted instruction. A special
** case is when there is no absolute info or the instruction is before
** the first absolute one.
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
** an integer division gets the right place. When the source file has
** large sequences of empty/comment lines, it may need extra entries,
** so the original estimate needs a correction.
** If the original estimate is -1, the initial 'if' ensures that the
** 'while' will run at least once.
** The assertion that the estimate is a lower bound for the correct base
** is valid as long as the debug info has been generated with the same
** value for MAXIWTHABS or smaller. (Previous releases use a little
** smaller value.)
*/
static int getbaseline (const Proto *f, int pc, int *basepc) {
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
@@ -59,20 +63,12 @@ static int getbaseline (const Proto *f, int pc, int *basepc) {
return f->linedefined;
}
else {
unsigned int i;
if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc)
i = f->sizeabslineinfo - 1; /* instruction is after last saved one */
else { /* binary search */
unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */
i = 0; /* abslineinfo[i] <= pc */
while (i < j - 1) {
unsigned int m = (j + i) / 2;
if (pc >= f->abslineinfo[m].pc)
i = m;
else
j = m;
}
}
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
/* estimate must be a lower bond of the correct base */
lua_assert(i < 0 ||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
i++; /* low estimate; adjust it */
*basepc = f->abslineinfo[i].pc;
return f->abslineinfo[i].line;
}
@@ -305,8 +301,8 @@ static void collectvalidlines (lua_State *L, Closure *f) {
sethvalue2s(L, L->top, t); /* push it on stack */
api_incr_top(L);
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */
currentline = nextline(p, currentline, i);
for (i = 0; i < p->sizelineinfo; i++) { /* for all instructions */
currentline = nextline(p, currentline, i); /* get its line */
luaH_setint(L, t, currentline, &v); /* table[line] = true */
}
}
@@ -629,12 +625,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
case OP_LEN: tm = TM_LEN; break;
case OP_CONCAT: tm = TM_CONCAT; break;
case OP_EQ: tm = TM_EQ; break;
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
*name = "order"; /* '<=' can call '__lt', etc. */
return "metamethod";
case OP_CLOSE: case OP_RETURN:
*name = "close";
return "metamethod";
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
default:
return NULL; /* cannot find a reasonable name */
}
@@ -647,14 +641,18 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
/*
** The subtraction of two potentially unrelated pointers is
** not ISO C, but it should not crash a program; the subsequent
** checks are ISO C and ensure a correct result.
** Check whether pointer 'o' points to some value in the stack
** frame of the current function. Because 'o' may not point to a
** value in this stack, we cannot compare it with the region
** boundaries (undefined behaviour in ISO C).
*/
static int isinstack (CallInfo *ci, const TValue *o) {
StkId base = ci->func + 1;
ptrdiff_t i = cast(StkId, o) - base;
return (0 <= i && i < (ci->top - base) && s2v(base + i) == o);
StkId pos;
for (pos = ci->func + 1; pos < ci->top; pos++) {
if (o == s2v(pos))
return 1;
}
return 0; /* not found */
}
@@ -697,6 +695,19 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
}
l_noret luaG_callerror (lua_State *L, const TValue *o) {
CallInfo *ci = L->ci;
const char *name = NULL; /* to avoid warnings */
const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL;
if (what != NULL) {
const char *t = luaT_objtypename(L, o);
luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t);
}
else
luaG_typeerror(L, o, "call");
}
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
what, luaT_objtypename(L, o));
@@ -722,7 +733,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
*/
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
lua_Integer temp;
if (!tointegerns(p1, &temp))
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
p2 = p1;
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
}
@@ -780,16 +791,30 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
/*
** Check whether new instruction 'newpc' is in a different line from
** previous instruction 'oldpc'.
** previous instruction 'oldpc'. More often than not, 'newpc' is only
** one or a few instructions after 'oldpc' (it must be after, see
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
** too far apart, there is a good chance of a ABSLINEINFO in the way,
** so it goes directly to 'luaG_getfuncline'.
*/
static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
while (oldpc++ < newpc) {
if (p->lineinfo[oldpc] != 0)
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
int delta = 0; /* line diference */
int pc = oldpc;
for (;;) {
int lineinfo = p->lineinfo[++pc];
if (lineinfo == ABSLINEINFO)
break; /* cannot compute delta; fall through */
delta += lineinfo;
if (pc == newpc)
return (delta != 0); /* delta computed successfully */
}
}
return 0; /* no line changes between positions */
/* either instructions are too far apart or there is an absolute line
info in the way; compute line difference explicitly */
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
}
@@ -797,20 +822,19 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
** function, 'npci' will be zero and will test as a new line without
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
** before. Some exceptional conditions may return to a function without
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
** call to a line hook.)
** function, 'npci' will be zero and will test as a new line whatever
** the value of 'oldpc'. Some exceptional conditions may return to
** a function without setting 'oldpc'. In that case, 'oldpc' may be
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
** at most causes an extra call to a line hook.)
** This function is not "Protected" when called, so it should correct
** 'L->top' before calling anything that can run the GC.
*/
int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
const Proto *p = ci_func(ci)->p;
int counthook;
/* 'L->oldpc' may be invalid; reset it in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */
@@ -826,15 +850,16 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return 1; /* do not call hook again (VM yielded, so it did not move) */
}
if (!isIT(*(ci->u.l.savedpc - 1)))
L->top = ci->top; /* prepare top */
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
L->top = ci->top; /* correct top */
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) {
/* 'L->oldpc' may be invalid; use zero in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
changedline(p, oldpc, npci)) { /* enter new line */
if (npci <= oldpc || /* call hook when jump back (loop), */
changedline(p, oldpc, npci)) { /* or when enter new line */
int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
}

View File

@@ -26,11 +26,22 @@
*/
#define ABSLINEINFO (-0x80)
/*
** MAXimum number of successive Instructions WiTHout ABSolute line
** information. (A power of two allows fast divisions.)
*/
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 128
#endif
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos);
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
const char *what);
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,

473
ldo.c
View File

@@ -98,11 +98,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case CLOSEPROTECT: {
case LUA_OK: { /* special case only for closing upvalues */
setnilvalue(s2v(oldtop)); /* no error message */
break;
}
default: {
lua_assert(errorstatus(errcode)); /* real error */
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
}
@@ -118,17 +119,13 @@ l_noret luaD_throw (lua_State *L, int errcode) {
}
else { /* thread has no error handler */
global_State *g = G(L);
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
L->status = cast_byte(errcode); /* mark it as dead */
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
}
else { /* no handler at all; abort */
if (g->panic) { /* panic function? */
luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
if (L->ci->top < L->top)
L->ci->top = L->top; /* pushing msg. can break this invariant */
lua_unlock(L);
g->panic(L); /* call panic function (last chance to jump out) */
}
@@ -139,8 +136,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
global_State *g = G(L);
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
l_uint32 oldnCcalls = L->nCcalls;
struct lua_longjmp lj;
lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */
@@ -149,7 +145,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci;
L->nCcalls = oldnCcalls;
return lj.status;
}
@@ -164,9 +160,8 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
CallInfo *ci;
UpVal *up;
if (oldstack == newstack)
return; /* stack address did not change */
L->top = (L->top - oldstack) + newstack;
L->tbclist = (L->tbclist - oldstack) + newstack;
for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v = s2v((uplevel(up) - oldstack) + newstack);
for (ci = L->ci; ci != NULL; ci = ci->previous) {
@@ -182,22 +177,37 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
/*
** Reallocate the stack to a new size, correcting all pointers into
** it. (There are pointers to a stack from its upvalues, from its list
** of call infos, plus a few individual pointers.) The reallocation is
** done in two steps (allocation + free) because the correction must be
** done while both addresses (the old stack and the new one) are valid.
** (In ISO C, any pointer use after the pointer has been deallocated is
** undefined behavior.)
** In case of allocation error, raise an error or return false according
** to 'raiseerror'.
*/
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
int lim = L->stacksize;
StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue);
int oldsize = stacksize(L);
int i;
StkId newstack = luaM_reallocvector(L, NULL, 0,
newsize + EXTRA_STACK, StackValue);
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
if (unlikely(newstack == NULL)) { /* reallocation failed? */
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
if (raiseerror)
luaM_error(L);
else return 0; /* do not raise an error */
}
for (; lim < newsize; lim++)
setnilvalue(s2v(newstack + lim)); /* erase new segment */
/* number of elements to be copied to the new stack */
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK;
memcpy(newstack, L->stack, i * sizeof(StackValue));
for (; i < newsize + EXTRA_STACK; i++)
setnilvalue(s2v(newstack + i)); /* erase new segment */
correctstack(L, L->stack, newstack);
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
L->stack = newstack;
L->stacksize = newsize;
L->stack_last = L->stack + newsize - EXTRA_STACK;
L->stack_last = L->stack + newsize;
return 1;
}
@@ -207,51 +217,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
** is true, raises any error; otherwise, return 0 in case of errors.
*/
int luaD_growstack (lua_State *L, int n, int raiseerror) {
int size = L->stacksize;
int newsize = 2 * size; /* tentative new size */
if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */
int size = stacksize(L);
if (l_unlikely(size > LUAI_MAXSTACK)) {
/* if stack is larger than maximum, thread is already using the
extra space reserved for errors, that is, thread is handling
a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror)
luaD_throw(L, LUA_ERRERR); /* error inside message handler */
else return 0;
return 0; /* if not 'raiseerror', just signal it */
}
else {
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
int newsize = 2 * size; /* tentative new size */
int needed = cast_int(L->top - L->stack) + n;
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
newsize = LUAI_MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */
newsize = needed;
if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */
if (l_likely(newsize <= LUAI_MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror);
else { /* stack overflow */
/* add extra size to be able to handle the error message */
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
if (raiseerror)
luaG_runerror(L, "stack overflow");
else return 0;
return 0;
}
} /* else no errors */
return luaD_reallocstack(L, newsize, raiseerror);
}
}
static int stackinuse (lua_State *L) {
CallInfo *ci;
int res;
StkId lim = L->top;
for (ci = L->ci; ci != NULL; ci = ci->previous) {
if (lim < ci->top) lim = ci->top;
}
lua_assert(lim <= L->stack_last);
return cast_int(lim - L->stack) + 1; /* part of stack in use */
res = cast_int(lim - L->stack) + 1; /* part of stack in use */
if (res < LUA_MINSTACK)
res = LUA_MINSTACK; /* ensure a minimum size */
return res;
}
/*
** If stack size is more than 3 times the current use, reduce that size
** to twice the current use. (So, the final stack size is at most 2/3 the
** previous size, and half of its entries are empty.)
** As a particular case, if stack was handling a stack overflow and now
** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
** will be reduced to a "regular" size.
*/
void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L);
int goodsize = inuse + BASIC_STACK_SIZE;
if (goodsize > LUAI_MAXSTACK)
goodsize = LUAI_MAXSTACK; /* respect stack limit */
int nsize = inuse * 2; /* proposed new size */
int max = inuse * 3; /* maximum "reasonable" size */
if (max > LUAI_MAXSTACK) {
max = LUAI_MAXSTACK; /* respect stack limit */
if (nsize > LUAI_MAXSTACK)
nsize = LUAI_MAXSTACK;
}
/* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize)
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
size is larger than maximum "reasonable" size, shrink it */
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
luaD_reallocstack(L, nsize, 0); /* ok if that fails */
else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */
luaE_shrinkCI(L); /* shrink CI list */
@@ -277,8 +309,8 @@ void luaD_hook (lua_State *L, int event, int line,
if (hook && L->allowhook) { /* make sure there is a hook */
int mask = CIST_HOOKED;
CallInfo *ci = L->ci;
ptrdiff_t top = savestack(L, L->top);
ptrdiff_t ci_top = savestack(L, ci->top);
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */
lua_Debug ar;
ar.event = event;
ar.currentline = line;
@@ -288,8 +320,10 @@ void luaD_hook (lua_State *L, int event, int line,
ci->u2.transferinfo.ftransfer = ftransfer;
ci->u2.transferinfo.ntransfer = ntransfer;
}
if (isLua(ci) && L->top < ci->top)
L->top = ci->top; /* protect entire activation register */
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
if (L->top + LUA_MINSTACK > ci->top)
if (ci->top < L->top + LUA_MINSTACK)
ci->top = L->top + LUA_MINSTACK;
L->allowhook = 0; /* cannot call hooks inside a hook */
ci->callstatus |= mask;
@@ -311,51 +345,53 @@ void luaD_hook (lua_State *L, int event, int line,
** active.
*/
void luaD_hookcall (lua_State *L, CallInfo *ci) {
int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL;
Proto *p;
if (!(L->hookmask & LUA_MASKCALL)) /* some other hook? */
return; /* don't call hook */
p = clLvalue(s2v(ci->func))->p;
L->top = ci->top; /* prepare top */
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
luaD_hook(L, hook, -1, 1, p->numparams);
ci->u.l.savedpc--; /* correct 'pc' */
L->oldpc = 0; /* set 'oldpc' for new function */
if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
: LUA_HOOKCALL;
Proto *p = ci_func(ci)->p;
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
luaD_hook(L, event, -1, 1, p->numparams);
ci->u.l.savedpc--; /* correct 'pc' */
}
}
static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0;
if (isLuacode(ci)) {
Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top)
L->top = ci->top; /* correct top to run hook */
}
/*
** Executes a return hook for Lua and C functions and sets/corrects
** 'oldpc'. (Note that this correction is needed by the line hook, so it
** is done even when return hooks are off.)
*/
static void rethook (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
StkId firstres = L->top - nres; /* index of first result */
int delta = 0; /* correction for vararg functions */
int ftransfer;
if (isLua(ci)) {
Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
}
ci->func += delta; /* if vararg, back to virtual 'func' */
ftransfer = cast(unsigned short, firstres - ci->func);
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta;
}
if (isLua(ci = ci->previous))
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop);
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
}
/*
** Check whether 'func' has a '__call' metafield. If so, put it in the
** stack, below original 'func', so that 'luaD_call' can call it. Raise
** stack, below original 'func', so that 'luaD_precall' can call it. Raise
** an error if there is no '__call' metafield.
*/
void luaD_tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
StkId p;
if (unlikely(ttisnil(tm)))
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
if (l_unlikely(ttisnil(tm)))
luaG_callerror(L, s2v(func)); /* nothing to call */
for (p = L->top; p > func; p--) /* open space for metamethod */
setobjs2s(L, p, p-1);
L->top++; /* stack space pre-allocated by the caller */
@@ -379,27 +415,34 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
case 1: /* one value needed */
if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */
else
else /* at least one result */
setobjs2s(L, res, L->top - nres); /* move it to proper place */
L->top = res + 1;
return;
case LUA_MULTRET:
wanted = nres; /* we want all results */
break;
default: /* multiple results (or to-be-closed variables) */
default: /* two/more results and/or to-be-closed variables */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
ptrdiff_t savedres = savestack(L, res);
luaF_close(L, res, LUA_OK); /* may change the stack */
res = restorestack(L, savedres);
wanted = codeNresults(wanted); /* correct value */
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
L->ci->u2.nres = nres;
luaF_close(L, res, CLOSEKTOP, 1);
L->ci->callstatus &= ~CIST_CLSRET;
if (L->hookmask) /* if needed, call hook after '__close's */
rethook(L, L->ci, nres);
res = restorestack(L, savedres); /* close and hook can move stack */
wanted = decodeNresults(wanted);
if (wanted == LUA_MULTRET)
wanted = nres;
wanted = nres; /* we want all results */
}
break;
}
/* generic case */
firstresult = L->top - nres; /* index of first result */
/* move all results to correct place */
for (i = 0; i < nres && i < wanted; i++)
if (nres > wanted) /* extra results? */
nres = wanted; /* don't need them */
for (i = 0; i < nres; i++) /* move all results to correct place */
setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i));
@@ -408,15 +451,21 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
/*
** Finishes a function call: calls hook if necessary, removes CallInfo,
** moves current number of results to proper place.
** Finishes a function call: calls hook if necessary, moves current
** number of results to proper place, and returns to previous call
** info. If function has to close variables, hook must be called after
** that.
*/
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask)
L->top = rethook(L, ci, L->top - nres, nres);
L->ci = ci->previous; /* back to caller */
int wanted = ci->nresults;
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
rethook(L, ci, nres);
/* move results to proper place */
moveresults(L, ci->func, nres, ci->nresults);
moveresults(L, ci->func, nres, wanted);
/* function cannot be in any of these cases when returning */
lua_assert(!(ci->callstatus &
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
L->ci = ci->previous; /* back to caller (after closing variables) */
}
@@ -449,12 +498,14 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
/*
** Call a function (C or Lua). The function to be called is at *func.
** The arguments are on the stack, right after the function.
** When returns, all the results are on the stack, starting at the original
** function position.
** Prepares the call to a function (C or Lua). For C functions, also do
** the call. The function to be called is at '*func'. The arguments
** are on the stack, right after the function. Returns the CallInfo
** to be executed, if it was a Lua function. Otherwise (a C function)
** returns NULL, with all the results on the stack, starting at the
** original function position.
*/
void luaD_call (lua_State *L, StkId func, int nresults) {
CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
retry:
switch (ttypetag(s2v(func))) {
@@ -473,7 +524,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) {
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
int narg = cast_int(L->top - func) - 1;
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
}
@@ -482,7 +533,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n);
break;
return NULL;
}
case LUA_VLCL: { /* Lua function */
CallInfo *ci;
@@ -494,15 +545,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
ci->top = func + 1 + fsize;
ci->func = func;
L->ci = ci;
for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last);
luaV_execute(L, ci); /* run the function */
break;
return ci;
}
default: { /* not a function */
checkstackGCp(L, 1, func); /* space for metamethod */
@@ -514,41 +563,108 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
/*
** Similar to 'luaD_call', but does not allow yields during the call.
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
** number of recursive invocations in the C stack) or nyci (the same
** plus increment number of non-yieldable calls).
*/
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L);
if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */
luaE_exitCcall(L); /* to compensate decrement in next call */
luaE_enterCcall(L); /* check properly */
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
CallInfo *ci;
L->nCcalls += inc;
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
luaV_execute(L, ci); /* call it */
}
luaD_call(L, func, nResults);
decXCcalls(L);
L->nCcalls -= inc;
}
/*
** Completes the execution of an interrupted C function, calling its
** continuation function.
** External interface for 'ccall'
*/
static void finishCcall (lua_State *L, int status) {
CallInfo *ci = L->ci;
int n;
/* must have a continuation and must be able to call it */
lua_assert(ci->u.c.k != NULL && yieldable(L));
/* error status can only happen in a protected call */
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
void luaD_call (lua_State *L, StkId func, int nResults) {
ccall(L, func, nResults, 1);
}
/*
** Similar to 'luaD_call', but does not allow yields during the call.
*/
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
ccall(L, func, nResults, nyci);
}
/*
** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
** If a '__close' method yields here, eventually control will be back
** to 'finishCcall' (when that '__close' method finally returns) and
** 'finishpcallk' will run again and close any still pending '__close'
** methods. Similarly, if a '__close' method errs, 'precover' calls
** 'unroll' which calls ''finishCcall' and we are back here again, to
** close any pending '__close' methods.
** Note that, up to the call to 'luaF_close', the corresponding
** 'CallInfo' is not modified, so that this repeated run works like the
** first one (except that it has at least one less '__close' to do). In
** particular, field CIST_RECST preserves the error status across these
** multiple runs, changing only if there is a new error.
*/
static int finishpcallk (lua_State *L, CallInfo *ci) {
int status = getcistrecst(ci); /* get original status */
if (l_likely(status == LUA_OK)) /* no error? */
status = LUA_YIELD; /* was interrupted by an yield */
else { /* error */
StkId func = restorestack(L, ci->u2.funcidx);
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
luaF_close(L, func, status, 1); /* can yield or raise an error */
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
luaD_seterrorobj(L, status, func);
luaD_shrinkstack(L); /* restore stack size in case of overflow */
setcistrecst(ci, LUA_OK); /* clear original status */
}
ci->callstatus &= ~CIST_YPCALL;
L->errfunc = ci->u.c.old_errfunc;
/* if it is here, there were errors or yields; unlike 'lua_pcallk',
do not change status */
return status;
}
/*
** Completes the execution of a C function interrupted by an yield.
** The interruption must have happened while the function was either
** closing its tbc variables in 'moveresults' or executing
** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
** finishes the interrupted execution of 'lua_pcallk'. After that, it
** calls the continuation of the interrupted function and finally it
** completes the job of the 'luaD_call' that called the function. In
** the call to 'adjustresults', we do not know the number of results
** of the function called by 'lua_callk'/'lua_pcallk', so we are
** conservative and use LUA_MULTRET (always adjust).
*/
static void finishCcall (lua_State *L, CallInfo *ci) {
int n; /* actual number of results from C function */
if (ci->callstatus & CIST_CLSRET) { /* was returning? */
lua_assert(hastocloseCfunc(ci->nresults));
n = ci->u2.nres; /* just redo 'luaD_poscall' */
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
}
else {
int status = LUA_YIELD; /* default if there were no errors */
/* must have a continuation and must be able to call it */
lua_assert(ci->u.c.k != NULL && yieldable(L));
if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
status = finishpcallk(L, ci); /* finish it */
adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
lua_unlock(L);
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
lua_lock(L);
api_checknelems(L, n);
}
/* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
handled */
adjustresults(L, ci->nresults);
lua_unlock(L);
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n); /* finish 'luaD_call' */
}
@@ -556,18 +672,14 @@ static void finishCcall (lua_State *L, int status) {
/*
** Executes "full continuation" (everything in the stack) of a
** previously interrupted coroutine until the stack is empty (or another
** interruption long-jumps out of the loop). If the coroutine is
** recovering from an error, 'ud' points to the error status, which must
** be passed to the first continuation function (otherwise the default
** status is LUA_YIELD).
** interruption long-jumps out of the loop).
*/
static void unroll (lua_State *L, void *ud) {
CallInfo *ci;
if (ud != NULL) /* error status? */
finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
UNUSED(ud);
while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
if (!isLua(ci)) /* C function? */
finishCcall(L, LUA_YIELD); /* complete its execution */
finishCcall(L, ci); /* complete its execution */
else { /* Lua function */
luaV_finishOp(L); /* finish interrupted instruction */
luaV_execute(L, ci); /* execute down to higher C 'boundary' */
@@ -590,28 +702,6 @@ static CallInfo *findpcall (lua_State *L) {
}
/*
** Recovers from an error in a coroutine. Finds a recover point (if
** there is one) and completes the execution of the interrupted
** 'luaD_pcall'. If there is no recover point, returns zero.
*/
static int recover (lua_State *L, int status) {
StkId oldtop;
CallInfo *ci = findpcall(L);
if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u2.funcidx);
luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
L->ci = ci;
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
luaD_shrinkstack(L);
L->errfunc = ci->u.c.old_errfunc;
return 1; /* continue running the coroutine */
}
/*
** Signal an error in the call to 'lua_resume', not in the execution
** of the coroutine itself. (Such errors should not be handled by any
@@ -637,14 +727,16 @@ static void resume (lua_State *L, void *ud) {
int n = *(cast(int*, ud)); /* number of arguments */
StkId firstArg = L->top - n; /* first argument */
CallInfo *ci = L->ci;
if (L->status == LUA_OK) { /* starting a coroutine? */
luaD_call(L, firstArg - 1, LUA_MULTRET);
}
if (L->status == LUA_OK) /* starting a coroutine? */
ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */
else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */
if (isLua(ci)) /* yielded inside a hook? */
luaE_incCstack(L); /* control the C stack */
if (isLua(ci)) { /* yielded inside a hook? */
L->top = firstArg; /* discard arguments */
luaV_execute(L, ci); /* just continue running Lua code */
}
else { /* 'common' yield */
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
lua_unlock(L);
@@ -658,6 +750,26 @@ static void resume (lua_State *L, void *ud) {
}
}
/*
** Unrolls a coroutine in protected mode while there are recoverable
** errors, that is, errors inside a protected call. (Any error
** interrupts 'unroll', and this loop protects it again so it can
** continue.) Stops with a normal end (status == LUA_OK), an yield
** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
** find a recover point).
*/
static int precover (lua_State *L, int status) {
CallInfo *ci;
while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
L->ci = ci; /* go down to recovery functions */
setcistrecst(ci, status); /* status to finish 'pcall' */
status = luaD_rawrunprotected(L, unroll, NULL);
}
return status;
}
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
int *nresults) {
int status;
@@ -670,21 +782,13 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
}
else if (L->status != LUA_YIELD) /* ended with errors? */
return resume_error(L, "cannot resume dead coroutine", nargs);
if (from == NULL)
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
L->nCcalls = getCcalls(from) - L->nci - CSTACKCF;
if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs);
L->nCcalls = (from) ? getCcalls(from) : 0;
luai_userstateresume(L, nargs);
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs);
/* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (likely(!errorstatus(status)))
status = precover(L, status);
if (l_likely(!errorstatus(status)))
lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */
L->status = cast_byte(status); /* mark thread as 'dead' */
@@ -710,22 +814,22 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
lua_lock(L);
ci = L->ci;
api_checknelems(L, nresults);
if (unlikely(!yieldable(L))) {
if (l_unlikely(!yieldable(L))) {
if (L != G(L)->mainthread)
luaG_runerror(L, "attempt to yield across a C-call boundary");
else
luaG_runerror(L, "attempt to yield from outside a coroutine");
}
L->status = LUA_YIELD;
ci->u2.nyield = nresults; /* save number of results */
if (isLua(ci)) { /* inside a hook? */
lua_assert(!isLuacode(ci));
api_check(L, nresults == 0, "hooks cannot yield values");
api_check(L, k == NULL, "hooks cannot continue after yielding");
ci->u2.nyield = 0; /* no results */
}
else {
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
ci->u.c.ctx = ctx; /* save context */
ci->u2.nyield = nresults; /* save number of results */
luaD_throw(L, LUA_YIELD);
}
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
@@ -734,6 +838,45 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
}
/*
** Auxiliary structure to call 'luaF_close' in protected mode.
*/
struct CloseP {
StkId level;
int status;
};
/*
** Auxiliary function to call 'luaF_close' in protected mode.
*/
static void closepaux (lua_State *L, void *ud) {
struct CloseP *pcl = cast(struct CloseP *, ud);
luaF_close(L, pcl->level, pcl->status, 0);
}
/*
** Calls 'luaF_close' in protected mode. Return the original status
** or, in case of errors, the new status.
*/
int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
CallInfo *old_ci = L->ci;
lu_byte old_allowhooks = L->allowhook;
for (;;) { /* keep closing upvalues until no more errors */
struct CloseP pcl;
pcl.level = restorestack(L, level); pcl.status = status;
status = luaD_rawrunprotected(L, &closepaux, &pcl);
if (l_likely(status == LUA_OK)) /* no more errors? */
return pcl.status;
else { /* an error occurred; restore saved state and repeat */
L->ci = old_ci;
L->allowhook = old_allowhooks;
}
}
}
/*
** Call the C function 'func' in protected mode, restoring basic
** thread information ('allowhook', etc.) and in particular
@@ -747,14 +890,12 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u);
if (unlikely(status != LUA_OK)) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
L->ci = old_ci;
L->allowhook = old_allowhooks;
status = luaF_close(L, oldtop, status);
oldtop = restorestack(L, old_top); /* previous call may change stack */
luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L);
status = luaD_closeprotected(L, old_top, status);
luaD_seterrorobj(L, status, restorestack(L, old_top));
luaD_shrinkstack(L); /* restore stack size in case of overflow */
}
L->errfunc = old_errfunc;
return status;

4
ldo.h
View File

@@ -23,7 +23,7 @@
** at every check.
*/
#define luaD_checkstackaux(L,n,pre,pos) \
if (L->stack_last - L->top <= (n)) \
if (l_unlikely(L->stack_last - L->top <= (n))) \
{ pre; luaD_growstack(L, n, 1); pos; } \
else { condmovestack(L,pre,pos); }
@@ -59,9 +59,11 @@ LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
int fTransfer, int nTransfer);
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);

172
lfunc.c
View File

@@ -53,7 +53,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
uv->v = &uv->u.value; /* make it closed */
setnilvalue(uv->v);
cl->upvals[i] = uv;
luaC_objbarrier(L, cl, o);
luaC_objbarrier(L, cl, uv);
}
}
@@ -100,115 +100,83 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
}
static void callclose (lua_State *L, void *ud) {
UNUSED(ud);
luaD_callnoyield(L, L->top - 3, 0);
}
/*
** Prepare closing method plus its arguments for object 'obj' with
** error message 'err'. (This function assumes EXTRA_STACK.)
** Call closing method for object 'obj' with error message 'err'. The
** boolean 'yy' controls whether the call is yieldable.
** (This function assumes EXTRA_STACK.)
*/
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
StkId top = L->top;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
return 0; /* nothing to call */
setobj2s(L, top, tm); /* will call metamethod... */
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
L->top = top + 3; /* add function and arguments */
return 1;
if (yy)
luaD_call(L, top, 0);
else
luaD_callnoyield(L, top, 0);
}
/*
** Raise an error with message 'msg', inserting the name of the
** local variable at position 'level' in the stack.
** Check whether object at given level has a close metamethod and raise
** an error if not.
*/
static void varerror (lua_State *L, StkId level, const char *msg) {
int idx = cast_int(level - L->ci->func);
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
if (vname == NULL) vname = "?";
luaG_runerror(L, msg, vname);
static void checkclosemth (lua_State *L, StkId level) {
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
if (ttisnil(tm)) { /* no metamethod? */
int idx = cast_int(level - L->ci->func); /* variable index */
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
if (vname == NULL) vname = "?";
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
}
}
/*
** Prepare and call a closing method. If status is OK, code is still
** inside the original protected call, and so any error will be handled
** there. Otherwise, a previous error already activated the original
** protected call, and so the call to the closing method must be
** protected here. (A status == CLOSEPROTECT behaves like a previous
** error, to also run the closing method in protected mode).
** If status is OK, the call to the closing method will be pushed
** at the top of the stack. Otherwise, values are pushed after
** the 'level' of the upvalue being closed, as everything after
** that won't be used again.
** Prepare and call a closing method.
** If status is CLOSEKTOP, the call to the closing method will be pushed
** at the top of the stack. Otherwise, values can be pushed right after
** the 'level' of the upvalue being closed, as everything after that
** won't be used again.
*/
static int callclosemth (lua_State *L, StkId level, int status) {
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
TValue *uv = s2v(level); /* value being closed */
if (likely(status == LUA_OK)) {
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
callclose(L, NULL); /* call closing method */
else if (!l_isfalse(uv)) /* non-closable non-false value? */
varerror(L, level, "attempt to close non-closable variable '%s'");
TValue *errobj;
if (status == CLOSEKTOP)
errobj = &G(L)->nilvalue; /* error object is nil */
else { /* 'luaD_seterrorobj' will set top to level + 2 */
errobj = s2v(level + 1); /* error object goes after 'uv' */
luaD_seterrorobj(L, status, level + 1); /* set error object */
}
else { /* must close the object in protected mode */
ptrdiff_t oldtop;
level++; /* space for error message */
oldtop = savestack(L, level + 1); /* top will be after that */
luaD_seterrorobj(L, status, level); /* set error message */
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
status = newstatus; /* this will be the new error */
else {
if (newstatus != LUA_OK) /* suppressed error? */
luaE_warnerror(L, "__close metamethod");
/* leave original error (or nil) on top */
L->top = restorestack(L, oldtop);
}
}
/* else no metamethod; ignore this case and keep original error */
}
return status;
callclosemethod(L, uv, errobj, yy);
}
/*
** Try to create a to-be-closed upvalue
** (can raise a memory-allocation error)
** Maximum value for deltas in 'tbclist', dependent on the type
** of delta. (This macro assumes that an 'L' is in scope where it
** is used.)
*/
static void trynewtbcupval (lua_State *L, void *ud) {
newupval(L, 1, cast(StkId, ud), &L->openupval);
}
#define MAXDELTA \
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
/*
** Create a to-be-closed upvalue. If there is a memory error
** when creating the upvalue, the closing method must be called here,
** as there is no upvalue to call it later.
** Insert a variable in the list of to-be-closed variables.
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
TValue *obj = s2v(level);
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
int status;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
varerror(L, level, "variable '%s' got a non-closable value");
status = luaD_rawrunprotected(L, trynewtbcupval, level);
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
lua_assert(status == LUA_ERRMEM);
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
/* next call must succeed, as object is closable */
prepclosingmethod(L, s2v(level), s2v(level + 1));
callclose(L, NULL); /* call closing method */
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
}
lua_assert(level > L->tbclist);
if (l_isfalse(s2v(level)))
return; /* false doesn't need to be closed */
checkclosemth(L, level); /* value must have a close method */
while (cast_uint(level - L->tbclist) > MAXDELTA) {
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
L->tbclist->tbclist.delta = 0;
}
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
L->tbclist = level;
}
@@ -220,18 +188,16 @@ void luaF_unlinkupval (UpVal *uv) {
}
int luaF_close (lua_State *L, StkId level, int status) {
/*
** Close all upvalues up to the given stack level.
*/
void luaF_closeupval (lua_State *L, StkId level) {
UpVal *uv;
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
StkId upl; /* stack index pointed by 'uv' */
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top);
if (uv->tbc && status != NOCLOSINGMETH) {
/* must run closing method, which may change the stack */
ptrdiff_t levelrel = savestack(L, level);
status = callclosemth(L, uplevel(uv), status);
level = restorestack(L, levelrel);
}
luaF_unlinkupval(uv);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
setobj(L, slot, uv->v); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */
if (!iswhite(uv)) { /* neither white nor dead? */
@@ -239,7 +205,35 @@ int luaF_close (lua_State *L, StkId level, int status) {
luaC_barrier(L, uv, slot);
}
}
return status;
}
/*
** Remove firt element from the tbclist plus its dummy nodes.
*/
static void poptbclist (lua_State *L) {
StkId tbc = L->tbclist;
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
tbc -= tbc->tbclist.delta;
while (tbc > L->stack && tbc->tbclist.delta == 0)
tbc -= MAXDELTA; /* remove dummy nodes */
L->tbclist = tbc;
}
/*
** Close all upvalues and to-be-closed variables up to the given stack
** level.
*/
void luaF_close (lua_State *L, StkId level, int status, int yy) {
ptrdiff_t levelrel = savestack(L, level);
luaF_closeupval(L, level); /* first, close the upvalues */
while (L->tbclist >= level) { /* traverse tbc's down to that level */
StkId tbc = L->tbclist; /* get variable index */
poptbclist(L); /* remove it from list */
prepcallclosemth(L, tbc, status, yy); /* close variable */
level = restorestack(L, levelrel);
}
}

13
lfunc.h
View File

@@ -42,15 +42,9 @@
#define MAXMISS 10
/*
** Special "status" for 'luaF_close'
*/
/* close upvalues without running their closing methods */
#define NOCLOSINGMETH (-1)
/* close upvalues running all closing methods in protected mode */
#define CLOSEPROTECT (-2)
/* special status to close upvalues preserving the top of the stack */
#define CLOSEKTOP (-1)
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
@@ -59,7 +53,8 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,

92
lgc.c
View File

@@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
/*
** Clear keys for empty entries in tables. If entry is empty
** and its key is not marked, mark its entry as dead. This allows the
** collection of the key, but keeps its entry in the table (its removal
** could break a chain). The main feature of a dead key is that it must
** be different from any other value, to do not disturb searches.
** Other places never manipulate dead keys, because its associated empty
** value is enough to signal that the entry is logically empty.
** Clear keys for empty entries in tables. If entry is empty, mark its
** entry as dead. This allows the collection of the key, but keeps its
** entry in the table: its removal could break a chain and could break
** a table traversal. Other places never manipulate dead keys, because
** its associated empty value is enough to signal that the entry is
** logically empty.
*/
static void clearkey (Node *n) {
lua_assert(isempty(gval(n)));
if (keyiswhite(n))
setdeadkey(n); /* unused and unmarked key; remove it */
if (keyiscollectable(n))
setdeadkey(n); /* unused key; remove it */
}
@@ -301,7 +300,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
if (upisopen(uv))
set2gray(uv); /* open upvalues are kept gray */
else
set2black(o); /* closed upvalues are visited here */
set2black(uv); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */
break;
}
@@ -309,7 +308,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
Udata *u = gco2u(o);
if (u->nuvalue == 0) { /* no user values? */
markobjectN(g, u->metatable); /* mark its metatable */
set2black(o); /* nothing else to mark */
set2black(u); /* nothing else to mark */
break;
}
/* else... */
@@ -633,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) {
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */
setnilvalue(s2v(o));
for (; o < th->stack_last + EXTRA_STACK; o++)
setnilvalue(s2v(o)); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) {
th->twups = g->twups; /* link it back to the list */
@@ -644,7 +642,7 @@ static int traversethread (global_State *g, lua_State *th) {
}
else if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */
return 1 + th->stacksize;
return 1 + stacksize(th);
}
@@ -771,12 +769,16 @@ static void freeobj (lua_State *L, GCObject *o) {
case LUA_VUPVAL:
freeupval(L, gco2upv(o));
break;
case LUA_VLCL:
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
case LUA_VLCL: {
LClosure *cl = gco2lcl(o);
luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
break;
case LUA_VCCL:
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
}
case LUA_VCCL: {
CClosure *cl = gco2ccl(o);
luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
break;
}
case LUA_VTABLE:
luaH_free(L, gco2t(o));
break;
@@ -788,13 +790,17 @@ static void freeobj (lua_State *L, GCObject *o) {
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
break;
}
case LUA_VSHRSTR:
luaS_remove(L, gco2ts(o)); /* remove it from hash table */
luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen));
case LUA_VSHRSTR: {
TString *ts = gco2ts(o);
luaS_remove(L, ts); /* remove it from hash table */
luaM_freemem(L, ts, sizelstring(ts->shrlen));
break;
case LUA_VLNGSTR:
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen));
}
case LUA_VLNGSTR: {
TString *ts = gco2ts(o);
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
break;
}
default: lua_assert(0);
}
}
@@ -910,7 +916,7 @@ static void GCTM (lua_State *L) {
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
L->allowhook = oldah; /* restore hooks */
g->gcrunning = running; /* restore state */
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
luaE_warnerror(L, "__gc metamethod");
L->top--; /* pops error object */
}
@@ -1569,52 +1575,64 @@ static int sweepstep (lua_State *L, global_State *g,
static lu_mem singlestep (lua_State *L) {
global_State *g = G(L);
lu_mem work;
lua_assert(!g->gcstopem); /* collector is not reentrant */
g->gcstopem = 1; /* no emergency collections while collecting */
switch (g->gcstate) {
case GCSpause: {
restartcollection(g);
g->gcstate = GCSpropagate;
return 1;
work = 1;
break;
}
case GCSpropagate: {
if (g->gray == NULL) { /* no more gray objects? */
g->gcstate = GCSenteratomic; /* finish propagate phase */
return 0;
work = 0;
}
else
return propagatemark(g); /* traverse one gray object */
work = propagatemark(g); /* traverse one gray object */
break;
}
case GCSenteratomic: {
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L);
g->GCestimate = gettotalbytes(g); /* first estimate */;
return work;
break;
}
case GCSswpallgc: { /* sweep "regular" objects */
return sweepstep(L, g, GCSswpfinobj, &g->finobj);
work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
break;
}
case GCSswpfinobj: { /* sweep objects with finalizers */
return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
break;
}
case GCSswptobefnz: { /* sweep objects to be finalized */
return sweepstep(L, g, GCSswpend, NULL);
work = sweepstep(L, g, GCSswpend, NULL);
break;
}
case GCSswpend: { /* finish sweeps */
checkSizes(L, g);
g->gcstate = GCScallfin;
return 0;
work = 0;
break;
}
case GCScallfin: { /* call remaining finalizers */
if (g->tobefnz && !g->gcemergency) {
int n = runafewfinalizers(L, GCFINMAX);
return n * GCFINALIZECOST;
g->gcstopem = 0; /* ok collections during finalizers */
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
}
else { /* emergency mode or no more finalizers */
g->gcstate = GCSpause; /* finish collection */
return 0;
work = 0;
}
break;
}
default: lua_assert(0); return 0;
}
g->gcstopem = 0;
return work;
}

View File

@@ -52,12 +52,6 @@ static int l_checkmode (const char *mode) {
** =======================================================
*/
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as mode */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
#if !defined(l_popen) /* { */
#if defined(LUA_USE_POSIX) /* { */
@@ -70,6 +64,12 @@ static int l_checkmode (const char *mode) {
#define l_popen(L,c,m) (_popen(c,m))
#define l_pclose(L,file) (_pclose(file))
#if !defined(l_checkmodep)
/* Windows accepts "[rw][bt]?" as valid modes */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
(m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
#endif
#else /* }{ */
/* ISO C definitions */
@@ -83,6 +83,12 @@ static int l_checkmode (const char *mode) {
#endif /* } */
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as valid modes */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
/* }====================================================== */
@@ -180,7 +186,7 @@ static int f_tostring (lua_State *L) {
static FILE *tofile (lua_State *L) {
LStream *p = tolstream(L);
if (isclosed(p))
if (l_unlikely(isclosed(p)))
luaL_error(L, "attempt to use a closed file");
lua_assert(p->f);
return p->f;
@@ -255,7 +261,7 @@ static LStream *newfile (lua_State *L) {
static void opencheck (lua_State *L, const char *fname, const char *mode) {
LStream *p = newfile(L);
p->f = fopen(fname, mode);
if (p->f == NULL)
if (l_unlikely(p->f == NULL))
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
}
@@ -303,7 +309,7 @@ static FILE *getiofile (lua_State *L, const char *findex) {
LStream *p;
lua_getfield(L, LUA_REGISTRYINDEX, findex);
p = (LStream *)lua_touserdata(L, -1);
if (isclosed(p))
if (l_unlikely(isclosed(p)))
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
return p->f;
}
@@ -430,7 +436,7 @@ typedef struct {
** Add current char to buffer (if not out of space) and read next one
*/
static int nextc (RN *rn) {
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
rn->buff[0] = '\0'; /* invalidate result */
return 0; /* fail */
}
@@ -493,8 +499,8 @@ static int read_number (lua_State *L, FILE *f) {
ungetc(rn.c, rn.f); /* unread look-ahead char */
l_unlockfile(rn.f);
rn.buff[rn.n] = '\0'; /* finish string */
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
return 1; /* ok */
if (l_likely(lua_stringtonumber(L, rn.buff)))
return 1; /* ok, it is a valid number */
else { /* invalid format */
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
@@ -670,7 +676,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
if (status) return 1; /* file handle already on stack top */
if (l_likely(status))
return 1; /* file handle already on stack top */
else return luaL_fileresult(L, status, NULL);
}
@@ -697,7 +704,7 @@ static int f_seek (lua_State *L) {
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range");
op = l_fseek(f, offset, mode[op]);
if (op)
if (l_unlikely(op))
return luaL_fileresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, (lua_Integer)l_ftell(f));

56
llex.c
View File

@@ -122,26 +122,29 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
/*
** creates a new string and anchors it in scanner's table so that
** it will not be collected until the end of the compilation
** (by that time it should be anchored somewhere)
** Creates a new string and anchors it in scanner's table so that it
** will not be collected until the end of the compilation; by that time
** it should be anchored somewhere. It also internalizes long strings,
** ensuring there is only one copy of each unique string. The table
** here is used as a set: the string enters as the key, while its value
** is irrelevant. We use the string itself as the value only because it
** is a TValue readly available. Later, the code generation can change
** this value.
*/
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TValue *o; /* entry for 'str' */
TString *ts = luaS_newlstr(L, str, l); /* create new string */
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
o = luaH_set(L, ls->h, s2v(L->top - 1));
if (isempty(o)) { /* not in use yet? */
/* boolean value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */
setbtvalue(o); /* t[string] = true */
const TValue *o = luaH_getstr(ls->h, ts);
if (!ttisnil(o)) /* string already present? */
ts = keystrval(nodefromval(o)); /* get saved copy */
else { /* not in use yet */
TValue *stv = s2v(L->top++); /* reserve stack space for string */
setsvalue(L, stv, ts); /* temporarily anchor the string */
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
/* table is not a metatable, so it does not need to invalidate cache */
luaC_checkGC(L);
L->top--; /* remove string from stack */
}
else { /* string already present */
ts = keystrval(nodefromval(o)); /* re-use value previously stored */
}
L->top--; /* remove string from stack */
return ts;
}
@@ -254,9 +257,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
/*
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
** If sequence is well formed, return its number of '='s + 2; otherwise,
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
** sequence is well formed, return its number of '='s + 2; otherwise,
** return 1 if it is a single bracket (no '='s and no 2nd bracket);
** otherwise (an unfinished '[==...') return 0.
*/
static size_t skip_sep (LexState *ls) {
size_t count = 0;
@@ -481,34 +485,34 @@ static int llex (LexState *ls, SemInfo *seminfo) {
}
case '=': {
next(ls);
if (check_next1(ls, '=')) return TK_EQ;
if (check_next1(ls, '=')) return TK_EQ; /* '==' */
else return '=';
}
case '<': {
next(ls);
if (check_next1(ls, '=')) return TK_LE;
else if (check_next1(ls, '<')) return TK_SHL;
if (check_next1(ls, '=')) return TK_LE; /* '<=' */
else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
else return '<';
}
case '>': {
next(ls);
if (check_next1(ls, '=')) return TK_GE;
else if (check_next1(ls, '>')) return TK_SHR;
if (check_next1(ls, '=')) return TK_GE; /* '>=' */
else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
else return '>';
}
case '/': {
next(ls);
if (check_next1(ls, '/')) return TK_IDIV;
if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
else return '/';
}
case '~': {
next(ls);
if (check_next1(ls, '=')) return TK_NE;
if (check_next1(ls, '=')) return TK_NE; /* '~=' */
else return '~';
}
case ':': {
next(ls);
if (check_next1(ls, ':')) return TK_DBCOLON;
if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
else return ':';
}
case '"': case '\'': { /* short literal strings */
@@ -547,7 +551,7 @@ static int llex (LexState *ls, SemInfo *seminfo) {
return TK_NAME;
}
}
else { /* single-char tokens (+ - / ...) */
else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
int c = ls->current;
next(ls);
return c;

View File

@@ -149,22 +149,6 @@ typedef LUAI_UACINT l_uacInt;
#endif
/*
** macros to improve jump prediction (used mainly for error handling)
*/
#if !defined(likely)
#if defined(__GNUC__)
#define likely(x) (__builtin_expect(((x) != 0), 1))
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#endif
/*
** non-return type
*/
@@ -234,6 +218,17 @@ typedef l_uint32 Instruction;
#endif
/*
** Maximum depth for nested C calls, syntactical nested non-terminals,
** and other features implemented through recursion in C. (Value must
** fit in a 16-bit unsigned integer. It must also be compatible with
** the size of the C stack.)
*/
#if !defined(LUAI_MAXCCALLS)
#define LUAI_MAXCCALLS 200
#endif
/*
** macros that are executed whenever program enters the Lua core
** ('lua_lock') and leaves the core ('lua_unlock')
@@ -315,7 +310,8 @@ typedef l_uint32 Instruction;
/* exponentiation */
#if !defined(luai_numpow)
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b))
#define luai_numpow(L,a,b) \
((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
#endif
/* the others are quite standard operations */
@@ -344,7 +340,7 @@ typedef l_uint32 Instruction;
#else
/* realloc stack keeping its size */
#define condmovestack(L,pre,pos) \
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; }
{ int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
#endif
#if !defined(HARDMEMTESTS)

View File

@@ -73,7 +73,7 @@ static int math_atan (lua_State *L) {
static int math_toint (lua_State *L) {
int valid;
lua_Integer n = lua_tointegerx(L, 1, &valid);
if (valid)
if (l_likely(valid))
lua_pushinteger(L, n);
else {
luaL_checkany(L, 1);
@@ -175,7 +175,8 @@ static int math_log (lua_State *L) {
lua_Number base = luaL_checknumber(L, 2);
#if !defined(LUA_USE_C89)
if (base == l_mathop(2.0))
res = l_mathop(log2)(x); else
res = l_mathop(log2)(x);
else
#endif
if (base == l_mathop(10.0))
res = l_mathop(log10)(x);

33
lmem.c
View File

@@ -24,12 +24,12 @@
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail whenever not building initial state
** and not shrinking a block. (This fail will trigger 'tryagain' and
** a full GC cycle at every allocation.)
** First allocation will fail whenever not building initial state.
** (This fail will trigger 'tryagain' and a full GC cycle at every
** allocation.)
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (ttisnil(&g->nilvalue) && ns > os)
if (completestate(g) && ns > 0) /* frees never fail */
return NULL; /* fail */
else /* normal allocation */
return (*g->frealloc)(g->ud, block, os, ns);
@@ -83,7 +83,7 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
if (nelems + 1 <= size) /* does one extra element still fit? */
return block; /* nothing to be done */
if (size >= limit / 2) { /* cannot double it? */
if (unlikely(size >= limit)) /* cannot grow even a little? */
if (l_unlikely(size >= limit)) /* cannot grow even a little? */
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
size = limit; /* still have at least one free place */
}
@@ -138,15 +138,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
/*
** In case of allocation fail, this function will call the GC to try
** to free some memory and then try the allocation again.
** (It should not be called when shrinking a block, because then the
** interpreter may be in the middle of a collection step.)
** In case of allocation fail, this function will do an emergency
** collection to free some memory and then try the allocation again.
** The GC should not be called while state is not fully built, as the
** collector is not yet fully initialized. Also, it should not be called
** when 'gcstopem' is true, because then the interpreter is in the
** middle of a collection step.
*/
static void *tryagain (lua_State *L, void *block,
size_t osize, size_t nsize) {
global_State *g = G(L);
if (ttisnil(&g->nilvalue)) { /* is state fully build? */
if (completestate(g) && !g->gcstopem) {
luaC_fullgc(L, 1); /* try to free some memory... */
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
}
@@ -156,17 +158,14 @@ static void *tryagain (lua_State *L, void *block,
/*
** Generic allocation routine.
** If allocation fails while shrinking a block, do not try again; the
** GC shrinks some blocks and it is not reentrant.
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
void *newblock;
global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL));
newblock = firsttry(g, block, osize, nsize);
if (unlikely(newblock == NULL && nsize > 0)) {
if (nsize > osize) /* not shrinking a block? */
newblock = tryagain(L, block, osize, nsize);
if (l_unlikely(newblock == NULL && nsize > 0)) {
newblock = tryagain(L, block, osize, nsize);
if (newblock == NULL) /* still no memory? */
return NULL; /* do not update 'GCdebt' */
}
@@ -179,7 +178,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
size_t nsize) {
void *newblock = luaM_realloc_(L, block, osize, nsize);
if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
luaM_error(L);
return newblock;
}
@@ -191,7 +190,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
else {
global_State *g = G(L);
void *newblock = firsttry(g, NULL, tag, size);
if (unlikely(newblock == NULL)) {
if (l_unlikely(newblock == NULL)) {
newblock = tryagain(L, NULL, tag, size);
if (newblock == NULL)
luaM_error(L);

View File

@@ -132,14 +132,16 @@ static void lsys_unloadlib (void *lib) {
static void *lsys_load (lua_State *L, const char *path, int seeglb) {
void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
if (lib == NULL) lua_pushstring(L, dlerror());
if (l_unlikely(lib == NULL))
lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = cast_func(dlsym(lib, sym));
if (f == NULL) lua_pushstring(L, dlerror());
if (l_unlikely(f == NULL))
lua_pushstring(L, dlerror());
return f;
}
@@ -410,7 +412,7 @@ static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = lookforfunc(L, path, init);
if (stat == 0) /* no errors? */
if (l_likely(stat == 0)) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
luaL_pushfail(L);
@@ -523,14 +525,14 @@ static const char *findfile (lua_State *L, const char *name,
const char *path;
lua_getfield(L, lua_upvalueindex(1), pname);
path = lua_tostring(L, -1);
if (path == NULL)
if (l_unlikely(path == NULL))
luaL_error(L, "'package.%s' must be a string", pname);
return searchpath(L, name, path, ".", dirsep);
}
static int checkload (lua_State *L, int stat, const char *filename) {
if (stat) { /* module loaded successfully? */
if (l_likely(stat)) { /* module loaded successfully? */
lua_pushstring(L, filename); /* will be 2nd argument to module */
return 2; /* return open function and file name */
}
@@ -623,13 +625,14 @@ static void findloader (lua_State *L, const char *name) {
int i;
luaL_Buffer msg; /* to build error message */
/* push 'package.searchers' to index 3 in the stack */
if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE)
if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers")
!= LUA_TTABLE))
luaL_error(L, "'package.searchers' must be a table");
luaL_buffinit(L, &msg);
/* iterate over available searchers to find a loader */
for (i = 1; ; i++) {
luaL_addstring(&msg, "\n\t"); /* error-message prefix */
if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */
if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */
lua_pop(L, 1); /* remove nil */
luaL_buffsub(&msg, 2); /* remove prefix */
luaL_pushresult(&msg); /* create error message */

View File

@@ -258,7 +258,7 @@ static const char *l_str2d (const char *s, lua_Number *result) {
if (endptr == NULL) { /* failed? may be a different locale */
char buff[L_MAXLENNUM + 1];
const char *pdot = strchr(s, '.');
if (strlen(s) > L_MAXLENNUM || pdot == NULL)
if (pdot == NULL || strlen(s) > L_MAXLENNUM)
return NULL; /* string too long or no dot; fail */
strcpy(buff, s); /* copy string to buffer */
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */

View File

@@ -21,10 +21,12 @@
*/
#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
/*
** number of all possible types (including LUA_TNONE)
** number of all possible types (including LUA_TNONE but excluding DEADKEY)
*/
#define LUA_TOTALTYPES (LUA_TPROTO + 2)
@@ -134,10 +136,19 @@ typedef struct TValue {
/*
** Entries in the Lua stack
** Entries in a Lua stack. Field 'tbclist' forms a list of all
** to-be-closed variables active in this stack. Dummy entries are
** used when the distance between two tbc variables does not fit
** in an unsigned short. They are represented by delta==0, and
** their real delta is always the maximum value that fits in
** that field.
*/
typedef union StackValue {
TValue val;
struct {
TValuefields;
unsigned short delta;
} tbclist;
} StackValue;
@@ -555,7 +566,7 @@ typedef struct Proto {
/*
** {==================================================================
** Closures
** Functions
** ===================================================================
*/
@@ -568,10 +579,11 @@ typedef struct Proto {
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL)
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
#define ttislcf(o) checktag((o), LUA_VLCF)
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
#define isLfunction(o) ttisLclosure(o)
@@ -743,13 +755,13 @@ typedef struct Table {
/*
** Use a "nil table" to mark dead keys in a table. Those keys serve
** to keep space for removed entries, which may still be part of
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
** set, so these values are considered not collectable and are different
** from any valid value.
** Dead keys in tables have the tag DEADKEY but keep their original
** gcvalue. This distinguishes them from regular keys but allows them to
** be found when searched in a special way. ('next' needs that to find
** keys removed from a table during a traversal.)
*/
#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL)
#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
/* }================================================================== */

View File

@@ -225,13 +225,13 @@ OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
OP_ADDI,/* A B sC R[A] := R[B] + sC */
OP_ADDK,/* A B C R[A] := R[B] + K[C] */
OP_SUBK,/* A B C R[A] := R[B] - K[C] */
OP_MULK,/* A B C R[A] := R[B] * K[C] */
OP_MODK,/* A B C R[A] := R[B] % K[C] */
OP_POWK,/* A B C R[A] := R[B] ^ K[C] */
OP_DIVK,/* A B C R[A] := R[B] / K[C] */
OP_IDIVK,/* A B C R[A] := R[B] // K[C] */
OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */
OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
@@ -261,7 +261,7 @@ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
OP_UNM,/* A B R[A] := -R[B] */
OP_BNOT,/* A B R[A] := ~R[B] */
OP_NOT,/* A B R[A] := not R[B] */
OP_LEN,/* A B R[A] := length of R[B] */
OP_LEN,/* A B R[A] := #R[B] (length operator) */
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
@@ -297,7 +297,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */
OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */

View File

@@ -170,7 +170,7 @@ static int os_tmpname (lua_State *L) {
char buff[LUA_TMPNAMBUFSIZE];
int err;
lua_tmpnam(buff, err);
if (err)
if (l_unlikely(err))
return luaL_error(L, "unable to generate a unique filename");
lua_pushstring(L, buff);
return 1;
@@ -208,7 +208,7 @@ static int os_clock (lua_State *L) {
*/
static void setfield (lua_State *L, const char *key, int value, int delta) {
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
if (value > LUA_MAXINTEGER - delta)
if (l_unlikely(value > LUA_MAXINTEGER - delta))
luaL_error(L, "field '%s' is out-of-bound", key);
#endif
lua_pushinteger(L, (lua_Integer)value + delta);
@@ -253,9 +253,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
int t = lua_getfield(L, -1, key); /* get field and its type */
lua_Integer res = lua_tointegerx(L, -1, &isnum);
if (!isnum) { /* field is not an integer? */
if (t != LUA_TNIL) /* some other value? */
if (l_unlikely(t != LUA_TNIL)) /* some other value? */
return luaL_error(L, "field '%s' is not an integer", key);
else if (d < 0) /* absent field; no default? */
else if (l_unlikely(d < 0)) /* absent field; no default? */
return luaL_error(L, "field '%s' missing in date table", key);
res = d;
}

120
lparser.c
View File

@@ -128,7 +128,7 @@ static void checknext (LexState *ls, int c) {
** in line 'where' (if that is not the current line).
*/
static void check_match (LexState *ls, int what, int who, int where) {
if (unlikely(!testnext(ls, what))) {
if (l_unlikely(!testnext(ls, what))) {
if (where == ls->linenumber) /* all in the same line? */
error_expected(ls, what); /* do not need a complex message */
else {
@@ -222,26 +222,26 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
/*
** Convert 'nvar', a compiler index level, to it corresponding
** stack index level. For that, search for the highest variable
** below that level that is in the stack and uses its stack
** index ('sidx').
** Convert 'nvar', a compiler index level, to its corresponding
** register. For that, search for the highest variable below that level
** that is in a register and uses its register index ('ridx') plus one.
*/
static int stacklevel (FuncState *fs, int nvar) {
static int reglevel (FuncState *fs, int nvar) {
while (nvar-- > 0) {
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
if (vd->vd.kind != RDKCTC) /* is in the stack? */
return vd->vd.sidx + 1;
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
if (vd->vd.kind != RDKCTC) /* is in a register? */
return vd->vd.ridx + 1;
}
return 0; /* no variables in the stack */
return 0; /* no variables in registers */
}
/*
** Return the number of variables in the stack for function 'fs'
** Return the number of variables in the register stack for the given
** function.
*/
int luaY_nvarstack (FuncState *fs) {
return stacklevel(fs, fs->nactvar);
return reglevel(fs, fs->nactvar);
}
@@ -267,7 +267,7 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
e->f = e->t = NO_JUMP;
e->k = VLOCAL;
e->u.var.vidx = vidx;
e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx;
}
@@ -310,12 +310,12 @@ static void check_readonly (LexState *ls, expdesc *e) {
*/
static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs;
int stklevel = luaY_nvarstack(fs);
int reglevel = luaY_nvarstack(fs);
int i;
for (i = 0; i < nvars; i++) {
int vidx = fs->nactvar++;
Vardesc *var = getlocalvardesc(fs, vidx);
var->vd.sidx = stklevel++;
var->vd.ridx = reglevel++;
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
}
}
@@ -366,7 +366,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
FuncState *prev = fs->prev;
if (v->k == VLOCAL) {
up->instack = 1;
up->idx = v->u.var.sidx;
up->idx = v->u.var.ridx;
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
}
@@ -489,12 +489,10 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
}
/*
** Macros to limit the maximum recursion depth while parsing
*/
#define enterlevel(ls) luaE_enterCcall((ls)->L)
#define enterlevel(ls) luaE_incCstack(ls->L)
#define leavelevel(ls) luaE_exitCcall((ls)->L)
#define leavelevel(ls) ((ls)->L->nCcalls--)
/*
@@ -519,7 +517,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) {
Labellist *gl = &ls->dyd->gt; /* list of goto's */
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
lua_assert(eqstr(gt->name, label->name));
if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
jumpscopeerror(ls, gt);
luaK_patchlist(ls->fs, gt->pc, label->pc);
for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
@@ -622,7 +620,7 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
Labeldesc *gt = &gl->arr[i];
/* leaving a variable scope? */
if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar))
gt->close |= bl->upval; /* jump may need a close */
gt->nactvar = bl->nactvar; /* update goto level */
}
@@ -663,7 +661,7 @@ static void leaveblock (FuncState *fs) {
BlockCnt *bl = fs->bl;
LexState *ls = fs->ls;
int hasclose = 0;
int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
if (bl->isloop) /* fix pending breaks? */
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
if (!hasclose && bl->previous && bl->upval)
@@ -947,7 +945,7 @@ static void setvararg (FuncState *fs, int nparams) {
static void parlist (LexState *ls) {
/* parlist -> [ param { ',' param } ] */
/* parlist -> [ {NAME ','} (NAME | '...') ] */
FuncState *fs = ls->fs;
Proto *f = fs->f;
int nparams = 0;
@@ -955,12 +953,12 @@ static void parlist (LexState *ls) {
if (ls->t.token != ')') { /* is 'parlist' not empty? */
do {
switch (ls->t.token) {
case TK_NAME: { /* param -> NAME */
case TK_NAME: {
new_localvar(ls, str_checkname(ls));
nparams++;
break;
}
case TK_DOTS: { /* param -> '...' */
case TK_DOTS: {
luaX_next(ls);
isvararg = 1;
break;
@@ -1332,13 +1330,13 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
}
}
else { /* table is a register */
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) {
conflict = 1; /* table is the local being assigned now */
lh->v.u.ind.t = extra; /* assignment will use safe copy */
}
/* is index the local being assigned? */
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
lh->v.u.ind.idx == v->u.var.sidx) {
lh->v.u.ind.idx == v->u.var.ridx) {
conflict = 1;
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
}
@@ -1348,7 +1346,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
if (conflict) {
/* copy upvalue/local value to a temporary (in position 'extra') */
if (v->k == VLOCAL)
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0);
else
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
luaK_reserveregs(fs, 1);
@@ -1413,7 +1411,7 @@ static void gotostat (LexState *ls) {
newgotoentry(ls, name, line, luaK_jump(fs));
else { /* found a label */
/* backward jump; will be resolved here */
int lblevel = stacklevel(fs, lb->nactvar); /* label level */
int lblevel = reglevel(fs, lb->nactvar); /* label level */
if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
/* create jump and link it to the label */
@@ -1437,7 +1435,7 @@ static void breakstat (LexState *ls) {
*/
static void checkrepeated (LexState *ls, TString *name) {
Labeldesc *lb = findlabel(ls, name);
if (unlikely(lb != NULL)) { /* already defined? */
if (l_unlikely(lb != NULL)) { /* already defined? */
const char *msg = "label '%s' already defined on line %d";
msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
luaK_semerror(ls, msg); /* error */
@@ -1490,7 +1488,7 @@ static void repeatstat (LexState *ls, int line) {
if (bl2.upval) { /* upvalues? */
int exit = luaK_jump(fs); /* normal exit must jump over fix */
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0);
condexit = luaK_jump(fs); /* repeat after closing upvalues */
luaK_patchtohere(fs, exit); /* normal exit comes to here */
}
@@ -1522,7 +1520,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
int offset = dest - (pc + 1);
if (back)
offset = -offset;
if (unlikely(offset > MAXARG_Bx))
if (l_unlikely(offset > MAXARG_Bx))
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_Bx(*jmp, offset);
}
@@ -1625,59 +1623,21 @@ static void forstat (LexState *ls, int line) {
}
/*
** Check whether next instruction is a single jump (a 'break', a 'goto'
** to a forward label, or a 'goto' to a backward label with no variable
** to close). If so, set the name of the 'label' it is jumping to
** ("break" for a 'break') or to where it is jumping to ('target') and
** return true. If not a single jump, leave input unchanged, to be
** handled as a regular statement.
*/
static int issinglejump (LexState *ls, TString **label, int *target) {
if (testnext(ls, TK_BREAK)) { /* a break? */
*label = luaS_newliteral(ls->L, "break");
return 1;
}
else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
return 0; /* not a valid goto */
else {
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
Labeldesc *lb = findlabel(ls, lname);
if (lb) { /* a backward jump? */
/* does it need to close variables? */
if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
return 0; /* not a single jump; cannot optimize */
*target = lb->pc;
}
else /* jump forward */
*label = lname;
luaX_next(ls); /* skip goto */
luaX_next(ls); /* skip name */
return 1;
}
}
static void test_then_block (LexState *ls, int *escapelist) {
/* test_then_block -> [IF | ELSEIF] cond THEN block */
BlockCnt bl;
int line;
FuncState *fs = ls->fs;
TString *jlb = NULL;
int target = NO_JUMP;
expdesc v;
int jf; /* instruction to skip 'then' code (if condition is false) */
luaX_next(ls); /* skip IF or ELSEIF */
expr(ls, &v); /* read condition */
checknext(ls, TK_THEN);
line = ls->linenumber;
if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
int line = ls->linenumber;
luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
luaX_next(ls); /* skip 'break' */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */
if (jlb != NULL) /* forward jump? */
newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
else /* backward jump */
luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
while (testnext(ls, ';')) {} /* skip semicolons */
if (block_follow(ls, 0)) { /* jump is the entire block? */
leaveblock(fs);
@@ -1686,7 +1646,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
else /* must skip over 'then' part if condition is false */
jf = luaK_jump(fs);
}
else { /* regular case (not a jump) */
else { /* regular case (not a break) */
luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
enterblock(fs, &bl, 0);
jf = v.f;
@@ -1748,13 +1708,13 @@ static void checktoclose (LexState *ls, int level) {
FuncState *fs = ls->fs;
markupval(fs, level + 1);
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
}
}
static void localstat (LexState *ls) {
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
/* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */
FuncState *fs = ls->fs;
int toclose = -1; /* index of to-be-closed variable (if any) */
Vardesc *var; /* last variable */

View File

@@ -23,7 +23,7 @@
/* kinds of variables/expressions */
typedef enum {
VVOID, /* when 'expdesc' describes the last expression a list,
VVOID, /* when 'expdesc' describes the last expression of a list,
this kind means an empty list (so, no expression) */
VNIL, /* constant nil */
VTRUE, /* constant true */
@@ -35,10 +35,11 @@ typedef enum {
(string is fixed by the lexer) */
VNONRELOC, /* expression has its value in a fixed register;
info = result register */
VLOCAL, /* local variable; var.sidx = stack index (local register);
VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */
VCONST, /* compile-time <const> variable;
info = absolute index in 'actvar.arr' */
VINDEXED, /* indexed variable;
ind.t = table register;
ind.idx = key's R index */
@@ -76,7 +77,7 @@ typedef struct expdesc {
lu_byte t; /* table (register or upvalue) */
} ind;
struct { /* for local variables */
lu_byte sidx; /* index in the stack */
lu_byte ridx; /* register holding the variable */
unsigned short vidx; /* compiler index (in 'actvar.arr') */
} var;
} u;
@@ -96,7 +97,7 @@ typedef union Vardesc {
struct {
TValuefields; /* constant value (if it is a compile-time constant) */
lu_byte kind;
lu_byte sidx; /* index of the variable in the stack */
lu_byte ridx; /* register holding the variable */
short pidx; /* index of the variable in the Proto's 'locvars' array */
TString *name; /* variable name */
} vd;

151
lstate.c
View File

@@ -76,7 +76,7 @@ static unsigned int luai_makeseed (lua_State *L) {
addbuff(buff, p, &h); /* local variable */
addbuff(buff, p, &lua_newstate); /* public function */
lua_assert(p == sizeof(buff));
return luaS_hash(buff, p, h, 1);
return luaS_hash(buff, p, h);
}
#endif
@@ -97,66 +97,14 @@ void luaE_setdebt (global_State *g, l_mem debt) {
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
global_State *g = G(L);
int ccalls;
luaE_freeCI(L); /* release unused CIs */
ccalls = getCcalls(L);
if (limit >= 40000)
return 0; /* out of bounds */
limit += CSTACKERR;
if (L != g-> mainthread)
return 0; /* only main thread can change the C stack */
else if (ccalls <= CSTACKERR)
return 0; /* handling overflow */
else {
int diff = limit - g->Cstacklimit;
if (ccalls + diff <= CSTACKERR)
return 0; /* new limit would cause an overflow */
g->Cstacklimit = limit; /* set new limit */
L->nCcalls += diff; /* correct 'nCcalls' */
return limit - diff - CSTACKERR; /* success; return previous limit */
}
}
/*
** Decrement count of "C calls" and check for overflows. In case of
** a stack overflow, check appropriate error ("regular" overflow or
** overflow while handling stack overflow). If 'nCcalls' is smaller
** than CSTACKERR but larger than CSTACKMARK, it means it has just
** entered the "overflow zone", so the function raises an overflow
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
** already handling an overflow) but larger than CSTACKERRMARK, does
** not report an error (to allow message handling to work). Otherwise,
** report a stack overflow while handling a stack overflow (probably
** caused by a repeating error in the message handling function).
*/
void luaE_enterCcall (lua_State *L) {
int ncalls = getCcalls(L);
L->nCcalls--;
if (ncalls <= CSTACKERR) { /* possible overflow? */
luaE_freeCI(L); /* release unused CIs */
ncalls = getCcalls(L); /* update call count */
if (ncalls <= CSTACKERR) { /* still overflow? */
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
else if (ncalls >= CSTACKMARK) {
/* not in error-handling zone; raise the error now */
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
luaG_runerror(L, "C stack overflow");
}
/* else stack is in the error-handling zone;
allow message handler to work */
}
}
UNUSED(L); UNUSED(limit);
return LUAI_MAXCCALLS; /* warning?? */
}
CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *ci;
lua_assert(L->ci->next == NULL);
luaE_enterCcall(L);
ci = luaM_new(L, CallInfo);
lua_assert(L->ci->next == NULL);
L->ci->next = ci;
@@ -175,13 +123,11 @@ void luaE_freeCI (lua_State *L) {
CallInfo *ci = L->ci;
CallInfo *next = ci->next;
ci->next = NULL;
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((ci = next) != NULL) {
next = ci->next;
luaM_free(L, ci);
L->nci--;
}
L->nCcalls -= L->nci; /* adjust result */
}
@@ -194,7 +140,6 @@ void luaE_shrinkCI (lua_State *L) {
CallInfo *next;
if (ci == NULL)
return; /* no extra elements */
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((next = ci->next) != NULL) { /* two extra elements? */
CallInfo *next2 = next->next; /* next's next */
ci->next = next2; /* remove next from the list */
@@ -207,19 +152,40 @@ void luaE_shrinkCI (lua_State *L) {
ci = next2; /* continue */
}
}
L->nCcalls -= L->nci; /* adjust result */
}
/*
** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
** If equal, raises an overflow error. If value is larger than
** LUAI_MAXCCALLS (which means it is handling an overflow) but
** not much larger, does not report an error (to allow overflow
** handling to work).
*/
void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
LUAI_FUNC void luaE_incCstack (lua_State *L) {
L->nCcalls++;
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
}
static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci;
/* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue);
L1->stacksize = BASIC_STACK_SIZE;
for (i = 0; i < BASIC_STACK_SIZE; i++)
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist = L1->stack;
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
L1->top = L1->stack;
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
L1->stack_last = L1->stack + BASIC_STACK_SIZE;
/* initialize first ci */
ci = &L1->base_ci;
ci->next = ci->previous = NULL;
@@ -240,7 +206,7 @@ static void freestack (lua_State *L) {
L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L);
lua_assert(L->nci == 0);
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */
luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
}
@@ -248,24 +214,19 @@ static void freestack (lua_State *L) {
** Create registry table and its predefined values
*/
static void init_registry (lua_State *L, global_State *g) {
TValue temp;
/* create registry */
Table *registry = luaH_new(L);
sethvalue(L, &g->l_registry, registry);
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
/* registry[LUA_RIDX_MAINTHREAD] = L */
setthvalue(L, &temp, L); /* temp = L */
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
/* registry[LUA_RIDX_GLOBALS] = table of globals */
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
setthvalue(L, &registry->array[LUA_RIDX_MAINTHREAD - 1], L);
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
sethvalue(L, &registry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
}
/*
** open parts of the state that may cause memory-allocation errors.
** ('g->nilvalue' being a nil value flags that the state was completely
** build.)
*/
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
@@ -276,7 +237,7 @@ static void f_luaopen (lua_State *L, void *ud) {
luaT_init(L);
luaX_init(L);
g->gcrunning = 1; /* allow gc */
setnilvalue(&g->nilvalue);
setnilvalue(&g->nilvalue); /* now state is complete */
luai_userstateopen(L);
}
@@ -290,8 +251,8 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->stack = NULL;
L->ci = NULL;
L->nci = 0;
L->stacksize = 0;
L->twups = L; /* thread has no upvalues */
L->nCcalls = 0;
L->errorJmp = NULL;
L->hook = NULL;
L->hookmask = 0;
@@ -307,10 +268,13 @@ static void preinit_thread (lua_State *L, global_State *g) {
static void close_state (lua_State *L) {
global_State *g = G(L);
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
luaC_freeallobjects(L); /* collect all objects */
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
if (!completestate(g)) /* closing a partially built state? */
luaC_freeallobjects(L); /* jucst collect its objects */
else { /* closing a fully built state */
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
luaC_freeallobjects(L); /* collect all objects */
luai_userstateclose(L);
}
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
freestack(L);
lua_assert(gettotalbytes(g) == sizeof(LG));
@@ -335,7 +299,6 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
setthvalue2s(L, L->top, L1);
api_incr_top(L);
preinit_thread(L1, g);
L1->nCcalls = getCcalls(L);
L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount;
L1->hook = L->hook;
@@ -352,7 +315,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1);
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
luaF_closeupval(L1, L1->stack); /* close all upvalues */
lua_assert(L1->openupval == NULL);
luai_userstatefree(L, L1);
freestack(L1);
@@ -360,23 +323,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
}
int lua_resetthread (lua_State *L) {
CallInfo *ci;
int status;
lua_lock(L);
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
int luaE_resetthread (lua_State *L, int status) {
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
ci->func = L->stack;
ci->callstatus = CIST_C;
status = luaF_close(L, L->stack, CLOSEPROTECT);
if (status != CLOSEPROTECT) /* real errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else {
if (status == LUA_YIELD)
status = LUA_OK;
status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else
L->top = L->stack + 1;
}
ci->top = L->top + LUA_MINSTACK;
L->status = status;
L->status = cast_byte(status);
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
return status;
}
LUA_API int lua_resetthread (lua_State *L) {
int status;
lua_lock(L);
status = luaE_resetthread(L, L->status);
lua_unlock(L);
return status;
}
@@ -396,7 +365,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
preinit_thread(L, g);
g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL;
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f;
g->ud = ud;
@@ -411,6 +379,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->panic = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_INC;
g->gcstopem = 0;
g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL;
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;

130
lstate.h
View File

@@ -87,49 +87,13 @@
/*
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
** how many "C calls" it still can do in the C stack, to avoid C-stack
** overflow. This count is very rough approximation; it considers only
** recursive functions inside the interpreter, as non-recursive calls
** can be considered using a fixed (although unknown) amount of stack
** space.
**
** The count has two parts: the lower part is the count itself; the
** higher part counts the number of non-yieldable calls in the stack.
** (They are together so that we can change both with one instruction.)
**
** Because calls to external C functions can use an unknown amount
** of space (e.g., functions using an auxiliary buffer), calls
** to these functions add more than one to the count (see CSTACKCF).
**
** The proper count excludes the number of CallInfo structures allocated
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
** (and "consumes" one CallInfo), it needs neither to decrement nor to
** check 'nCcalls', as its use of C stack is already accounted for.
** About 'nCcalls': This count has two parts: the lower 16 bits counts
** the number of recursive invocations in the C stack; the higher
** 16 bits counts the number of non-yieldable calls in the stack.
** (They are together so that we can change and save both with one
** instruction.)
*/
/* number of "C stack slots" used by an external C function */
#define CSTACKCF 10
/*
** The C-stack size is sliced in the following zones:
** - larger than CSTACKERR: normal stack;
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
** (Because the counter can be decremented CSTACKCF at once, we need
** the so called "buffer zones", with at least that size, to properly
** detect a change from one zone to the next.)
*/
#define CSTACKERR (8 * CSTACKCF)
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
#define CSTACKERRMARK (CSTACKCF + 2)
/* initial limit for the C-stack of threads */
#define CSTACKTHREAD (2 * CSTACKERR)
/* true if this thread does not have non-yieldable calls in the stack */
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
@@ -144,13 +108,8 @@
/* Decrement the number of non-yieldable calls */
#define decnny(L) ((L)->nCcalls -= 0x10000)
/* Increment the number of non-yieldable calls and decrement nCcalls */
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
/* Decrement the number of non-yieldable calls and increment nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
/* Non-yieldable call increment */
#define nyci (0x10000 | 1)
@@ -168,12 +127,20 @@ struct lua_longjmp; /* defined in ldo.c */
#endif
/* extra stack space to handle TM calls and some other extras */
/*
** Extra stack space to handle TM calls and some other extras. This
** space is not included in 'stack_last'. It is used only to avoid stack
** checks, either because the element will be promptly popped or because
** there will be a stack check soon after the push. Function frames
** never use this extra space, so it does not need to be kept clean.
*/
#define EXTRA_STACK 5
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
/* kinds of Garbage Collection */
#define KGC_INC 0 /* incremental gc */
@@ -189,6 +156,18 @@ typedef struct stringtable {
/*
** Information about a call.
** About union 'u':
** - field 'l' is used only for Lua functions;
** - field 'c' is used only for C functions.
** About union 'u2':
** - field 'funcidx' is used only by C functions while doing a
** protected call;
** - field 'nyield' is used only while a function is "doing" an
** yield (from the yield until the next resume);
** - field 'nres' is used only while closing tbc variables when
** returning from a C function;
** - field 'transferinfo' is used only during call/returnhooks,
** before the function starts or after it ends.
*/
typedef struct CallInfo {
StkId func; /* function index in the stack */
@@ -209,6 +188,7 @@ typedef struct CallInfo {
union {
int funcidx; /* called-function index */
int nyield; /* number of values yielded */
int nres; /* number of values returned */
struct { /* info about transferred values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transferred */
unsigned short ntransfer; /* number of values transferred */
@@ -224,16 +204,34 @@ typedef struct CallInfo {
*/
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
#define CIST_C (1<<1) /* call is running a C function */
#define CIST_HOOKED (1<<2) /* call is running a debug hook */
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
#define CIST_TAIL (1<<4) /* call was tail called */
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
#define CIST_FIN (1<<6) /* call is running a finalizer */
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
#define CIST_HOOKED (1<<3) /* call is running a debug hook */
#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
#define CIST_TAIL (1<<5) /* call was tail called */
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
#define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
/* Bits 10-12 are used for CIST_RECST (see below) */
#define CIST_RECST 10
#if defined(LUA_COMPAT_LT_LE)
#define CIST_LEQ (1<<8) /* using __lt for __le */
#define CIST_LEQ (1<<13) /* using __lt for __le */
#endif
/*
** Field CIST_RECST stores the "recover status", used to keep the error
** status while closing to-be-closed variables in coroutines, so that
** Lua can correctly resume after an yield from a __close method called
** because of an error. (Three bits are enough for error status.)
*/
#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
#define setcistrecst(ci,st) \
check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
| ((st) << CIST_RECST)))
/* active function is a Lua function */
#define isLua(ci) (!((ci)->callstatus & CIST_C))
@@ -262,6 +260,7 @@ typedef struct global_State {
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */
lu_byte gcstopem; /* stops emergency collections */
lu_byte genminormul; /* control for minor generational collections */
lu_byte genmajormul; /* control for major generational collections */
lu_byte gcrunning; /* true if GC is running */
@@ -296,7 +295,6 @@ typedef struct global_State {
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
lua_WarnFunction warnf; /* warning function */
void *ud_warn; /* auxiliary data to 'warnf' */
unsigned int Cstacklimit; /* current limit for the C stack */
} global_State;
@@ -311,18 +309,18 @@ struct lua_State {
StkId top; /* first free slot in the stack */
global_State *l_G;
CallInfo *ci; /* call info for current function */
StkId stack_last; /* last free slot in the stack */
StkId stack_last; /* end of stack (last element + 1) */
StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */
StkId tbclist; /* list of to-be-closed variables */
GCObject *gclist;
struct lua_State *twups; /* list of threads with open upvalues */
struct lua_longjmp *errorJmp; /* current error recover point */
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
int oldpc; /* last pc traced */
int stacksize;
int basehookcount;
int hookcount;
volatile l_signalT hookmask;
@@ -331,6 +329,12 @@ struct lua_State {
#define G(L) (L->l_G)
/*
** 'g->nilvalue' being a nil value flags that the state was completely
** build.
*/
#define completestate(g) ttisnil(&g->nilvalue)
/*
** Union of all collectable objects (only for conversions)
@@ -389,12 +393,12 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_enterCcall (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
#define luaE_exitCcall(L) ((L)->nCcalls++)
#endif

View File

@@ -22,16 +22,6 @@
#include "lstring.h"
/*
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to
** compute its hash
*/
#if !defined(LUAI_HASHLIMIT)
#define LUAI_HASHLIMIT 5
#endif
/*
** Maximum size for string table.
*/
@@ -50,10 +40,9 @@ int luaS_eqlngstr (TString *a, TString *b) {
}
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed,
size_t step) {
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
unsigned int h = seed ^ cast_uint(l);
for (; l >= step; l -= step)
for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h;
}
@@ -63,8 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
lua_assert(ts->tt == LUA_VLNGSTR);
if (ts->extra == 0) { /* no hash? */
size_t len = ts->u.lnglen;
size_t step = (len >> LUAI_HASHLIMIT) + 1;
ts->hash = luaS_hash(getstr(ts), len, ts->hash, step);
ts->hash = luaS_hash(getstr(ts), len, ts->hash);
ts->extra = 1; /* now it has its hash */
}
return ts->hash;
@@ -101,7 +89,7 @@ void luaS_resize (lua_State *L, int nsize) {
if (nsize < osize) /* shrinking table? */
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
if (unlikely(newvect == NULL)) { /* reallocation failed? */
if (l_unlikely(newvect == NULL)) { /* reallocation failed? */
if (nsize < osize) /* was it shrinking table? */
tablerehash(tb->hash, nsize, osize); /* restore to original size */
/* leave table as it was */
@@ -184,7 +172,7 @@ void luaS_remove (lua_State *L, TString *ts) {
static void growstrtab (lua_State *L, stringtable *tb) {
if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
luaC_fullgc(L, 1); /* try to free some... */
if (tb->nuse == MAX_INT) /* still too many? */
luaM_error(L); /* cannot even create a message... */
@@ -201,7 +189,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
TString *ts;
global_State *g = G(L);
stringtable *tb = &g->strt;
unsigned int h = luaS_hash(str, l, g->seed, 1);
unsigned int h = luaS_hash(str, l, g->seed);
TString **list = &tb->hash[lmod(h, tb->size)];
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
for (ts = *list; ts != NULL; ts = ts->u.hnext) {
@@ -235,7 +223,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return internshrstr(L, str, l);
else {
TString *ts;
if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
luaM_toobig(L);
ts = luaS_createlngstrobj(L, l);
memcpy(getstr(ts), str, l * sizeof(char));
@@ -271,7 +259,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
Udata *u;
int i;
GCObject *o;
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
luaM_toobig(L);
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
u = gco2u(o);

View File

@@ -41,8 +41,7 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l,
unsigned int seed, size_t step);
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);

122
lstrlib.c
View File

@@ -152,8 +152,9 @@ static int str_rep (lua_State *L) {
const char *s = luaL_checklstring(L, 1, &l);
lua_Integer n = luaL_checkinteger(L, 2);
const char *sep = luaL_optlstring(L, 3, "", &lsep);
if (n <= 0) lua_pushliteral(L, "");
else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */
if (n <= 0)
lua_pushliteral(L, "");
else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
return luaL_error(L, "resulting string too large");
else {
size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
@@ -181,7 +182,7 @@ static int str_byte (lua_State *L) {
size_t pose = getendpos(L, 3, pi, l);
int n, i;
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */
return luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1;
luaL_checkstack(L, n, "string slice too long");
@@ -235,7 +236,7 @@ static int str_dump (lua_State *L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1); /* ensure function is on the top of the stack */
state.init = 0;
if (lua_dump(L, writer, &state, strip) != 0)
if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
return luaL_error(L, "unable to dump given function");
luaL_pushresult(&state.B);
return 1;
@@ -275,7 +276,8 @@ static int tonum (lua_State *L, int arg) {
static void trymt (lua_State *L, const char *mtname) {
lua_settop(L, 2); /* back to the original arguments */
if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
!luaL_getmetafield(L, 2, mtname)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
luaL_typename(L, -2), luaL_typename(L, -1));
lua_insert(L, -3); /* put metamethod before arguments */
@@ -383,7 +385,8 @@ static const char *match (MatchState *ms, const char *s, const char *p);
static int check_capture (MatchState *ms, int l) {
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
if (l_unlikely(l < 0 || l >= ms->level ||
ms->capture[l].len == CAP_UNFINISHED))
return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
return l;
}
@@ -400,14 +403,14 @@ static int capture_to_close (MatchState *ms) {
static const char *classend (MatchState *ms, const char *p) {
switch (*p++) {
case L_ESC: {
if (p == ms->p_end)
if (l_unlikely(p == ms->p_end))
luaL_error(ms->L, "malformed pattern (ends with '%%')");
return p+1;
}
case '[': {
if (*p == '^') p++;
do { /* look for a ']' */
if (p == ms->p_end)
if (l_unlikely(p == ms->p_end))
luaL_error(ms->L, "malformed pattern (missing ']')");
if (*(p++) == L_ESC && p < ms->p_end)
p++; /* skip escapes (e.g. '%]') */
@@ -482,7 +485,7 @@ static int singlematch (MatchState *ms, const char *s, const char *p,
static const char *matchbalance (MatchState *ms, const char *s,
const char *p) {
if (p >= ms->p_end - 1)
if (l_unlikely(p >= ms->p_end - 1))
luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
if (*s != *p) return NULL;
else {
@@ -565,7 +568,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
static const char *match (MatchState *ms, const char *s, const char *p) {
if (ms->matchdepth-- == 0)
if (l_unlikely(ms->matchdepth-- == 0))
luaL_error(ms->L, "pattern too complex");
init: /* using goto's to optimize tail recursion */
if (p != ms->p_end) { /* end of pattern? */
@@ -599,7 +602,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
case 'f': { /* frontier? */
const char *ep; char previous;
p += 2;
if (*p != '[')
if (l_unlikely(*p != '['))
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
ep = classend(ms, p); /* points to what is next */
previous = (s == ms->src_init) ? '\0' : *(s - 1);
@@ -699,7 +702,7 @@ static const char *lmemfind (const char *s1, size_t l1,
static size_t get_onecapture (MatchState *ms, int i, const char *s,
const char *e, const char **cap) {
if (i >= ms->level) {
if (i != 0)
if (l_unlikely(i != 0))
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
*cap = s;
return e - s;
@@ -707,7 +710,7 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s,
else {
ptrdiff_t capl = ms->capture[i].len;
*cap = ms->capture[i].init;
if (capl == CAP_UNFINISHED)
if (l_unlikely(capl == CAP_UNFINISHED))
luaL_error(ms->L, "unfinished capture");
else if (capl == CAP_POSITION)
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
@@ -926,7 +929,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
luaL_addlstring(b, s, e - s); /* keep original text */
return 0; /* no changes */
}
else if (!lua_isstring(L, -1))
else if (l_unlikely(!lua_isstring(L, -1)))
return luaL_error(L, "invalid replacement value (a %s)",
luaL_typename(L, -1));
else {
@@ -1058,7 +1061,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
for (i = 0; i < n; i++)
buff[i] = toupper(uchar(buff[i]));
}
else if (fmt[SIZELENMOD] != 'a')
else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
return n;
}
@@ -1358,17 +1361,6 @@ struct cD {
#define MAXALIGN (offsetof(struct cD, u))
/*
** Union for serializing floats
*/
typedef union Ftypes {
float f;
double d;
lua_Number n;
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
} Ftypes;
/*
** information to pack/unpack stuff
*/
@@ -1385,7 +1377,9 @@ typedef struct Header {
typedef enum KOption {
Kint, /* signed integers */
Kuint, /* unsigned integers */
Kfloat, /* floating-point numbers */
Kfloat, /* single-precision floating-point numbers */
Knumber, /* Lua "native" floating-point numbers */
Kdouble, /* double-precision floating-point numbers */
Kchar, /* fixed-length strings */
Kstring, /* strings with prefixed length */
Kzstr, /* zero-terminated strings */
@@ -1420,7 +1414,7 @@ static int getnum (const char **fmt, int df) {
*/
static int getnumlimit (Header *h, const char **fmt, int df) {
int sz = getnum(fmt, df);
if (sz > MAXINTSIZE || sz <= 0)
if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
sz, MAXINTSIZE);
return sz;
@@ -1454,14 +1448,14 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
case 'J': *size = sizeof(lua_Integer); return Kuint;
case 'T': *size = sizeof(size_t); return Kuint;
case 'f': *size = sizeof(float); return Kfloat;
case 'd': *size = sizeof(double); return Kfloat;
case 'n': *size = sizeof(lua_Number); return Kfloat;
case 'n': *size = sizeof(lua_Number); return Knumber;
case 'd': *size = sizeof(double); return Kdouble;
case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
case 'c':
*size = getnum(fmt, -1);
if (*size == -1)
if (l_unlikely(*size == -1))
luaL_error(h->L, "missing size for format option 'c'");
return Kchar;
case 'z': return Kzstr;
@@ -1500,7 +1494,7 @@ static KOption getdetails (Header *h, size_t totalsize,
else {
if (align > h->maxalign) /* enforce maximum alignment */
align = h->maxalign;
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
}
@@ -1535,12 +1529,10 @@ static void packint (luaL_Buffer *b, lua_Unsigned n,
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
** given 'islittle' is different from native endianness.
*/
static void copywithendian (volatile char *dest, volatile const char *src,
static void copywithendian (char *dest, const char *src,
int size, int islittle) {
if (islittle == nativeendian.little) {
while (size-- != 0)
*(dest++) = *(src++);
}
if (islittle == nativeendian.little)
memcpy(dest, src, size);
else {
dest += size - 1;
while (size-- != 0)
@@ -1583,15 +1575,27 @@ static int str_pack (lua_State *L) {
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
break;
}
case Kfloat: { /* floating-point options */
volatile Ftypes u;
char *buff = luaL_prepbuffsize(&b, size);
lua_Number n = luaL_checknumber(L, arg); /* get argument */
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
else if (size == sizeof(u.d)) u.d = (double)n;
else u.n = n;
/* move 'u' to final result, correcting endianness if needed */
copywithendian(buff, u.buff, size, h.islittle);
case Kfloat: { /* C float */
float f = (float)luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
case Knumber: { /* Lua float */
lua_Number f = luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
case Kdouble: { /* C double */
double f = (double)luaL_checknumber(L, arg); /* get argument */
char *buff = luaL_prepbuffsize(&b, sizeof(f));
/* move 'f' to final result, correcting endianness if needed */
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
luaL_addsize(&b, size);
break;
}
@@ -1682,7 +1686,7 @@ static lua_Integer unpackint (lua_State *L, const char *str,
else if (size > SZINT) { /* must check unread bytes */
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
for (i = limit; i < size; i++) {
if ((unsigned char)str[islittle ? i : size - 1 - i] != mask)
if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask))
luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
}
}
@@ -1717,13 +1721,21 @@ static int str_unpack (lua_State *L) {
break;
}
case Kfloat: {
volatile Ftypes u;
lua_Number num;
copywithendian(u.buff, data + pos, size, h.islittle);
if (size == sizeof(u.f)) num = (lua_Number)u.f;
else if (size == sizeof(u.d)) num = (lua_Number)u.d;
else num = u.n;
lua_pushnumber(L, num);
float f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, (lua_Number)f);
break;
}
case Knumber: {
lua_Number f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, f);
break;
}
case Kdouble: {
double f;
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
lua_pushnumber(L, (lua_Number)f);
break;
}
case Kchar: {
@@ -1738,7 +1750,7 @@ static int str_unpack (lua_State *L) {
break;
}
case Kzstr: {
size_t len = (int)strlen(data + pos);
size_t len = strlen(data + pos);
luaL_argcheck(L, pos + len < ld, 2,
"unfinished string for format 'z'");
lua_pushlstring(L, data + pos, len);

157
ltable.c
View File

@@ -68,20 +68,25 @@
#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
/*
** When the original hash value is good, hashing by a power of 2
** avoids the cost of '%'.
*/
#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
/*
** for other types, it is better to avoid modulo by power of 2, as
** they can have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashstr(t,str) hashpow2(t, (str)->hash)
#define hashboolean(t,p) hashpow2(t, p)
#define hashint(t,i) hashpow2(t, i)
/*
** for some types, it is better to avoid modulus by power of 2, as
** they tend to have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashpointer(t,p) hashmod(t, point2uint(p))
@@ -135,24 +140,38 @@ static int l_hashfloat (lua_Number n) {
*/
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
switch (withvariant(ktt)) {
case LUA_VNUMINT:
return hashint(t, ivalueraw(*kvl));
case LUA_VNUMFLT:
return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
case LUA_VSHRSTR:
return hashstr(t, tsvalueraw(*kvl));
case LUA_VLNGSTR:
return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
case LUA_VNUMINT: {
lua_Integer key = ivalueraw(*kvl);
return hashint(t, key);
}
case LUA_VNUMFLT: {
lua_Number n = fltvalueraw(*kvl);
return hashmod(t, l_hashfloat(n));
}
case LUA_VSHRSTR: {
TString *ts = tsvalueraw(*kvl);
return hashstr(t, ts);
}
case LUA_VLNGSTR: {
TString *ts = tsvalueraw(*kvl);
return hashpow2(t, luaS_hashlongstr(ts));
}
case LUA_VFALSE:
return hashboolean(t, 0);
case LUA_VTRUE:
return hashboolean(t, 1);
case LUA_VLIGHTUSERDATA:
return hashpointer(t, pvalueraw(*kvl));
case LUA_VLCF:
return hashpointer(t, fvalueraw(*kvl));
default:
return hashpointer(t, gcvalueraw(*kvl));
case LUA_VLIGHTUSERDATA: {
void *p = pvalueraw(*kvl);
return hashpointer(t, p);
}
case LUA_VLCF: {
lua_CFunction f = fvalueraw(*kvl);
return hashpointer(t, f);
}
default: {
GCObject *o = gcvalueraw(*kvl);
return hashpointer(t, o);
}
}
}
@@ -166,17 +185,30 @@ static Node *mainpositionTV (const Table *t, const TValue *key) {
/*
** Check whether key 'k1' is equal to the key in node 'n2'.
** This equality is raw, so there are no metamethods. Floats
** with integer values have been normalized, so integers cannot
** be equal to floats. It is assumed that 'eqshrstr' is simply
** pointer equality, so that short strings are handled in the
** default case.
** Check whether key 'k1' is equal to the key in node 'n2'. This
** equality is raw, so there are no metamethods. Floats with integer
** values have been normalized, so integers cannot be equal to
** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
** that short strings are handled in the default case.
** A true 'deadok' means to accept dead keys as equal to their original
** values. All dead keys are compared in the default case, by pointer
** identity. (Only collectable objects can produce dead keys.) Note that
** dead long strings are also compared by identity.
** Once a key is dead, its corresponding value may be collected, and
** then another value can be created with the same address. If this
** other value is given to 'next', 'equalkey' will signal a false
** positive. In a regular traversal, this situation should never happen,
** as all keys given to 'next' came from the table itself, and therefore
** could not have been collected. Outside a regular traversal, we
** have garbage in, garbage out. What is relevant is that this false
** positive does not break anything. (In particular, 'next' will return
** some other valid item on the table or nil.)
*/
static int equalkey (const TValue *k1, const Node *n2) {
if (rawtt(k1) != keytt(n2)) /* not the same variants? */
static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
!(deadok && keyisdead(n2) && iscollectable(k1)))
return 0; /* cannot be same key */
switch (ttypetag(k1)) {
switch (keytt(n2)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1;
case LUA_VNUMINT:
@@ -187,7 +219,7 @@ static int equalkey (const TValue *k1, const Node *n2) {
return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_VLCF:
return fvalue(k1) == fvalueraw(keyval(n2));
case LUA_VLNGSTR:
case ctb(LUA_VLNGSTR):
return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
default:
return gcvalue(k1) == gcvalueraw(keyval(n2));
@@ -251,11 +283,12 @@ static unsigned int setlimittosize (Table *t) {
/*
** "Generic" get version. (Not that generic: not valid for integers,
** which may be in array part, nor for floats with integral values.)
** See explanation about 'deadok' in function 'equalkey'.
*/
static const TValue *getgeneric (Table *t, const TValue *key) {
static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
Node *n = mainpositionTV(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */
if (equalkey(key, n))
if (equalkey(key, n, deadok))
return gval(n); /* that's it */
else {
int nx = gnext(n);
@@ -292,8 +325,8 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
if (i - 1u < asize) /* is 'key' inside array part? */
return i; /* yes; that's the index */
else {
const TValue *n = getgeneric(t, key);
if (unlikely(isabstkey(n)))
const TValue *n = getgeneric(t, key, 1);
if (l_unlikely(isabstkey(n)))
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
/* hash elements are numbered after array ones */
@@ -471,7 +504,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
already present in the table */
TValue k;
getnodekey(L, &k, old);
setobjt2t(L, luaH_set(L, t, &k), gval(old));
luaH_set(L, t, &k, gval(old));
}
}
}
@@ -527,7 +560,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
}
/* allocate new array */
newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
if (unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
freehash(L, &newt); /* release new hash part */
luaM_error(L); /* raise error (with array unchanged) */
}
@@ -618,10 +651,10 @@ static Node *getfreepos (Table *t) {
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
Node *mp;
TValue aux;
if (unlikely(ttisnil(key)))
if (l_unlikely(ttisnil(key)))
luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) {
lua_Number f = fltvalue(key);
@@ -630,9 +663,11 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
setivalue(&aux, k);
key = &aux; /* insert it as an integer */
}
else if (unlikely(luai_numisnan(f)))
else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN");
}
if (ttisnil(value))
return; /* do not insert nil values */
mp = mainpositionTV(t, key);
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
Node *othern;
@@ -640,7 +675,8 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
if (f == NULL) { /* cannot find a free place? */
rehash(L, t, key); /* grow table */
/* whatever called 'newkey' takes care of TM cache */
return luaH_set(L, t, key); /* insert key into grown table */
luaH_set(L, t, key, value); /* insert key into grown table */
return;
}
lua_assert(!isdummy(t));
othern = mainposition(t, keytt(mp), &keyval(mp));
@@ -668,7 +704,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
setnodekey(L, mp, key);
luaC_barrierback(L, obj2gco(t), key);
lua_assert(isempty(gval(mp)));
return gval(mp);
setobj2t(L, gval(mp), value);
}
@@ -730,7 +766,7 @@ const TValue *luaH_getstr (Table *t, TString *key) {
else { /* for long strings, use generic case */
TValue ko;
setsvalue(cast(lua_State *, NULL), &ko, key);
return getgeneric(t, &ko);
return getgeneric(t, &ko, 0);
}
}
@@ -750,34 +786,45 @@ const TValue *luaH_get (Table *t, const TValue *key) {
/* else... */
} /* FALLTHROUGH */
default:
return getgeneric(t, key);
return getgeneric(t, key, 0);
}
}
/*
** Finish a raw "set table" operation, where 'slot' is where the value
** should have been (the result of a previous "get table").
** Beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
void luaH_finishset (lua_State *L, Table *t, const TValue *key,
const TValue *slot, TValue *value) {
if (isabstkey(slot))
luaH_newkey(L, t, key, value);
else
setobj2t(L, cast(TValue *, slot), value);
}
/*
** beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key);
if (!isabstkey(p))
return cast(TValue *, p);
else return luaH_newkey(L, t, key);
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
const TValue *slot = luaH_get(t, key);
luaH_finishset(L, t, key, slot, value);
}
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
const TValue *p = luaH_getint(t, key);
TValue *cell;
if (!isabstkey(p))
cell = cast(TValue *, p);
else {
if (isabstkey(p)) {
TValue k;
setivalue(&k, key);
cell = luaH_newkey(L, t, &k);
luaH_newkey(L, t, &k, value);
}
setobj2t(L, cell, value);
else
setobj2t(L, cast(TValue *, p), value);
}

View File

@@ -41,8 +41,12 @@ LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
const TValue *slot, TValue *value);
LUAI_FUNC Table *luaH_new (lua_State *L);
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
unsigned int nhsize);

View File

@@ -145,8 +145,8 @@ static int tmove (lua_State *L) {
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
lua_geti(L, 1, i);
if (!lua_isstring(L, -1))
luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
luaL_typename(L, -1), i);
luaL_addvalue(b);
}
@@ -196,7 +196,8 @@ static int tunpack (lua_State *L) {
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
if (i > e) return 0; /* empty range */
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
if (l_unlikely(n >= (unsigned int)INT_MAX ||
!lua_checkstack(L, (int)(++n))))
return luaL_error(L, "too many results to unpack");
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
lua_geti(L, 1, i);
@@ -300,14 +301,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
for (;;) {
/* next loop: repeat ++i while a[i] < P */
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */
}
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
/* next loop: repeat --j while P < a[j] */
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j < i) /* j < i but a[j] > P ?? */
if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */
}

139
ltests.c
View File

@@ -121,6 +121,7 @@ static void warnf (void *ud, const char *msg, int tocont) {
strcat(buff, msg); /* add new message to current warning */
if (!tocont) { /* message finished? */
lua_unlock(L);
luaL_checkstack(L, 1, "warn stack space");
lua_getglobal(L, "_WARN");
if (!lua_toboolean(L, -1))
lua_pop(L, 1); /* ok, no previous unexpected warning */
@@ -142,6 +143,7 @@ static void warnf (void *ud, const char *msg, int tocont) {
}
case 2: { /* store */
lua_unlock(L);
luaL_checkstack(L, 1, "warn stack space");
lua_pushstring(L, buff);
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
lua_lock(L);
@@ -272,11 +274,15 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
/*
** {======================================================
** Functions to check memory consistency
** =======================================================
** {=====================================================================
** Functions to check memory consistency.
** Most of these checks are done through asserts, so this code does
** not make sense with asserts off. For this reason, it uses 'assert'
** directly, instead of 'lua_assert'.
** ======================================================================
*/
#include <assert.h>
/*
** Check GC invariants. For incremental mode, a black object cannot
@@ -330,13 +336,23 @@ static int testobjref (global_State *g, GCObject *f, GCObject *t) {
return r1;
}
#define checkobjref(g,f,t) \
{ if (t) lua_longassert(testobjref(g,f,obj2gco(t))); }
static void checkobjref (global_State *g, GCObject *f, GCObject *t) {
assert(testobjref(g, f, t));
}
/*
** Version where 't' can be NULL. In that case, it should not apply the
** macro 'obj2gco' over the object. ('t' may have several types, so this
** definition must be a macro.) Most checks need this version, because
** the check may run while an object is still being created.
*/
#define checkobjrefN(g,f,t) { if (t) checkobjref(g,f,obj2gco(t)); }
static void checkvalref (global_State *g, GCObject *f, const TValue *t) {
lua_assert(!iscollectable(t) ||
(righttt(t) && testobjref(g, f, gcvalue(t))));
assert(!iscollectable(t) || (righttt(t) && testobjref(g, f, gcvalue(t))));
}
@@ -345,14 +361,14 @@ static void checktable (global_State *g, Table *h) {
unsigned int asize = luaH_realasize(h);
Node *n, *limit = gnode(h, sizenode(h));
GCObject *hgc = obj2gco(h);
checkobjref(g, hgc, h->metatable);
checkobjrefN(g, hgc, h->metatable);
for (i = 0; i < asize; i++)
checkvalref(g, hgc, &h->array[i]);
for (n = gnode(h, 0); n < limit; n++) {
if (!isempty(gval(n))) {
TValue k;
getnodekey(g->mainthread, &k, n);
lua_assert(!keyisnil(n));
assert(!keyisnil(n));
checkvalref(g, hgc, &k);
checkvalref(g, hgc, gval(n));
}
@@ -363,30 +379,26 @@ static void checktable (global_State *g, Table *h) {
static void checkudata (global_State *g, Udata *u) {
int i;
GCObject *hgc = obj2gco(u);
checkobjref(g, hgc, u->metatable);
checkobjrefN(g, hgc, u->metatable);
for (i = 0; i < u->nuvalue; i++)
checkvalref(g, hgc, &u->uv[i].uv);
}
/*
** All marks are conditional because a GC may happen while the
** prototype is still being created
*/
static void checkproto (global_State *g, Proto *f) {
int i;
GCObject *fgc = obj2gco(f);
checkobjref(g, fgc, f->source);
checkobjrefN(g, fgc, f->source);
for (i=0; i<f->sizek; i++) {
if (ttisstring(f->k + i))
checkobjref(g, fgc, tsvalue(f->k + i));
if (iscollectable(f->k + i))
checkobjref(g, fgc, gcvalue(f->k + i));
}
for (i=0; i<f->sizeupvalues; i++)
checkobjref(g, fgc, f->upvalues[i].name);
checkobjrefN(g, fgc, f->upvalues[i].name);
for (i=0; i<f->sizep; i++)
checkobjref(g, fgc, f->p[i]);
checkobjrefN(g, fgc, f->p[i]);
for (i=0; i<f->sizelocvars; i++)
checkobjref(g, fgc, f->locvars[i].varname);
checkobjrefN(g, fgc, f->locvars[i].varname);
}
@@ -401,11 +413,11 @@ static void checkCclosure (global_State *g, CClosure *cl) {
static void checkLclosure (global_State *g, LClosure *cl) {
GCObject *clgc = obj2gco(cl);
int i;
checkobjref(g, clgc, cl->p);
checkobjrefN(g, clgc, cl->p);
for (i=0; i<cl->nupvalues; i++) {
UpVal *uv = cl->upvals[i];
if (uv) {
checkobjref(g, clgc, uv);
checkobjrefN(g, clgc, uv);
if (!upisopen(uv))
checkvalref(g, obj2gco(uv), uv->v);
}
@@ -428,19 +440,20 @@ static void checkstack (global_State *g, lua_State *L1) {
StkId o;
CallInfo *ci;
UpVal *uv;
lua_assert(!isdead(g, L1));
assert(!isdead(g, L1));
if (L1->stack == NULL) { /* incomplete thread? */
lua_assert(L1->stacksize == 0 && L1->openupval == NULL &&
L1->ci == NULL);
assert(L1->openupval == NULL && L1->ci == NULL);
return;
}
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
lua_assert(upisopen(uv)); /* must be open */
assert(upisopen(uv)); /* must be open */
assert(L1->top <= L1->stack_last);
assert(L1->tbclist <= L1->top);
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
lua_assert(ci->top <= L1->stack_last);
lua_assert(lua_checkpc(ci));
assert(ci->top <= L1->stack_last);
assert(lua_checkpc(ci));
}
for (o = L1->stack; o < L1->stack_last + EXTRA_STACK; o++)
for (o = L1->stack; o < L1->stack_last; o++)
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
}
@@ -477,10 +490,10 @@ static void checkrefs (global_State *g, GCObject *o) {
}
case LUA_VSHRSTR:
case LUA_VLNGSTR: {
lua_assert(!isgray(o)); /* strings are never gray */
assert(!isgray(o)); /* strings are never gray */
break;
}
default: lua_assert(0);
default: assert(0);
}
}
@@ -499,14 +512,14 @@ static void checkrefs (global_State *g, GCObject *o) {
static void checkobject (global_State *g, GCObject *o, int maybedead,
int listage) {
if (isdead(g, o))
lua_assert(maybedead);
assert(maybedead);
else {
lua_assert(g->gcstate != GCSpause || iswhite(o));
assert(g->gcstate != GCSpause || iswhite(o));
if (g->gckind == KGC_GEN) { /* generational mode? */
lua_assert(getage(o) >= listage);
lua_assert(!iswhite(o) || !isold(o));
assert(getage(o) >= listage);
assert(!iswhite(o) || !isold(o));
if (isold(o)) {
lua_assert(isblack(o) ||
assert(isblack(o) ||
getage(o) == G_TOUCHED1 ||
getage(o) == G_OLD0 ||
o->tt == LUA_VTHREAD ||
@@ -522,9 +535,8 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
int total = 0; /* count number of elements in the list */
((void)g); /* better to keep it available if we need to print an object */
while (o) {
lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
//lua_assert(isgray(o) || getage(o) == G_TOUCHED2);
lua_assert(!testbit(o->marked, TESTBIT));
assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
assert(!testbit(o->marked, TESTBIT));
if (keepinvariant(g))
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
total++;
@@ -535,10 +547,10 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
case LUA_VTHREAD: o = gco2th(o)->gclist; break;
case LUA_VPROTO: o = gco2p(o)->gclist; break;
case LUA_VUSERDATA:
lua_assert(gco2u(o)->nuvalue > 0);
assert(gco2u(o)->nuvalue > 0);
o = gco2u(o)->gclist;
break;
default: lua_assert(0); /* other objects cannot be in a gray list */
default: assert(0); /* other objects cannot be in a gray list */
}
}
return total;
@@ -570,13 +582,13 @@ static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
return; /* gray lists not being kept in these phases */
if (o->tt == LUA_VUPVAL) {
/* only open upvalues can be gray */
lua_assert(!isgray(o) || upisopen(gco2upv(o)));
assert(!isgray(o) || upisopen(gco2upv(o)));
return; /* upvalues are never in gray lists */
}
/* these are the ones that must be in gray lists */
if (isgray(o) || getage(o) == G_TOUCHED2) {
(*count)++;
lua_assert(testbit(o->marked, TESTBIT));
assert(testbit(o->marked, TESTBIT));
resetbit(o->marked, TESTBIT); /* prepare for next cycle */
}
}
@@ -589,22 +601,22 @@ static lu_mem checklist (global_State *g, int maybedead, int tof,
for (o = newl; o != survival; o = o->next) {
checkobject(g, o, maybedead, G_NEW);
incifingray(g, o, &total);
lua_assert(!tof == !tofinalize(o));
assert(!tof == !tofinalize(o));
}
for (o = survival; o != old; o = o->next) {
checkobject(g, o, 0, G_SURVIVAL);
incifingray(g, o, &total);
lua_assert(!tof == !tofinalize(o));
assert(!tof == !tofinalize(o));
}
for (o = old; o != reallyold; o = o->next) {
checkobject(g, o, 0, G_OLD1);
incifingray(g, o, &total);
lua_assert(!tof == !tofinalize(o));
assert(!tof == !tofinalize(o));
}
for (o = reallyold; o != NULL; o = o->next) {
checkobject(g, o, 0, G_OLD);
incifingray(g, o, &total);
lua_assert(!tof == !tofinalize(o));
assert(!tof == !tofinalize(o));
}
return total;
}
@@ -617,16 +629,16 @@ int lua_checkmemory (lua_State *L) {
lu_mem totalin; /* total of objects that are in gray lists */
lu_mem totalshould; /* total of objects that should be in gray lists */
if (keepinvariant(g)) {
lua_assert(!iswhite(g->mainthread));
lua_assert(!iswhite(gcvalue(&g->l_registry)));
assert(!iswhite(g->mainthread));
assert(!iswhite(gcvalue(&g->l_registry)));
}
lua_assert(!isdead(g, gcvalue(&g->l_registry)));
lua_assert(g->sweepgc == NULL || issweepphase(g));
assert(!isdead(g, gcvalue(&g->l_registry)));
assert(g->sweepgc == NULL || issweepphase(g));
totalin = checkgrays(g);
/* check 'fixedgc' list */
for (o = g->fixedgc; o != NULL; o = o->next) {
lua_assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD);
assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD);
}
/* check 'allgc' list */
@@ -642,11 +654,11 @@ int lua_checkmemory (lua_State *L) {
for (o = g->tobefnz; o != NULL; o = o->next) {
checkobject(g, o, 0, G_NEW);
incifingray(g, o, &totalshould);
lua_assert(tofinalize(o));
lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
assert(tofinalize(o));
assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
}
if (keepinvariant(g))
lua_assert(totalin == totalshould);
assert(totalin == totalshould);
return 0;
}
@@ -864,7 +876,7 @@ static int alloc_failnext (lua_State *L) {
l_memcontrol.failnext = 1;
return 0;
}
static int settrick (lua_State *L) {
if (ttisnil(obj_at(L, 1)))
@@ -969,7 +981,7 @@ static int hash_query (lua_State *L) {
static int stacklevel (lua_State *L) {
unsigned long a = 0;
lua_pushinteger(L, (L->top - L->stack));
lua_pushinteger(L, (L->stack_last - L->stack));
lua_pushinteger(L, stacksize(L));
lua_pushinteger(L, L->nCcalls);
lua_pushinteger(L, L->nci);
lua_pushinteger(L, (unsigned long)&a);
@@ -1043,6 +1055,7 @@ static int tref (lua_State *L) {
luaL_checkany(L, 1);
lua_pushvalue(L, 1);
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
(void)level; /* to avoid warnings */
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
return 1;
}
@@ -1050,6 +1063,7 @@ static int tref (lua_State *L) {
static int getref (lua_State *L) {
int level = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1));
(void)level; /* to avoid warnings */
lua_assert(lua_gettop(L) == level+1);
return 1;
}
@@ -1057,6 +1071,7 @@ static int getref (lua_State *L) {
static int unref (lua_State *L) {
int level = lua_gettop(L);
luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1)));
(void)level; /* to avoid warnings */
lua_assert(lua_gettop(L) == level);
return 0;
}
@@ -1725,6 +1740,7 @@ static struct X { int x; } x;
else if EQ("tostring") {
const char *s = lua_tostring(L1, getindex);
const char *s1 = lua_pushstring(L1, s);
(void)s1; /* to avoid warnings */
lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0);
}
else if EQ("type") {
@@ -1753,6 +1769,9 @@ static struct X { int x; } x;
else if EQ("toclose") {
lua_toclose(L1, getnum);
}
else if EQ("closeslot") {
lua_closeslot(L1, getnum);
}
else luaL_error(L, "unknown instruction %s", buff);
}
return 0;
@@ -1938,15 +1957,15 @@ static void checkfinalmem (void) {
int luaB_opentests (lua_State *L) {
void *ud;
lua_Alloc f = lua_getallocf(L, &ud);
lua_atpanic(L, &tpanic);
lua_setwarnf(L, &warnf, L);
lua_pushboolean(L, 0);
lua_setglobal(L, "_WARN"); /* _WARN = false */
regcodes(L);
atexit(checkfinalmem);
lua_assert(lua_getallocf(L, &ud) == debug_realloc);
lua_assert(ud == cast_voidp(&l_memcontrol));
lua_setallocf(L, lua_getallocf(L, NULL), ud);
lua_assert(f == debug_realloc && ud == cast_voidp(&l_memcontrol));
lua_setallocf(L, f, ud); /* exercise this function */
luaL_newlib(L, tests_funcs);
return 1;
}

View File

@@ -23,11 +23,6 @@
#define LUAI_ASSERT
/* compiled with -O0, Lua uses a lot of C stack space... */
#undef LUAI_MAXCSTACK
#define LUAI_MAXCSTACK 400
/* to avoid warnings, and to make sure value is really unused */
#define UNUSED(x) (x=0, (void)(x))
@@ -135,6 +130,11 @@ LUA_API void *debug_realloc (void *ud, void *block,
#define LUAI_MAXSTACK 50000
/* test mode uses more stack space */
#undef LUAI_MAXCCALLS
#define LUAI_MAXCCALLS 180
/* force Lua to use its own implementations */
#undef lua_strx2number
#undef lua_number2strx

5
ltm.c
View File

@@ -147,7 +147,7 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
if (!callbinTM(L, p1, p2, res, event)) {
if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
switch (event) {
case TM_BAND: case TM_BOR: case TM_BXOR:
case TM_SHL: case TM_SHR: case TM_BNOT: {
@@ -166,7 +166,8 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_tryconcatTM (lua_State *L) {
StkId top = L->top;
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
TM_CONCAT)))
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
}

42
lua.c
View File

@@ -37,6 +37,26 @@ static lua_State *globalL = NULL;
static const char *progname = LUA_PROGNAME;
#if defined(LUA_USE_POSIX) /* { */
/*
** Use 'sigaction' when available.
*/
static void setsignal (int sig, void (*handler)(int)) {
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask); /* do not mask any signal */
sigaction(sig, &sa, NULL);
}
#else /* }{ */
#define setsignal signal
#endif /* } */
/*
** Hook set by signal function to stop the interpreter.
*/
@@ -55,7 +75,7 @@ static void lstop (lua_State *L, lua_Debug *ar) {
*/
static void laction (int i) {
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
lua_sethook(globalL, lstop, flag, 1);
}
@@ -135,9 +155,9 @@ static int docall (lua_State *L, int narg, int nres) {
lua_pushcfunction(L, msghandler); /* push message handler */
lua_insert(L, base); /* put it under function and args */
globalL = L; /* to be available to 'laction' */
signal(SIGINT, laction); /* set C-signal handler */
setsignal(SIGINT, laction); /* set C-signal handler */
status = lua_pcall(L, narg, nres, base);
signal(SIGINT, SIG_DFL); /* reset C-signal handler */
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
lua_remove(L, base); /* remove message handler from the stack */
return status;
}
@@ -416,14 +436,18 @@ static int handle_luainit (lua_State *L) {
/*
** Returns the string to be used as a prompt by the interpreter.
** Return the string to be used as a prompt by the interpreter. Leave
** the string (or nil, if using the default value) on the stack, to keep
** it anchored.
*/
static const char *get_prompt (lua_State *L, int firstline) {
const char *p;
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
p = lua_tostring(L, -1);
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
return p;
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
else { /* apply 'tostring' over the value */
const char *p = luaL_tolstring(L, -1, NULL);
lua_remove(L, -2); /* remove original value */
return p;
}
}
/* mark in error messages for incomplete statements */

9
lua.h
View File

@@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "1"
#define LUA_VERSION_RELEASE "3"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio"
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2021 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -347,7 +347,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_toclose) (lua_State *L, int idx);
LUA_API void (lua_closeslot) (lua_State *L, int idx);
/*
@@ -491,7 +492,7 @@ struct lua_Debug {
/******************************************************************************
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
* Copyright (C) 1994-2021 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the

128
luaconf.h
View File

@@ -16,13 +16,13 @@
** ===================================================================
** General Configuration File for Lua
**
** Some definitions here can be changed externally, through the
** compiler (e.g., with '-D' options). Those are protected by
** '#if !defined' guards. However, several other definitions should
** be changed directly here, either because they affect the Lua
** ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the
** same configuration); or because they are seldom changed.
** Some definitions here can be changed externally, through the compiler
** (e.g., with '-D' options): They are commented out or protected
** by '#if !defined' guards. However, several other definitions
** should be changed directly here, either because they affect the
** Lua ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the same
** configuration); or because they are seldom changed.
**
** Search for "@@" to find all configurable definitions.
** ===================================================================
@@ -36,21 +36,6 @@
** =====================================================================
*/
/*
@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and
** also limits the maximum depth of other recursive algorithms in
** the implementation, such as syntactic analysis. A value too
** large may allow the interpreter to crash (C-stack overflow).
** The default value seems ok for regular machines, but may be
** too high for restricted hardware.
** The test file 'cstack.lua' may help finding a good limit.
** (It will crash with a limit too high.)
*/
#if !defined(LUAI_MAXCSTACK)
#define LUAI_MAXCSTACK 2000
#endif
/*
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
** Define it if you want Lua to avoid the use of a few C99 features
@@ -96,26 +81,12 @@
/*
** {==================================================================
** Configuration for Number types.
** Configuration for Number types. These options should not be
** set externally, because any other code connected to Lua must
** use the same configuration.
** ===================================================================
*/
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
/* #define LUA_32BITS */
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
** not need to use this case.
*/
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
#define LUA_C89_NUMBERS
#endif
/*
@@ LUA_INT_TYPE defines the type for Lua integers.
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
@@ -136,7 +107,31 @@
#define LUA_FLOAT_DOUBLE 2
#define LUA_FLOAT_LONGDOUBLE 3
#if defined(LUA_32BITS) /* { */
/* Default configuration ('long long' and 'double', for 64-bit Lua) */
#define LUA_INT_DEFAULT LUA_INT_LONGLONG
#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
#define LUA_32BITS 0
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
** not need to use this case.
*/
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
#define LUA_C89_NUMBERS 1
#else
#define LUA_C89_NUMBERS 0
#endif
#if LUA_32BITS /* { */
/*
** 32-bit integers and 'float'
*/
@@ -147,27 +142,22 @@
#endif
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
#elif defined(LUA_C89_NUMBERS) /* }{ */
#elif LUA_C89_NUMBERS /* }{ */
/*
** largest types available for C89 ('long' and 'double')
*/
#define LUA_INT_TYPE LUA_INT_LONG
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#else /* }{ */
/* use defaults */
#define LUA_INT_TYPE LUA_INT_DEFAULT
#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
#endif /* } */
/*
** default configuration for 64-bit Lua ('long long' and 'double')
*/
#if !defined(LUA_INT_TYPE)
#define LUA_INT_TYPE LUA_INT_LONGLONG
#endif
#if !defined(LUA_FLOAT_TYPE)
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#endif
/* }================================================================== */
@@ -388,14 +378,13 @@
/*
** {==================================================================
** Configuration for Numbers.
** Configuration for Numbers (low-level part).
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
** satisfy your needs.
** ===================================================================
*/
/*
@@ LUA_NUMBER is the floating-point type used by Lua.
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
@@ over a floating number.
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
@@ -488,10 +477,7 @@
/*
@@ LUA_INTEGER is the integer type used by Lua.
**
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
**
@@ LUAI_UACINT is the result of a 'default argument promotion'
@@ over a LUA_INTEGER.
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
@@ -674,6 +660,34 @@
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
#endif
/*
** macros to improve jump prediction, used mostly for error handling
** and debug facilities. (Some macros in the Lua API use these macros.
** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
** code.)
*/
#if !defined(luai_likely)
#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else
#define luai_likely(x) (x)
#define luai_unlikely(x) (x)
#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
/* }================================================================== */

View File

@@ -49,10 +49,4 @@ LUAMOD_API int (luaopen_package) (lua_State *L);
LUALIB_API void (luaL_openlibs) (lua_State *L);
#if !defined(lua_assert)
#define lua_assert(x) ((void)0)
#endif
#endif

148
lvm.c
View File

@@ -229,17 +229,17 @@ static int forprep (lua_State *L, StkId ra) {
count /= l_castS2U(-(step + 1)) + 1u;
}
/* store the counter in place of the limit (which won't be
needed anymore */
needed anymore) */
setivalue(plimit, l_castU2S(count));
}
}
else { /* try making all values floats */
lua_Number init; lua_Number limit; lua_Number step;
if (unlikely(!tonumber(plimit, &limit)))
if (l_unlikely(!tonumber(plimit, &limit)))
luaG_forerror(L, plimit, "limit");
if (unlikely(!tonumber(pstep, &step)))
if (l_unlikely(!tonumber(pstep, &step)))
luaG_forerror(L, pstep, "step");
if (unlikely(!tonumber(pinit, &init)))
if (l_unlikely(!tonumber(pinit, &init)))
luaG_forerror(L, pinit, "initial value");
if (step == 0)
luaG_runerror(L, "'for' step is zero");
@@ -292,7 +292,7 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
if (slot == NULL) { /* 't' is not a table? */
lua_assert(!ttistable(t));
tm = luaT_gettmbyobj(L, t, TM_INDEX);
if (unlikely(notm(tm)))
if (l_unlikely(notm(tm)))
luaG_typeerror(L, t, "index"); /* no metamethod */
/* else will try the metamethod */
}
@@ -337,10 +337,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */
if (isabstkey(slot)) /* no previous entry? */
slot = luaH_newkey(L, h, key); /* create one */
/* no metamethod and (now) there is an entry with given key */
setobj2t(L, cast(TValue *, slot), val); /* set its new value */
luaH_finishset(L, h, key, slot, val); /* set new value */
invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val);
return;
@@ -349,7 +346,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
}
else { /* not a table; check metamethod */
tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
if (unlikely(notm(tm)))
if (l_unlikely(notm(tm)))
luaG_typeerror(L, t, "index");
}
/* try the metamethod */
@@ -571,8 +568,13 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
return 0; /* only numbers can be equal with different variants */
else { /* two numbers with different variants */
lua_Integer i1, i2; /* compare them as integers */
return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2);
/* One of them is an integer. If the other does not have an
integer value, they cannot be equal; otherwise, compare their
integer values. */
lua_Integer i1, i2;
return (luaV_tointegerns(t1, &i1, F2Ieq) &&
luaV_tointegerns(t2, &i2, F2Ieq) &&
i1 == i2);
}
}
/* values have same type and same variant */
@@ -654,7 +656,7 @@ void luaV_concat (lua_State *L, int total) {
/* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
size_t l = vslen(s2v(top - n - 1));
if (unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
luaG_runerror(L, "string length overflow");
tl += l;
}
@@ -698,7 +700,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
}
default: { /* try metamethod */
tm = luaT_gettmbyobj(L, rb, TM_LEN);
if (unlikely(notm(tm))) /* no metamethod? */
if (l_unlikely(notm(tm))) /* no metamethod? */
luaG_typeerror(L, rb, "get length of");
break;
}
@@ -714,7 +716,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
** otherwise 'floor(q) == trunc(q) - 1'.
*/
lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
luaG_runerror(L, "attempt to divide by zero");
return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
@@ -734,7 +736,7 @@ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
** about luaV_idiv.)
*/
lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
if (n == 0)
luaG_runerror(L, "attempt to perform 'n%%0'");
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
@@ -845,6 +847,10 @@ void luaV_finishOp (lua_State *L) {
luaV_concat(L, total); /* concat them (may yield again) */
break;
}
case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */
ci->u.l.savedpc--; /* repeat instruction to close other vars. */
break;
}
default: {
/* only these other opcodes can yield */
lua_assert(op == OP_TFORCALL || op == OP_CALL ||
@@ -920,7 +926,7 @@ void luaV_finishOp (lua_State *L) {
*/
#define op_arithfK(L,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); }
@@ -949,7 +955,7 @@ void luaV_finishOp (lua_State *L) {
*/
#define op_arithK(L,iop,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arith_aux(L, v1, v2, iop, fop); }
@@ -1048,7 +1054,8 @@ void luaV_finishOp (lua_State *L) {
#define updatebase(ci) (base = ci->func + 1)
#define updatestack(ci) { if (trap) { updatebase(ci); ra = RA(i); } }
#define updatestack(ci) \
{ if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } }
/*
@@ -1092,15 +1099,11 @@ void luaV_finishOp (lua_State *L) {
#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
/*
** Protect code that will finish the loop (returns) or can only raise
** errors. (That is, it will not return to the interpreter main loop
** after changing the stack or hooks.)
** Protect code that can only raise errors. (That is, it cannnot change
** the stack or hooks.)
*/
#define halfProtect(exp) (savestate(L,ci), (exp))
/* idem, but without changing the stack */
#define halfProtectNT(exp) (savepc(L), (exp))
/* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \
{ luaC_condGC(L, (savepc(L), L->top = (c)), \
@@ -1110,7 +1113,7 @@ void luaV_finishOp (lua_State *L) {
/* fetch an instruction and prepare its execution */
#define vmfetch() { \
if (trap) { /* stack reallocation or hooks? */ \
if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
trap = luaG_traceexec(L, pc); /* handle hooks */ \
updatebase(ci); /* correct stack */ \
} \
@@ -1132,17 +1135,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
#if LUA_USE_JUMPTABLE
#include "ljumptab.h"
#endif
tailcall:
startfunc:
trap = L->hookmask;
returning: /* trap already set */
cl = clLvalue(s2v(ci->func));
k = cl->p->k;
pc = ci->u.l.savedpc;
if (trap) {
if (cl->p->is_vararg)
trap = 0; /* hooks will start after VARARGPREP instruction */
else if (pc == cl->p->code) /* first instruction (not resuming)? */
luaD_hookcall(L, ci);
ci->u.l.trap = 1; /* there may be other hooks */
if (l_unlikely(trap)) {
if (pc == cl->p->code) { /* first instruction (not resuming)? */
if (cl->p->is_vararg)
trap = 0; /* hooks will start after VARARGPREP instruction */
else /* check 'call' hook */
luaD_hookcall(L, ci);
}
ci->u.l.trap = 1; /* assume trap is on, for now */
}
base = ci->func + 1;
/* main loop of interpreter */
@@ -1150,8 +1156,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
Instruction i; /* instruction being executed */
StkId ra; /* instruction's A register */
vmfetch();
// low-level line tracing for debugging Lua
// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
lua_assert(base <= L->top && L->top < L->stack_last);
/* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (cast_void(L->top = base), 1));
vmdispatch (GET_OPCODE(i)) {
@@ -1528,7 +1536,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_CLOSE) {
Protect(luaF_close(L, ra, LUA_OK));
Protect(luaF_close(L, ra, LUA_OK, 1));
vmbreak;
}
vmcase(OP_TBC) {
@@ -1606,29 +1614,35 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_CALL) {
CallInfo *newci;
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) /* fixed number of arguments? */
L->top = ra + b; /* top signals number of arguments */
/* else previous instruction set top */
ProtectNT(luaD_call(L, ra, nresults));
savepc(L); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */
ci = newci;
ci->callstatus = 0; /* call re-uses 'luaV_execute' */
goto startfunc;
}
vmbreak;
}
vmcase(OP_TAILCALL) {
int b = GETARG_B(i); /* number of arguments + 1 (function) */
int nparams1 = GETARG_C(i);
/* delat is virtual 'func' - real 'func' (vararg functions) */
/* delta is virtual 'func' - real 'func' (vararg functions) */
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
if (b != 0)
L->top = ra + b;
else /* previous instruction set top */
b = cast_int(L->top - ra);
savepc(ci); /* some calls here can raise errors */
savepc(ci); /* several calls here can raise errors */
if (TESTARG_k(i)) {
/* close upvalues from current call; the compiler ensures
that there are no to-be-closed variables here, so this
call cannot change the stack */
luaF_close(L, base, NOCLOSINGMETH);
luaF_closeupval(L, base); /* close upvalues from current call */
lua_assert(L->tbclist < base); /* no pending tbc variables */
lua_assert(base == ci->func + 1);
}
while (!ttisfunction(s2v(ra))) { /* not a function? */
@@ -1637,16 +1651,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
checkstackGCp(L, 1, ra);
}
if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */
luaD_precall(L, ra, LUA_MULTRET); /* call it */
updatetrap(ci);
updatestack(ci); /* stack may have been relocated */
ci->func -= delta;
luaD_poscall(L, ci, cast_int(L->top - ra));
return;
ci->func -= delta; /* restore 'func' (if vararg) */
luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret; /* caller returns after the tail call */
}
ci->func -= delta;
ci->func -= delta; /* restore 'func' (if vararg) */
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
goto tailcall;
goto startfunc; /* execute the callee */
}
vmcase(OP_RETURN) {
int n = GETARG_B(i) - 1; /* number of results */
@@ -1657,7 +1672,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
if (TESTARG_k(i)) { /* may there be open upvalues? */
if (L->top < ci->top)
L->top = ci->top;
luaF_close(L, base, LUA_OK);
luaF_close(L, base, CLOSEKTOP, 1);
updatetrap(ci);
updatestack(ci);
}
@@ -1665,26 +1680,31 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
ci->func -= ci->u.l.nextraargs + nparams1;
L->top = ra + n; /* set call for 'luaD_poscall' */
luaD_poscall(L, ci, n);
return;
updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret;
}
vmcase(OP_RETURN0) {
if (L->hookmask) {
if (l_unlikely(L->hookmask)) {
L->top = ra;
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */
savepc(ci);
luaD_poscall(L, ci, 0); /* no hurry... */
trap = 1;
}
else { /* do the 'poscall' here */
int nres = ci->nresults;
int nres;
L->ci = ci->previous; /* back to caller */
L->top = base - 1;
while (nres-- > 0)
for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
setnilvalue(s2v(L->top++)); /* all results are nil */
}
return;
goto ret;
}
vmcase(OP_RETURN1) {
if (L->hookmask) {
if (l_unlikely(L->hookmask)) {
L->top = ra + 1;
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */
savepc(ci);
luaD_poscall(L, ci, 1); /* no hurry... */
trap = 1;
}
else { /* do the 'poscall' here */
int nres = ci->nresults;
@@ -1694,11 +1714,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
else {
setobjs2s(L, base - 1, ra); /* at least this result */
L->top = base;
while (--nres > 0) /* complete missing results */
setnilvalue(s2v(L->top++));
for (; l_unlikely(nres > 1); nres--)
setnilvalue(s2v(L->top++)); /* complete missing results */
}
}
return;
ret: /* return from a Lua function */
if (ci->callstatus & CIST_FRESH)
return; /* end this frame */
else {
ci = ci->previous;
goto returning; /* continue running caller in this frame */
}
}
vmcase(OP_FORLOOP) {
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
@@ -1792,7 +1818,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_VARARGPREP) {
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
if (trap) {
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
luaD_hookcall(L, ci);
L->oldpc = 1; /* next opcode will be seen as a "new" line */
}

6
lvm.h
View File

@@ -60,12 +60,14 @@ typedef enum {
/* convert an object to an integer (including string coercion) */
#define tointeger(o,i) \
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
: luaV_tointeger(o,i,LUA_FLOORN2I))
/* convert an object to an integer (without string coercion) */
#define tointegerns(o,i) \
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I))
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
: luaV_tointegerns(o,i,LUA_FLOORN2I))
#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))

View File

@@ -5,7 +5,7 @@
# Warnings valid for both C and C++
CWARNSCPP= \
-fmax-errors=5 \
-Wfatal-errors \
-Wextra \
-Wshadow \
-Wsign-compare \
@@ -14,8 +14,6 @@ CWARNSCPP= \
-Wredundant-decls \
-Wdisabled-optimization \
-Wdouble-promotion \
-Wlogical-op \
-Wno-aggressive-loop-optimizations \
# the next warnings might be useful sometimes,
# but usually they generate too much noise
# -Werror \
@@ -26,6 +24,13 @@ CWARNSCPP= \
# -Wformat=2 \
# -Wcast-qual \
# Warnings for gcc, not valid for clang
CWARNGCC= \
-Wlogical-op \
-Wno-aggressive-loop-optimizations \
# The next warnings are neither valid nor needed for C++
CWARNSC= -Wdeclaration-after-statement \
-Wmissing-prototypes \
@@ -35,7 +40,7 @@ CWARNSC= -Wdeclaration-after-statement \
-Wold-style-definition \
CWARNS= $(CWARNSCPP) $(CWARNSC)
CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
# Some useful compiler options for internal tests:
# -DLUAI_ASSERT turns on all assertions inside Lua.
@@ -109,16 +114,6 @@ $(LUA_T): $(LUA_O) $(CORE_T)
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL)
llex.o:
$(CC) $(CFLAGS) -Os -c llex.c
lparser.o:
$(CC) $(CFLAGS) -Os -c lparser.c
lcode.o:
$(CC) $(CFLAGS) -Os -c lcode.c
clean:
$(RM) $(ALL_T) $(ALL_O)

View File

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

View File

@@ -88,6 +88,11 @@ The type @emph{boolean} has two values, @false and @true.
Both @nil and @false make a condition false;
they are collectively called @def{false values}.
Any other value makes a condition true.
Despite its name,
@false is frequently used as an alternative to @nil,
with the key difference that @false behaves
like a regular value in a table,
while a @nil in a table represents an absent key.
The type @emph{number} represents both
integer numbers and real (floating-point) numbers,
@@ -326,7 +331,7 @@ under certain events.
You can change several aspects of the behavior
of a value by setting specific fields in its metatable.
For instance, when a non-numeric value is the operand of an addition,
Lua checks for a function in the field @St{__add} of the value's metatable.
Lua checks for a function in the field @idx{__add} of the value's metatable.
If it finds one,
Lua calls this function to perform the addition.
@@ -339,7 +344,7 @@ In the previous example, the key is the string @St{__add}
and the metamethod is the function that performs the addition.
Unless stated otherwise,
a metamethod may in fact be any @x{callable value},
which is either a function or a value with a @id{__call} metamethod.
which is either a function or a value with a @idx{__call} metamethod.
You can query the metatable of any value
using the @Lid{getmetatable} function.
@@ -500,7 +505,7 @@ when @id{key} is not present in @id{table}.
The metavalue is looked up in the metatable of @id{table}.
The metavalue for this event can be either a function, a table,
or any value with an @id{__index} metavalue.
or any value with an @idx{__index} metavalue.
If it is a function,
it is called with @id{table} and @id{key} as arguments,
and the result of the call
@@ -509,7 +514,7 @@ is the result of the operation.
Otherwise,
the final result is the result of indexing this metavalue with @id{key}.
This indexing is regular, not raw,
and therefore can trigger another @id{__index} metavalue.
and therefore can trigger another @idx{__index} metavalue.
}
@item{@idx{__newindex}|
@@ -521,14 +526,14 @@ The metavalue is looked up in the metatable of @id{table}.
Like with indexing,
the metavalue for this event can be either a function, a table,
or any value with an @id{__newindex} metavalue.
or any value with an @idx{__newindex} metavalue.
If it is a function,
it is called with @id{table}, @id{key}, and @id{value} as arguments.
Otherwise,
Lua repeats the indexing assignment over this metavalue
with the same key and value.
This assignment is regular, not raw,
and therefore can trigger another @id{__newindex} metavalue.
and therefore can trigger another @idx{__newindex} metavalue.
Whenever a @idx{__newindex} metavalue is invoked,
Lua does not perform the primitive assignment.
@@ -737,7 +742,7 @@ For an object (table or userdata) to be finalized when collected,
you must @emph{mark} it for finalization.
@index{mark (for finalization)}
You mark an object for finalization when you set its metatable
and the metatable has a field indexed by the string @St{__gc}.
and the metatable has a @idx{__gc} metamethod.
Note that if you set a metatable without a @idx{__gc} field
and later create that field in the metatable,
the object will not be marked for finalization.
@@ -1625,14 +1630,8 @@ they are closed in the reverse order that they were declared.
If there is any error while running a closing method,
that error is handled like an error in the regular code
where the variable was defined.
However, Lua may call the method one more time.
After an error,
the other pending closing methods will still be called.
Errors in these methods
interrupt the respective method and generate a warning,
but are otherwise ignored;
the error reported is only the original one.
If a coroutine yields and is never resumed again,
some variables may never go out of scope,
@@ -2436,8 +2435,16 @@ When you interact with the Lua API,
you are responsible for ensuring consistency.
In particular,
@emph{you are responsible for controlling stack overflow}.
You can use the function @Lid{lua_checkstack}
to ensure that the stack has enough space for pushing new elements.
When you call any API function,
you must ensure the stack has enough room to accommodate the results.
There is one exception to the above rule:
When you call a Lua function
without a fixed number of results @seeF{lua_call},
Lua ensures that the stack has enough space for all results.
However, it does not ensure any extra space.
So, before pushing anything on the stack after such a call
you should use @Lid{lua_checkstack}.
Whenever Lua calls C,
it ensures that the stack has space for
@@ -2446,13 +2453,9 @@ that is, you can safely push up to @id{LUA_MINSTACK} values into it.
@id{LUA_MINSTACK} is defined as 20,
so that usually you do not have to worry about stack space
unless your code has loops pushing elements onto the stack.
When you call a Lua function
without a fixed number of results @seeF{lua_call},
Lua ensures that the stack has enough space for all results,
but it does not ensure any extra space.
So, before pushing anything on the stack after such a call
you should use @Lid{lua_checkstack}.
Whenever necessary,
you can use the function @Lid{lua_checkstack}
to ensure that the stack has enough space for pushing new elements.
}
@@ -2512,7 +2515,8 @@ Lua's garbage collection can free or move internal memory
and then invalidate pointers to internal strings.
To allow a safe use of these pointers,
The API guarantees that any pointer to a string in a stack index
is valid while the value at that index is neither modified nor popped.
is valid while the string value at that index is not removed from the stack.
(It can be moved to another index, though.)
When the index is a pseudo-index (referring to an upvalue),
the pointer is valid while the corresponding call is active and
the corresponding upvalue is not modified.
@@ -2695,7 +2699,7 @@ Therefore, if a @N{C function} @id{foo} calls an API function
and this API function yields
(directly or indirectly by calling another function that yields),
Lua cannot return to @id{foo} any more,
because the @id{longjmp} removes its frame from the C stack.
because the @id{longjmp} removes its frame from the @N{C stack}.
To avoid this kind of problem,
Lua raises an error whenever it tries to yield across an API call,
@@ -2719,7 +2723,7 @@ After the thread resumes,
it eventually will finish running the callee function.
However,
the callee function cannot return to the original function,
because its frame in the C stack was destroyed by the yield.
because its frame in the @N{C stack} was destroyed by the yield.
Instead, Lua calls a @def{continuation function},
which was given as an argument to the callee function.
As the name implies,
@@ -2841,7 +2845,7 @@ and therefore may raise any errors.
Converts the @x{acceptable index} @id{idx}
into an equivalent @x{absolute index}
(that is, one that does not depend on the stack top).
(that is, one that does not depend on the stack size).
}
@@ -3091,6 +3095,21 @@ will probably need to close states as soon as they are not needed.
}
@APIEntry{void lua_closeslot (lua_State *L, int index);|
@apii{0,0,e}
Close the to-be-closed slot at the given index and set its value to @nil.
The index must be the last index previously marked to be closed
@see{lua_toclose} that is still active (that is, not closed yet).
A @idx{__close} metamethod cannot yield
when called through this function.
(Exceptionally, this function was introduced in release 5.4.3.
It is not present in previous 5.4 releases.)
}
@APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);|
@apii{0,0,e}
@@ -3740,9 +3759,10 @@ except that it allows the called function to yield @see{continuations}.
}
@APIEntry{void lua_pop (lua_State *L, int n);|
@apii{n,0,-}
@apii{n,0,e}
Pops @id{n} elements from the stack.
It is implemented as a macro over @Lid{lua_settop}.
}
@@ -4085,10 +4105,12 @@ and then pops the top element.
Resets a thread, cleaning its call stack and closing all pending
to-be-closed variables.
Returns a status code:
@Lid{LUA_OK} for no errors in closing methods,
@Lid{LUA_OK} for no errors in the thread
(either the original error that stopped the thread or
errors in closing methods),
or an error status otherwise.
In case of error,
leaves the error object on the top of the stack,
leaves the error object on the top of the stack.
}
@@ -4223,7 +4245,7 @@ for the @Q{newindex} event @see{metatable}.
}
@APIEntry{void lua_settop (lua_State *L, int index);|
@apii{?,?,-}
@apii{?,?,e}
Accepts any index, @N{or 0},
and sets the stack top to this index.
@@ -4231,6 +4253,9 @@ If the new top is greater than the old one,
then the new elements are filled with @nil.
If @id{index} @N{is 0}, then all stack elements are removed.
This function can run arbitrary code when removing an index
marked as to-be-closed from the stack.
}
@APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);|
@@ -4318,30 +4343,28 @@ otherwise, returns @id{NULL}.
@apii{0,0,m}
Marks the given index in the stack as a
to-be-closed @Q{variable} @see{to-be-closed}.
to-be-closed slot @see{to-be-closed}.
Like a to-be-closed variable in Lua,
the value at that index in the stack will be closed
the value at that slot in the stack will be closed
when it goes out of scope.
Here, in the context of a C function,
to go out of scope means that the running function returns to Lua,
there is an error,
or the index is removed from the stack through
@Lid{lua_settop} or @Lid{lua_pop}.
An index marked as to-be-closed should not be removed from the stack
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
or there is an error,
or the slot is removed from the stack through
@Lid{lua_settop} or @Lid{lua_pop},
or there is a call to @Lid{lua_closeslot}.
A slot marked as to-be-closed should not be removed from the stack
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop},
unless previously deactivated by @Lid{lua_closeslot}.
This function should not be called for an index
that is equal to or below an active to-be-closed index.
In the case of an out-of-memory error,
the value in the given index is immediately closed,
as if it was already marked.
that is equal to or below an active to-be-closed slot.
Note that, both in case of errors and of a regular return,
by the time the @idx{__close} metamethod runs,
the @N{C stack} was already unwound,
so that any automatic C variable declared in the calling function
will be out of scope.
so that any automatic @N{C variable} declared in the calling function
(e.g., a buffer) will be out of scope.
}
@@ -4955,20 +4978,6 @@ calling @Lid{lua_yield} with @id{nresults} equal to zero
}
@APIEntry{int (lua_setcstacklimit) (lua_State *L, unsigned int limit);|
@apii{0,0,-}
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
Returns the old limit in case of success,
or zero in case of error.
For more details about this function,
see @Lid{debug.setcstacklimit},
its equivalent in the standard library.
}
@APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);|
@apii{0,0,-}
@@ -6575,7 +6584,9 @@ that is,
closes all its pending to-be-closed variables
and puts the coroutine in a dead state.
The given coroutine must be dead or suspended.
In case of error closing some variable,
In case of error
(either the original error that stopped the coroutine or
errors in closing methods),
returns @false plus the error object;
otherwise returns @true.
@@ -7808,19 +7819,19 @@ from @T{list[1]} to @T{list[#list]}.
If @id{comp} is given,
then it must be a function that receives two list elements
and returns true when the first element must come
before the second in the final order
(so that, after the sort,
@T{i < j} implies @T{not comp(list[j],list[i])}).
before the second in the final order,
so that, after the sort,
@T{i <= j} implies @T{not comp(list[j],list[i])}.
If @id{comp} is not given,
then the standard Lua operator @T{<} is used instead.
Note that the @id{comp} function must define
a strict partial order over the elements in the list;
that is, it must be asymmetric and transitive.
Otherwise, no valid sort may be possible.
The @id{comp} function must define a consistent order;
more formally, the function must define a strict weak order.
(A weak order is similar to a total order,
but it can equate different elements for comparison purposes.)
The sort algorithm is not stable:
elements considered equal by the given order
Different elements considered equal by the given order
may have their relative positions changed by the sort.
}
@@ -8756,34 +8767,6 @@ to the userdata @id{u} plus a boolean,
}
@LibEntry{debug.setcstacklimit (limit)|
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
A limit too small restricts recursive calls pointlessly;
a limit too large exposes the interpreter to stack-overflow crashes.
Unfortunately, there is no way to know a priori
the maximum safe limit for a platform.
Each call made from Lua code counts one unit.
Other operations (e.g., calls made from C to Lua or resuming a coroutine)
may have a higher cost.
This function has the following restrictions:
@description{
@item{It can only be called from the main coroutine (thread);}
@item{It cannot be called while handling a stack-overflow error;}
@item{@id{limit} must be less than 40000;}
@item{@id{limit} cannot be less than the amount of C stack in use.}
}
If a call does not respect some restriction,
it returns a false value.
Otherwise,
the call returns the old limit.
}
@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
Sets the given function as the debug hook.
@@ -9110,7 +9093,7 @@ want the old behavior
}
@item{
The use of the @idx{__lt} metamethod to emulate @id{__le}
The use of the @idx{__lt} metamethod to emulate @idx{__le}
has been removed.
When needed, this metamethod must be explicitly defined.
}
@@ -9146,7 +9129,7 @@ like any other error when calling a finalizer.)
The function @Lid{print} does not call @Lid{tostring}
to format its arguments;
instead, it has this functionality hardwired.
You should use @id{__tostring} to modify how values are printed.
You should use @idx{__tostring} to modify how values are printed.
}
@item{

View File

@@ -127,8 +127,8 @@ else
end
Cstacklevel = function ()
local _, _, ncalls, nci = T.stacklevel()
return ncalls + nci -- number of free slots in the C stack
local _, _, ncalls = T.stacklevel()
return ncalls -- number of C calls
end
end
@@ -154,18 +154,8 @@ end
dofile('main.lua')
do
local next, setmetatable, stderr = next, setmetatable, io.stderr
-- track collections
local mt = {}
-- each time a table is collected, remark it for finalization
-- on next cycle
mt.__gc = function (o)
stderr:write'.' -- mark progress
local n = setmetatable(o, mt) -- remark it
end
local n = setmetatable({}, mt) -- create object
end
-- trace GC cycles
require"tracegc".start()
report"gc.lua"
local f = assert(loadfile('gc.lua'))

View File

@@ -507,10 +507,12 @@ function checkerrnopro (code, msg)
end
if not _soft then
collectgarbage("stop") -- avoid __gc with full stack
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
print"testing stack overflow in unprotected thread"
function f () f() end
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
collectgarbage("restart")
end
print"+"
@@ -1125,22 +1127,49 @@ do
assert(#openresource == n)
end
-- closing resources with 'settop'
-- closing resources with 'closeslot'
_ENV.xxx = true
local a = T.testC([[
pushvalue 2
call 0 1 # create resource
pushvalue 2 # stack: S, NR, CH, NR
call 0 1 # create resource; stack: S, NR, CH, R
toclose -1 # mark it to be closed
pushvalue 2
call 0 1 # create another resource
pushvalue 2 # stack: S, NR, CH, R, NR
call 0 1 # create another resource; stack: S, NR, CH, R, R
toclose -1 # mark it to be closed
pushvalue 3
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 2 # there should be two open resources
call 1 0
pop 1 # pop second resource from the stack
pushvalue 3
call 1 0 # stack: S, NR, CH, R, R
closeslot -1 # close second resource
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 1 # there should be one open resource
call 1 0
pop 1 # pop second resource from the stack
call 1 0 # stack: S, NR, CH, R, R
closeslot 4
setglobal "xxx" # previous op. erased the slot
pop 1 # pop other resource from the stack
pushint *
return 1 # return stack size
]], newresource, check)
assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack
-- closing resources with 'pop'
local a = T.testC([[
pushvalue 2 # stack: S, NR, CH, NR
call 0 1 # create resource; stack: S, NR, CH, R
toclose -1 # mark it to be closed
pushvalue 2 # stack: S, NR, CH, R, NR
call 0 1 # create another resource; stack: S, NR, CH, R, R
toclose -1 # mark it to be closed
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 2 # there should be two open resources
call 1 0 # stack: S, NR, CH, R, R
pop 1 # pop second resource
pushvalue 3 # stack: S, NR, CH, R, CH
pushint 1 # there should be one open resource
call 1 0 # stack: S, NR, CH, R
pop 1 # pop other resource from the stack
pushvalue 3 # stack: S, NR, CH, CH
pushint 0 # there should be no open resources
call 1 0 # stack: S, NR, CH
pushint *
return 1 # return stack size
]], newresource, check)

View File

@@ -242,7 +242,7 @@ end
assert(debug.upvalueid(foo1, 1))
assert(debug.upvalueid(foo1, 2))
assert(not pcall(debug.upvalueid, foo1, 3))
assert(not debug.upvalueid(foo1, 3))
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
assert(debug.upvalueid(foo3, 1))

View File

@@ -55,6 +55,20 @@ end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
foo = function (f, a)
f(100 * 1000)
f(100.0 * 1000)
f(-100 * 1000)
f(-100 * 1000.0)
f(100000)
f(100000.0)
f(-100000)
f(-100000.0)
end
checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
-- testing opcodes
-- check that 'f' opcodes match '...'
@@ -392,28 +406,6 @@ check(function (a, b)
end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a)
::loop::
if not (a < 10) then goto exit end
a = a + 1
goto loop
::exit::
end
)
checkequal(
function (a) repeat local x = a + 1; a = x until a > 0 end,
function (a)
::loop:: do
local x = a + 1
a = x
end
if not (a > 0) then goto loop end
end
)
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)

View File

@@ -123,13 +123,19 @@ assert(#a == 22 and a[#a] == 79)
x, a = nil
-- coroutine closing
print("to-be-closed variables in coroutines")
local function func2close (f)
return setmetatable({}, {__close = f})
end
do
-- ok to close a dead coroutine
local co = coroutine.create(print)
assert(coroutine.resume(co, "testing 'coroutine.close'"))
assert(coroutine.status(co) == "dead")
assert(coroutine.close(co))
local st, msg = coroutine.close(co)
assert(st and msg == nil)
-- cannot close the running coroutine
local st, msg = pcall(coroutine.close, coroutine.running())
@@ -146,9 +152,12 @@ do
-- to-be-closed variables in coroutines
local X
local function func2close (f)
return setmetatable({}, {__close = f})
end
-- closing a coroutine after an error
local co = coroutine.create(error)
local st, msg = coroutine.resume(co, 100)
assert(not st and msg == 100)
st, msg = coroutine.close(co)
assert(not st and msg == 100)
co = coroutine.create(function ()
local x <close> = func2close(function (self, err)
@@ -163,13 +172,12 @@ do
assert(not X and coroutine.status(co) == "dead")
-- error closing a coroutine
warn("@on")
local x = 0
co = coroutine.create(function()
local y <close> = func2close(function (self,err)
if (err ~= 111) then os.exit(false) end -- should not happen
assert(err == 111)
x = 200
error("200")
error(200)
end)
local x <close> = func2close(function (self, err)
assert(err == nil); error(111)
@@ -178,18 +186,64 @@ do
end)
coroutine.resume(co)
assert(x == 0)
-- with test library, use 'store' mode to check warnings
warn(not T and "@off" or "@store")
local st, msg = coroutine.close(co)
if not T then
warn("@on")
else -- test library
assert(string.find(_WARN, "200")); _WARN = false
warn("@normal")
end
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
assert(st == false and coroutine.status(co) == "dead" and msg == 200)
assert(x == 200)
end
do
-- <close> versus pcall in coroutines
local X = false
local Y = false
function foo ()
local x <close> = func2close(function (self, err)
Y = debug.getinfo(2)
X = err
end)
error(43)
end
co = coroutine.create(function () return pcall(foo) end)
local st1, st2, err = coroutine.resume(co)
assert(st1 and not st2 and err == 43)
assert(X == 43 and Y.name == "pcall")
-- recovering from errors in __close metamethods
local track = {}
local function h (o)
local hv <close> = o
return 1
end
local function foo ()
local x <close> = func2close(function(_,msg)
track[#track + 1] = msg or false
error(20)
end)
local y <close> = func2close(function(_,msg)
track[#track + 1] = msg or false
return 1000
end)
local z <close> = func2close(function(_,msg)
track[#track + 1] = msg or false
error(10)
end)
coroutine.yield(1)
h(func2close(function(_,msg)
track[#track + 1] = msg or false
error(2)
end))
end
local co = coroutine.create(pcall)
local st, res = coroutine.resume(co, foo) -- call 'foo' protected
assert(st and res == 1) -- yield 1
local st, res1, res2 = coroutine.resume(co) -- continue
assert(coroutine.status(co) == "dead")
assert(st and not res1 and res2 == 20) -- last error (20)
assert(track[1] == false and track[2] == 2 and track[3] == 10 and
track[4] == 10)
end
@@ -444,6 +498,28 @@ else
assert(B // A == 7) -- fact(7) // fact(6)
do -- hooks vs. multiple values
local done
local function test (n)
done = false
return coroutine.wrap(function ()
local a = {}
for i = 1, n do a[i] = i end
-- 'pushint' just to perturb the stack
T.sethook("pushint 10; yield 0", "", 1) -- yield at each op.
local a1 = {table.unpack(a)} -- must keep top between ops.
assert(#a1 == n)
for i = 1, n do assert(a[i] == i) end
done = true
end)
end
-- arguments to the coroutine are just to perturb its stack
local co = test(0); while not done do co(30) end
co = test(1); while not done do co(20, 10) end
co = test(3); while not done do co() end
co = test(100); while not done do co() end
end
local line = debug.getinfo(1, "l").currentline + 2 -- get line number
local function foo ()
local x = 10 --<< this line is 'line'

View File

@@ -1,73 +1,33 @@
-- $Id: testes/cstack.lua $
-- See Copyright Notice in file all.lua
local debug = require "debug"
print"testing C-stack overflow detection"
print"If this test crashes, see its file ('cstack.lua')"
local tracegc = require"tracegc"
print"testing stack overflow detection"
-- Segmentation faults in these tests probably result from a C-stack
-- overflow. To avoid these errors, you can use the function
-- 'debug.setcstacklimit' to set a smaller limit for the use of
-- C stack by Lua. After finding a reliable limit, you might want
-- to recompile Lua with this limit as the value for
-- the constant 'LUAI_MAXCCALLS', which defines the default limit.
-- (The default limit is printed by this test.)
-- overflow. To avoid these errors, you should set a smaller limit for
-- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
-- Alternatively, you can ensure a larger stack for the program.
-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
-- higher than 2_000.
-- get and print original limit
local origlimit <const> = debug.setcstacklimit(400)
print("default stack limit: " .. origlimit)
-- Do the tests using the original limit. Or else you may want to change
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
-- values to check whether they are reliable.
local currentlimit <const> = origlimit
debug.setcstacklimit(currentlimit)
print("current stack limit: " .. currentlimit)
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
-- auxiliary function to keep 'count' on the screen even if the program
-- crashes.
local count
local back = string.rep("\b", 8)
local function progress ()
count = count + 1
local n = string.format("%-8d", count)
io.stderr:write(back, n) -- erase previous value and write new one
end
do print("testing simple recursion:")
count = 0
local function foo ()
progress()
foo() -- do recursive calls until a stack error (or crash)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
end
do print("testing stack overflow in message handling")
count = 0
local count = 0
local function loop (x, y, z)
progress()
count = count + 1
return 1 + loop(x, y, z)
end
tracegc.stop() -- __gc should not be called with a full stack
local res, msg = xpcall(loop, loop)
tracegc.start()
assert(msg == "error in error handling")
print("\tfinal count: ", count)
print("final count: ", count)
end
@@ -80,97 +40,118 @@ do print("testing recursion inside pattern matching")
end
local m = f(80)
assert(#m == 80)
checkerror("too complex", f, 200000)
checkerror("too complex", f, 2000)
end
do print("testing stack-overflow in recursive 'gsub'")
count = 0
local count = 0
local function foo ()
progress()
count = count + 1
string.gsub("a", ".", foo)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
print("final count: ", count)
print("testing stack-overflow in recursive 'gsub' with metatables")
count = 0
local count = 0
local t = setmetatable({}, {__index = foo})
foo = function ()
count = count + 1
progress(count)
string.gsub("a", ".", t)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
print("final count: ", count)
end
do -- bug in 5.4.0
print("testing limits in coroutines inside deep calls")
count = 0
local count = 0
local lim = 1000
local function stack (n)
progress()
if n > 0 then return stack(n - 1) + 1
else coroutine.wrap(function ()
count = count + 1
stack(lim)
end)()
end
end
print(xpcall(stack, function () return "ok" end, lim))
local st, msg = xpcall(stack, function () return "ok" end, lim)
assert(not st and msg == "ok")
print("final count: ", count)
end
do print("testing changes in C-stack limit")
do
print("nesting of resuming yielded coroutines")
local count = 0
-- Just an alternative limit, different from the current one
-- (smaller to avoid stack overflows)
local alterlimit <const> = currentlimit * 8 // 10
assert(not debug.setcstacklimit(0)) -- limit too small
assert(not debug.setcstacklimit(50000)) -- limit too large
local co = coroutine.wrap (function ()
return debug.setcstacklimit(alterlimit)
end)
assert(not co()) -- cannot change C stack inside coroutine
local n
local function foo () n = n + 1; foo () end
local function check ()
n = 0
pcall(foo)
return n
local function body ()
coroutine.yield()
local f = coroutine.wrap(body)
f(); -- start new coroutine (will stop in previous yield)
count = count + 1
f() -- call it recursively
end
-- set limit to 'alterlimit'
assert(debug.setcstacklimit(alterlimit) == currentlimit)
local limalter <const> = check()
-- set a very low limit (given that there are already several active
-- calls to arrive here)
local lowlimit <const> = 38
assert(debug.setcstacklimit(lowlimit) == alterlimit)
-- usable limit is much lower, due to active calls
local actuallow = check()
assert(actuallow < lowlimit - 30)
-- now, add 'lowlimit' extra slots, which should all be available
assert(debug.setcstacklimit(lowlimit + lowlimit) == lowlimit)
local lim2 <const> = check()
assert(lim2 == actuallow + lowlimit)
-- 'setcstacklimit' works inside protected calls. (The new stack
-- limit is kept when 'pcall' returns.)
assert(pcall(function ()
assert(debug.setcstacklimit(alterlimit) == lowlimit * 2)
assert(check() <= limalter)
end))
assert(check() == limalter)
-- restore original limit
assert(debug.setcstacklimit(origlimit) == alterlimit)
local f = coroutine.wrap(body)
f()
assert(not pcall(f))
print("final count: ", count)
end
if T then
print("testing stack recovery")
local N = 0 -- trace number of calls
local LIM = -1 -- will store N just before stack overflow
-- trace stack size; after stack overflow, it should be
-- the maximum allowed stack size.
local stack1
local dummy
local function err(msg)
assert(string.find(msg, "stack overflow"))
local _, stacknow = T.stacklevel()
assert(stacknow == stack1 + 200)
end
-- When LIM==-1, the 'if' is not executed, so this function only
-- counts and stores the stack limits up to overflow. Then, LIM
-- becomes N, and then the 'if' code is run when the stack is
-- full. Then, there is a stack overflow inside 'xpcall', after which
-- the stack must have been restored back to its maximum normal size.
local function f()
dummy, stack1 = T.stacklevel()
if N == LIM then
xpcall(f, err)
local _, stacknow = T.stacklevel()
assert(stacknow == stack1)
return
end
N = N + 1
f()
end
local topB, sizeB -- top and size Before overflow
local topA, sizeA -- top and size After overflow
topB, sizeB = T.stacklevel()
tracegc.stop() -- __gc should not be called with a full stack
xpcall(f, err)
tracegc.start()
topA, sizeA = T.stacklevel()
-- sizes should be comparable
assert(topA == topB and sizeA < sizeB * 2)
print(string.format("maximum stack size: %d", stack1))
LIM = N -- will stop recursion at maximum level
N = 0 -- to count again
tracegc.stop() -- __gc should not be called with a full stack
f()
tracegc.start()
print"+"
end
print'OK'

View File

@@ -31,6 +31,7 @@ end
do
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
assert(not pcall(debug.getinfo, 0, ">")) -- invalid option
assert(not debug.getinfo(1000)) -- out of range level
assert(not debug.getinfo(-1)) -- out of range level
local a = debug.getinfo(print)
@@ -119,6 +120,16 @@ else
end
]], {2,3,4,7})
test([[
local function foo()
end
foo()
A = 1
A = 2
A = 3
]], {2, 3, 2, 4, 5, 6})
test([[--
if nil then
a=1
@@ -813,8 +824,10 @@ assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
-a == "unm" and #a == "len" and a & 3 == "band")
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
assert (a==b and a.op == "eq")
assert (a>=b and a.op == "order")
assert (a>b and a.op == "order")
assert (a>=b and a.op == "le")
assert ("x">=a and a.op == "le")
assert (a>b and a.op == "lt")
assert (a>10 and a.op == "lt")
assert(~a == "bnot")
do -- testing for-iterator name

View File

@@ -24,8 +24,9 @@ local function doit (s)
end
local function checkmessage (prog, msg)
local function checkmessage (prog, msg, debug)
local m = doit(prog)
if debug then print(m) end
assert(string.find(m, msg, 1, true))
end
@@ -67,6 +68,27 @@ checksyntax([[
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
do -- testing errors in goto/break
local function checksyntax (prog, msg, line)
local st, err = load(prog)
assert(string.find(err, "line " .. line))
assert(string.find(err, msg, 1, true))
end
checksyntax([[
::A:: a = 1
::A::
]], "label 'A' already defined", 1)
checksyntax([[
a = 1
goto A
do ::A:: end
]], "no visible label 'A'", 2)
end
if not T then
(Message or print)
('\n >>> testC not active: skipping memory message test <<<\n')
@@ -99,6 +121,17 @@ assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
checkmessage("a=(1)..{}", "a table value")
-- calls
checkmessage("local a; a(13)", "local 'a'")
checkmessage([[
local a = setmetatable({}, {__add = 34})
a = a + 1
]], "metamethod 'add'")
checkmessage([[
local a = setmetatable({}, {__lt = {}})
a = a > a
]], "metamethod 'lt'")
-- tail calls
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
@@ -158,6 +191,13 @@ checkmessage("a = 24 // 0", "divide by zero")
checkmessage("a = 1 % 0", "'n%0'")
-- type error for an object which is neither in an upvalue nor a register.
-- The following code will try to index the value 10 that is stored in
-- the metatable, without moving it to a register.
checkmessage("local a = setmetatable({}, {__index = 10}).x",
"attempt to index a number value")
-- numeric for loops
checkmessage("for i = {}, 10 do end", "table")
checkmessage("for i = io.stdin, 10 do end", "FILE")
@@ -380,31 +420,47 @@ if not b then
end
end]], 5)
do
-- Force a negative estimate for base line. Error in instruction 2
-- (after VARARGPREP, GETGLOBAL), with first absolute line information
-- (forced by too many lines) in instruction 0.
local s = string.format("%s return __A.x", string.rep("\n", 300))
lineerror(s, 301)
end
if not _soft then
-- several tests that exaust the Lua stack
collectgarbage()
print"testing stack overflow"
C = 0
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
-- get line where stack overflow will happen
local l = debug.getinfo(1, "l").currentline + 1
local function auxy () C=C+1; auxy() end -- produce a stack overflow
function y ()
collectgarbage("stop") -- avoid running finalizers without stack space
auxy()
collectgarbage("restart")
end
local function checkstackmessage (m)
print("(expected stack overflow after " .. C .. " calls)")
C = 0 -- prepare next count
return (string.find(m, "stack overflow"))
end
-- repeated stack overflows (to check stack recovery)
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
-- error lines in stack overflow
C = 0
local l1
local function g(x)
l1 = debug.getinfo(x, "l").currentline; y()
l1 = debug.getinfo(x, "l").currentline + 2
collectgarbage("stop") -- avoid running finalizers without stack space
auxy()
collectgarbage("restart")
end
local _, stackmsg = xpcall(g, debug.traceback, 1)
print('+')
@@ -530,10 +586,10 @@ local function testrep (init, rep, close, repc, finalresult)
if (finalresult) then
assert(res() == finalresult)
end
s = init .. string.rep(rep, 10000)
local res, msg = load(s) -- 10000 levels not ok
assert(not res and (string.find(msg, "too many registers") or
string.find(msg, "stack overflow")))
s = init .. string.rep(rep, 500)
local res, msg = load(s) -- 500 levels not ok
assert(not res and (string.find(msg, "too many") or
string.find(msg, "overflow")))
end
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment

View File

@@ -676,6 +676,14 @@ end
-- just to make sure
assert(collectgarbage'isrunning')
do -- check that the collector is reentrant in incremental mode
setmetatable({}, {__gc = function ()
collectgarbage()
end})
collectgarbage()
end
collectgarbage(oldmode)
print('OK')

View File

@@ -5,6 +5,8 @@ print('testing local variables and environments')
local debug = require"debug"
local tracegc = require"tracegc"
-- bug in 5.1:
@@ -232,7 +234,11 @@ end
do
local X = false
local x, closescope = func2close(function () stack(10); X = true end, 100)
local x, closescope = func2close(function (_, msg)
stack(10);
assert(msg == nil)
X = true
end, 100)
assert(x == 100); x = 101; -- 'x' is not read-only
-- closing functions do not corrupt returning values
@@ -246,6 +252,12 @@ do
X = false
foo = function (x)
local _<close> = func2close(function (_, msg)
-- without errors, enclosing function should be still active when
-- __close is called
assert(debug.getinfo(2).name == "foo")
assert(msg == nil)
end)
local _<close> = closescope
local y = 15
return y
@@ -323,73 +335,33 @@ do
end
-- auxiliary functions for testing warnings in '__close'
local function prepwarn ()
if not T then -- no test library?
warn("@off") -- do not show (lots of) warnings
else
warn("@store") -- to test the warnings
end
end
local function endwarn ()
if not T then
warn("@on") -- back to normal
else
assert(_WARN == false)
warn("@normal")
end
end
local function checkwarn (msg)
if T then
assert(string.find(_WARN, msg))
_WARN = false -- reset variable to check next warning
end
end
warn("@on")
do print("testing errors in __close")
prepwarn()
-- original error is in __close
local function foo ()
local x <close> =
func2close(function (self, msg)
assert(string.find(msg, "@z"))
assert(string.find(msg, "@y"))
error("@x")
end)
local x1 <close> =
func2close(function (self, msg)
checkwarn("@y")
assert(string.find(msg, "@z"))
assert(string.find(msg, "@y"))
end)
local gc <close> = func2close(function () collectgarbage() end)
local y <close> =
func2close(function (self, msg)
assert(string.find(msg, "@z")) -- first error in 'z'
checkwarn("@z") -- second error in 'z' generated a warning
assert(string.find(msg, "@z")) -- error in 'z'
error("@y")
end)
local first = true
local z <close> =
-- 'z' close is called twice
func2close(function (self, msg)
if first then
assert(msg == nil)
first = false
else
assert(string.find(msg, "@z")) -- own error
end
assert(msg == nil)
error("@z")
end)
@@ -397,8 +369,7 @@ do print("testing errors in __close")
end
local stat, msg = pcall(foo, false)
assert(string.find(msg, "@z"))
checkwarn("@x")
assert(string.find(msg, "@x"))
-- original error not in __close
@@ -406,13 +377,16 @@ do print("testing errors in __close")
local x <close> =
func2close(function (self, msg)
assert(msg == 4)
-- after error, 'foo' was discarded, so caller now
-- must be 'pcall'
assert(debug.getinfo(2).name == "pcall")
assert(string.find(msg, "@x1"))
end)
local x1 <close> =
func2close(function (self, msg)
checkwarn("@y")
assert(msg == 4)
assert(debug.getinfo(2).name == "pcall")
assert(string.find(msg, "@y"))
error("@x1")
end)
@@ -420,14 +394,15 @@ do print("testing errors in __close")
local y <close> =
func2close(function (self, msg)
assert(msg == 4) -- error in body
checkwarn("@z")
assert(debug.getinfo(2).name == "pcall")
assert(string.find(msg, "@z"))
error("@y")
end)
local first = true
local z <close> =
func2close(function (self, msg)
assert(debug.getinfo(2).name == "pcall")
-- 'z' close is called once
assert(first and msg == 4)
first = false
@@ -438,20 +413,20 @@ do print("testing errors in __close")
end
local stat, msg = pcall(foo, true)
assert(msg == 4)
checkwarn("@x1") -- last error
assert(string.find(msg, "@x1"))
-- error leaving a block
local function foo (...)
do
local x1 <close> =
func2close(function ()
checkwarn("@X")
func2close(function (self, msg)
assert(string.find(msg, "@X"))
error("@Y")
end)
local x123 <close> =
func2close(function ()
func2close(function (_, msg)
assert(msg == nil)
error("@X")
end)
end
@@ -459,9 +434,7 @@ do print("testing errors in __close")
end
local st, msg = xpcall(foo, debug.traceback)
assert(string.match(msg, "^[^ ]* @X"))
assert(string.find(msg, "in metamethod 'close'"))
checkwarn("@Y")
assert(string.match(msg, "^[^ ]* @Y"))
-- error in toclose in vararg function
local function foo (...)
@@ -471,9 +444,6 @@ do print("testing errors in __close")
local st, msg = xpcall(foo, debug.traceback)
assert(string.match(msg, "^[^ ]* @x123"))
assert(string.find(msg, "in metamethod 'close'"))
checkwarn("@x123") -- from second call to close 'x123'
endwarn()
end
@@ -491,15 +461,112 @@ do -- errors due to non-closable values
getmetatable(xyz).__close = nil -- remove metamethod
end
local stat, msg = pcall(foo)
assert(not stat and
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
assert(not stat and string.find(msg, "metamethod 'close'"))
local function foo ()
local a1 <close> = func2close(function (_, msg)
assert(string.find(msg, "number value"))
error(12)
end)
local a2 <close> = setmetatable({}, {__close = print})
local a3 <close> = func2close(function (_, msg)
assert(msg == nil)
error(123)
end)
getmetatable(a2).__close = 4 -- invalidate metamethod
end
local stat, msg = pcall(foo)
assert(not stat and msg == 12)
end
do -- tbc inside close methods
local track = {}
local function foo ()
local x <close> = func2close(function ()
local xx <close> = func2close(function (_, msg)
assert(msg == nil)
track[#track + 1] = "xx"
end)
track[#track + 1] = "x"
end)
track[#track + 1] = "foo"
return 20, 30, 40
end
local a, b, c, d = foo()
assert(a == 20 and b == 30 and c == 40 and d == nil)
assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx")
-- again, with errors
local track = {}
local function foo ()
local x0 <close> = func2close(function (_, msg)
assert(msg == 202)
track[#track + 1] = "x0"
end)
local x <close> = func2close(function ()
local xx <close> = func2close(function (_, msg)
assert(msg == 101)
track[#track + 1] = "xx"
error(202)
end)
track[#track + 1] = "x"
error(101)
end)
track[#track + 1] = "foo"
return 20, 30, 40
end
local st, msg = pcall(foo)
assert(not st and msg == 202)
assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx" and
track[4] == "x0")
end
local function checktable (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
do -- test for tbc variable high in the stack
-- function to force a stack overflow
local function overflow (n)
overflow(n + 1)
end
-- error handler will create tbc variable handling a stack overflow,
-- high in the stack
local function errorh (m)
assert(string.find(m, "stack overflow"))
local x <close> = func2close(function (o) o[1] = 10 end)
return x
end
local flag
local st, obj
-- run test in a coroutine so as not to swell the main stack
local co = coroutine.wrap(function ()
-- tbc variable down the stack
local y <close> = func2close(function (obj, msg)
assert(msg == nil)
obj[1] = 100
flag = obj
end)
tracegc.stop()
st, obj = xpcall(overflow, errorh, 0)
tracegc.start()
end)
co()
assert(not st and obj[1] == 10 and flag[1] == 100)
end
if rawget(_G, "T") then
warn("@off")
-- memory error inside closing function
local function foo ()
local y <close> = func2close(function () T.alloccount() end)
@@ -514,33 +581,30 @@ if rawget(_G, "T") then
-- despite memory error, 'y' will be executed and
-- memory limit will be lifted
local _, msg = pcall(foo)
assert(msg == 1000)
assert(msg == "not enough memory")
local closemsg
local close = func2close(function (self, msg)
T.alloccount()
assert(msg == "not enough memory")
closemsg = msg
end)
-- set a memory limit and return a closing object to remove the limit
local function enter (count)
stack(10) -- reserve some stack space
T.alloccount(count)
closemsg = nil
return close
end
local function test ()
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
assert(false) -- should not run
local y = {} -- raise a memory error
end
local _, msg = pcall(test)
assert(msg == "not enough memory")
assert(msg == "not enough memory" and closemsg == "not enough memory")
-- now use metamethod for closing
close = setmetatable({}, {__close = function ()
T.alloccount()
end})
-- repeat test with extra closing upvalues
local function test ()
@@ -552,12 +616,11 @@ if rawget(_G, "T") then
assert(msg == "not enough memory");
end)
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
os.exit(false) -- should not run
local y = {} -- raise a memory error
end
local _, msg = pcall(test)
assert(msg == "not enough memory") -- reported error is the first one
assert(msg == 1000 and closemsg == "not enough memory")
do -- testing 'toclose' in C string buffer
collectgarbage()
@@ -579,7 +642,7 @@ if rawget(_G, "T") then
-- concat this table needs two buffer resizes (one for each 's')
local a = {s, s}
collectgarbage()
collectgarbage(); collectgarbage()
m = T.totalmem()
collectgarbage("stop")
@@ -602,7 +665,7 @@ if rawget(_G, "T") then
-- second buffer was released by 'toclose'
assert(T.totalmem() - m <= extra)
-- userdata, upvalue, buffer, buffer, final string
-- userdata, buffer, buffer, final string
T.totalmem(m + 4*lim + extra)
assert(#table.concat(a) == 2*lim)
@@ -612,12 +675,185 @@ if rawget(_G, "T") then
print'+'
end
warn("@on")
do
-- '__close' vs. return hooks in C functions
local trace = {}
local function hook (event)
trace[#trace + 1] = event .. " " .. (debug.getinfo(2).name or "?")
end
-- create tbc variables to be used by C function
local x = func2close(function (_,msg)
trace[#trace + 1] = "x"
end)
local y = func2close(function (_,msg)
trace[#trace + 1] = "y"
end)
debug.sethook(hook, "r")
local t = {T.testC([[
toclose 2 # x
pushnum 10
pushint 20
toclose 3 # y
return 2
]], x, y)}
debug.sethook()
-- hooks ran before return hook from 'testC'
checktable(trace,
{"return sethook", "y", "return ?", "x", "return ?", "return testC"})
-- results are correct
checktable(t, {10, 20})
end
end
do -- '__close' vs. return hooks in Lua functions
local trace = {}
local function hook (event)
trace[#trace + 1] = event .. " " .. debug.getinfo(2).name
end
local function foo (...)
local x <close> = func2close(function (_,msg)
trace[#trace + 1] = "x"
end)
local y <close> = func2close(function (_,msg)
debug.sethook(hook, "r")
end)
return ...
end
local t = {foo(10,20,30)}
debug.sethook()
checktable(t, {10, 20, 30})
checktable(trace,
{"return sethook", "return close", "x", "return close", "return foo"})
end
print "to-be-closed variables in coroutines"
do
-- yielding inside closing metamethods
local trace = {}
local co = coroutine.wrap(function ()
trace[#trace + 1] = "nowX"
-- will be closed after 'y'
local x <close> = func2close(function (_, msg)
assert(msg == nil)
trace[#trace + 1] = "x1"
coroutine.yield("x")
trace[#trace + 1] = "x2"
end)
return pcall(function ()
do -- 'z' will be closed first
local z <close> = func2close(function (_, msg)
assert(msg == nil)
trace[#trace + 1] = "z1"
coroutine.yield("z")
trace[#trace + 1] = "z2"
end)
end
trace[#trace + 1] = "nowY"
-- will be closed after 'z'
local y <close> = func2close(function(_, msg)
assert(msg == nil)
trace[#trace + 1] = "y1"
coroutine.yield("y")
trace[#trace + 1] = "y2"
end)
return 10, 20, 30
end)
end)
assert(co() == "z")
assert(co() == "y")
assert(co() == "x")
checktable({co()}, {true, 10, 20, 30})
checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"})
end
do
-- yielding inside closing metamethods after an error
local co = coroutine.wrap(function ()
local function foo (err)
local z <close> = func2close(function(_, msg)
assert(msg == nil or msg == err + 20)
coroutine.yield("z")
return 100, 200
end)
local y <close> = func2close(function(_, msg)
-- still gets the original error (if any)
assert(msg == err or (msg == nil and err == 1))
coroutine.yield("y")
if err then error(err + 20) end -- creates or changes the error
end)
local x <close> = func2close(function(_, msg)
assert(msg == err or (msg == nil and err == 1))
coroutine.yield("x")
return 100, 200
end)
if err == 10 then error(err) else return 10, 20 end
end
coroutine.yield(pcall(foo, nil)) -- no error
coroutine.yield(pcall(foo, 1)) -- error in __close
return pcall(foo, 10) -- 'foo' will raise an error
end)
local a, b = co() -- first foo: no error
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
a, b = co()
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
a, b = co()
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
local a, b, c = co()
assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)'
local a, b = co() -- second foo: error in __close
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
a, b = co()
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
a, b = co()
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
local st, msg = co() -- reports the error in 'y'
assert(not st and msg == 21)
local a, b = co() -- third foo: error in function body
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
a, b = co()
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
a, b = co()
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
local st, msg = co() -- gets final error
assert(not st and msg == 10 + 20)
end
do
-- an error in a wrapped coroutine closes variables
local x = false
@@ -642,37 +878,43 @@ end
do
prepwarn()
-- error in a wrapped coroutine raising errors when closing a variable
local x = 0
local co = coroutine.wrap(function ()
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
local xx <close> = func2close(function (_, msg)
x = x + 1;
assert(string.find(msg, "@XXX"))
error("@YYY")
end)
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
coroutine.yield(100)
error(200)
coroutine.yield(100)
error(200)
end)
assert(co() == 100); assert(x == 0)
local st, msg = pcall(co); assert(x == 2)
assert(not st and msg == 200) -- should get first error raised
checkwarn("@YYY")
assert(not st and string.find(msg, "@YYY")) -- should get error raised
local x = 0
local y = 0
co = coroutine.wrap(function ()
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
coroutine.yield(100)
return 200
local xx <close> = func2close(function (_, err)
y = y + 1;
assert(string.find(err, "XXX"))
error("YYY")
end)
local xv <close> = func2close(function ()
x = x + 1; error("XXX")
end)
coroutine.yield(100)
return 200
end)
assert(co() == 100); assert(x == 0)
local st, msg = pcall(co)
assert(x == 2 and y == 1) -- first close is called twice
assert(x == 1 and y == 1)
-- should get first error raised
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
checkwarn("YYY")
assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY"))
endwarn()
end
@@ -689,6 +931,81 @@ assert(co == nil) -- eventually it will be collected
collectgarbage()
if rawget(_G, "T") then
print("to-be-closed variables x coroutines in C")
do
local token = 0
local count = 0
local f = T.makeCfunc[[
toclose 1
toclose 2
return .
]]
local obj = func2close(function (_, msg)
count = count + 1
token = coroutine.yield(count, token)
end)
local co = coroutine.wrap(f)
local ct, res = co(obj, obj, 10, 20, 30, 3) -- will return 10, 20, 30
-- initial token value, after closing 2nd obj
assert(ct == 1 and res == 0)
-- run until yield when closing 1st obj
ct, res = co(100)
assert(ct == 2 and res == 100)
res = {co(200)} -- run until end
assert(res[1] == 10 and res[2] == 20 and res[3] == 30 and res[4] == nil)
assert(token == 200)
end
do
local f = T.makeCfunc[[
toclose 1
return .
]]
local obj = func2close(function ()
local temp
local x <close> = func2close(function ()
coroutine.yield(temp)
return 1,2,3 -- to be ignored
end)
temp = coroutine.yield("closing obj")
return 1,2,3 -- to be ignored
end)
local co = coroutine.wrap(f)
local res = co(obj, 10, 30, 1) -- will return only 30
assert(res == "closing obj")
res = co("closing x")
assert(res == "closing x")
res = {co()}
assert(res[1] == 30 and res[2] == nil)
end
do
-- still cannot yield inside 'closeslot'
local f = T.makeCfunc[[
toclose 1
closeslot 1
]]
local obj = func2close(coroutine.yield)
local co = coroutine.create(f)
local st, msg = coroutine.resume(co, obj)
assert(not st and string.find(msg, "attempt to yield across"))
-- nor outside a coroutine
local f = T.makeCfunc[[
toclose 1
]]
local st, msg = pcall(f, obj)
assert(not st and string.find(msg, "attempt to yield from outside"))
end
end
-- to-be-closed variables in generic for loops
do
local numopen = 0

View File

@@ -287,6 +287,33 @@ RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
local t = getoutput()
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- using the prompt default
prepfile[[ --
a = 2
]]
RUN([[lua -i < %s > %s]], prog, out)
local t = getoutput()
prompt = "> " -- the default
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- non-string prompt
prompt =
"local C = 0;\z
_PROMPT=setmetatable({},{__tostring = function () \z
C = C + 1; return C end})"
prepfile[[ --
a = 2
]]
RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out)
local t = getoutput()
assert(string.find(t, [[
1 --
2a = 2
3
]], 1, true))
-- test for error objects
prepfile[[
debug = require "debug"

View File

@@ -359,6 +359,38 @@ end
assert(n == 5)
do
print("testing next x GC of deleted keys")
-- bug in 5.4.1
local co = coroutine.wrap(function (t)
for k, v in pairs(t) do
local k1 = next(t) -- all previous keys were deleted
assert(k == k1) -- current key is the first in the table
t[k] = nil
local expected = (type(k) == "table" and k[1] or
type(k) == "function" and k() or
string.sub(k, 1, 1))
assert(expected == v)
coroutine.yield(v)
end
end)
local t = {}
t[{1}] = 1 -- add several unanchored, collectable keys
t[{2}] = 2
t[string.rep("a", 50)] = "a" -- long string
t[string.rep("b", 50)] = "b"
t[{3}] = 3
t[string.rep("c", 10)] = "c" -- short string
t[function () return 10 end] = 10
local count = 7
while co(t) do
collectgarbage("collect") -- collect dead keys
count = count - 1
end
assert(count == 0 and next(t) == nil) -- traversed the whole table
end
local function test (a)
assert(not pcall(table.insert, a, 2, 20));
table.insert(a, 10); table.insert(a, 2, 20);

View File

@@ -33,6 +33,7 @@ $NAME/pm.lua \
$NAME/sort.lua \
$NAME/strings.lua \
$NAME/tpack.lua \
$NAME/tracegc.lua \
$NAME/utf8.lua \
$NAME/vararg.lua \
$NAME/verybig.lua \

View File

@@ -361,6 +361,9 @@ assert(load("return 1\n--comment without ending EOL")() == 1)
checkerror("table expected", table.concat, 3)
checkerror("at index " .. maxi, table.concat, {}, " ", maxi, maxi)
-- '%' escapes following minus signal
checkerror("at index %" .. mini, table.concat, {}, " ", mini, mini)
assert(table.concat{} == "")
assert(table.concat({}, 'x') == "")
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")

40
testes/tracegc.lua Normal file
View File

@@ -0,0 +1,40 @@
-- track collections
local M = {}
-- import list
local setmetatable, stderr, collectgarbage =
setmetatable, io.stderr, collectgarbage
_ENV = nil
local active = false
-- each time a table is collected, remark it for finalization on next
-- cycle
local mt = {}
function mt.__gc (o)
stderr:write'.' -- mark progress
if active then
setmetatable(o, mt) -- remark object for finalization
end
end
function M.start ()
if not active then
active = true
setmetatable({}, mt) -- create initial object
end
end
function M.stop ()
if active then
active = false
collectgarbage() -- call finalizer for the last time
end
end
return M