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

Compare commits

...

76 Commits

Author SHA1 Message Date
Roberto Ierusalimschy 6c0e44464b Improvements in the manual around metamethods 2019-10-08 10:34:43 -03:00
Roberto Ierusalimschy 6a84c32900 No coercion string->number in arithmetic with LUA_NOCVTS2N 2019-10-08 10:29:38 -03:00
Roberto Ierusalimschy 6a10f03ff8 Makefile compiles the Lua compiler with '-Os'
The performance of the Lua compiler is not critical for Lua
performance, but it is a big component in the source. So, it makes
sense to trade speed for size in this component.
2019-10-08 10:26:02 -03:00
Roberto Ierusalimschy 7bd1e53753 Fixed a warning and other minor issues
Fixed some minor issues from the feedback for 5.4-beta rc1.
2019-10-04 16:17:04 -03:00
Roberto Ierusalimschy b98d41db99 Script 'packtests' gets Lua version as a parameter 2019-10-02 17:04:06 -03:00
Roberto Ierusalimschy b2a580bdb1 Janitorial work
- Several details in 'lcode.c'
- A few more tests for code generation
- Bug in assert in 'lcode.c' ("=" x "==")
- Comments in 'lopcodes.h' and 'ltable.c'
2019-10-01 17:24:37 -03:00
Roberto Ierusalimschy 89f6a85f03 Details in the makefile (warning options) 2019-09-24 14:43:32 -03:00
Roberto Ierusalimschy 6ef366644f Subtraction of small constant integers optimized with OP_ADDI 2019-09-24 14:34:52 -03:00
Roberto Ierusalimschy 03cde80b58 'setCstacklimit' renamed to 'setcstacklimit'
Function names in the API use only lowercase letters.
2019-09-24 14:31:06 -03:00
Roberto Ierusalimschy 6b2e202df5 Janitorial work in 'lcode.c' 2019-09-19 14:29:21 -03:00
Roberto Ierusalimschy 40d8832ee0 Simplification in the call to 'constfolding' 2019-09-11 10:20:10 -03:00
Roberto Ierusalimschy 91dad09f65 Removed arithmetic opcodes with immediate operand
The difference in performance between immediate operands and K operands
does not seem to justify all those extra opcodes. We only keep OP_ADDI,
due to its ubiquity and because the difference is a little more relevant.
(Later, OP_SUBI will be implemented by OP_ADDI, negating the constant.)
2019-09-10 13:20:03 -03:00
Roberto Ierusalimschy 4518e5df24 Added macro 'testMMMode'
Macro 'testMMMode' checks whether opcode is an MM opcode.
2019-09-06 14:14:11 -03:00
Roberto Ierusalimschy 72a094bda7 Undo change in the handling of 'L->top' (commit b80077b8f3)
With MMBIN instructions, there are fewer opcodes that need to update
'L->top', so that change does not seem to pay for the increased
complexity.
2019-08-29 12:52:37 -03:00
Roberto Ierusalimschy 46b84580d6 Use of 'MMBIN' opcodes extended to shift operators
Plus, this commit removes useless 'tm' parameters in 'op_*' macros.
2019-08-28 09:58:03 -03:00
Roberto Ierusalimschy df13f25948 First version of OP_MMBIN opcodes
In arithmetic/bitwise operators, the call to metamethods is made
in a separate opcode following the main one. (The main
opcode skips this next one when the operation succeeds.) This
change reduces slightly the size of the binary and the complexity
of the arithmetic/bitwise opcodes. It also simplfies the treatment
of errors and yeld/resume in these operations, as there are much
fewer cases to consider. (Only OP_MMBIN/OP_MMBINI/OP_MMBINK,
instead of all variants of all arithmetic/bitwise operators.)
2019-08-27 13:59:39 -03:00
Roberto Ierusalimschy 643188d6e5 Fixed missing case in 'luaV_finishOp'
A metamethod call like '1 << a' was not being properly resumed
if it got yielded.
2019-08-27 10:28:09 -03:00
Roberto Ierusalimschy 3df5624ff4 Fixed bug when yiedling inside OP_ADDK opcode
The family of opcodes OP_ADDK (arithmetic operators with K constant)
were not being handled in 'luaV_finishOp', which completes their
task after an yield.
2019-08-21 12:19:47 -03:00
Roberto Ierusalimschy be78aeae4c Default for warnings changed to "off"
Warnings are mostly a tool to help developers (e.g., by showing hidden
error messages); regular users usually don't need to see them.
2019-08-20 13:42:26 -03:00
Roberto Ierusalimschy 5bc47fe830 Detail (extra test for warnings when closing state) 2019-08-19 14:41:48 -03:00
Roberto Ierusalimschy 9405472565 Improvement in warn-mode '@store' (for testing)
When using warn-mode '@store', from the test library, the tests ensure
not only that the expected warnings were issued, but also that there was
no extra warnings.
2019-08-18 17:29:46 -03:00
Roberto Ierusalimschy 45948e7e55 Manual corrected with the new syntax for attributes
Commit 0d52913804, with the change in the syntax of attributes,
did not update the manual accordingly.
2019-08-16 16:11:21 -03:00
Roberto Ierusalimschy b96b0b5abb Added macro 'luaL_pushfail'
The macro 'luaL_pushfail' documents all places in the standard libraries
that return nil to signal some kind of failure. It is defined as
'lua_pushnil'. The manual also got a notation (@fail) to document those
returns. The tests were changed to be agnostic regarding whether 'fail'
is 'nil' or 'false'.
2019-08-16 14:58:02 -03:00
Roberto Ierusalimschy ca13be9af7 Supressed errors in '__close' generate warnings 2019-08-16 09:51:54 -03:00
Roberto Ierusalimschy a1d8eb2743 Added control messages to warnings
Added the concept of control messages to the warning system, plus the
implementation of the controls "@on"/"@off" to turn warnings on/off.
Moreover, the warning system in the test library adds some other
controls to ease the test of warnings.
2019-08-15 13:44:36 -03:00
Roberto Ierusalimschy f64a1b175a Small optimization in 'convergeephemerons'
When converging marks on ephemeron tables, change the direction the
tables are traversed at each iteration, to try to avoid bad-case
scenarios with linked lists of entries in a table.
2019-08-12 15:32:44 -03:00
Roberto Ierusalimschy 09b4e527a0 Detail in the manual (method 'file:setvbuf')
ANSI C is vague about 'setvbuf'; most details are implementation
defined. So, the manual cannot give any guaranties, either.
2019-08-12 11:26:08 -03:00
Roberto Ierusalimschy 35a28a58b3 Details
- removed rule about RCS from makefile
- comments and nitpicking in 'llex.c'
2019-08-01 14:11:33 -03:00
Roberto Ierusalimschy 223bb04090 Correction in the documentation of 'io.lines'
The loop does not end on end of file, but when the iterator function
fails to read a value. (In particular, the format "a" never fails,
so a loop with 'io.lines(fname, "a")' never ends.)
2019-07-31 11:41:59 -03:00
Roberto Ierusalimschy fe040633a1 Tracebacks recognize metamethods '__close' 2019-07-31 11:22:39 -03:00
Roberto Ierusalimschy f645d31573 To-be-closed variables must be closed on initialization
When initializing a to-be-closed variable, check whether it has a
'__close' metamethod (or is a false value) and raise an error if
if it hasn't. This produces more accurate error messages. (The
check before closing still need to be done: in the C API, the value
is not constant; and the object may lose its '__close' metamethod
during the block.)
2019-07-31 10:43:51 -03:00
Roberto Ierusalimschy 35b4efc270 Fixed test in 'main.lua'
The test "to-be-closed variables in main chunk" was broken,
as it used the removed feature of functions as to-be-closed values.
The error was not detected because its expected result had no lines
to be checked (due to missing new lines).
2019-07-30 13:48:40 -03:00
Roberto Ierusalimschy 0d52913804 Change in the syntax of attributes
Attributes changed to posfixed ('x <const>', instead of '<const> x'),
and "toclose" renamed to "close". Posfixed attributes seem to make it
clearer that it applies to only one variable when there are multiple
variables.
2019-07-30 12:18:19 -03:00
Roberto Ierusalimschy b80077b8f3 Change in the handling of 'L->top' when calling metamethods
Instead of updating 'L->top' in every place that may call a
metamethod, the metamethod functions themselves (luaT_trybinTM and
luaT_callorderTM) correct the top. (When calling metamethods from
the C API, however, the callers must preserve 'L->top'.)
2019-07-26 14:59:39 -03:00
Roberto Ierusalimschy e70f275f32 Bug: 'Vardesc' array can be reallocated in 'localstat'
A reference to a 'Vardesc*' (as done by 'localstat') can be
invalidated by the creation of any new variable.
2019-07-26 13:27:43 -03:00
Roberto Ierusalimschy 9a37dc0ce6 Small corrections when setting 'L->top'
- OP_NEWTABLE can use 'ra + 1' to set top (instead of ci->top);
- OP_CLOSE doesn't need to set top ('Protect' already does that);
- OP_TFORCALL must use 'ProtectNT', to preserve the top already set.
  (That was a small bug, because iterators could be called with
   extra parameters besides the state and the control variable.)
- Comments and an extra test for the bug in previous item.
2019-07-25 13:55:29 -03:00
Roberto Ierusalimschy 0eb6aa4013 Some improvements in date/time functions
- Range in date table extended to full 32 bits.
- Easier support for times represented as floats.
- Added more tests.
2019-07-24 15:01:59 -03:00
Roberto Ierusalimschy 7f5c31cdca Fixed bug in 'string.format' with option '%f'
As an example, 'print(string.format("%.99f", 1e70))' may have a
lot of garbage after the number.

The old test to ensure that 'string.format("%.99f", n)' was not too
large, 'fabs(n) < 1e100', assumes that the number will fit in the 99
bytes; but the 99 is not the space for the number, it is the added
extra zeros.  The option worked for smaller numbers because of the
extra space added to MAX_ITEM.
2019-07-23 12:46:33 -03:00
Roberto Ierusalimschy 9e6807c3c9 Do not collect open upvalues
Open upvalues are kept alive together with their corresponding
stack. This change makes a simpler and safer fix to the issue in
commit 440a5ee78c, about upvalues in the list of open upvalues
being collected while others are being created. (That previous fix
may not be correct.)
2019-07-22 09:41:10 -03:00
Roberto Ierusalimschy 2f22c6bb79 'math.randomseed' always returns the two seed components 2019-07-19 13:31:53 -03:00
Roberto Ierusalimschy 3c0d3c6fbe Avoid using addresses of static variables as unique keys
The addresses of static variables may be different for different
instances of Lua, making these instances incompatible if they use
these addresses as unique keys in the registry (or other tables).
2019-07-19 13:14:06 -03:00
Roberto Ierusalimschy 440a5ee78c Fixed bug for emergency collection in upvalue creation
When creating an upvalue, an emergency collection can collect the
previous upvalue where the new one would be linked. The following
code can trigger the bug, using valgrind on Lua compiled with the
-DHARDMEMTESTS option:

  local x; local y
  (function () return y end)();
  (function () return x end)()
2019-07-19 12:13:00 -03:00
Roberto Ierusalimschy dc07719b0d Tag LUA_TUPVALTBC replaced by a flag
It is simpler to signal a to-be-closed upvalue with a boolean flag,
instead of using a different tag.
2019-07-19 11:12:31 -03:00
Roberto Ierusalimschy 9cdf6b7082 Some details in 'lmem.c' and 'lgc.c'
- Several new comments in 'lmem.c'.
- Both 'luaM_growaux_' and 'luaM_shrinkvector_' use 'luaM_saferealloc_'
  to check for errors. Moreover, the use of 'luaM_saferealloc_' makes
  'luaM_shrinkvector_' try again if shrink fails (which can happen now).
- In 'checkSizes', save old debt only when needed.
2019-07-19 09:43:35 -03:00
Roberto Ierusalimschy 3c1d415bd3 Details
- Macro 'checkliveness' (for debug) always uses 'L', to avoid warnings.
- Some old 'while' changed to 'for' in 'testes/gc.lua'.
- In 'testes/libs/makefile', do not make files depend on 'ltests.h',
which may not even exist.
2019-07-18 15:31:22 -03:00
Roberto Ierusalimschy d36a31e673 Reviving HARDMEMTESTS
This commit brings a new implementation for HARDMEMTESTS, which forces
an emergency GC whenever possible. It also fixes some issues detected
with this option:
  - A small bug in lvm.c: a closure could be collected by an emergency
  GC while being initialized.
  - Some tests: a memory address can be immediatly reused after a GC;
  for instance, two consecutive '{}' expressions can return exactly the
  same address, if the first one is not anchored.
2019-07-18 14:58:15 -03:00
Roberto Ierusalimschy 024a6071ca Small bug with stack reallocation
OP_RETURN must update trap before updating stack. (Bug detected with
-DHARDSTACKTESTS). Also, in 'luaF_close', do not create a variable
with 'uplevel(uv)', as the stack may change and invalidate this
value. (This is not a bug, but could become one if 'upl' was used
again.)
2019-07-18 11:26:03 -03:00
Roberto Ierusalimschy 4eefef07ab 'math.randomseed()' returns the seeds it used
A call to 'math.randomseed()' returns the two components of the seed
it set, so that they can be used to set that same seed again.
2019-07-17 16:00:24 -03:00
Roberto Ierusalimschy 9c28ed05c9 Calls 'luaF_close' in 'lua_settop' only when needed
In 'lua_settop', avoid calling 'luaF_close' when increasing the stack
or when the function has no to-be-closed variables.
2019-07-17 15:22:11 -03:00
Roberto Ierusalimschy 8082906c05 Fixed small issue with constant propagation
Constants directly assigned to other constants were not propagating:
For instance, in
  local <const> k1 = 10
  local <const> k2 = k1
'k2' were not treated as a compile-time constant.
2019-07-17 14:50:42 -03:00
Roberto Ierusalimschy d6af81084d New kind of expression VKSTR
String literal expressions have their own kind VKSTR, instead of the
generic VK. This allows strings to "cross" functions without entering
their constant tables (e.g., if they are used only by some nested
function).
2019-07-17 14:26:56 -03:00
Roberto Ierusalimschy 4846f7e3bb Micro optimization in OP_RETURN and OP_TAILCALL
Many functions are vararg but create no upvalues, so it is better
to separate the tests for these two kinds of "extra work".
2019-07-16 15:44:37 -03:00
Roberto Ierusalimschy c220b0a5d0 '__close' method may be called again in case of error
An error in a closing method may be caused by a lack of resources,
such as memory or stack space, and the error may free enough resources
(by unwinding the stack) to allow the method to work if called again.

If the closing method is already running after some error (including
its own), it is not called again.
2019-07-16 15:17:47 -03:00
Roberto Ierusalimschy 298f383ffc Avoid setting the stack top below upvalues to be closed
When leaving a scope, the new stack top should be set only after
closing any upvalue, to avoid manipulating values in an "invalid"
part of the stack.
2019-07-16 14:13:22 -03:00
Roberto Ierusalimschy 758c1ef445 Unification of size representation in OP_NEWTABLE and OP_SETLIST
Opcodes OP_NEWTABLE and OP_SETLIST use the same representation to
store the size of the array part of a table. This new representation
can go up to 2^33 (8 + 25 bits).
2019-07-15 14:59:35 -03:00
Roberto Ierusalimschy dd6d8db49a Reordering of instructions in the main loop
The instructions in the main interpreter loop were reordered to
the same order of their enumeration in 'lopcodes.h'.
2019-07-12 16:47:02 -03:00
Roberto Ierusalimschy 1fb4d53925 OP_NEWTABLE keeps exact size of arrays
OP_NEWTABLE is followed by an OP_EXTRAARG, so that it can keep
the exact size of the array part of the table to be created.
(Functions 'luaO_int2fb'/'luaO_fb2int' were removed.)
2019-07-12 16:13:50 -03:00
Roberto Ierusalimschy f6aab3ec1f First implementation of constant propagation
Local constant variables initialized with compile-time constants
are optimized away from the code.
2019-07-12 11:38:42 -03:00
Roberto Ierusalimschy be8445d7e4 Details
In the generic for loop, it is simpler for OP_TFORLOOP to use the
same 'ra' as OP_TFORCALL. Moreover, the internal names of the loop
temporaries "(for ...)" don't need to leak internal details (even
because the numerical for loop doesn't have a fixed role for each of
its temporaries).
2019-07-10 14:58:31 -03:00
Roberto Ierusalimschy 3d296304ef Towards constant propagation
This commit detaches the number of active variables from the
number of variables in the stack, during compilation. Soon,
compile-time constants will be propagated and therefore will
not exist during run time (in the stack).
2019-07-10 14:00:22 -03:00
Roberto Ierusalimschy 54f7b46c1e New implementation for constants
VLOCAL expressions keep a reference to their corresponding 'Vardesc',
and 'Upvaldesc' (for upvalues) has a field 'ro' (read-only). So, it is
easier to check whether a variable is read-only. The decoupling in
VLOCAL between 'vidx' ('Vardesc' index) and 'sidx' (stack index)
should also help the forthcoming implementation of compile-time
constant propagation.
2019-07-09 10:43:17 -03:00
Roberto Ierusalimschy e888976bc6 Details (typos in comments) 2019-07-05 15:03:15 -03:00
Roberto Ierusalimschy 4d46289331 Local attributes can be used in list of local variables
The syntax for local attributes ('const'/'toclose') was unified with
the regular syntax for local variables, so that we can have variables
with attributes in local definitions with multiple names; for instance:

  local <toclose> f, <const> err = io.open(fname)

This new syntax does not implement constant propagation, yet.

This commit also has some small improvements to the manual.
2019-07-03 14:18:07 -03:00
Roberto Ierusalimschy 8eca21c2e8 First take on constant propagation 2019-07-01 12:42:31 -03:00
Roberto Ierusalimschy 924bed7297 Methods separated from metamethods in 'io'
In the 'io' library, changed the use of the metatable also as its
own "method table", so that metamethods cannot be accessed as if they
were methods. (For instance, 'io.stdin.__gc' does not result in
the finalizer metamethod anymore.)
2019-07-01 12:25:00 -03:00
Roberto Ierusalimschy 8b7cfee26b Small changes around C-stack limit
- Better documentation in 'testes/cstack.lua' about using
'debug.setCstacklimit' to find a good limit.

- Constant LUAI_MAXCSTACK gets added CSTACKERR (extra stack for
error handling), so that it is compatible with the argument to
'debug.setCstacklimit'.
2019-06-26 13:26:36 -03:00
Roberto Ierusalimschy c1a63c45f8 '__call' metamethod can be any callable object
Removed the restriction that a '__call' metamethod must be an actual
function.
2019-06-25 17:45:50 -03:00
Roberto Ierusalimschy 4487c28ced A few more tests for table access in the API
Added tests where the table being accessed is also the index or
value in the operation.
2019-06-25 17:38:58 -03:00
Roberto Ierusalimschy 05ba288049 Added script 'packtests' to the project
The script 'packtests' creates the 'tar.gz' to deploy the test suite
for Lua.
2019-06-21 10:46:41 -03:00
Roberto Ierusalimschy 6b9490bd72 Details in tests
- Added a test for calling 'debug.traceback' after yields inside
hooks. (Lua 5.3 seems to have a bug there.)

- Removed test "repeat test with '__open' metamethod instead of a
function", as the previous test already uses the '__open' metamethod.
(It changed when functions were removed as possible to-be-closed
variables).
2019-06-21 10:21:07 -03:00
Roberto Ierusalimschy e4b02ca8e4 Structure 'Vardesc' does not need a 'name' field
Removed the field 'name' from the structure 'Vardesc', as the name
of the local variable is already available in the prototype of the
function, through the index 'idx'.
2019-06-21 10:16:57 -03:00
Roberto Ierusalimschy 20a9853e02 Cleaning macros in 'luaV_execute'
Ensure that operation macros, such as 'luai_numdiv' and 'luai_numidiv',
operate only on variables, or at most at 's2v(ra)'. ('s2v' is a nop, a
cast from pointer to pointer.)
2019-06-21 10:00:50 -03:00
Roberto Ierusalimschy 1d70708a78 Fixed bug [5.4 alpha] for errors in finalizers
Fixes the bug related in [1] (Lua can crash after raising an error
in a finalizer), following the lead in [2].

[1] http://lua-users.org/lists/lua-l/2019-06/msg00448.html
[2] http://lua-users.org/lists/lua-l/2019-06/msg00450.html
2019-06-21 09:34:49 -03:00
Roberto Ierusalimschy be73f72fcc New function 'setCstacklimit'
Added new functions to dynamically set the C-stack limit
('lua_setCstacklimit' in the C-API, 'debug.setCstacklimit' in Lua).
2019-06-18 16:52:22 -03:00
Roberto Ierusalimschy 3cd9b56ae6 Revamp around 'L->nCcalls' count
The field 'L->nCcalls' now counts downwards, so that the C-stack
limits do not depend on the stack size.
2019-06-12 10:31:38 -03:00
Roberto Ierusalimschy d2a9b4ffb8 Detail in the manual
More precision describing the variables that won't be closed if a
coroutine yields forever.
2019-06-10 13:59:19 -03:00
71 changed files with 2782 additions and 1631 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ make -s -j
cd testes/libs; make -s
cd .. # back to directory 'testes'
ulimit -S -s 2000
if { ../lua all.lua; } then
if { ../lua -W all.lua; } then
echo -e "\n\n final OK!!!!\n\n"
else
echo -e "\n\n >>>> BUG!!!!\n\n"
+11 -8
View File
@@ -170,20 +170,23 @@ LUA_API int lua_gettop (lua_State *L) {
LUA_API void lua_settop (lua_State *L, int idx) {
StkId func = L->ci->func;
CallInfo *ci = L->ci;
StkId func = ci->func;
ptrdiff_t diff; /* difference for new top */
lua_lock(L);
if (idx >= 0) {
StkId newtop = (func + 1) + idx;
api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
while (L->top < newtop)
setnilvalue(s2v(L->top++));
L->top = newtop;
api_check(L, idx <= ci->top - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top;
for (; diff > 0; diff--)
setnilvalue(s2v(L->top++)); /* clear new slots */
}
else {
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
L->top += idx+1; /* 'subtract' index (index is negative) */
diff = idx + 1; /* will "subtract" index (as it is negative) */
}
luaF_close(L, L->top, LUA_OK);
if (diff < 0 && hastocloseCfunc(ci->nresults))
luaF_close(L, L->top + diff, LUA_OK);
L->top += diff; /* correct top only after closing any upvalue */
lua_unlock(L);
}
+10
View File
@@ -11,12 +11,22 @@
#include "llimits.h"
#include "lstate.h"
/* Increments 'L->top', checking for stack overflows */
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
"stack overflow");}
/*
** If a call returns too many multiple returns, the callee may not have
** stack space to accomodate all results. In this case, this macro
** increases its stack space ('L->ci->top').
*/
#define adjustresults(L,nres) \
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
/* Ensure the stack has at least 'n' elements */
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
"not enough elements in the stack")
+28 -14
View File
@@ -62,7 +62,7 @@ static int findfield (lua_State *L, int objidx, int level) {
else if (findfield(L, objidx, level - 1)) { /* try recursively */
/* stack: lib_name, lib_table, field_name (top) */
lua_pushliteral(L, "."); /* place '.' between the two names */
lua_replace(L, -3); /* (in the slot ocupied by table) */
lua_replace(L, -3); /* (in the slot occupied by table) */
lua_concat(L, 3); /* lib_name.field_name */
return 1;
}
@@ -249,7 +249,7 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
return 1;
}
else {
lua_pushnil(L);
luaL_pushfail(L);
if (fname)
lua_pushfstring(L, "%s: %s", fname, strerror(en));
else
@@ -291,10 +291,10 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) {
if (*what == 'e' && stat == 0) /* successful termination? */
lua_pushboolean(L, 1);
else
lua_pushnil(L);
luaL_pushfail(L);
lua_pushstring(L, what);
lua_pushinteger(L, stat);
return 3; /* return true/nil,what,code */
return 3; /* return true/fail,what,code */
}
}
@@ -1002,29 +1002,43 @@ static int panic (lua_State *L) {
/*
** Emit a warning. '*previoustocont' signals whether previous message
** was to be continued by the current one.
** Emit a warning. '*warnstate' means:
** 0 - warning system is off;
** 1 - ready to start a new message;
** 2 - previous message is to be continued.
*/
static void warnf (void *ud, const char *message, int tocont) {
int *previoustocont = (int *)ud;
if (!*previoustocont) /* previous message was the last? */
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;
}
else if (*warnstate == 0) /* warnings off? */
return;
if (*warnstate == 1) /* previous message was the last? */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
lua_writestringerror("%s", message); /* write message */
if (!tocont) /* is this the last part? */
if (tocont) /* not the last part? */
*warnstate = 2; /* to be continued */
else { /* last part */
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
*previoustocont = tocont;
*warnstate = 1; /* ready to start a new message */
}
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) {
int *previoustocont; /* space for warning state */
int *warnstate; /* space for warning state */
lua_atpanic(L, &panic);
previoustocont = (int *)lua_newuserdatauv(L, sizeof(int), 0);
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*previoustocont = 0; /* next message starts a new warning */
lua_setwarnf(L, warnf, previoustocont);
*warnstate = 0; /* default is warnings off */
lua_setwarnf(L, warnf, warnstate);
}
return L;
}
+4
View File
@@ -153,6 +153,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
/* push the value used to represent failure/error */
#define luaL_pushfail(L) lua_pushnil(L)
/*
** {======================================================
** Generic Buffer manipulation
+5 -5
View File
@@ -48,9 +48,9 @@ static int luaB_warn (lua_State *L) {
luaL_checkstring(L, 1); /* at least one argument */
for (i = 2; i <= n; i++)
luaL_checkstring(L, i); /* make sure all arguments are strings */
for (i = 1; i <= n; i++) /* compose warning */
for (i = 1; i < n; i++) /* compose warning */
lua_warning(L, lua_tostring(L, i), 1);
lua_warning(L, "", 0); /* close warning */
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
return 0;
}
@@ -106,7 +106,7 @@ static int luaB_tonumber (lua_State *L) {
return 1;
} /* else not a number */
} /* else not a number */
lua_pushnil(L); /* not a number */
luaL_pushfail(L); /* not a number */
return 1;
}
@@ -308,9 +308,9 @@ static int load_aux (lua_State *L, int status, int envidx) {
return 1;
}
else { /* error (message is on top of the stack) */
lua_pushnil(L);
luaL_pushfail(L);
lua_insert(L, -2); /* put before error message */
return 2; /* return nil plus error message */
return 2; /* return fail plus error message */
}
}
+231 -123
View File
@@ -52,7 +52,7 @@ l_noret luaK_semerror (LexState *ls, const char *msg) {
** If expression is a numeric constant, fills 'v' with its value
** and returns 1. Otherwise, returns 0.
*/
static int tonumeral(const expdesc *e, TValue *v) {
static int tonumeral (const expdesc *e, TValue *v) {
if (hasjumps(e))
return 0; /* not a numeral */
switch (e->k) {
@@ -67,6 +67,42 @@ static int tonumeral(const expdesc *e, TValue *v) {
}
/*
** Get the constant value from a constant expression
*/
static TValue *const2val (FuncState *fs, const expdesc *e) {
lua_assert(e->k == VCONST);
return &fs->ls->dyd->actvar.arr[e->u.info].k;
}
/*
** If expression is a constant, fills 'v' with its value
** and returns 1. Otherwise, returns 0.
*/
int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
if (hasjumps(e))
return 0; /* not a constant */
switch (e->k) {
case VFALSE: case VTRUE:
setbvalue(v, e->k == VTRUE);
return 1;
case VNIL:
setnilvalue(v);
return 1;
case VKSTR: {
setsvalue(fs->ls->L, v, e->u.strval);
return 1;
}
case VCONST: {
setobj(fs->ls->L, v, const2val(fs, e));
return 1;
}
default: return tonumeral(e, v);
}
}
/*
** Return the previous instruction of the current code. If there
** may be a jump target between the current instruction and the
@@ -323,12 +359,12 @@ static void removelastlineinfo (FuncState *fs) {
Proto *f = fs->f;
int pc = fs->pc - 1; /* last instruction coded */
if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
fs->previousline -= f->lineinfo[pc]; /* last line saved */
fs->iwthabs--;
fs->previousline -= f->lineinfo[pc]; /* correct last line saved */
fs->iwthabs--; /* undo previous increment */
}
else { /* absolute line information */
lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc);
fs->nabslineinfo--; /* remove it */
lua_assert(f->abslineinfo[fs->nabslineinfo].pc = pc);
fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
}
}
@@ -348,7 +384,7 @@ static void removelastinstruction (FuncState *fs) {
** Emit instruction 'i', checking for array sizes and saving also its
** line information. Return 'i' position.
*/
static int luaK_code (FuncState *fs, Instruction i) {
int luaK_code (FuncState *fs, Instruction i) {
Proto *f = fs->f;
/* put new instruction in code array */
luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
@@ -458,7 +494,7 @@ void luaK_reserveregs (FuncState *fs, int n) {
)
*/
static void freereg (FuncState *fs, int reg) {
if (reg >= fs->nactvar) {
if (reg >= luaY_nvarstack(fs)) {
fs->freereg--;
lua_assert(reg == fs->freereg);
}
@@ -537,7 +573,7 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
/*
** Add a string to list of constants and return its index.
*/
int luaK_stringK (FuncState *fs, TString *s) {
static int stringK (FuncState *fs, TString *s) {
TValue o;
setsvalue(fs->ls->L, &o, s);
return addk(fs, &o, &o); /* use string itself as key */
@@ -590,12 +626,12 @@ static int nilK (FuncState *fs) {
/*
** Check whether 'i' can be stored in an 'sC' operand.
** Equivalent to (0 <= i + OFFSET_sC && i + OFFSET_sC <= MAXARG_C)
** but without risk of overflows in the addition.
** Check whether 'i' can be stored in an 'sC' operand. Equivalent to
** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of
** overflows in the hidden addition inside 'int2sC'.
*/
static int fitsC (lua_Integer i) {
return (-OFFSET_sC <= i && i <= MAXARG_C - OFFSET_sC);
return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C));
}
@@ -615,20 +651,40 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
}
static int floatI (lua_Number f, lua_Integer *fi) {
return (luaV_flttointeger(f, fi, 0) && fitsBx(*fi));
}
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
lua_Integer fi;
if (floatI(f, &fi))
if (luaV_flttointeger(f, &fi, 0) && fitsBx(fi))
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
else
luaK_codek(fs, reg, luaK_numberK(fs, f));
}
/*
** Convert a constant in 'v' into an expression description 'e'
*/
static void const2exp (TValue *v, expdesc *e) {
switch (ttypetag(v)) {
case LUA_TNUMINT:
e->k = VKINT; e->u.ival = ivalue(v);
break;
case LUA_TNUMFLT:
e->k = VKFLT; e->u.nval = fltvalue(v);
break;
case LUA_TBOOLEAN:
e->k = bvalue(v) ? VTRUE : VFALSE;
break;
case LUA_TNIL:
e->k = VNIL;
break;
case LUA_TSHRSTR: case LUA_TLNGSTR:
e->k = VKSTR; e->u.strval = tsvalue(v);
break;
default: lua_assert(0);
}
}
/*
** Fix an expression to return the number of results 'nresults'.
** Either 'e' is a multi-ret expression (function call or vararg)
@@ -647,6 +703,16 @@ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
}
/*
** Convert a VKSTR to a VK
*/
static void str2K (FuncState *fs, expdesc *e) {
lua_assert(e->k == VKSTR);
e->u.info = stringK(fs, e->u.strval);
e->k = VK;
}
/*
** Fix an expression to return one result.
** If expression is not a multi-ret expression (function call or
@@ -672,18 +738,22 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
/*
** Ensure that expression 'e' is not a variable.
** Ensure that expression 'e' is not a variable (nor a constant).
** (Expression still may have jump lists.)
*/
void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) {
case VCONST: {
const2exp(const2val(fs, e), e);
break;
}
case VLOCAL: { /* already in a register */
e->u.info = e->u.var.idx;
e->u.info = e->u.var.sidx;
e->k = VNONRELOC; /* becomes a non-relocatable value */
break;
}
case VUPVAL: { /* move value to some (pending) register */
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0);
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
e->k = VRELOC;
break;
}
@@ -735,6 +805,9 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
break;
}
case VKSTR: {
str2K(fs, e);
} /* FALLTHROUGH */
case VK: {
luaK_codek(fs, reg, e->u.info);
break;
@@ -850,7 +923,7 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
if (e->k == VNONRELOC) { /* expression already has a register? */
if (!hasjumps(e)) /* no jumps? */
return e->u.info; /* result is already in a register */
if (e->u.info >= fs->nactvar) { /* reg. is not a local? */
if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */
exp2reg(fs, e, e->u.info); /* put final result in it */
return e->u.info;
}
@@ -895,6 +968,7 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
case VNIL: info = nilK(fs); break;
case VKINT: info = luaK_intK(fs, e->u.ival); break;
case VKFLT: info = luaK_numberK(fs, e->u.nval); break;
case VKSTR: info = stringK(fs, e->u.strval); break;
case VK: info = e->u.info; break;
default: return 0; /* not a constant */
}
@@ -939,12 +1013,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
return;
}
case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
break;
}
case VINDEXUP: {
@@ -1029,7 +1103,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
pc = e->u.info; /* save jump position */
break;
}
case VK: case VKFLT: case VKINT: case VTRUE: {
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
pc = NO_JUMP; /* always true; do nothing */
break;
}
@@ -1074,13 +1148,12 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) {
** Code 'not e', doing constant folding.
*/
static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
e->k = VTRUE; /* true == not nil == not false */
break;
}
case VK: case VKFLT: case VKINT: case VTRUE: {
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */
break;
}
@@ -1143,15 +1216,16 @@ static int isSCint (expdesc *e) {
** Check whether expression 'e' is a literal integer or float in
** proper range to fit in a register (sB or sC).
*/
static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
lua_Integer i;
if (e->k == VKINT)
*i = e->u.ival;
else if (!(e->k == VKFLT && floatI(e->u.nval, i)))
return 0; /* not a number */
else
i = e->u.ival;
else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, 0))
*isfloat = 1;
if (!hasjumps(e) && fitsC(*i)) {
*i += OFFSET_sC;
else
return 0; /* not a number */
if (!hasjumps(e) && fitsC(i)) {
*pi = int2sC(cast_int(i));
return 1;
}
else
@@ -1166,18 +1240,20 @@ static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
** values in registers.
*/
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (k->k == VKSTR)
str2K(fs, k);
lua_assert(!hasjumps(t) &&
(t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) {
t->u.ind.t = t->u.var.idx; /* upvalue index */
t->u.ind.t = t->u.info; /* upvalue index */
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXUP;
}
else {
/* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info;
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXSTR;
@@ -1218,7 +1294,7 @@ static int validop (int op, TValue *v1, TValue *v2) {
** (In this case, 'e1' has the final result.)
*/
static int constfolding (FuncState *fs, int op, expdesc *e1,
const expdesc *e2) {
const expdesc *e2) {
TValue v1, v2, res;
if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
return 0; /* non-numeric operands or not safe to fold */
@@ -1257,18 +1333,18 @@ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
** (everything but logical operators 'and'/'or' and comparison
** operators).
** Expression to produce final result will be encoded in 'e1'.
** Because 'luaK_exp2anyreg' can free registers, its calls must be
** in "stack order" (that is, first on 'e2', which may have more
** recent registers to be released).
*/
static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
OpCode op, int v2, int k, int line) {
OpCode op, int v2, int flip, int line,
OpCode mmop, TMS event) {
int v1 = luaK_exp2anyreg(fs, e1);
int pc = luaK_codeABCk(fs, op, 0, v1, v2, k);
int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0);
freeexps(fs, e1, e2);
e1->u.info = pc;
e1->k = VRELOC; /* all those operations are relocatable */
luaK_fixline(fs, line);
luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */
luaK_fixline(fs, line);
}
@@ -1279,17 +1355,43 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
static void codebinexpval (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int line) {
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
finishbinexpval(fs, e1, e2, op, v2, 0, line);
lua_assert(OP_ADD <= op && op <= OP_SHR);
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN,
cast(TMS, (op - OP_ADD) + TM_ADD));
}
/*
** Code binary operators ('+', '-', ...) with immediate operands.
** Code binary operators with immediate operands.
*/
static void codebini (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int k, int line) {
int v2 = cast_int(e2->u.ival) + OFFSET_sC; /* immediate operand */
finishbinexpval(fs, e1, e2, op, v2, k, line);
expdesc *e1, expdesc *e2, int flip, int line,
TMS event) {
int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */
lua_assert(e2->k == VKINT);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event);
}
/* Try to code a binary operator negating its second operand.
** For the metamethod, 2nd operand must keep its original value.
*/
static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
OpCode op, int line, TMS event) {
if (!luaK_isKint(e2))
return 0; /* not an integer constant */
else {
lua_Integer i2 = e2->u.ival;
if (!(fitsC(i2) && fitsC(-i2)))
return 0; /* not in the proper range */
else { /* operating a small integer constant */
int v2 = cast_int(i2);
finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event);
/* correct metamethod argument */
SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2));
return 1; /* successfully coded */
}
}
}
@@ -1300,19 +1402,18 @@ static void swapexps (expdesc *e1, expdesc *e2) {
/*
** Code arithmetic operators ('+', '-', ...). If second operand is a
** constant in the proper range, use variant opcodes with immediate
** operands or K operands.
** constant in the proper range, use variant opcodes with K operands.
*/
static void codearith (FuncState *fs, OpCode op,
static void codearith (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
if (isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
TMS event = cast(TMS, opr + TM_ADD);
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
int v2 = e2->u.info; /* K index */
op = cast(OpCode, op - OP_ADD + OP_ADDK);
finishbinexpval(fs, e1, e2, op, v2, flip, line);
OpCode op = cast(OpCode, opr + OP_ADDK);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
}
else { /* 'e2' is neither an immediate nor a K operand */
OpCode op = cast(OpCode, opr + OP_ADD);
if (flip)
swapexps(e1, e2); /* back to original order */
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
@@ -1325,14 +1426,17 @@ static void codearith (FuncState *fs, OpCode op,
** numeric constant, change order of operands to try to use an
** immediate or K operator.
*/
static void codecommutative (FuncState *fs, OpCode op,
static void codecommutative (FuncState *fs, BinOpr op,
expdesc *e1, expdesc *e2, int line) {
int flip = 0;
if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
swapexps(e1, e2); /* change order */
flip = 1;
}
codearith(fs, op, e1, e2, flip, line);
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
else
codearith(fs, op, e1, e2, flip, line);
}
@@ -1342,41 +1446,23 @@ static void codecommutative (FuncState *fs, OpCode op,
*/
static void codebitwise (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) {
int inv = 0;
int flip = 0;
int v2;
OpCode op;
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
swapexps(e1, e2); /* 'e2' will be the constant operand */
inv = 1;
flip = 1;
}
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
op = cast(OpCode, opr - OPR_BAND + OP_BAND);
op = cast(OpCode, opr + OP_ADD);
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
return;
}
v2 = e2->u.info; /* index in K array */
op = cast(OpCode, opr - OPR_BAND + OP_BANDK);
op = cast(OpCode, opr + OP_ADDK);
lua_assert(ttisinteger(&fs->f->k[v2]));
finishbinexpval(fs, e1, e2, op, v2, inv, line);
}
/*
** Code shift operators. If second operand is constant, use immediate
** operand (negating it if shift is in the other direction).
*/
static void codeshift (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int line) {
if (isSCint(e2)) {
int changedir = 0;
if (op == OP_SHL) {
changedir = 1;
e2->u.ival = -(e2->u.ival);
}
codebini(fs, OP_SHRI, e1, e2, changedir, line);
}
else
codebinexpval(fs, op, e1, e2, line);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
cast(TMS, opr + TM_ADD));
}
@@ -1386,18 +1472,18 @@ static void codeshift (FuncState *fs, OpCode op,
*/
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
int r1, r2;
lua_Integer im;
int im;
int isfloat = 0;
if (isSCnumber(e2, &im, &isfloat)) {
/* use immediate operand */
r1 = luaK_exp2anyreg(fs, e1);
r2 = cast_int(im);
r2 = im;
op = cast(OpCode, (op - OP_LT) + OP_LTI);
}
else if (isSCnumber(e1, &im, &isfloat)) {
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
r1 = luaK_exp2anyreg(fs, e2);
r2 = cast_int(im);
r2 = im;
op = (op == OP_LT) ? OP_GTI : OP_GEI;
}
else { /* regular case, compare two registers */
@@ -1416,7 +1502,7 @@ static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
*/
static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
int r1, r2;
lua_Integer im;
int im;
int isfloat = 0; /* not needed here, but kept for symmetry */
OpCode op;
if (e1->k != VNONRELOC) {
@@ -1426,7 +1512,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
r1 = luaK_exp2anyreg(fs, e1); /* 1nd expression must be in register */
if (isSCnumber(e2, &im, &isfloat)) {
op = OP_EQI;
r2 = cast_int(im); /* immediate operand */
r2 = im; /* immediate operand */
}
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
op = OP_EQK;
@@ -1447,11 +1533,12 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
*/
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
luaK_dischargevars(fs, e);
switch (op) {
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
break;
/* FALLTHROUGH */
/* else */ /* FALLTHROUGH */
case OPR_LEN:
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
break;
@@ -1466,6 +1553,7 @@ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
** 2nd operand.
*/
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
luaK_dischargevars(fs, v);
switch (op) {
case OPR_AND: {
luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
@@ -1497,8 +1585,7 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
}
case OPR_LT: case OPR_LE:
case OPR_GT: case OPR_GE: {
lua_Integer dummy;
int dummy2;
int dummy, dummy2;
if (!isSCnumber(v, &dummy, &dummy2))
luaK_exp2anyreg(fs, v);
/* else keep numeral, which may be an immediate operand */
@@ -1535,17 +1622,18 @@ static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) {
*/
void luaK_posfix (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) {
luaK_dischargevars(fs, e2);
if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2))
return; /* done by folding */
switch (opr) {
case OPR_AND: {
lua_assert(e1->t == NO_JUMP); /* list closed by 'luK_infix' */
luaK_dischargevars(fs, e2);
lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */
luaK_concat(fs, &e2->f, e1->f);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list closed by 'luK_infix' */
luaK_dischargevars(fs, e2);
lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
@@ -1556,35 +1644,39 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
break;
}
case OPR_ADD: case OPR_MUL: {
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
codecommutative(fs, cast(OpCode, opr + OP_ADD), e1, e2, line);
codecommutative(fs, opr, e1, e2, line);
break;
}
case OPR_SUB: case OPR_DIV:
case OPR_IDIV: case OPR_MOD: case OPR_POW: {
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
codearith(fs, cast(OpCode, opr + OP_ADD), e1, e2, 0, line);
case OPR_SUB: {
if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB))
break; /* coded as (r1 + -I) */
/* ELSE */
} /* FALLTHROUGH */
case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: {
codearith(fs, opr, e1, e2, 0, line);
break;
}
case OPR_BAND: case OPR_BOR: case OPR_BXOR: {
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
codebitwise(fs, opr, e1, e2, line);
codebitwise(fs, opr, e1, e2, line);
break;
}
case OPR_SHL: {
if (!constfolding(fs, LUA_OPSHL, e1, e2)) {
if (isSCint(e1)) {
swapexps(e1, e2);
codebini(fs, OP_SHLI, e1, e2, 1, line);
}
else
codeshift(fs, OP_SHL, e1, e2, line);
if (isSCint(e1)) {
swapexps(e1, e2);
codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */
}
else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) {
/* coded as (r1 >> -I) */;
}
else /* regular case (two registers) */
codebinexpval(fs, OP_SHL, e1, e2, line);
break;
}
case OPR_SHR: {
if (!constfolding(fs, LUA_OPSHR, e1, e2))
codeshift(fs, OP_SHR, e1, e2, line);
if (isSCint(e2))
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
else /* regular case (two registers) */
codebinexpval(fs, OP_SHR, e1, e2, line);
break;
}
case OPR_EQ: case OPR_NE: {
@@ -1618,6 +1710,22 @@ void luaK_fixline (FuncState *fs, int line) {
}
void luaK_settablesize (FuncState *fs, int pc, int ra, int rc, int rb) {
Instruction *inst = &fs->f->code[pc];
int extra = 0;
int k = 0;
if (rb != 0)
rb = luaO_ceillog2(rb) + 1; /* hash size */
if (rc > MAXARG_C) { /* does it need the extra argument? */
extra = rc / (MAXARG_C + 1);
rc %= (MAXARG_C + 1);
k = 1;
}
*inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
*(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
}
/*
** Emit a SETLIST instruction.
** 'base' is register that keeps table;
@@ -1626,17 +1734,17 @@ void luaK_fixline (FuncState *fs, int line) {
** table (or LUA_MULTRET to add up to stack top).
*/
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
if (c <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, b, c);
else if (c <= MAXARG_Ax) {
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
codeextraarg(fs, c);
if (tostore == LUA_MULTRET)
tostore = 0;
if (nelems <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
else {
int extra = nelems / (MAXARG_C + 1);
nelems %= (MAXARG_C + 1);
luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
codeextraarg(fs, extra);
}
else
luaX_syntaxerror(fs->ls, "constructor too long");
fs->freereg = base + 1; /* free registers with list values */
}
@@ -1675,10 +1783,10 @@ void luaK_finish (FuncState *fs) {
SET_OPCODE(*pc, OP_RETURN);
} /* FALLTHROUGH */
case OP_RETURN: case OP_TAILCALL: {
if (fs->needclose || p->is_vararg) {
SETARG_C(*pc, p->is_vararg ? p->numparams + 1 : 0);
SETARG_k(*pc, 1); /* signal that there is extra work */
}
if (fs->needclose)
SETARG_k(*pc, 1); /* signal that it needs to close */
if (p->is_vararg)
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
break;
}
case OP_JMP: {
+14 -3
View File
@@ -24,19 +24,27 @@
** grep "ORDER OPR" if you change these enums (ORDER OP)
*/
typedef enum BinOpr {
/* arithmetic operators */
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
OPR_DIV,
OPR_IDIV,
OPR_DIV, OPR_IDIV,
/* bitwise operators */
OPR_BAND, OPR_BOR, OPR_BXOR,
OPR_SHL, OPR_SHR,
/* string operator */
OPR_CONCAT,
/* comparison operators */
OPR_EQ, OPR_LT, OPR_LE,
OPR_NE, OPR_GT, OPR_GE,
/* logical operators */
OPR_AND, OPR_OR,
OPR_NOBINOPR
} BinOpr;
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
#define foldbinop(op) ((op) <= OPR_SHR)
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
@@ -51,16 +59,17 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
int B, int C, int k);
LUAI_FUNC int luaK_isKint (expdesc *e);
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
@@ -85,6 +94,8 @@ LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
expdesc *v2, int line);
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
int ra, int rb, int rc);
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
LUAI_FUNC void luaK_finish (FuncState *fs);
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
+2 -1
View File
@@ -116,7 +116,8 @@ static int luaB_yield (lua_State *L) {
#define COS_NORM 3
static const char *statname[] = {"running", "dead", "suspended", "normal"};
static const char *const statname[] =
{"running", "dead", "suspended", "normal"};
static int auxstatus (lua_State *L, lua_State *co) {
+28 -16
View File
@@ -21,10 +21,10 @@
/*
** The hook table at registry[&HOOKKEY] maps threads to their current
** hook function. (We only need the unique address of 'HOOKKEY'.)
** The hook table at registry[HOOKKEY] maps threads to their current
** hook function.
*/
static const int HOOKKEY = 0;
static const char *const HOOKKEY = "_HOOKKEY";
/*
@@ -65,7 +65,7 @@ static int db_setmetatable (lua_State *L) {
static int db_getuservalue (lua_State *L) {
int n = (int)luaL_optinteger(L, 2, 1);
if (lua_type(L, 1) != LUA_TUSERDATA)
lua_pushnil(L);
luaL_pushfail(L);
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
lua_pushboolean(L, 1);
return 2;
@@ -80,7 +80,7 @@ static int db_setuservalue (lua_State *L) {
luaL_checkany(L, 2);
lua_settop(L, 2);
if (!lua_setiuservalue(L, 1, n))
lua_pushnil(L);
luaL_pushfail(L);
return 1;
}
@@ -159,7 +159,7 @@ static int db_getinfo (lua_State *L) {
}
else { /* stack level */
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
lua_pushnil(L); /* level out of range */
luaL_pushfail(L); /* level out of range */
return 1;
}
}
@@ -223,7 +223,7 @@ static int db_getlocal (lua_State *L) {
return 2;
}
else {
lua_pushnil(L); /* no name (nor value) */
luaL_pushfail(L); /* no name (nor value) */
return 1;
}
}
@@ -314,7 +314,7 @@ static int db_upvaluejoin (lua_State *L) {
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"};
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
lua_pushthread(L);
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
@@ -367,14 +367,12 @@ static int db_sethook (lua_State *L) {
count = (int)luaL_optinteger(L, arg + 3, 0);
func = hookf; mask = makemask(smask, count);
}
if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) {
lua_createtable(L, 0, 2); /* create a hook table */
lua_pushvalue(L, -1);
lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
/* table just created; initialize it */
lua_pushstring(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
}
checkstack(L, L1, 1);
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
@@ -391,12 +389,14 @@ static int db_gethook (lua_State *L) {
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook == NULL) /* no hook? */
lua_pushnil(L);
if (hook == NULL) { /* no hook? */
luaL_pushfail(L);
return 1;
}
else if (hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else { /* hook table must exist */
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
checkstack(L, L1, 1);
lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
@@ -437,6 +437,17 @@ 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);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getuservalue", db_getuservalue},
@@ -454,6 +465,7 @@ static const luaL_Reg dblib[] = {
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_traceback},
{"setcstacklimit", db_setcstacklimit},
{NULL, NULL}
};
+7 -21
View File
@@ -465,12 +465,14 @@ static int filterpc (int pc, int jmptarget) {
/*
** try to find last instruction before 'lastpc' that modified register 'reg'
** Try to find last instruction before 'lastpc' that modified register 'reg'.
*/
static int findsetreg (const Proto *p, int lastpc, int reg) {
int pc;
int setreg = -1; /* keep last instruction that changed 'reg' */
int jmptarget = 0; /* any code before this address is conditional */
if (testMMMode(GET_OPCODE(p->code[lastpc])))
lastpc--; /* previous instruction was not actually executed */
for (pc = 0; pc < lastpc; pc++) {
Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i);
@@ -620,24 +622,8 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
tm = TM_NEWINDEX;
break;
case OP_ADDI: case OP_SUBI: case OP_MULI: case OP_MODI:
case OP_POWI: case OP_DIVI: case OP_IDIVI: {
int offset = GET_OPCODE(i) - OP_ADDI; /* ORDER OP */
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
break;
}
case OP_ADDK: case OP_SUBK: case OP_MULK: case OP_MODK:
case OP_POWK: case OP_DIVK: case OP_IDIVK:
case OP_BANDK: case OP_BORK: case OP_BXORK: {
int offset = GET_OPCODE(i) - OP_ADDK; /* ORDER OP */
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
break;
}
case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD:
case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND:
case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: {
int offset = GET_OPCODE(i) - OP_ADD; /* ORDER OP */
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
tm = cast(TMS, GETARG_C(i));
break;
}
case OP_UNM: tm = TM_UNM; break;
@@ -648,8 +634,8 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
*name = "order"; /* '<=' can call '__lt', etc. */
return "metamethod";
case OP_SHRI: case OP_SHLI:
*name = "shift";
case OP_CLOSE: case OP_RETURN:
*name = "close";
return "metamethod";
default:
return NULL; /* cannot find a reasonable name */
+21 -21
View File
@@ -139,9 +139,9 @@ l_noret luaD_throw (lua_State *L, int errcode) {
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
l_uint32 oldnCcalls = L->nCcalls - L->nci;
global_State *g = G(L);
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
struct lua_longjmp lj;
lua_assert(L->nCcalls >= L->nci);
lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
@@ -149,7 +149,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
L->nCcalls = oldnCcalls + L->nci;
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci;
return lj.status;
}
@@ -348,18 +348,18 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
/*
** Check whether __call metafield of 'func' is a function. If so, put
** it in stack below original 'func' so that 'luaD_call' can call
** it. Raise an error if __call metafield is not a function.
** 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
** 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(!ttisfunction(tm)))
luaG_typeerror(L, s2v(func), "call");
for (p = L->top; p > func; p--)
if (unlikely(ttisnil(tm)))
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
for (p = L->top; p > func; p--) /* open space for metamethod */
setobjs2s(L, p, p-1);
L->top++; /* assume EXTRA_STACK */
L->top++; /* stack space pre-allocated by the caller */
setobj2s(L, func, tm); /* metamethod is the new function to be called */
}
@@ -457,13 +457,13 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
*/
void luaD_call (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
TValue *funcv = s2v(func);
switch (ttypetag(funcv)) {
retry:
switch (ttypetag(s2v(func))) {
case LUA_TCCL: /* C closure */
f = clCvalue(funcv)->f;
f = clCvalue(s2v(func))->f;
goto Cfunc;
case LUA_TLCF: /* light C function */
f = fvalue(funcv);
f = fvalue(s2v(func));
Cfunc: {
int n; /* number of returns */
CallInfo *ci;
@@ -487,7 +487,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
}
case LUA_TLCL: { /* Lua function */
CallInfo *ci;
Proto *p = clLvalue(funcv)->p;
Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */
@@ -505,9 +505,9 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break;
}
default: { /* not a function */
checkstackp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
luaD_call(L, func, nresults); /* now it must be a function */
break;
goto retry; /* try again with metamethod */
}
}
}
@@ -521,7 +521,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
*/
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L);
if (getCcalls(L) >= LUAI_MAXCSTACK) /* possible stack overflow? */
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
luaE_freeCI(L);
luaD_call(L, func, nResults);
decXCcalls(L);
@@ -672,10 +672,10 @@ 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 = 1;
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
L->nCcalls = getCcalls(from) - from->nci + L->nci + CSTACKCF;
if (L->nCcalls >= LUAI_MAXCSTACK)
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs);
luai_userstateresume(L, nargs);
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
+1
View File
@@ -149,6 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
for (i = 0; i < n; i++) {
DumpByte(f->upvalues[i].instack, D);
DumpByte(f->upvalues[i].idx, D);
DumpByte(f->upvalues[i].kind, D);
}
}
+59 -36
View File
@@ -59,14 +59,15 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
/*
** Create a new upvalue with the given tag at the given level,
** and link it to the list of open upvalues of 'L' after entry 'prev'.
** Create a new upvalue at the given level, and link it to the list of
** open upvalues of 'L' after entry 'prev'.
**/
static UpVal *newupval (lua_State *L, int tag, StkId level, UpVal **prev) {
GCObject *o = luaC_newobj(L, tag, sizeof(UpVal));
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
UpVal *uv = gco2upv(o);
UpVal *next = *prev;
uv->v = s2v(level); /* current value lives in the stack */
uv->tbc = tbc;
uv->u.open.next = next; /* link it to list of open upvalues */
uv->u.open.previous = prev;
if (next)
@@ -81,7 +82,7 @@ static UpVal *newupval (lua_State *L, int tag, StkId level, UpVal **prev) {
/*
** Find and reuse, or create if it does not exist, a regular upvalue
** Find and reuse, or create if it does not exist, an upvalue
** at the given level.
*/
UpVal *luaF_findupval (lua_State *L, StkId level) {
@@ -89,12 +90,13 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
UpVal *p;
lua_assert(isintwups(L) || L->openupval == NULL);
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
if (uplevel(p) == level && !isdead(G(L), p)) /* corresponding upvalue? */
lua_assert(!isdead(G(L), p));
if (uplevel(p) == level) /* corresponding upvalue? */
return p; /* return it */
pp = &p->u.open.next;
}
/* not found: create a new upvalue after 'pp' */
return newupval(L, LUA_TUPVAL, level, pp);
return newupval(L, 0, level, pp);
}
@@ -121,39 +123,53 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
}
/*
** Raise an error with message 'msg', inserting the name of the
** local variable at position 'level' in the stack.
*/
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);
}
/*
** 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 original
** 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
** 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.
*/
static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
static int callclosemth (lua_State *L, StkId level, int status) {
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 (!ttisnil(uv)) { /* non-closable non-nil value? */
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, "attempt to close non-closable variable '%s'", vname);
}
else if (!l_isfalse(uv)) /* non-closable non-false value? */
varerror(L, level, "attempt to close non-closable variable '%s'");
}
else { /* must close the object in protected mode */
ptrdiff_t oldtop = savestack(L, level + 1);
/* save error message and set stack top to 'level + 1' */
luaD_seterrorobj(L, status, level);
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 /* leave original error (or nil) on top */
else {
if (newstatus != LUA_OK) /* supressed 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 */
}
@@ -166,9 +182,7 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
** (can raise a memory-allocation error)
*/
static void trynewtbcupval (lua_State *L, void *ud) {
StkId level = cast(StkId, ud);
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
newupval(L, LUA_TUPVALTBC, level, &L->openupval);
newupval(L, 1, cast(StkId, ud), &L->openupval);
}
@@ -178,13 +192,22 @@ static void trynewtbcupval (lua_State *L, void *ud) {
** as there is no upvalue to call it later.
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
int 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 */
if (prepclosingmethod(L, s2v(level), s2v(level + 1)))
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 */
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
}
}
}
@@ -200,20 +223,20 @@ void luaF_unlinkupval (UpVal *uv) {
int luaF_close (lua_State *L, StkId level, int status) {
UpVal *uv;
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
StkId upl = uplevel(uv);
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);
setobj(L, slot, uv->v); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */
if (!iswhite(uv))
gray2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot);
if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) {
/* must run closing method */
ptrdiff_t levelrel = savestack(L, level);
status = callclosemth(L, uv->v, upl, status); /* may change the stack */
level = restorestack(L, levelrel);
}
}
return status;
}
+34 -31
View File
@@ -273,8 +273,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
gray2black(o);
break;
}
case LUA_TUPVAL:
case LUA_TUPVALTBC: {
case LUA_TUPVAL: {
UpVal *uv = gco2upv(o);
if (!upisopen(uv)) /* open upvalues are kept gray */
gray2black(o);
@@ -414,13 +413,13 @@ static void traverseweakvalue (global_State *g, Table *h) {
** (in the atomic phase). In generational mode, it (like all visited
** tables) must be kept in some gray list for post-processing.
*/
static int traverseephemeron (global_State *g, Table *h) {
static int traverseephemeron (global_State *g, Table *h, int inv) {
int marked = 0; /* true if an object is marked in this traversal */
int hasclears = 0; /* true if table has white keys */
int hasww = 0; /* true if table has entry "white-key -> white-value" */
Node *n, *limit = gnodelast(h);
unsigned int i;
unsigned int asize = luaH_realasize(h);
unsigned int nsize = sizenode(h);
/* traverse array part */
for (i = 0; i < asize; i++) {
if (valiswhite(&h->array[i])) {
@@ -428,8 +427,10 @@ static int traverseephemeron (global_State *g, Table *h) {
reallymarkobject(g, gcvalue(&h->array[i]));
}
}
/* traverse hash part */
for (n = gnode(h, 0); n < limit; n++) {
/* traverse hash part; if 'inv', traverse descending
(see 'convergeephemerons') */
for (i = 0; i < nsize; i++) {
Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
if (isempty(gval(n))) /* entry is empty? */
clearkey(n); /* clear its key */
else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
@@ -491,7 +492,7 @@ static lu_mem traversetable (global_State *g, Table *h) {
if (!weakkey) /* strong keys? */
traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */
traverseephemeron(g, h);
traverseephemeron(g, h, 0);
else /* all weak */
linkgclist(h, g->allweak); /* nothing to traverse now */
}
@@ -570,10 +571,8 @@ static int traversethread (global_State *g, lua_State *th) {
th->openupval == NULL || isintwups(th));
for (; o < th->top; o++) /* mark live elements in the stack */
markvalue(g, s2v(o));
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) {
if (uv->tt == LUA_TUPVALTBC) /* to be closed? */
markobject(g, uv); /* cannot be collected */
}
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 */
@@ -623,21 +622,30 @@ static lu_mem propagateall (global_State *g) {
}
/*
** Traverse all ephemeron tables propagating marks from keys to values.
** Repeat until it converges, that is, nothing new is marked. 'dir'
** inverts the direction of the traversals, trying to speed up
** convergence on chains in the same table.
**
*/
static void convergeephemerons (global_State *g) {
int changed;
int dir = 0;
do {
GCObject *w;
GCObject *next = g->ephemeron; /* get ephemeron list */
g->ephemeron = NULL; /* tables may return to this list when traversed */
changed = 0;
while ((w = next) != NULL) {
next = gco2t(w)->gclist;
if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */
while ((w = next) != NULL) { /* for each ephemeron table */
next = gco2t(w)->gclist; /* list is rebuilt during loop */
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */
propagateall(g); /* propagate changes */
changed = 1; /* will have to revisit all ephemeron tables */
}
}
} while (changed);
dir = !dir; /* invert direction next time */
} while (changed); /* repeat until no more changes */
}
/* }====================================================== */
@@ -706,7 +714,6 @@ static void freeobj (lua_State *L, GCObject *o) {
luaF_freeproto(L, gco2p(o));
break;
case LUA_TUPVAL:
case LUA_TUPVALTBC:
freeupval(L, gco2upv(o));
break;
case LUA_TLCL:
@@ -794,10 +801,11 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) {
*/
static void checkSizes (lua_State *L, global_State *g) {
if (!g->gcemergency) {
l_mem olddebt = g->GCdebt;
if (g->strt.nuse < g->strt.size / 4) /* string table too big? */
if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */
l_mem olddebt = g->GCdebt;
luaS_resize(L, g->strt.size / 2);
g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
}
}
}
@@ -838,21 +846,16 @@ static void GCTM (lua_State *L) {
int running = g->gcrunning;
L->allowhook = 0; /* stop debug hooks during GC metamethod */
g->gcrunning = 0; /* avoid GC steps */
setobj2s(L, L->top, tm); /* push finalizer... */
setobj2s(L, L->top + 1, &v); /* ... and its argument */
L->top += 2; /* and (next line) call the finalizer */
setobj2s(L, L->top++, tm); /* push finalizer... */
setobj2s(L, L->top++, &v); /* ... and its argument */
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
L->allowhook = oldah; /* restore hooks */
g->gcrunning = running; /* restore state */
if (status != LUA_OK) { /* error while running __gc? */
const char *msg = (ttisstring(s2v(L->top - 1)))
? svalue(s2v(L->top - 1))
: "error object is not a string";
luaE_warning(L, "error in __gc metamethod (", 1);
luaE_warning(L, msg, 1);
luaE_warning(L, ")", 0);
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
luaE_warnerror(L, "__gc metamethod");
L->top--; /* pops error object */
}
}
}
@@ -995,7 +998,7 @@ static void sweep2old (lua_State *L, GCObject **p) {
*/
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit) {
static lu_byte nextage[] = {
static const lu_byte nextage[] = {
G_SURVIVAL, /* from G_NEW */
G_OLD1, /* from G_SURVIVAL */
G_OLD1, /* from G_OLD0 */
@@ -1250,7 +1253,7 @@ static void setminordebt (global_State *g) {
/*
** Does a major collection after last collection was a "bad collection".
**
** When the program is building a big struture, it allocates lots of
** When the program is building a big structure, it allocates lots of
** memory but generates very little garbage. In those scenarios,
** the generational mode just wastes time doing small collections, and
** major collections are frequently what we call a "bad collection", a
+26 -16
View File
@@ -153,7 +153,7 @@ static int io_type (lua_State *L) {
luaL_checkany(L, 1);
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
if (p == NULL)
lua_pushnil(L); /* not a file */
luaL_pushfail(L); /* not a file */
else if (isclosed(p))
lua_pushliteral(L, "closed file");
else
@@ -338,7 +338,7 @@ static int io_readline (lua_State *L);
#define MAXARGLINE 250
/*
** Auxiliar function to create the iteration function for 'lines'.
** Auxiliary function to create the iteration function for 'lines'.
** The iteration function is a closure over 'io_readline', with
** the following upvalues:
** 1) The file being read (first value in the stack)
@@ -593,7 +593,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
return luaL_fileresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
luaL_pushfail(L); /* push nil instead */
}
return n - first;
}
@@ -626,7 +626,7 @@ static int io_readline (lua_State *L) {
lua_assert(n > 0); /* should return at least a nil */
if (lua_toboolean(L, -n)) /* read at least one value? */
return n; /* return them */
else { /* first result is nil: EOF or error */
else { /* first result is false: EOF or error */
if (n > 1) { /* is there error information? */
/* 2nd result is error message */
return luaL_error(L, "%s", lua_tostring(L, -n + 1));
@@ -742,14 +742,23 @@ static const luaL_Reg iolib[] = {
/*
** methods for file handles
*/
static const luaL_Reg flib[] = {
{"close", f_close},
{"flush", f_flush},
{"lines", f_lines},
static const luaL_Reg meth[] = {
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"lines", f_lines},
{"flush", f_flush},
{"seek", f_seek},
{"close", f_close},
{"setvbuf", f_setvbuf},
{NULL, NULL}
};
/*
** metamethods for file handles
*/
static const luaL_Reg metameth[] = {
{"__index", NULL}, /* place holder */
{"__gc", f_gc},
{"__close", f_gc},
{"__tostring", f_tostring},
@@ -758,11 +767,12 @@ static const luaL_Reg flib[] = {
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
lua_pop(L, 1); /* pop new metatable */
luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */
luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */
luaL_newlibtable(L, meth); /* create method table */
luaL_setfuncs(L, meth, 0); /* add file methods to method table */
lua_setfield(L, -2, "__index"); /* metatable.__index = method table */
lua_pop(L, 1); /* pop metatable */
}
@@ -772,7 +782,7 @@ static void createmeta (lua_State *L) {
static int io_noclose (lua_State *L) {
LStream *p = tolstream(L);
p->closef = &io_noclose; /* keep file opened */
lua_pushnil(L);
luaL_pushfail(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
+3 -6
View File
@@ -45,12 +45,6 @@ static void *disptab[NUM_OPCODES] = {
&&L_OP_NEWTABLE,
&&L_OP_SELF,
&&L_OP_ADDI,
&&L_OP_SUBI,
&&L_OP_MULI,
&&L_OP_MODI,
&&L_OP_POWI,
&&L_OP_DIVI,
&&L_OP_IDIVI,
&&L_OP_ADDK,
&&L_OP_SUBK,
&&L_OP_MULK,
@@ -75,6 +69,9 @@ static void *disptab[NUM_OPCODES] = {
&&L_OP_BXOR,
&&L_OP_SHL,
&&L_OP_SHR,
&&L_OP_MMBIN,
&&L_OP_MMBINI,
&&L_OP_MMBINK,
&&L_OP_UNM,
&&L_OP_BNOT,
&&L_OP_NOT,
+13 -7
View File
@@ -211,8 +211,16 @@ static int check_next2 (LexState *ls, const char *set) {
/* LUA_NUMBER */
/*
** this function is quite liberal in what it accepts, as 'luaO_str2num'
** will reject ill-formed numerals.
** This function is quite liberal in what it accepts, as 'luaO_str2num'
** will reject ill-formed numerals. Roughly, it accepts the following
** pattern:
**
** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))*
**
** The only tricky part is to accept [+-] only after a valid exponent
** mark, to avoid reading '3-4' or '0xe+1' as a single number.
**
** The caller might have already read an initial dot.
*/
static int read_numeral (LexState *ls, SemInfo *seminfo) {
TValue obj;
@@ -223,15 +231,13 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
expo = "Pp";
for (;;) {
if (check_next2(ls, expo)) /* exponent part? */
if (check_next2(ls, expo)) /* exponent mark? */
check_next2(ls, "-+"); /* optional exponent sign */
if (lisxdigit(ls->current))
save_and_next(ls);
else if (ls->current == '.')
else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */
save_and_next(ls);
else break;
}
if (lislalnum(ls->current)) /* is numeral touching an alpha num? */
if (lislalpha(ls->current)) /* is numeral touching a letter? */
save_and_next(ls); /* force an error */
save(ls, '\0');
if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
+12 -7
View File
@@ -77,7 +77,7 @@ static int math_toint (lua_State *L) {
lua_pushinteger(L, n);
else {
luaL_checkany(L, 1);
lua_pushnil(L); /* value is not convertible to integer */
luaL_pushfail(L); /* value is not convertible to integer */
}
return 1;
}
@@ -235,7 +235,7 @@ static int math_type (lua_State *L) {
lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
else {
luaL_checkany(L, 1);
lua_pushnil(L);
luaL_pushfail(L);
}
return 1;
}
@@ -586,7 +586,8 @@ static int math_random (lua_State *L) {
}
static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
static void setseed (lua_State *L, Rand64 *state,
lua_Unsigned n1, lua_Unsigned n2) {
int i;
state[0] = Int2I(n1);
state[1] = Int2I(0xff); /* avoid a zero state */
@@ -594,6 +595,8 @@ static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
state[3] = Int2I(0);
for (i = 0; i < 16; i++)
nextrand(state); /* discard initial values to "spread" seed */
lua_pushinteger(L, n1);
lua_pushinteger(L, n2);
}
@@ -605,20 +608,21 @@ static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
static void randseed (lua_State *L, RanState *state) {
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
setseed(state->s, seed1, seed2);
setseed(L, state->s, seed1, seed2);
}
static int math_randomseed (lua_State *L) {
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
if (lua_isnone(L, 1))
if (lua_isnone(L, 1)) {
randseed(L, state);
}
else {
lua_Integer n1 = luaL_checkinteger(L, 1);
lua_Integer n2 = luaL_optinteger(L, 2, 0);
setseed(state->s, n1, n2);
setseed(L, state->s, n1, n2);
}
return 0;
return 2; /* return seeds */
}
@@ -635,6 +639,7 @@ static const luaL_Reg randfuncs[] = {
static void setrandfunc (lua_State *L) {
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
randseed(L, state); /* initialize with a "random" seed */
lua_pop(L, 2); /* remove pushed seeds */
luaL_setfuncs(L, randfuncs, 1);
}
+54 -31
View File
@@ -23,33 +23,56 @@
#if defined(HARDMEMTESTS)
#define hardtest(L,os,s) /* force a GC whenever possible */ \
if ((s) > (os) && (G(L))->gcrunning) luaC_fullgc(L, 1);
/*
** 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 alocation.)
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (ttisnil(&g->nilvalue) && ns > os)
return NULL; /* fail */
else /* normal allocation */
return (*g->frealloc)(g->ud, block, os, ns);
}
#else
#define hardtest(L,os,s) ((void)0)
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
#endif
/*
** About the realloc function:
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
** ('osize' is the old size, 'nsize' is the new size)
**
** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no
** matter 'x').
** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL.
** Particularly, frealloc(ud, NULL, 0, 0) does nothing,
** which is equivalent to free(NULL) in ISO C.
**
** * frealloc(ud, p, x, 0) frees the block 'p'
** (in this specific case, frealloc must return NULL);
** particularly, frealloc(ud, NULL, 0, 0) does nothing
** (which is equivalent to free(NULL) in ISO C)
** - frealloc(ud, NULL, x, s) creates a new block of size 's'
** (no matter 'x'). Returns NULL if it cannot create the new block.
**
** frealloc returns NULL if it cannot create or reallocate the area
** (any reallocation to an equal or smaller size cannot fail!)
** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from
** size 'x' to size 'y'. Returns NULL if it cannot reallocate the
** block to the new size.
*/
/*
** {==================================================================
** Functions to allocate/deallocate arrays for the Parser
** ===================================================================
*/
/*
** Minimum size for arrays during parsing, to avoid overhead of
** reallocating to size 1, then 2, and then 4. All these arrays
** will be reallocated to exact sizes or erased when parsing ends.
*/
#define MINSIZEARRAY 4
@@ -71,32 +94,32 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
}
lua_assert(nelems + 1 <= size && size <= limit);
/* 'limit' ensures that multiplication will not overflow */
newblock = luaM_realloc_(L, block, cast_sizet(*psize) * size_elems,
cast_sizet(size) * size_elems);
if (unlikely(newblock == NULL))
luaM_error(L);
newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems,
cast_sizet(size) * size_elems);
*psize = size; /* update only when everything else is OK */
return newblock;
}
/*
** In prototypes, the size of the array is also its number of
** elements (to save memory). So, if it cannot shrink an array
** to its number of elements, the only option is to raise an
** error.
*/
void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
int final_n, int size_elem) {
global_State *g = G(L);
void *newblock;
size_t oldsize = cast_sizet((*size) * size_elem);
size_t newsize = cast_sizet(final_n * size_elem);
lua_assert(newsize <= oldsize);
newblock = (*g->frealloc)(g->ud, block, oldsize, newsize);
if (unlikely(newblock == NULL && final_n > 0)) /* allocation failed? */
luaM_error(L);
else {
g->GCdebt += newsize - oldsize;
*size = final_n;
return newblock;
}
newblock = luaM_saferealloc_(L, block, oldsize, newsize);
*size = final_n;
return newblock;
}
/* }================================================================== */
l_noret luaM_toobig (lua_State *L) {
luaG_runerror(L, "memory allocation error: block too big");
@@ -132,19 +155,20 @@ static void *tryagain (lua_State *L, void *block,
/*
** generic allocation routine.
** 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));
hardtest(L, osize, nsize);
newblock = (*g->frealloc)(g->ud, block, osize, nsize);
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 (newblock == NULL) /* still no memory? */
return NULL;
return NULL; /* do not update 'GCdebt' */
}
lua_assert((nsize == 0) == (newblock == NULL));
g->GCdebt = (g->GCdebt + nsize) - osize;
@@ -162,12 +186,11 @@ void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
hardtest(L, 0, size);
if (size == 0)
return NULL; /* that's all */
else {
global_State *g = G(L);
void *newblock = (*g->frealloc)(g->ud, NULL, tag, size);
void *newblock = firsttry(g, NULL, tag, size);
if (unlikely(newblock == NULL)) {
newblock = tryagain(L, NULL, tag, size);
if (newblock == NULL)
+10 -11
View File
@@ -56,10 +56,10 @@
/*
** unique key for table in the registry that keeps handles
** key for table in the registry that keeps handles
** for all loaded C libraries
*/
static const int CLIBS = 0;
static const char *const CLIBS = "_CLIBS";
#define LIB_FAIL "open"
@@ -308,7 +308,7 @@ static void setpath (lua_State *L, const char *fieldname,
luaL_addchar(&b, *LUA_PATH_SEP);
}
luaL_addstring(&b, dft); /* add default */
if (dftmark < path + len - 2) { /* is there a sufix after ';;'? */
if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */
luaL_addchar(&b, *LUA_PATH_SEP);
luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
}
@@ -327,7 +327,7 @@ static void setpath (lua_State *L, const char *fieldname,
*/
static void *checkclib (lua_State *L, const char *path) {
void *plib;
lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS);
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_getfield(L, -1, path);
plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
lua_pop(L, 2); /* pop CLIBS table and 'plib' */
@@ -340,7 +340,7 @@ static void *checkclib (lua_State *L, const char *path) {
** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
*/
static void addtoclib (lua_State *L, const char *path, void *plib) {
lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS);
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushlightuserdata(L, plib);
lua_pushvalue(L, -1);
lua_setfield(L, -3, path); /* CLIBS[path] = plib */
@@ -408,10 +408,10 @@ static int ll_loadlib (lua_State *L) {
if (stat == 0) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
lua_pushnil(L);
luaL_pushfail(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
return 3; /* return fail, error message, and where */
}
}
@@ -505,9 +505,9 @@ static int ll_searchpath (lua_State *L) {
luaL_optstring(L, 4, LUA_DIRSEP));
if (f != NULL) return 1;
else { /* error message is on top of the stack */
lua_pushnil(L);
luaL_pushfail(L);
lua_insert(L, -2);
return 2; /* return nil + error message */
return 2; /* return fail + error message */
}
}
@@ -716,12 +716,11 @@ static void createsearcherstable (lua_State *L) {
** setting a finalizer to close all libraries when closing state.
*/
static void createclibstable (lua_State *L) {
lua_newtable(L); /* create CLIBS table */
luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
lua_createtable(L, 0, 1); /* create metatable for CLIBS */
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
lua_setmetatable(L, -2);
lua_rawsetp(L, LUA_REGISTRYINDEX, &CLIBS); /* set CLIBS table in registry */
}
-26
View File
@@ -29,32 +29,6 @@
#include "lvm.h"
/*
** converts an integer to a "floating point byte", represented as
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
** eeeee != 0 and (xxx) otherwise.
*/
int luaO_int2fb (unsigned int x) {
int e = 0; /* exponent */
if (x < 8) return x;
while (x >= (8 << 4)) { /* coarse steps */
x = (x + 0xf) >> 4; /* x = ceil(x / 16) */
e += 4;
}
while (x >= (8 << 1)) { /* fine steps */
x = (x + 1) >> 1; /* x = ceil(x / 2) */
e++;
}
return ((e+1) << 3) | (cast_int(x) - 8);
}
/* converts back */
int luaO_fb2int (int x) {
return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1);
}
/*
** Computes ceil(log2(x))
*/
+7 -10
View File
@@ -89,8 +89,8 @@ typedef struct TValue {
#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
#define checkliveness(L,obj) \
lua_longassert(!iscollectable(obj) || \
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))
((void)L, lua_longassert(!iscollectable(obj) || \
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
/* Macros to set values */
@@ -100,7 +100,7 @@ typedef struct TValue {
#define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
(void)L; checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
/*
** different types of assignments, according to destination
@@ -460,6 +460,7 @@ typedef struct Upvaldesc {
TString *name; /* upvalue name (for debug information) */
lu_byte instack; /* whether it is in stack (register) */
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
lu_byte kind; /* kind of corresponding variable */
} Upvaldesc;
@@ -567,6 +568,7 @@ typedef struct Proto {
*/
typedef struct UpVal {
CommonHeader;
lu_byte tbc; /* true if it represents a to-be-closed variable */
TValue *v; /* points to stack or to its own value */
union {
struct { /* (when open) */
@@ -578,9 +580,6 @@ typedef struct UpVal {
} UpVal;
/* variant for "To Be Closed" upvalues */
#define LUA_TUPVALTBC (LUA_TUPVAL | (1 << 4))
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist
@@ -650,14 +649,14 @@ typedef union Node {
#define setnodekey(L,node,obj) \
{ Node *n_=(node); const TValue *io_=(obj); \
n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
(void)L; checkliveness(L,io_); }
checkliveness(L,io_); }
/* copy a value from a key */
#define getnodekey(L,obj,node) \
{ TValue *io_=(obj); const Node *n_=(node); \
io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
(void)L; checkliveness(L,io_); }
checkliveness(L,io_); }
/*
@@ -733,8 +732,6 @@ typedef struct Table {
/* size of buffer for 'luaO_utf8esc' function */
#define UTF8BUFFSZ 8
LUAI_FUNC int luaO_int2fb (unsigned int x);
LUAI_FUNC int luaO_fb2int (int x);
LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
LUAI_FUNC int luaO_ceillog2 (unsigned int x);
LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
+82 -85
View File
@@ -18,90 +18,87 @@
/* ORDER OP */
LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
/* OT IT T A mode opcode */
opmode(0, 0, 0, 1, iABC) /* OP_MOVE */
,opmode(0, 0, 0, 1, iAsBx) /* OP_LOADI */
,opmode(0, 0, 0, 1, iAsBx) /* OP_LOADF */
,opmode(0, 0, 0, 1, iABx) /* OP_LOADK */
,opmode(0, 0, 0, 1, iABx) /* OP_LOADKX */
,opmode(0, 0, 0, 1, iABC) /* OP_LOADBOOL */
,opmode(0, 0, 0, 1, iABC) /* OP_LOADNIL */
,opmode(0, 0, 0, 1, iABC) /* OP_GETUPVAL */
,opmode(0, 0, 0, 0, iABC) /* OP_SETUPVAL */
,opmode(0, 0, 0, 1, iABC) /* OP_GETTABUP */
,opmode(0, 0, 0, 1, iABC) /* OP_GETTABLE */
,opmode(0, 0, 0, 1, iABC) /* OP_GETI */
,opmode(0, 0, 0, 1, iABC) /* OP_GETFIELD */
,opmode(0, 0, 0, 0, iABC) /* OP_SETTABUP */
,opmode(0, 0, 0, 0, iABC) /* OP_SETTABLE */
,opmode(0, 0, 0, 0, iABC) /* OP_SETI */
,opmode(0, 0, 0, 0, iABC) /* OP_SETFIELD */
,opmode(0, 0, 0, 1, iABC) /* OP_NEWTABLE */
,opmode(0, 0, 0, 1, iABC) /* OP_SELF */
,opmode(0, 0, 0, 1, iABC) /* OP_ADDI */
,opmode(0, 0, 0, 1, iABC) /* OP_SUBI */
,opmode(0, 0, 0, 1, iABC) /* OP_MULI */
,opmode(0, 0, 0, 1, iABC) /* OP_MODI */
,opmode(0, 0, 0, 1, iABC) /* OP_POWI */
,opmode(0, 0, 0, 1, iABC) /* OP_DIVI */
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVI */
,opmode(0, 0, 0, 1, iABC) /* OP_ADDK */
,opmode(0, 0, 0, 1, iABC) /* OP_SUBK */
,opmode(0, 0, 0, 1, iABC) /* OP_MULK */
,opmode(0, 0, 0, 1, iABC) /* OP_MODK */
,opmode(0, 0, 0, 1, iABC) /* OP_POWK */
,opmode(0, 0, 0, 1, iABC) /* OP_DIVK */
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVK */
,opmode(0, 0, 0, 1, iABC) /* OP_BANDK */
,opmode(0, 0, 0, 1, iABC) /* OP_BORK */
,opmode(0, 0, 0, 1, iABC) /* OP_BXORK */
,opmode(0, 0, 0, 1, iABC) /* OP_SHRI */
,opmode(0, 0, 0, 1, iABC) /* OP_SHLI */
,opmode(0, 0, 0, 1, iABC) /* OP_ADD */
,opmode(0, 0, 0, 1, iABC) /* OP_SUB */
,opmode(0, 0, 0, 1, iABC) /* OP_MUL */
,opmode(0, 0, 0, 1, iABC) /* OP_MOD */
,opmode(0, 0, 0, 1, iABC) /* OP_POW */
,opmode(0, 0, 0, 1, iABC) /* OP_DIV */
,opmode(0, 0, 0, 1, iABC) /* OP_IDIV */
,opmode(0, 0, 0, 1, iABC) /* OP_BAND */
,opmode(0, 0, 0, 1, iABC) /* OP_BOR */
,opmode(0, 0, 0, 1, iABC) /* OP_BXOR */
,opmode(0, 0, 0, 1, iABC) /* OP_SHL */
,opmode(0, 0, 0, 1, iABC) /* OP_SHR */
,opmode(0, 0, 0, 1, iABC) /* OP_UNM */
,opmode(0, 0, 0, 1, iABC) /* OP_BNOT */
,opmode(0, 0, 0, 1, iABC) /* OP_NOT */
,opmode(0, 0, 0, 1, iABC) /* OP_LEN */
,opmode(0, 0, 0, 1, iABC) /* OP_CONCAT */
,opmode(0, 0, 0, 0, iABC) /* OP_CLOSE */
,opmode(0, 0, 0, 0, iABC) /* OP_TBC */
,opmode(0, 0, 0, 0, isJ) /* OP_JMP */
,opmode(0, 0, 1, 0, iABC) /* OP_EQ */
,opmode(0, 0, 1, 0, iABC) /* OP_LT */
,opmode(0, 0, 1, 0, iABC) /* OP_LE */
,opmode(0, 0, 1, 0, iABC) /* OP_EQK */
,opmode(0, 0, 1, 0, iABC) /* OP_EQI */
,opmode(0, 0, 1, 0, iABC) /* OP_LTI */
,opmode(0, 0, 1, 0, iABC) /* OP_LEI */
,opmode(0, 0, 1, 0, iABC) /* OP_GTI */
,opmode(0, 0, 1, 0, iABC) /* OP_GEI */
,opmode(0, 0, 1, 0, iABC) /* OP_TEST */
,opmode(0, 0, 1, 1, iABC) /* OP_TESTSET */
,opmode(1, 1, 0, 1, iABC) /* OP_CALL */
,opmode(1, 1, 0, 1, iABC) /* OP_TAILCALL */
,opmode(0, 1, 0, 0, iABC) /* OP_RETURN */
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN0 */
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN1 */
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP */
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP */
,opmode(0, 0, 0, 0, iABx) /* OP_TFORPREP */
,opmode(0, 0, 0, 0, iABC) /* OP_TFORCALL */
,opmode(0, 0, 0, 1, iABx) /* OP_TFORLOOP */
,opmode(0, 1, 0, 0, iABC) /* OP_SETLIST */
,opmode(0, 0, 0, 1, iABx) /* OP_CLOSURE */
,opmode(1, 0, 0, 1, iABC) /* OP_VARARG */
,opmode(0, 0, 0, 1, iABC) /* OP_VARARGPREP */
,opmode(0, 0, 0, 0, iAx) /* OP_EXTRAARG */
/* MM OT IT T A mode opcode */
opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADBOOL */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/
,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */
,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */
,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */
,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */
,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */
,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */
,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */
,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */
,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
};
+32 -24
View File
@@ -97,6 +97,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define MAXARG_C ((1<<SIZE_C)-1)
#define OFFSET_sC (MAXARG_C >> 1)
#define int2sC(i) ((i) + OFFSET_sC)
#define sC2int(i) ((i) - OFFSET_sC)
/* creates a mask with 'n' 1 bits at position 'p' */
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
@@ -123,11 +126,11 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
#define GETARG_sB(i) (GETARG_B(i) - OFFSET_sC)
#define GETARG_sB(i) sC2int(GETARG_B(i))
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
#define GETARG_sC(i) (GETARG_C(i) - OFFSET_sC)
#define GETARG_sC(i) sC2int(GETARG_C(i))
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
#define TESTARG_k(i) (cast_int(((i) & (1u << POS_k))))
@@ -214,17 +217,11 @@ OP_SETTABLE,/* A B C R(A)[R(B)] := RK(C) */
OP_SETI,/* A B C R(A)[B] := RK(C) */
OP_SETFIELD,/* A B C R(A)[K(B):string] := RK(C) */
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
OP_NEWTABLE,/* A B C R(A) := {} */
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) + C */
OP_SUBI,/* A B sC R(A) := R(B) - C */
OP_MULI,/* A B sC R(A) := R(B) * C */
OP_MODI,/* A B sC R(A) := R(B) % C */
OP_POWI,/* A B sC R(A) := R(B) ^ C */
OP_DIVI,/* A B sC R(A) := R(B) / C */
OP_IDIVI,/* A B sC R(A) := R(B) // C */
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) */
@@ -238,8 +235,8 @@ OP_BANDK,/* A B C R(A) := R(B) & K(C):integer */
OP_BORK,/* A B C R(A) := R(B) | K(C):integer */
OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */
OP_SHRI,/* A B sC R(A) := R(B) >> C */
OP_SHLI,/* A B sC R(A) := C << R(B) */
OP_SHRI,/* A B sC R(A) := R(B) >> sC */
OP_SHLI,/* A B sC R(A) := sC << R(B) */
OP_ADD,/* A B C R(A) := R(B) + R(C) */
OP_SUB,/* A B C R(A) := R(B) - R(C) */
@@ -255,6 +252,10 @@ OP_BXOR,/* A B C R(A) := R(B) ~ R(C) */
OP_SHL,/* A B C R(A) := R(B) << R(C) */
OP_SHR,/* A B C R(A) := R(B) >> R(C) */
OP_MMBIN,/* A B C call C metamethod over R(A) and R(B) */
OP_MMBINI,/* A sB C call C metamethod over R(A) and sB */
OP_MMBINK,/* A B C 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) */
@@ -286,9 +287,9 @@ OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
OP_RETURN0,/* return */
OP_RETURN1,/* A return R(A) */
OP_FORLOOP,/* A Bx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */
OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
OP_FORPREP,/* A Bx <check values and prepare counters>;
if not to run then pc+=Bx+1; */
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)); */
@@ -321,10 +322,16 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if (C == 0) then
next 'instruction' is EXTRAARG(real C).
(*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
EXTRAARG.
(*) In OP_LOADKX, the next 'instruction' is always EXTRAARG.
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
bits of C).
(*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
power of 2) plus 1, or zero for size zero. If not k, the array size
is C. Otherwise, the array size is EXTRAARG _ C.
(*) For comparisons, k specifies what condition the test should accept
(true or false).
@@ -332,10 +339,9 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
(*) All 'skips' (pc++) assume that next instruction is a jump.
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
function either builds upvalues, which may need to be closed, or is
vararg, which must be corrected before returning. When 'k' is true,
C > 0 means the function is vararg and (C - 1) is its number of
fixed parameters.
function builds upvalues, which may need to be closed. C > 0 means
the function is vararg, so that its 'func' must be corrected before
returning; in this case, (C - 1) is its number of fixed parameters.
(*) In comparisons with an immediate operand, C signals whether the
original operand was a float.
@@ -350,6 +356,7 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
** bit 4: operator is a test (next instruction must be a jump)
** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
** bit 7: instruction is an MM instruction (call a metamethod)
*/
LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
@@ -359,6 +366,7 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
#define testTMode(m) (luaP_opmodes[m] & (1 << 4))
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
#define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
/* "out top" (set top for next instruction) */
#define isOT(i) \
@@ -368,11 +376,11 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
/* "in top" (uses top from previous instruction) */
#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
#define opmode(ot,it,t,a,m) (((ot)<<6) | ((it)<<5) | ((t)<<4) | ((a)<<3) | (m))
#define opmode(mm,ot,it,t,a,m) \
(((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
/* number of list items to accumulate before a SETLIST instruction */
#define LFIELDS_PER_FLUSH 50
#endif
+3 -6
View File
@@ -30,12 +30,6 @@ static const char *const opnames[] = {
"NEWTABLE",
"SELF",
"ADDI",
"SUBI",
"MULI",
"MODI",
"POWI",
"DIVI",
"IDIVI",
"ADDK",
"SUBK",
"MULK",
@@ -60,6 +54,9 @@ static const char *const opnames[] = {
"BXOR",
"SHL",
"SHR",
"MMBIN",
"MMBINI",
"MMBINK",
"UNM",
"BNOT",
"NOT",
+48 -28
View File
@@ -58,18 +58,20 @@
** ===================================================================
*/
#if !defined(l_time_t) /* { */
/*
** type to represent time_t in Lua
*/
#if !defined(LUA_NUMTIME) /* { */
#define l_timet lua_Integer
#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
#define l_gettime(L,arg) luaL_checkinteger(L, arg)
static time_t l_checktime (lua_State *L, int arg) {
lua_Integer t = luaL_checkinteger(L, arg);
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
return (time_t)t;
}
#else /* }{ */
#define l_timet lua_Number
#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
#define l_gettime(L,arg) luaL_checknumber(L, arg)
#endif /* } */
@@ -193,11 +195,25 @@ static int os_clock (lua_State *L) {
** =======================================================
*/
static void setfield (lua_State *L, const char *key, int value) {
lua_pushinteger(L, value);
/*
** About the overflow check: an overflow cannot occurr when time
** is represented by a lua_Integer, because either lua_Integer is
** large enough to represent all int fields or it is not large enough
** to represent a time that cause a field to overflow. However, if
** times are represented as doubles and lua_Integer is int, then the
** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
** to compute the year.
*/
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)
luaL_error(L, "field '%s' is out-of-bound", key);
#endif
lua_pushinteger(L, (lua_Integer)value + delta);
lua_setfield(L, -2, key);
}
static void setboolfield (lua_State *L, const char *key, int value) {
if (value < 0) /* undefined? */
return; /* does not set field */
@@ -210,14 +226,14 @@ static void setboolfield (lua_State *L, const char *key, int value) {
** Set all fields from structure 'tm' in the table on top of the stack
*/
static void setallfields (lua_State *L, struct tm *stm) {
setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour);
setfield(L, "day", stm->tm_mday);
setfield(L, "month", stm->tm_mon + 1);
setfield(L, "year", stm->tm_year + 1900);
setfield(L, "wday", stm->tm_wday + 1);
setfield(L, "yday", stm->tm_yday + 1);
setfield(L, "year", stm->tm_year, 1900);
setfield(L, "month", stm->tm_mon, 1);
setfield(L, "day", stm->tm_mday, 0);
setfield(L, "hour", stm->tm_hour, 0);
setfield(L, "min", stm->tm_min, 0);
setfield(L, "sec", stm->tm_sec, 0);
setfield(L, "yday", stm->tm_yday, 1);
setfield(L, "wday", stm->tm_wday, 1);
setboolfield(L, "isdst", stm->tm_isdst);
}
@@ -230,11 +246,6 @@ static int getboolfield (lua_State *L, const char *key) {
}
/* maximum value for date fields (to avoid arithmetic overflows with 'int') */
#if !defined(L_MAXDATEFIELD)
#define L_MAXDATEFIELD (INT_MAX / 2)
#endif
static int getfield (lua_State *L, const char *key, int d, int delta) {
int isnum;
int t = lua_getfield(L, -1, key); /* get field and its type */
@@ -247,7 +258,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
res = d;
}
else {
if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD))
/* unsigned avoids overflow when lua_Integer has 32 bits */
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
: (lua_Integer)INT_MIN + delta <= res))
return luaL_error(L, "field '%s' is out-of-bound", key);
res -= delta;
}
@@ -275,6 +288,13 @@ static const char *checkoption (lua_State *L, const char *conv,
}
static time_t l_checktime (lua_State *L, int arg) {
l_timet t = l_gettime(L, arg);
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
return (time_t)t;
}
/* maximum size for an individual 'strftime' item */
#define SIZETIMEFMT 250
@@ -293,7 +313,7 @@ static int os_date (lua_State *L) {
stm = l_localtime(&t, &tmr);
if (stm == NULL) /* invalid date? */
return luaL_error(L,
"time result cannot be represented in this installation");
"date result cannot be represented in this installation");
if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setallfields(L, stm);
@@ -329,12 +349,12 @@ static int os_time (lua_State *L) {
struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0, 0);
ts.tm_min = getfield(L, "min", 0, 0);
ts.tm_hour = getfield(L, "hour", 12, 0);
ts.tm_mday = getfield(L, "day", -1, 0);
ts.tm_mon = getfield(L, "month", -1, 1);
ts.tm_year = getfield(L, "year", -1, 1900);
ts.tm_mon = getfield(L, "month", -1, 1);
ts.tm_mday = getfield(L, "day", -1, 0);
ts.tm_hour = getfield(L, "hour", 12, 0);
ts.tm_min = getfield(L, "min", 0, 0);
ts.tm_sec = getfield(L, "sec", 0, 0);
ts.tm_isdst = getboolfield(L, "isdst");
t = mktime(&ts);
setallfields(L, &ts); /* update fields with normalized values */
+255 -178
View File
@@ -156,20 +156,15 @@ static void init_exp (expdesc *e, expkind k, int i) {
}
static void init_var (expdesc *e, expkind k, int i) {
static void codestring (expdesc *e, TString *s) {
e->f = e->t = NO_JUMP;
e->k = k;
e->u.var.idx = i;
}
static void codestring (LexState *ls, expdesc *e, TString *s) {
init_exp(e, VK, luaK_stringK(ls->fs, s));
e->k = VKSTR;
e->u.strval = s;
}
static void codename (LexState *ls, expdesc *e) {
codestring(ls, e, str_checkname(ls));
codestring(e, str_checkname(ls));
}
@@ -177,41 +172,42 @@ static void codename (LexState *ls, expdesc *e) {
** Register a new local variable in the active 'Proto' (for debug
** information).
*/
static int registerlocalvar (LexState *ls, TString *varname) {
FuncState *fs = ls->fs;
static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
Proto *f = fs->f;
int oldsize = f->sizelocvars;
luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
LocVar, SHRT_MAX, "local variables");
while (oldsize < f->sizelocvars)
f->locvars[oldsize++].varname = NULL;
f->locvars[fs->nlocvars].varname = varname;
f->locvars[fs->ndebugvars].varname = varname;
f->locvars[fs->ndebugvars].startpc = fs->pc;
luaC_objbarrier(ls->L, f, varname);
return fs->nlocvars++;
return fs->ndebugvars++;
}
/*
** Create a new local variable with the given 'name'.
** Create a new local variable with the given 'name'. Return its index
** in the function.
*/
static Vardesc *new_localvar (LexState *ls, TString *name) {
static int new_localvar (LexState *ls, TString *name) {
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Dyndata *dyd = ls->dyd;
Vardesc *var;
int reg = registerlocalvar(ls, name);
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
MAXVARS, "local variables");
luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, MAX_INT, "local variables");
MAXVARS, "local variables");
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
var = &dyd->actvar.arr[dyd->actvar.n++];
var->idx = cast(short, reg);
var->name = name;
var->ro = 0;
return var;
var->vd.kind = VDKREG; /* default */
var->vd.name = name;
return dyd->actvar.n - 1 - fs->firstlocal;
}
#define new_localvarliteral(ls,v) \
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
new_localvar(ls, \
luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
@@ -223,48 +219,80 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
}
/*
** Get the debug-information entry for current variable 'i'.
** Convert 'nvar' (number of active variables at some point) to
** number of variables in the stack at that point.
*/
static LocVar *getlocvar (FuncState *fs, int i) {
int idx = getlocalvardesc(fs, i)->idx;
lua_assert(idx < fs->nlocvars);
return &fs->f->locvars[idx];
static int stacklevel (FuncState *fs, int nvar) {
while (nvar > 0) {
Vardesc *vd = getlocalvardesc(fs, nvar - 1);
if (vd->vd.kind != RDKCTC) /* is in the stack? */
return vd->vd.sidx + 1;
else
nvar--; /* try previous variable */
}
return 0; /* no variables */
}
/*
** Return the "variable description" (Vardesc) of a given
** variable or upvalue
** Return the number of variables in the stack for function 'fs'
*/
static Vardesc *getvardesc (FuncState *fs, expdesc *e) {
if (e->k == VLOCAL)
return getlocalvardesc(fs, e->u.var.idx);
else if (e->k != VUPVAL)
return NULL; /* not a local variable */
else { /* upvalue: must go up all levels up to the original local */
int idx = e->u.var.idx;
for (;;) {
Upvaldesc *up = &fs->f->upvalues[idx];
fs = fs->prev; /* must look at the previous level */
idx = up->idx; /* at this index */
if (fs == NULL) { /* no more levels? (can happen only with _ENV) */
lua_assert(strcmp(getstr(up->name), LUA_ENV) == 0);
return NULL;
}
else if (up->instack) /* got to the original level? */
return getlocalvardesc(fs, idx);
/* else repeat for previous level */
}
int luaY_nvarstack (FuncState *fs) {
return stacklevel(fs, fs->nactvar);
}
/*
** Get the debug-information entry for current variable 'i'.
*/
static LocVar *localdebuginfo (FuncState *fs, int i) {
Vardesc *vd = getlocalvardesc(fs, i);
if (vd->vd.kind == RDKCTC)
return NULL; /* no debug info. for constants */
else {
int idx = vd->vd.pidx;
lua_assert(idx < fs->ndebugvars);
return &fs->f->locvars[idx];
}
}
static void init_var (FuncState *fs, expdesc *e, int i) {
e->f = e->t = NO_JUMP;
e->k = VLOCAL;
e->u.var.vidx = i;
e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
}
static void check_readonly (LexState *ls, expdesc *e) {
Vardesc *vardesc = getvardesc(ls->fs, e);
if (vardesc && vardesc->ro) { /* is variable local and const? */
FuncState *fs = ls->fs;
TString *varname = NULL; /* to be set if variable is const */
switch (e->k) {
case VCONST: {
varname = ls->dyd->actvar.arr[e->u.info].vd.name;
break;
}
case VLOCAL: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
varname = vardesc->vd.name;
break;
}
case VUPVAL: {
Upvaldesc *up = &fs->f->upvalues[e->u.info];
if (up->kind != VDKREG)
varname = up->name;
break;
}
default:
return; /* other cases cannot be read-only */
}
if (varname) {
const char *msg = luaO_pushfstring(ls->L,
"attempt to assign to const variable '%s'", getstr(vardesc->name));
"attempt to assign to const variable '%s'", getstr(varname));
luaK_semerror(ls, msg); /* error */
}
}
@@ -272,13 +300,16 @@ static void check_readonly (LexState *ls, expdesc *e) {
/*
** Start the scope for the last 'nvars' created variables.
** (debug info.)
*/
static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs;
fs->nactvar = cast_byte(fs->nactvar + nvars);
for (; nvars; nvars--) {
getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc;
int stklevel = luaY_nvarstack(fs);
int i;
for (i = 0; i < nvars; i++) {
int varidx = fs->nactvar++;
Vardesc *var = getlocalvardesc(fs, varidx);
var->vd.sidx = stklevel++;
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
}
}
@@ -289,8 +320,11 @@ static void adjustlocalvars (LexState *ls, int nvars) {
*/
static void removevars (FuncState *fs, int tolevel) {
fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
while (fs->nactvar > tolevel)
getlocvar(fs, --fs->nactvar)->endpc = fs->pc;
while (fs->nactvar > tolevel) {
LocVar *var = localdebuginfo(fs, --fs->nactvar);
if (var) /* does it have debug information? */
var->endpc = fs->pc;
}
}
@@ -308,7 +342,7 @@ static int searchupvalue (FuncState *fs, TString *name) {
}
static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
static Upvaldesc *allocupvalue (FuncState *fs) {
Proto *f = fs->f;
int oldsize = f->sizeupvalues;
checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
@@ -316,11 +350,28 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
Upvaldesc, MAXUPVAL, "upvalues");
while (oldsize < f->sizeupvalues)
f->upvalues[oldsize++].name = NULL;
f->upvalues[fs->nups].instack = (v->k == VLOCAL);
f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx);
f->upvalues[fs->nups].name = name;
luaC_objbarrier(fs->ls->L, f, name);
return fs->nups++;
return &f->upvalues[fs->nups++];
}
static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
Upvaldesc *up = allocupvalue(fs);
FuncState *prev = fs->prev;
if (v->k == VLOCAL) {
up->instack = 1;
up->idx = v->u.var.sidx;
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
}
else {
up->instack = 0;
up->idx = cast_byte(v->u.info);
up->kind = prev->f->upvalues[v->u.info].kind;
lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
}
up->name = name;
luaC_objbarrier(fs->ls->L, fs->f, name);
return fs->nups - 1;
}
@@ -328,11 +379,17 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
** Look for an active local variable with the name 'n' in the
** function 'fs'.
*/
static int searchvar (FuncState *fs, TString *n) {
static int searchvar (FuncState *fs, TString *n, expdesc *var) {
int i;
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
if (eqstr(n, getlocvar(fs, i)->varname))
return i;
Vardesc *vd = getlocalvardesc(fs, i);
if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i);
else /* real variable */
init_var(fs, var, i);
return var->k;
}
}
return -1; /* not found */
}
@@ -360,22 +417,21 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
if (fs == NULL) /* no more levels? */
init_exp(var, VVOID, 0); /* default is global */
else {
int v = searchvar(fs, n); /* look up locals at current level */
int v = searchvar(fs, n, var); /* look up locals at current level */
if (v >= 0) { /* found? */
init_var(var, VLOCAL, v); /* variable is local */
if (!base)
markupval(fs, v); /* local will be used as an upval */
if (v == VLOCAL && !base)
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
}
else { /* not found as local at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */
if (idx < 0) { /* not found? */
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
if (var->k == VVOID) /* not found? */
return; /* it is a global */
/* else was LOCAL or UPVAL */
idx = newupvalue(fs, n, var); /* will be a new upvalue */
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
idx = newupvalue(fs, n, var); /* will be a new upvalue */
else /* it is a global or a constant */
return; /* don't need to do anything at this level */
}
init_var(var, VUPVAL, idx); /* new or old upvalue */
init_exp(var, VUPVAL, idx); /* new or old upvalue */
}
}
}
@@ -393,7 +449,7 @@ static void singlevar (LexState *ls, expdesc *var) {
expdesc key;
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
lua_assert(var->k != VVOID); /* this one must exist */
codestring(ls, &key, varname); /* key is variable name */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
}
}
@@ -438,7 +494,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
** local variable.
*/
static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname);
const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
luaK_semerror(ls, msg); /* raise the error */
@@ -541,7 +597,7 @@ static int createlabel (LexState *ls, TString *name, int line,
ll->arr[l].nactvar = fs->bl->nactvar;
}
if (solvegotos(ls, &ll->arr[l])) { /* need close? */
luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
return 1;
}
return 0;
@@ -557,10 +613,10 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
/* correct pending gotos to current block */
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
Labeldesc *gt = &gl->arr[i];
if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */
gt->nactvar = bl->nactvar; /* update goto level */
/* leaving a variable scope? */
if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
gt->close |= bl->upval; /* jump may need a close */
}
gt->nactvar = bl->nactvar; /* update goto level */
}
}
@@ -574,7 +630,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
bl->previous = fs->bl;
fs->bl = bl;
lua_assert(fs->freereg == fs->nactvar);
lua_assert(fs->freereg == luaY_nvarstack(fs));
}
@@ -599,14 +655,15 @@ 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 */
if (bl->isloop) /* fix pending breaks? */
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
if (!hasclose && bl->previous && bl->upval)
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
fs->bl = bl->previous;
removevars(fs, bl->nactvar);
lua_assert(bl->nactvar == fs->nactvar);
fs->freereg = fs->nactvar; /* free registers */
fs->freereg = stklevel; /* free registers */
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
if (bl->previous) /* inner block? */
movegotosout(fs, bl); /* update pending gotos to outer block */
@@ -639,9 +696,10 @@ static Proto *addprototype (LexState *ls) {
/*
** codes instruction to create new closure in parent function.
** The OP_CLOSURE instruction must use the last available register,
** The OP_CLOSURE instruction uses the last available register,
** so that, if it invokes the GC, the GC knows which registers
** are in use at that time.
*/
static void codeclosure (LexState *ls, expdesc *v) {
FuncState *fs = ls->fs->prev;
@@ -664,7 +722,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
fs->nabslineinfo = 0;
fs->np = 0;
fs->nups = 0;
fs->nlocvars = 0;
fs->ndebugvars = 0;
fs->nactvar = 0;
fs->needclose = 0;
fs->firstlocal = ls->dyd->actvar.n;
@@ -680,7 +738,7 @@ static void close_func (LexState *ls) {
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Proto *f = fs->f;
luaK_ret(fs, fs->nactvar, 0); /* final return */
luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
leaveblock(fs);
lua_assert(fs->bl == NULL);
luaK_finish(fs);
@@ -690,7 +748,7 @@ static void close_func (LexState *ls) {
fs->nabslineinfo, AbsLineInfo);
luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
ls->fs = fs->prev;
luaC_checkGC(L);
@@ -758,16 +816,16 @@ static void yindex (LexState *ls, expdesc *v) {
*/
struct ConsControl {
typedef struct ConsControl {
expdesc v; /* last list item read */
expdesc *t; /* table descriptor */
int nh; /* total number of 'record' elements */
int na; /* total number of array elements */
int na; /* number of array elements already stored */
int tostore; /* number of array elements pending to be stored */
};
} ConsControl;
static void recfield (LexState *ls, struct ConsControl *cc) {
static void recfield (LexState *ls, ConsControl *cc) {
/* recfield -> (NAME | '['exp']') = exp */
FuncState *fs = ls->fs;
int reg = ls->fs->freereg;
@@ -788,18 +846,19 @@ static void recfield (LexState *ls, struct ConsControl *cc) {
}
static void closelistfield (FuncState *fs, struct ConsControl *cc) {
static void closelistfield (FuncState *fs, ConsControl *cc) {
if (cc->v.k == VVOID) return; /* there is no list item */
luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID;
if (cc->tostore == LFIELDS_PER_FLUSH) {
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
cc->na += cc->tostore;
cc->tostore = 0; /* no more items pending */
}
}
static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
static void lastlistfield (FuncState *fs, ConsControl *cc) {
if (cc->tostore == 0) return;
if (hasmultret(cc->v.k)) {
luaK_setmultret(fs, &cc->v);
@@ -811,19 +870,18 @@ static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
luaK_exp2nextreg(fs, &cc->v);
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
}
cc->na += cc->tostore;
}
static void listfield (LexState *ls, struct ConsControl *cc) {
static void listfield (LexState *ls, ConsControl *cc) {
/* listfield -> exp */
expr(ls, &cc->v);
checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
cc->na++;
cc->tostore++;
}
static void field (LexState *ls, struct ConsControl *cc) {
static void field (LexState *ls, ConsControl *cc) {
/* field -> listfield | recfield */
switch(ls->t.token) {
case TK_NAME: { /* may be 'listfield' or 'recfield' */
@@ -851,12 +909,13 @@ static void constructor (LexState *ls, expdesc *t) {
FuncState *fs = ls->fs;
int line = ls->linenumber;
int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
struct ConsControl cc;
ConsControl cc;
luaK_code(fs, 0); /* space for extra arg. */
cc.na = cc.nh = cc.tostore = 0;
cc.t = t;
init_exp(t, VRELOC, pc);
init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */
luaK_reserveregs(fs, 1);
init_exp(&cc.v, VVOID, 0); /* no value (yet) */
luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */
checknext(ls, '{');
do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
@@ -866,8 +925,7 @@ static void constructor (LexState *ls, expdesc *t) {
} while (testnext(ls, ',') || testnext(ls, ';'));
check_match(ls, '}', '{', line);
lastlistfield(fs, &cc);
SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
}
/* }====================================================================== */
@@ -966,7 +1024,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
break;
}
case TK_STRING: { /* funcargs -> STRING */
codestring(ls, &args, ls->t.seminfo.ts);
codestring(&args, ls->t.seminfo.ts);
luaX_next(ls); /* must use 'seminfo' before 'next' */
break;
}
@@ -1074,7 +1132,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
break;
}
case TK_STRING: {
codestring(ls, v, ls->t.seminfo.ts);
codestring(v, ls->t.seminfo.ts);
break;
}
case TK_NIL: {
@@ -1257,20 +1315,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
for (; lh; lh = lh->prev) { /* check all previous assignments */
if (vkisindexed(lh->v.k)) { /* assignment to table field? */
if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) {
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
conflict = 1; /* table is the upvalue being assigned now */
lh->v.k = VINDEXSTR;
lh->v.u.ind.t = extra; /* assignment will use safe copy */
}
}
else { /* table is a register */
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) {
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
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.idx) {
lh->v.u.ind.idx == v->u.var.sidx) {
conflict = 1;
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
}
@@ -1279,14 +1337,16 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
}
if (conflict) {
/* copy upvalue/local value to a temporary (in position 'extra') */
OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
luaK_codeABC(fs, op, extra, v->u.var.idx, 0);
if (v->k == VLOCAL)
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
else
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
luaK_reserveregs(fs, 1);
}
}
/*
** Parse and compile a mulitple assignment. The first "variable"
** Parse and compile a multiple assignment. The first "variable"
** (a 'suffixedexp') was already read by the caller.
**
** assignment -> suffixedexp restassign
@@ -1343,8 +1403,9 @@ static void gotostat (LexState *ls) {
newgotoentry(ls, name, line, luaK_jump(fs));
else { /* found a label */
/* backward jump; will be resolved here */
if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
luaK_codeABC(fs, OP_CLOSE, lb->nactvar, 0, 0);
int lblevel = stacklevel(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 */
luaK_patchlist(fs, luaK_jump(fs), lb->pc);
}
@@ -1419,7 +1480,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, bl2.nactvar, 0, 0);
luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
condexit = luaK_jump(fs); /* repeat after closing upvalues */
luaK_patchtohere(fs, exit); /* normal exit comes to here */
}
@@ -1462,8 +1523,8 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
*/
static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
/* forbody -> DO block */
static OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
static OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
BlockCnt bl;
FuncState *fs = ls->fs;
int prep, endfor;
@@ -1478,7 +1539,6 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
if (isgen) { /* generic for? */
luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
luaK_fixline(fs, line);
base += 2; /* base for 'OP_TFORLOOP' (skips function and state) */
}
endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
fixforjump(fs, endfor, prep + 1, 1);
@@ -1490,9 +1550,9 @@ static void fornum (LexState *ls, TString *varname, int line) {
/* fornum -> NAME = exp,exp[,exp] forbody */
FuncState *fs = ls->fs;
int base = fs->freereg;
new_localvarliteral(ls, "(for index)");
new_localvarliteral(ls, "(for limit)");
new_localvarliteral(ls, "(for step)");
new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)");
new_localvar(ls, varname);
checknext(ls, '=');
exp1(ls); /* initial value */
@@ -1517,11 +1577,10 @@ static void forlist (LexState *ls, TString *indexname) {
int line;
int base = fs->freereg;
/* create control variables */
new_localvarliteral(ls, "(for generator)");
new_localvarliteral(ls, "(for state)");
markupval(fs, fs->nactvar); /* state may create an upvalue */
new_localvarliteral(ls, "(for control)");
new_localvarliteral(ls, "(for toclose)");
new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)");
/* create declared variables */
new_localvar(ls, indexname);
while (testnext(ls, ',')) {
@@ -1532,6 +1591,7 @@ static void forlist (LexState *ls, TString *indexname) {
line = ls->linenumber;
adjust_assign(ls, 4, explist(ls, &e), &e);
adjustlocalvars(ls, 4); /* control variables */
markupval(fs, luaY_nvarstack(fs)); /* state may create an upvalue */
luaK_checkstack(fs, 3); /* extra space to call generator */
forbody(ls, base, line, nvars - 4, 1);
}
@@ -1574,7 +1634,8 @@ static int issinglejump (LexState *ls, TString **label, int *target) {
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
Labeldesc *lb = findlabel(ls, lname);
if (lb) { /* a backward jump? */
if (ls->fs->nactvar > lb->nactvar) /* needs to close variables? */
/* 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;
}
@@ -1646,21 +1707,60 @@ static void ifstat (LexState *ls, int line) {
static void localfunc (LexState *ls) {
expdesc b;
FuncState *fs = ls->fs;
int fvar = fs->nactvar; /* function's variable index */
new_localvar(ls, str_checkname(ls)); /* new local variable */
adjustlocalvars(ls, 1); /* enter its scope */
body(ls, &b, 0, ls->linenumber); /* function created in next register */
/* debug information will only see the variable after this point! */
getlocvar(fs, b.u.info)->startpc = fs->pc;
localdebuginfo(fs, fvar)->startpc = fs->pc;
}
static void commonlocalstat (LexState *ls) {
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
static int getlocalattribute (LexState *ls) {
/* ATTRIB -> ['<' Name '>'] */
if (testnext(ls, '<')) {
const char *attr = getstr(str_checkname(ls));
checknext(ls, '>');
if (strcmp(attr, "const") == 0)
return RDKCONST; /* read-only variable */
else if (strcmp(attr, "close") == 0)
return RDKTOCLOSE; /* to-be-closed variable */
else
luaK_semerror(ls,
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
}
return VDKREG;
}
static void checktoclose (LexState *ls, int level) {
if (level != -1) { /* is there a to-be-closed variable? */
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, level, 0, 0);
}
}
static void localstat (LexState *ls) {
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
FuncState *fs = ls->fs;
int toclose = -1; /* index of to-be-closed variable (if any) */
Vardesc *var; /* last variable */
int ivar, kind; /* index and kind of last variable */
int nvars = 0;
int nexps;
expdesc e;
do {
new_localvar(ls, str_checkname(ls));
ivar = new_localvar(ls, str_checkname(ls));
kind = getlocalattribute(ls);
getlocalvardesc(fs, ivar)->vd.kind = kind;
if (kind == RDKTOCLOSE) { /* to-be-closed? */
if (toclose != -1) /* one already present? */
luaK_semerror(ls, "multiple to-be-closed variables in local list");
toclose = luaY_nvarstack(fs) + nvars;
}
nvars++;
} while (testnext(ls, ','));
if (testnext(ls, '='))
@@ -1669,45 +1769,19 @@ static void commonlocalstat (LexState *ls) {
e.k = VVOID;
nexps = 0;
}
adjust_assign(ls, nvars, nexps, &e);
adjustlocalvars(ls, nvars);
}
static void tocloselocalstat (LexState *ls, Vardesc *var) {
FuncState *fs = ls->fs;
var->ro = 1; /* to-be-closed variables are always read-only */
markupval(fs, fs->nactvar);
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
}
static void attriblocalstat (LexState *ls) {
Vardesc *var;
TString *attr = str_checkname(ls);
testnext(ls, '>');
var = new_localvar(ls, str_checkname(ls));
checknext(ls, '=');
exp1(ls);
adjustlocalvars(ls, 1);
if (strcmp(getstr(attr), "const") == 0)
var->ro = 1; /* set variable as read-only */
else if (strcmp(getstr(attr), "toclose") == 0)
tocloselocalstat(ls, var);
else
luaK_semerror(ls,
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
}
static void localstat (LexState *ls) {
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
| LOCAL *toclose NAME '=' exp */
if (testnext(ls, '<'))
attriblocalstat(ls);
else
commonlocalstat(ls);
var = getlocalvardesc(fs, ivar); /* get last variable */
if (nvars == nexps && /* no adjustments? */
var->vd.kind == RDKCONST && /* last variable is const? */
luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
var->vd.kind = RDKCTC; /* variable is a compile-time constant */
adjustlocalvars(ls, nvars - 1); /* exclude last variable */
fs->nactvar++; /* but count it */
}
else {
adjust_assign(ls, nvars, nexps, &e);
adjustlocalvars(ls, nvars);
}
checktoclose(ls, toclose);
}
@@ -1759,7 +1833,7 @@ static void retstat (LexState *ls) {
FuncState *fs = ls->fs;
expdesc e;
int nret; /* number of values being returned */
int first = fs->nactvar; /* first slot to be returned */
int first = luaY_nvarstack(fs); /* first slot to be returned */
if (block_follow(ls, 1) || ls->t.token == ';')
nret = 0; /* return no values */
else {
@@ -1768,7 +1842,7 @@ static void retstat (LexState *ls) {
luaK_setmultret(fs, &e);
if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
}
nret = LUA_MULTRET; /* return all values */
}
@@ -1853,8 +1927,8 @@ static void statement (LexState *ls) {
}
}
lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
ls->fs->freereg >= ls->fs->nactvar);
ls->fs->freereg = ls->fs->nactvar; /* free registers */
ls->fs->freereg >= luaY_nvarstack(ls->fs));
ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */
leavelevel(ls);
}
@@ -1867,11 +1941,14 @@ static void statement (LexState *ls) {
*/
static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl;
expdesc v;
Upvaldesc *env;
open_func(ls, fs, &bl);
setvararg(fs, 0); /* main function is always declared vararg */
init_var(&v, VLOCAL, 0); /* create and... */
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
env = allocupvalue(fs); /* ...set environment upvalue */
env->instack = 1;
env->idx = 0;
env->kind = VDKREG;
env->name = ls->envn;
luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */
check(ls, TK_EOS);
+30 -11
View File
@@ -30,11 +30,15 @@ typedef enum {
VFALSE, /* constant false */
VK, /* constant in 'k'; info = index of constant in 'k' */
VKFLT, /* floating constant; nval = numerical float value */
VKINT, /* integer constant; nval = numerical integer value */
VKINT, /* integer constant; ival = numerical integer value */
VKSTR, /* string constant; strval = TString address;
(string is fixed by the lexer) */
VNONRELOC, /* expression has its value in a fixed register;
info = result register */
VLOCAL, /* local variable; var.idx = local register */
VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */
VLOCAL, /* local variable; var.ridx = local register;
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' */
VINDEXED, /* indexed variable;
ind.t = table register;
ind.idx = key's R index */
@@ -65,13 +69,15 @@ typedef struct expdesc {
union {
lua_Integer ival; /* for VKINT */
lua_Number nval; /* for VKFLT */
TString *strval; /* for VKSTR */
int info; /* for generic use */
struct { /* for indexed variables */
short idx; /* index (R or "long" K) */
lu_byte t; /* table (register or upvalue) */
} ind;
struct { /* for local variables and upvalues */
lu_byte idx; /* index of the variable */
struct { /* for local variables */
lu_byte sidx; /* index in the stack */
unsigned short vidx; /* index in 'actvar.arr' */
} var;
} u;
int t; /* patch list of 'exit when true' */
@@ -79,20 +85,32 @@ typedef struct expdesc {
} expdesc;
/* kinds of variables */
#define VDKREG 0 /* regular */
#define RDKCONST 1 /* constant */
#define RDKTOCLOSE 2 /* to-be-closed */
#define RDKCTC 3 /* compile-time constant */
/* description of an active local variable */
typedef struct Vardesc {
TString *name;
short idx; /* index of the variable in the Proto's 'locvars' array */
lu_byte ro; /* true if variable is 'const' */
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 */
short pidx; /* index of the variable in the Proto's 'locvars' array */
TString *name; /* variable name */
} vd;
TValue k; /* constant value (if any) */
} Vardesc;
/* description of pending goto statements and label statements */
typedef struct Labeldesc {
TString *name; /* label identifier */
int pc; /* position in code */
int line; /* line where it appeared */
lu_byte nactvar; /* local level where it appears in current block */
lu_byte nactvar; /* number of active variables in that position */
lu_byte close; /* goto that escapes upvalues */
} Labeldesc;
@@ -135,7 +153,7 @@ typedef struct FuncState {
int nabslineinfo; /* number of elements in 'abslineinfo' */
int firstlocal; /* index of first local var (in Dyndata array) */
int firstlabel; /* index of first label (in 'dyd->label->arr') */
short nlocvars; /* number of elements in 'f->locvars' */
short ndebugvars; /* number of elements in 'f->locvars' */
lu_byte nactvar; /* number of active local variables */
lu_byte nups; /* number of upvalues */
lu_byte freereg; /* first free register */
@@ -144,6 +162,7 @@ typedef struct FuncState {
} FuncState;
LUAI_FUNC int luaY_nvarstack (FuncState *fs);
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar);
+65 -26
View File
@@ -96,36 +96,58 @@ 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 */
}
}
/*
** Increment count of "C calls" and check for overflows. In case of
** 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 larger than LUAI_MAXCSTACK but smaller than
** LUAI_MAXCSTACK + CSTACKCF (plus 2 to avoid by-one errors), it means
** it has just entered the "overflow zone", so the function raises an
** overflow error.
** If 'nCcalls' is larger than LUAI_MAXCSTACK + CSTACKCF + 2
** (which means it is already handling an overflow) but smaller than
** 9/8 of LUAI_MAXCSTACK, 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).
** 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 >= LUAI_MAXCSTACK) { /* possible overflow? */
L->nCcalls--;
if (ncalls <= CSTACKERR) { /* possible overflow? */
luaE_freeCI(L); /* release unused CIs */
ncalls = getCcalls(L); /* update call count */
if (ncalls >= LUAI_MAXCSTACK) { /* still overflow? */
if (ncalls <= LUAI_MAXCSTACK + CSTACKCF + 2) {
/* no error before increments; raise the error now */
L->nCcalls += (CSTACKCF + 4); /* avoid raising it again */
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 if (ncalls >= (LUAI_MAXCSTACK + (LUAI_MAXCSTACK >> 3)))
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
/* else stack is in the error-handling zone;
allow message handler to work */
}
}
}
@@ -153,13 +175,13 @@ void luaE_freeCI (lua_State *L) {
CallInfo *ci = L->ci;
CallInfo *next = ci->next;
ci->next = NULL;
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
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 */
L->nCcalls -= L->nci; /* adjust result */
}
@@ -169,7 +191,7 @@ void luaE_freeCI (lua_State *L) {
void luaE_shrinkCI (lua_State *L) {
CallInfo *ci = L->ci;
CallInfo *next2; /* next's next */
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
/* while there are two nexts */
while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
luaM_free(L, ci->next); /* free next */
@@ -178,7 +200,7 @@ void luaE_shrinkCI (lua_State *L) {
next2->previous = ci;
ci = next2; /* keep next's next */
}
L->nCcalls += L->nci; /* adjust result */
L->nCcalls -= L->nci; /* adjust result */
}
@@ -264,7 +286,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->stacksize = 0;
L->twups = L; /* thread has no upvalues */
L->errorJmp = NULL;
L->nCcalls = 0;
L->nCcalls = CSTACKTHREAD;
L->hook = NULL;
L->hookmask = 0;
L->basehookcount = 0;
@@ -366,6 +388,7 @@ 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;
g->frealloc = f;
g->ud = ud;
g->warnf = NULL;
@@ -420,3 +443,19 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
}
/*
** Generate a warning from an error message
*/
void luaE_warnerror (lua_State *L, const char *where) {
TValue *errobj = s2v(L->top - 1); /* error object */
const char *msg = (ttisstring(errobj))
? svalue(errobj)
: "error object is not a string";
/* produce warning "error in %s (%s)" (where, msg) */
luaE_warning(L, "error in ", 1);
luaE_warning(L, where, 1);
luaE_warning(L, " (", 1);
luaE_warning(L, msg, 1);
luaE_warning(L, ")", 0);
}
+45 -23
View File
@@ -64,28 +64,49 @@
/*
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
** how many "C calls" it 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.
** 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 itself has two parts: the lower part is the count itself;
** the higher part counts the number of non-yieldable calls in the stack.
** 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 of unkown amount
** 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.
** to these functions add more than one to the count (see CSTACKCF).
**
** The proper count also includes 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
** increment nor to check 'nCcalls', as its use of C stack is already
** accounted for.
** 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.
*/
/* 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)
@@ -99,11 +120,11 @@
/* Decrement the number of non-yieldable calls */
#define decnny(L) ((L)->nCcalls -= 0x10000)
/* Increment the number of non-yieldable calls and nCcalls */
#define incXCcalls(L) ((L)->nCcalls += 0x10000 + CSTACKCF)
/* 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 nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 + CSTACKCF)
/* Decrement the number of non-yieldable calls and increment nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
@@ -164,9 +185,9 @@ typedef struct CallInfo {
union {
int funcidx; /* called-function index */
int nyield; /* number of values yielded */
struct { /* info about transfered values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transfered */
unsigned short ntransfer; /* number of values transfered */
struct { /* info about transferred values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transferred */
unsigned short ntransfer; /* number of values transferred */
} transferinfo;
} u2;
short nresults; /* expected number of results from this function */
@@ -250,6 +271,7 @@ 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;
@@ -313,8 +335,7 @@ union GCUnion {
#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))
#define gco2upv(o) \
check_exp(novariant((o)->tt) == LUA_TUPVAL, &((cast_u(o))->upv))
#define gco2upv(o) check_exp((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
/*
@@ -334,9 +355,10 @@ 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_warning (lua_State *L, const char *msg, int tocont);
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
#define luaE_exitCcall(L) ((L)->nCcalls--)
#define luaE_exitCcall(L) ((L)->nCcalls++)
#endif
+21 -10
View File
@@ -233,6 +233,17 @@ static int str_dump (lua_State *L) {
** =======================================================
*/
#if defined(LUA_NOCVTS2N) /* { */
/* no coercion from strings to numbers */
static const luaL_Reg stringmetamethods[] = {
{"__index", NULL}, /* placeholder */
{NULL, NULL}
};
#else /* }{ */
static int tonum (lua_State *L, int arg) {
if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */
lua_pushvalue(L, arg);
@@ -311,6 +322,8 @@ static const luaL_Reg stringmetamethods[] = {
{NULL, NULL}
};
#endif /* } */
/* }====================================================== */
/*
@@ -744,7 +757,7 @@ static int str_find_aux (lua_State *L, int find) {
const char *p = luaL_checklstring(L, 2, &lp);
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
if (init > ls) { /* start after string's end? */
lua_pushnil(L); /* cannot find anything */
luaL_pushfail(L); /* cannot find anything */
return 1;
}
/* explicit request or no special characters? */
@@ -779,7 +792,7 @@ static int str_find_aux (lua_State *L, int find) {
}
} while (s1++ < ms.src_end && !anchor);
}
lua_pushnil(L); /* not found */
luaL_pushfail(L); /* not found */
return 1;
}
@@ -1227,16 +1240,14 @@ static int str_format (lua_State *L) {
nb = lua_number2strx(L, buff, maxitem, form,
luaL_checknumber(L, arg));
break;
case 'e': case 'E': case 'f':
case 'g': case 'G': {
case 'f':
maxitem = MAX_ITEMF; /* extra space for '%f' */
buff = luaL_prepbuffsize(&b, maxitem);
/* FALLTHROUGH */
case 'e': case 'E': case 'g': case 'G': {
lua_Number n = luaL_checknumber(L, arg);
if (*(strfrmt - 1) == 'f' && l_mathop(fabs)(n) >= 1e100) {
/* 'n' needs more than 99 digits */
maxitem = MAX_ITEMF; /* extra space for '%f' */
buff = luaL_prepbuffsize(&b, maxitem);
}
addlenmod(form, LUA_NUMBER_FRMLEN);
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
nb = snprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
break;
}
case 'p': {
+24 -20
View File
@@ -833,39 +833,41 @@ static unsigned int binsearch (const TValue *array, unsigned int i,
** and 'maxinteger' if t[maxinteger] is present.)
** (In the next explanation, we use Lua indices, that is, with base 1.
** The code itself uses base 0 when indexing the array part of the table.)
** The code starts with 'limit', a position in the array part that may
** be a boundary.
** The code starts with 'limit = t->alimit', a position in the array
** part that may be a boundary.
**
** (1) If 't[limit]' is empty, there must be a boundary before it.
** As a common case (e.g., after 't[#t]=nil'), check whether 'hint-1'
** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1'
** is present. If so, it is a boundary. Otherwise, do a binary search
** between 0 and limit to find a boundary. In both cases, try to
** use this boundary as the new 'limit', as a hint for the next call.
** use this boundary as the new 'alimit', as a hint for the next call.
**
** (2) If 't[limit]' is not empty and the array has more elements
** after 'limit', try to find a boundary there. Again, try first
** the special case (which should be quite frequent) where 'limit+1'
** is empty, so that 'limit' is a boundary. Otherwise, check the
** last element of the array part (set it as a new limit). If it is empty,
** there must be a boundary between the old limit (present) and the new
** limit (absent), which is found with a binary search. (This boundary
** always can be a new limit.)
** last element of the array part. If it is empty, there must be a
** boundary between the old limit (present) and the last element
** (absent), which is found with a binary search. (This boundary always
** can be a new limit.)
**
** (3) The last case is when there are no elements in the array part
** (limit == 0) or its last element (the new limit) is present.
** In this case, must check the hash part. If there is no hash part,
** the boundary is 0. Otherwise, if 'limit+1' is absent, 'limit' is
** a boundary. Finally, if 'limit+1' is present, call 'hash_search'
** to find a boundary in the hash part of the table. (In those
** cases, the boundary is not inside the array part, and therefore
** cannot be used as a new limit.)
** In this case, must check the hash part. If there is no hash part
** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call
** 'hash_search' to find a boundary in the hash part of the table.
** (In those cases, the boundary is not inside the array part, and
** therefore cannot be used as a new limit.)
*/
lua_Unsigned luaH_getn (Table *t) {
unsigned int limit = t->alimit;
if (limit > 0 && isempty(&t->array[limit - 1])) {
/* (1) there must be a boundary before 'limit' */
if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */
/* there must be a boundary before 'limit' */
if (limit >= 2 && !isempty(&t->array[limit - 2])) {
/* 'limit - 1' is a boundary; can it be a new limit? */
if (ispow2realasize(t) && !ispow2(limit - 1)) {
t->alimit = limit - 1;
setnorealasize(t);
setnorealasize(t); /* now 'alimit' is not the real size */
}
return limit - 1;
}
@@ -880,8 +882,8 @@ lua_Unsigned luaH_getn (Table *t) {
}
}
/* 'limit' is zero or present in table */
if (!limitequalsasize(t)) {
/* (2) 'limit' > 0 and array has more elements after 'limit' */
if (!limitequalsasize(t)) { /* (2)? */
/* 'limit' > 0 and array has more elements after 'limit' */
if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */
return limit; /* this is the boundary */
/* else, try last element in the array */
@@ -899,7 +901,7 @@ lua_Unsigned luaH_getn (Table *t) {
lua_assert(limit == luaH_realasize(t) &&
(limit == 0 || !isempty(&t->array[limit - 1])));
if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1))))
return limit; /* 'limit + 1' is absent... */
return limit; /* 'limit + 1' is absent */
else /* 'limit + 1' is also present */
return hash_search(t, limit);
}
@@ -908,6 +910,8 @@ lua_Unsigned luaH_getn (Table *t) {
#if defined(LUA_DEBUG)
/* export these functions for the test library */
Node *luaH_mainposition (const Table *t, const TValue *key) {
return mainpositionTV(t, key);
}
+1 -1
View File
@@ -338,7 +338,7 @@ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
/*
** QuickSort algorithm (recursive function)
** Quicksort algorithm (recursive function)
*/
static void auxsort (lua_State *L, IdxT lo, IdxT up,
unsigned int rnd) {
+82 -31
View File
@@ -62,8 +62,10 @@ static void pushobject (lua_State *L, const TValue *o) {
}
static void badexit (const char *fmt, const char *s) {
fprintf(stderr, fmt, s);
static void badexit (const char *fmt, const char *s1, const char *s2) {
fprintf(stderr, fmt, s1);
if (s2)
fprintf(stderr, "extra info: %s\n", s2);
/* avoid assertion failures when exiting */
l_memcontrol.numblocks = l_memcontrol.total = 0;
exit(EXIT_FAILURE);
@@ -72,39 +74,79 @@ static void badexit (const char *fmt, const char *s) {
static int tpanic (lua_State *L) {
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1)),
lua_tostring(L, -1), NULL),
0); /* do not return to Lua */
}
/*
** Warning function for tests. Fist, it concatenates all parts of
** a warning in buffer 'buff'. Then:
** - messages starting with '#' are shown on standard output (used to
** test explicit warnings);
** - messages containing '@' are stored in global '_WARN' (used to test
** errors that generate warnings);
** a warning in buffer 'buff'. Then, it has three modes:
** - 0.normal: messages starting with '#' are shown on standard output;
** - other messages abort the tests (they represent real warning
** conditions; the standard tests should not generate these conditions
** unexpectedly).
** unexpectedly);
** - 1.allow: all messages are shown;
** - 2.store: all warnings go to the global '_WARN';
*/
static void warnf (void *ud, const char *msg, int tocont) {
lua_State *L = cast(lua_State *, ud);
static char buff[200] = ""; /* should be enough for tests... */
static int onoff = 0;
static int mode = 0; /* start in normal mode */
static int lasttocont = 0;
if (!lasttocont && !tocont && *msg == '@') { /* control message? */
if (buff[0] != '\0')
badexit("Control warning during warning: %s\naborting...\n", msg, buff);
if (strcmp(msg, "@off") == 0)
onoff = 0;
else if (strcmp(msg, "@on") == 0)
onoff = 1;
else if (strcmp(msg, "@normal") == 0)
mode = 0;
else if (strcmp(msg, "@allow") == 0)
mode = 1;
else if (strcmp(msg, "@store") == 0)
mode = 2;
else
badexit("Invalid control warning in test mode: %s\naborting...\n",
msg, NULL);
return;
}
lasttocont = tocont;
if (strlen(msg) >= sizeof(buff) - strlen(buff))
badexit("%s", "warnf-buffer overflow");
badexit("warnf-buffer overflow (%s)\n", msg, buff);
strcat(buff, msg); /* add new message to current warning */
if (!tocont) { /* message finished? */
if (buff[0] == '#') /* expected warning? */
printf("Expected Lua warning: %s\n", buff); /* print it */
else if (strchr(buff, '@') != NULL) { /* warning for test purposes? */
lua_State *L = cast(lua_State *, ud);
lua_unlock(L);
lua_pushstring(L, buff);
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
lua_lock(L);
lua_unlock(L);
if (lua_getglobal(L, "_WARN") == LUA_TNIL)
lua_pop(L, 1); /* ok, no previous unexpected warning */
else {
badexit("Unhandled warning in store mode: %s\naborting...\n",
lua_tostring(L, -1), buff);
}
lua_lock(L);
switch (mode) {
case 0: { /* normal */
if (buff[0] != '#' && onoff) /* unexpected warning? */
badexit("Unexpected warning in test mode: %s\naborting...\n",
buff, NULL);
/* else */ /* FALLTHROUGH */
}
case 1: { /* allow */
if (onoff)
fprintf(stderr, "Lua warning: %s\n", buff); /* print warning */
break;
}
case 2: { /* store */
lua_unlock(L);
lua_pushstring(L, buff);
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
lua_lock(L);
buff[0] = '\0'; /* prepare buffer for next warning */
break;
}
}
else /* a real warning; should not happen during tests */
badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
buff[0] = '\0'; /* prepare buffer for next warning */
}
}
@@ -397,8 +439,7 @@ static void checkrefs (global_State *g, GCObject *o) {
checkudata(g, gco2u(o));
break;
}
case LUA_TUPVAL:
case LUA_TUPVALTBC: {
case LUA_TUPVAL: {
checkvalref(g, o, gco2upv(o)->v);
break;
}
@@ -1103,14 +1144,6 @@ static int doremote (lua_State *L) {
}
static int int2fb_aux (lua_State *L) {
int b = luaO_int2fb((unsigned int)luaL_checkinteger(L, 1));
lua_pushinteger(L, b);
lua_pushinteger(L, (unsigned int)luaO_fb2int(b));
return 2;
}
static int log2_aux (lua_State *L) {
unsigned int x = (unsigned int)luaL_checkinteger(L, 1);
lua_pushinteger(L, luaO_ceillog2(x));
@@ -1488,6 +1521,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
else if EQ("pushfstringP") {
lua_pushfstring(L1, lua_tostring(L, -2), lua_topointer(L, -1));
}
else if EQ("rawget") {
int t = getindex;
lua_rawget(L1, t);
}
else if EQ("rawgeti") {
int t = getindex;
lua_rawgeti(L1, t, getnum);
@@ -1496,6 +1533,14 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
int t = getindex;
lua_rawgetp(L1, t, cast_voidp(cast_sizet(getnum)));
}
else if EQ("rawset") {
int t = getindex;
lua_rawset(L1, t);
}
else if EQ("rawseti") {
int t = getindex;
lua_rawseti(L1, t, getnum);
}
else if EQ("rawsetp") {
int t = getindex;
lua_rawsetp(L1, t, cast_voidp(cast_sizet(getnum)));
@@ -1538,6 +1583,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
const char *s = getstring;
lua_setfield(L1, t, s);
}
else if EQ("seti") {
int t = getindex;
lua_seti(L1, t, getnum);
}
else if EQ("setglobal") {
const char *s = getstring;
lua_setglobal(L1, s);
@@ -1565,6 +1614,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
else if EQ("error") {
lua_error(L1);
}
else if EQ("abort") {
abort();
}
else if EQ("throw") {
#if defined(__cplusplus)
static struct X { int x; } x;
@@ -1764,7 +1816,6 @@ static const struct luaL_Reg tests_funcs[] = {
{"pobj", gc_printobj},
{"getref", getref},
{"hash", hash_query},
{"int2fb", int2fb_aux},
{"log2", log2_aux},
{"limits", get_limits},
{"listcode", listcode},
+1 -1
View File
@@ -31,7 +31,7 @@
/* compiled with -O0, Lua uses a lot of C stack space... */
#undef LUAI_MAXCSTACK
#define LUAI_MAXCSTACK 400
#define LUAI_MAXCSTACK 400
/* to avoid warnings, and to make sure value is really unused */
#define UNUSED(x) (x=0, (void)(x))
+13 -9
View File
@@ -149,9 +149,6 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
if (!callbinTM(L, p1, p2, res, event)) {
switch (event) {
case TM_CONCAT:
luaG_concaterror(L, p1, p2);
/* call never returns, but to avoid warnings: *//* FALLTHROUGH */
case TM_BAND: case TM_BOR: case TM_BXOR:
case TM_SHL: case TM_SHR: case TM_BNOT: {
if (ttisnumber(p1) && ttisnumber(p2))
@@ -167,9 +164,16 @@ 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))
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
}
void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, int inv, TMS event) {
if (inv)
int flip, StkId res, TMS event) {
if (flip)
luaT_trybinTM(L, p2, p1, res, event);
else
luaT_trybinTM(L, p1, p2, res, event);
@@ -177,10 +181,10 @@ void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
int inv, StkId res, TMS event) {
int flip, StkId res, TMS event) {
TValue aux;
setivalue(&aux, i2);
luaT_trybinassocTM(L, p1, &aux, res, inv, event);
luaT_trybinassocTM(L, p1, &aux, flip, res, event);
}
@@ -205,14 +209,14 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event) {
int flip, int isfloat, TMS event) {
TValue aux; const TValue *p2;
if (isfloat) {
setfltvalue(&aux, cast_num(v2));
}
else
setivalue(&aux, v2);
if (inv) { /* arguments were exchanged? */
if (flip) { /* arguments were exchanged? */
p2 = p1; p1 = &aux; /* correct them */
}
else
+2 -1
View File
@@ -75,8 +75,9 @@ LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
const TValue *p1, const TValue *p2, StkId p3);
LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event);
LUAI_FUNC void luaT_tryconcatTM (lua_State *L);
LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1,
const TValue *p2, StkId res, int inv, TMS event);
const TValue *p2, int inv, StkId res, TMS event);
LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
int inv, StkId res, TMS event);
LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
+24 -12
View File
@@ -73,6 +73,7 @@ static void print_usage (const char *badoption) {
" -l name require library 'name' into global 'name'\n"
" -v show version information\n"
" -E ignore environment variables\n"
" -W turn warnings on\n"
" -- stop handling options\n"
" - stop handling options and execute stdin\n"
,
@@ -259,14 +260,18 @@ static int collectargs (char **argv, int *first) {
case '\0': /* '-' */
return args; /* script "name" is '-' */
case 'E':
if (argv[i][2] != '\0') /* extra characters after 1st? */
if (argv[i][2] != '\0') /* extra characters? */
return has_error; /* invalid option */
args |= has_E;
break;
case 'W':
if (argv[i][2] != '\0') /* extra characters? */
return has_error; /* invalid option */
break;
case 'i':
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
case 'v':
if (argv[i][2] != '\0') /* extra characters after 1st? */
if (argv[i][2] != '\0') /* extra characters? */
return has_error; /* invalid option */
args |= has_v;
break;
@@ -289,7 +294,8 @@ static int collectargs (char **argv, int *first) {
/*
** Processes options 'e' and 'l', which involve running Lua code.
** Processes options 'e' and 'l', which involve running Lua code, and
** 'W', which also affects the state.
** Returns 0 if some code raises an error.
*/
static int runargs (lua_State *L, char **argv, int n) {
@@ -297,15 +303,21 @@ static int runargs (lua_State *L, char **argv, int n) {
for (i = 1; i < n; i++) {
int option = argv[i][1];
lua_assert(argv[i][0] == '-'); /* already checked */
if (option == 'e' || option == 'l') {
int status;
const char *extra = argv[i] + 2; /* both options need an argument */
if (*extra == '\0') extra = argv[++i];
lua_assert(extra != NULL);
status = (option == 'e')
? dostring(L, extra, "=(command line)")
: dolibrary(L, extra);
if (status != LUA_OK) return 0;
switch (option) {
case 'e': case 'l': {
int status;
const char *extra = argv[i] + 2; /* both options need an argument */
if (*extra == '\0') extra = argv[++i];
lua_assert(extra != NULL);
status = (option == 'e')
? dostring(L, extra, "=(command line)")
: dolibrary(L, extra);
if (status != LUA_OK) return 0;
break;
}
case 'W':
lua_warning(L, "@on", 0); /* warnings on */
break;
}
}
return 1;
+1
View File
@@ -462,6 +462,7 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L);
LUA_API int (lua_gethookmask) (lua_State *L);
LUA_API int (lua_gethookcount) (lua_State *L);
LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
struct lua_Debug {
int event;
+2 -2
View File
@@ -47,7 +47,7 @@
** (It will crash with a limit too high.)
*/
#if !defined(LUAI_MAXCSTACK)
#define LUAI_MAXCSTACK 2200
#define LUAI_MAXCSTACK 2000
#endif
@@ -493,7 +493,7 @@
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
**
@@ LUAI_UACINT is the result of a 'default argument promotion'
@@ over a lUA_INTEGER.
@@ over a LUA_INTEGER.
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
@@ LUA_INTEGER_FMT is the format for writing integers.
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
+2 -2
View File
@@ -198,11 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
n = LoadInt(S);
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
f->sizeupvalues = n;
for (i = 0; i < n; i++)
f->upvalues[i].name = NULL;
for (i = 0; i < n; i++) {
f->upvalues[i].name = NULL;
f->upvalues[i].instack = LoadByte(S);
f->upvalues[i].idx = LoadByte(S);
f->upvalues[i].kind = LoadByte(S);
}
}
+2 -2
View File
@@ -103,7 +103,7 @@ static int utflen (lua_State *L) {
while (posi <= posj) {
const char *s1 = utf8_decode(s + posi, NULL, !lax);
if (s1 == NULL) { /* conversion error? */
lua_pushnil(L); /* return nil ... */
luaL_pushfail(L); /* return fail ... */
lua_pushinteger(L, posi + 1); /* ... and current position */
return 2;
}
@@ -216,7 +216,7 @@ static int byteoffset (lua_State *L) {
if (n == 0) /* did it find given character? */
lua_pushinteger(L, posi + 1);
else /* no such character */
lua_pushnil(L);
luaL_pushfail(L);
return 1;
}
+183 -201
View File
@@ -515,8 +515,10 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
}
if (tm == NULL) /* no TM? */
return 0; /* objects are different */
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */
return !l_isfalse(s2v(L->top));
else {
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */
return !l_isfalse(s2v(L->top));
}
}
@@ -548,7 +550,7 @@ void luaV_concat (lua_State *L, int total) {
int n = 2; /* number of elements handled in this pass (at least 2) */
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
!tostring(L, s2v(top - 1)))
luaT_trybinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT);
luaT_tryconcatTM(L);
else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
@@ -671,6 +673,8 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
/*
** Shift left operation. (Shift right just negates 'y'.)
*/
#define luaV_shiftr(x,y) luaV_shiftl(x,-(y))
lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
if (y < 0) { /* shift right? */
if (y <= -NBITS) return 0;
@@ -714,15 +718,10 @@ void luaV_finishOp (lua_State *L) {
Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
OpCode op = GET_OPCODE(inst);
switch (op) { /* finish its execution */
case OP_ADDI: case OP_SUBI:
case OP_MULI: case OP_DIVI: case OP_IDIVI:
case OP_MODI: case OP_POWI:
case OP_ADD: case OP_SUB:
case OP_MUL: case OP_DIV: case OP_IDIV:
case OP_BANDK: case OP_BORK: case OP_BXORK:
case OP_BAND: case OP_BOR: case OP_BXOR:
case OP_SHRI: case OP_SHL: case OP_SHR:
case OP_MOD: case OP_POW:
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top);
break;
}
case OP_UNM: case OP_BNOT: case OP_LEN:
case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
case OP_GETFIELD: case OP_SELF: {
@@ -747,7 +746,7 @@ void luaV_finishOp (lua_State *L) {
break;
}
case OP_CONCAT: {
StkId top = L->top - 1; /* top when 'luaT_trybinTM' was called */
StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */
int a = GETARG_A(inst); /* first element to concatenate */
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
setobjs2s(L, top - 2, top); /* put TM result in proper position */
@@ -779,9 +778,9 @@ void luaV_finishOp (lua_State *L) {
#define l_addi(L,a,b) intop(+, a, b)
#define l_subi(L,a,b) intop(-, a, b)
#define l_muli(L,a,b) intop(*, a, b)
#define l_band(L,a,b) intop(&, a, b)
#define l_bor(L,a,b) intop(|, a, b)
#define l_bxor(L,a,b) intop(^, a, b)
#define l_band(a,b) intop(&, a, b)
#define l_bor(a,b) intop(|, a, b)
#define l_bxor(a,b) intop(^, a, b)
#define l_lti(a,b) (a < b)
#define l_lei(a,b) (a <= b)
@@ -797,10 +796,9 @@ void luaV_finishOp (lua_State *L) {
#define op_arithfI_aux(L,v1,imm,fop,tm,flip) { \
lua_Number nb; \
if (tonumberns(v1, nb)) { \
setfltvalue(s2v(ra), fop(L, nb, cast_num(imm))); \
} \
else \
Protect(luaT_trybiniTM(L, v1, imm, flip, ra, tm)); }
lua_Number fimm = cast_num(imm); \
pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
}}
/*
@@ -819,7 +817,8 @@ void luaV_finishOp (lua_State *L) {
TValue *v1 = vRB(i); \
int imm = GETARG_sC(i); \
if (ttisinteger(v1)) { \
setivalue(s2v(ra), iop(L, ivalue(v1), imm)); \
lua_Integer iv1 = ivalue(v1); \
pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
} \
else op_arithfI_aux(L, v1, imm, fop, tm, flip); }
@@ -828,97 +827,87 @@ void luaV_finishOp (lua_State *L) {
** Auxiliary function for arithmetic operations over floats and others
** with two register operands.
*/
#define op_arithf_aux(L,v1,v2,fop,tm) { \
#define op_arithf_aux(L,v1,v2,fop) { \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
setfltvalue(s2v(ra), fop(L, n1, n2)); \
} \
else \
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
}}
/*
** Arithmetic operations over floats and others with register operands.
*/
#define op_arithf(L,fop,tm) { \
#define op_arithf(L,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
op_arithf_aux(L, v1, v2, fop, tm); }
op_arithf_aux(L, v1, v2, fop); }
/*
** Arithmetic operations with register operands.
*/
#define op_arith(L,iop,fop,tm) { \
#define op_arith(L,iop,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
setivalue(s2v(ra), iop(L, i1, i2)); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
} \
else op_arithf_aux(L, v1, v2, fop, tm); }
else op_arithf_aux(L, v1, v2, fop); }
/*
** Arithmetic operations with K operands.
*/
#define op_arithK(L,iop,fop,tm,flip) { \
#define op_arithK(L,iop,fop,flip) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
setivalue(s2v(ra), iop(L, i1, i2)); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
} \
else { \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
setfltvalue(s2v(ra), fop(L, n1, n2)); \
} \
else \
Protect(luaT_trybinassocTM(L, v1, v2, ra, flip, tm)); } }
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
}}}
/*
** Arithmetic operations with K operands for floats.
*/
#define op_arithfK(L,fop,tm) { \
#define op_arithfK(L,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
setfltvalue(s2v(ra), fop(L, n1, n2)); \
} \
else \
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
}}
/*
** Bitwise operations with constant operand.
*/
#define op_bitwiseK(L,op,tm) { \
#define op_bitwiseK(L,op) { \
TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \
lua_Integer i1; \
lua_Integer i2 = ivalue(v2); \
if (tointegerns(v1, &i1)) { \
setivalue(s2v(ra), op(L, i1, i2)); \
} \
else \
Protect(luaT_trybiniTM(L, v1, i2, TESTARG_k(i), ra, tm)); }
pc++; setivalue(s2v(ra), op(i1, i2)); \
}}
/*
** Bitwise operations with register operands.
*/
#define op_bitwise(L,op,tm) { \
#define op_bitwise(L,op) { \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
lua_Integer i1; lua_Integer i2; \
if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
setivalue(s2v(ra), op(L, i1, i2)); \
} \
else \
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
pc++; setivalue(s2v(ra), op(i1, i2)); \
}}
/*
@@ -927,8 +916,11 @@ void luaV_finishOp (lua_State *L) {
#define op_order(L,opi,opf,other) { \
int cond; \
TValue *rb = vRB(i); \
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) \
cond = opi(ivalue(s2v(ra)), ivalue(rb)); \
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
lua_Integer ia = ivalue(s2v(ra)); \
lua_Integer ib = ivalue(rb); \
cond = opi(ia, ib); \
} \
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
cond = opf(s2v(ra), rb); \
else \
@@ -944,8 +936,11 @@ void luaV_finishOp (lua_State *L) {
int im = GETARG_sB(i); \
if (ttisinteger(s2v(ra))) \
cond = opi(ivalue(s2v(ra)), im); \
else if (ttisfloat(s2v(ra))) \
cond = opf(fltvalue(s2v(ra)), cast_num(im)); \
else if (ttisfloat(s2v(ra))) { \
lua_Number fa = fltvalue(s2v(ra)); \
lua_Number fim = cast_num(im); \
cond = opf(fa, fim); \
} \
else { \
int isf = GETARG_C(i); \
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
@@ -993,7 +988,7 @@ void luaV_finishOp (lua_State *L) {
/* for test instructions, execute the jump instruction that follows it */
#define donextjump(ci) { i = *pc; dojump(ci, i, 1); }
#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); }
/*
** do a conditional jump: skip next instruction if 'cond' is not what
@@ -1030,7 +1025,10 @@ void luaV_finishOp (lua_State *L) {
** errors. (That is, it will not return to the interpreter main loop
** after changing the stack or hooks.)
*/
#define halfProtect(exp) (savepc(L), (exp))
#define halfProtect(exp) (savestate(L,ci), (exp))
/* idem, but without changing the stack */
#define halfProtectNT(exp) (savepc(L), (exp))
#define checkGC(L,c) \
@@ -1083,17 +1081,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmfetch();
lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
lua_assert(ci->top < L->stack + L->stacksize);
/* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (L->top = base));
vmdispatch (GET_OPCODE(i)) {
vmcase(OP_MOVE) {
setobjs2s(L, ra, RB(i));
vmbreak;
}
vmcase(OP_LOADK) {
TValue *rb = k + GETARG_Bx(i);
setobj2s(L, ra, rb);
vmbreak;
}
vmcase(OP_LOADI) {
lua_Integer b = GETARG_sBx(i);
setivalue(s2v(ra), b);
@@ -1104,6 +1098,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
setfltvalue(s2v(ra), cast_num(b));
vmbreak;
}
vmcase(OP_LOADK) {
TValue *rb = k + GETARG_Bx(i);
setobj2s(L, ra, rb);
vmbreak;
}
vmcase(OP_LOADKX) {
TValue *rb;
rb = k + GETARG_Ax(*pc); pc++;
@@ -1239,14 +1238,19 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_NEWTABLE) {
int b = GETARG_B(i);
int c = GETARG_C(i);
int b = GETARG_B(i); /* log2(hash size) + 1 */
int c = GETARG_C(i); /* array size */
Table *t;
L->top = ci->top; /* correct top in case of GC */
if (b > 0)
b = 1 << (b - 1); /* size is 2^(b - 1) */
if (TESTARG_k(i))
c += GETARG_Ax(*pc) * (MAXARG_C + 1);
pc++; /* skip extra argument */
L->top = ra + 1; /* correct top in case of emergency GC */
t = luaH_new(L); /* memory allocation */
sethvalue2s(L, ra, t);
if (b != 0 || c != 0)
luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); /* idem */
luaH_resize(L, t, c, b); /* idem */
checkGC(L, ra + 1);
vmbreak;
}
@@ -1267,108 +1271,44 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_arithI(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
vmbreak;
}
vmcase(OP_SUBI) {
op_arithI(L, l_subi, luai_numsub, TM_SUB, 0);
vmbreak;
}
vmcase(OP_MULI) {
op_arithI(L, l_muli, luai_nummul, TM_MUL, GETARG_k(i));
vmbreak;
}
vmcase(OP_MODI) {
op_arithI(L, luaV_mod, luaV_modf, TM_MOD, 0);
vmbreak;
}
vmcase(OP_POWI) {
op_arithfI(L, luai_numpow, TM_POW);
vmbreak;
}
vmcase(OP_DIVI) {
op_arithfI(L, luai_numdiv, TM_DIV);
vmbreak;
}
vmcase(OP_IDIVI) {
op_arithI(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
vmbreak;
}
vmcase(OP_ADDK) {
op_arithK(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
op_arithK(L, l_addi, luai_numadd, GETARG_k(i));
vmbreak;
}
vmcase(OP_SUBK) {
op_arithK(L, l_subi, luai_numsub, TM_SUB, 0);
op_arithK(L, l_subi, luai_numsub, 0);
vmbreak;
}
vmcase(OP_MULK) {
op_arithK(L, l_muli, luai_nummul, TM_MUL, GETARG_k(i));
op_arithK(L, l_muli, luai_nummul, GETARG_k(i));
vmbreak;
}
vmcase(OP_MODK) {
op_arithK(L, luaV_mod, luaV_modf, TM_MOD, 0);
op_arithK(L, luaV_mod, luaV_modf, 0);
vmbreak;
}
vmcase(OP_POWK) {
op_arithfK(L, luai_numpow, TM_POW);
op_arithfK(L, luai_numpow);
vmbreak;
}
vmcase(OP_DIVK) {
op_arithfK(L, luai_numdiv, TM_DIV);
op_arithfK(L, luai_numdiv);
vmbreak;
}
vmcase(OP_IDIVK) {
op_arithK(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
vmbreak;
}
vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd, TM_ADD);
vmbreak;
}
vmcase(OP_SUB) {
op_arith(L, l_subi, luai_numsub, TM_SUB);
vmbreak;
}
vmcase(OP_MUL) {
op_arith(L, l_muli, luai_nummul, TM_MUL);
vmbreak;
}
vmcase(OP_MOD) {
op_arith(L, luaV_mod, luaV_modf, TM_MOD);
vmbreak;
}
vmcase(OP_POW) {
op_arithf(L, luai_numpow, TM_POW);
vmbreak;
}
vmcase(OP_DIV) { /* float division (always with floats) */
op_arithf(L, luai_numdiv, TM_DIV);
vmbreak;
}
vmcase(OP_IDIV) { /* floor division */
op_arith(L, luaV_idiv, luai_numidiv, TM_IDIV);
op_arithK(L, luaV_idiv, luai_numidiv, 0);
vmbreak;
}
vmcase(OP_BANDK) {
op_bitwiseK(L, l_band, TM_BAND);
op_bitwiseK(L, l_band);
vmbreak;
}
vmcase(OP_BORK) {
op_bitwiseK(L, l_bor, TM_BOR);
op_bitwiseK(L, l_bor);
vmbreak;
}
vmcase(OP_BXORK) {
op_bitwiseK(L, l_bxor, TM_BXOR);
vmbreak;
}
vmcase(OP_BAND) {
op_bitwise(L, l_band, TM_BAND);
vmbreak;
}
vmcase(OP_BOR) {
op_bitwise(L, l_bor, TM_BOR);
vmbreak;
}
vmcase(OP_BXOR) {
op_bitwise(L, l_bxor, TM_BXOR);
op_bitwiseK(L, l_bxor);
vmbreak;
}
vmcase(OP_SHRI) {
@@ -1376,14 +1316,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
int ic = GETARG_sC(i);
lua_Integer ib;
if (tointegerns(rb, &ib)) {
setivalue(s2v(ra), luaV_shiftl(ib, -ic));
}
else {
TMS ev = TM_SHR;
if (TESTARG_k(i)) {
ic = -ic; ev = TM_SHL;
}
Protect(luaT_trybiniTM(L, rb, ic, 0, ra, ev));
pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
}
vmbreak;
}
@@ -1392,32 +1325,83 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
int ic = GETARG_sC(i);
lua_Integer ib;
if (tointegerns(rb, &ib)) {
setivalue(s2v(ra), luaV_shiftl(ic, ib));
pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib));
}
else
Protect(luaT_trybiniTM(L, rb, ic, 1, ra, TM_SHL));
vmbreak;
}
vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd);
vmbreak;
}
vmcase(OP_SUB) {
op_arith(L, l_subi, luai_numsub);
vmbreak;
}
vmcase(OP_MUL) {
op_arith(L, l_muli, luai_nummul);
vmbreak;
}
vmcase(OP_MOD) {
op_arith(L, luaV_mod, luaV_modf);
vmbreak;
}
vmcase(OP_POW) {
op_arithf(L, luai_numpow);
vmbreak;
}
vmcase(OP_DIV) { /* float division (always with floats) */
op_arithf(L, luai_numdiv);
vmbreak;
}
vmcase(OP_IDIV) { /* floor division */
op_arith(L, luaV_idiv, luai_numidiv);
vmbreak;
}
vmcase(OP_BAND) {
op_bitwise(L, l_band);
vmbreak;
}
vmcase(OP_BOR) {
op_bitwise(L, l_bor);
vmbreak;
}
vmcase(OP_BXOR) {
op_bitwise(L, l_bxor);
vmbreak;
}
vmcase(OP_SHR) {
TValue *rb = vRB(i);
TValue *rc = vRC(i);
lua_Integer ib; lua_Integer ic;
if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
setivalue(s2v(ra), luaV_shiftl(ib, -ic));
}
else
Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHR));
op_bitwise(L, luaV_shiftr);
vmbreak;
}
vmcase(OP_SHL) {
op_bitwise(L, luaV_shiftl);
vmbreak;
}
vmcase(OP_MMBIN) {
Instruction pi = *(pc - 2); /* original arith. expression */
TValue *rb = vRB(i);
TValue *rc = vRC(i);
lua_Integer ib; lua_Integer ic;
if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
setivalue(s2v(ra), luaV_shiftl(ib, ic));
}
else
Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHL));
TMS tm = (TMS)GETARG_C(i);
StkId result = RA(pi);
lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR);
Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm));
vmbreak;
}
vmcase(OP_MMBINI) {
Instruction pi = *(pc - 2); /* original arith. expression */
int imm = GETARG_sB(i);
TMS tm = (TMS)GETARG_C(i);
int flip = GETARG_k(i);
StkId result = RA(pi);
Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm));
vmbreak;
}
vmcase(OP_MMBINK) {
Instruction pi = *(pc - 2); /* original arith. expression */
TValue *imm = KB(i);
TMS tm = (TMS)GETARG_C(i);
int flip = GETARG_k(i);
StkId result = RA(pi);
Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm));
vmbreak;
}
vmcase(OP_UNM) {
@@ -1462,7 +1446,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_CLOSE) {
L->top = ra + 1; /* everything is free after this slot */
Protect(luaF_close(L, ra, LUA_OK));
vmbreak;
}
@@ -1551,19 +1534,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_TAILCALL) {
int b = GETARG_B(i); /* number of arguments + 1 (function) */
int delta = 0; /* virtual 'func' - real 'func' (vararg functions) */
int nparams1 = GETARG_C(i);
/* delat 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 */
if (TESTARG_k(i)) {
int nparams1 = GETARG_C(i);
if (nparams1) /* vararg function? */
delta = ci->u.l.nextraargs + nparams1;
/* close upvalues from current call; the compiler ensures
that there are no to-be-closed variables here */
that there are no to-be-closed variables here, so this
call cannot change the stack */
luaF_close(L, base, NOCLOSINGMETH);
lua_assert(base == ci->func + 1);
}
if (!ttisfunction(s2v(ra))) { /* not a function? */
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
@@ -1586,24 +1570,27 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_RETURN) {
int n = GETARG_B(i) - 1; /* number of results */
int nparams1 = GETARG_C(i);
if (n < 0) /* not fixed? */
n = cast_int(L->top - ra); /* get what is available */
else
L->top = ra + n; /* set call for 'luaD_poscall' */
savepc(ci);
if (TESTARG_k(i)) {
int nparams1 = GETARG_C(i);
luaF_close(L, base, LUA_OK); /* there may be open upvalues */
if (nparams1) /* vararg function? */
ci->func -= ci->u.l.nextraargs + nparams1;
if (TESTARG_k(i)) { /* may there be open upvalues? */
if (L->top < ci->top)
L->top = ci->top;
luaF_close(L, base, LUA_OK);
updatetrap(ci);
updatestack(ci);
}
if (nparams1) /* vararg function? */
ci->func -= ci->u.l.nextraargs + nparams1;
L->top = ra + n; /* set call for 'luaD_poscall' */
luaD_poscall(L, ci, n);
return;
}
vmcase(OP_RETURN0) {
if (L->hookmask) {
L->top = ra;
halfProtect(luaD_poscall(L, ci, 0)); /* no hurry... */
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */
}
else { /* do the 'poscall' here */
int nres = ci->nresults;
@@ -1617,7 +1604,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmcase(OP_RETURN1) {
if (L->hookmask) {
L->top = ra + 1;
halfProtect(luaD_poscall(L, ci, 1)); /* no hurry... */
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */
}
else { /* do the 'poscall' here */
int nres = ci->nresults;
@@ -1716,10 +1703,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak;
}
vmcase(OP_TFORPREP) {
if (!ttisnil(s2v(ra + 3))) { /* is 'toclose' not nil? */
/* create to-be-closed upvalue for it */
halfProtect(luaF_newtbcupval(L, ra + 3));
}
/* create to-be-closed upvalue (if needed) */
halfProtect(luaF_newtbcupval(L, ra + 3));
pc += GETARG_Bx(i);
i = *(pc++); /* go to next instruction */
lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));
@@ -1735,35 +1720,33 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
/* push function, state, and control variable */
memcpy(ra + 4, ra, 3 * sizeof(*ra));
L->top = ra + 4 + 3;
Protect(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
updatestack(ci); /* stack may have changed */
i = *(pc++); /* go to next instruction */
ra += 2; /* adjust for next instruction */
lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
goto l_tforloop;
}
vmcase(OP_TFORLOOP) {
l_tforloop:
if (!ttisnil(s2v(ra + 2))) { /* continue loop? */
setobjs2s(L, ra, ra + 2); /* save control variable */
if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
setobjs2s(L, ra + 2, ra + 4); /* save control variable */
pc -= GETARG_Bx(i); /* jump back */
}
vmbreak;
}
vmcase(OP_SETLIST) {
int n = GETARG_B(i);
int c = GETARG_C(i);
unsigned int last;
Table *h;
unsigned int last = GETARG_C(i);
Table *h = hvalue(s2v(ra));
if (n == 0)
n = cast_int(L->top - ra) - 1;
n = cast_int(L->top - ra) - 1; /* get up to the top */
else
L->top = ci->top; /* correct top in case of GC */
if (c == 0) {
c = GETARG_Ax(*pc); pc++;
L->top = ci->top; /* correct top in case of emergency GC */
last += n;
if (TESTARG_k(i)) {
last += GETARG_Ax(*pc) * (MAXARG_C + 1);
pc++;
}
h = hvalue(s2v(ra));
last = ((c-1)*LFIELDS_PER_FLUSH) + n;
if (last > luaH_realasize(h)) /* needs more space? */
luaH_resizearray(L, h, last); /* preallocate it at once */
for (; n > 0; n--) {
@@ -1804,4 +1787,3 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
/* }================================================================== */
+18 -9
View File
@@ -6,6 +6,7 @@
# Warnings valid for both C and C++
CWARNSCPP= \
-fmax-errors=5 \
-Wextra \
-Wshadow \
-Wsign-compare \
@@ -14,14 +15,13 @@ CWARNSCPP= \
-Wredundant-decls \
-Wdisabled-optimization \
-Wdouble-promotion \
#-Wno-aggressive-loop-optimizations \
#-Wlogical-op \
#-Wfatal-errors \
#-Wstrict-aliasing=3 \
-Wlogical-op \
-Wno-aggressive-loop-optimizations \
# the next warnings might be useful sometimes,
# but usually they generate too much noise
# -Werror \
# -pedantic # warns if we use jump tables \
# the next warnings generate too much noise, so they are disabled
# -Wconversion -Wno-sign-conversion \
# -Wconversion \
# -Wsign-conversion \
# -Wstrict-overflow=2 \
# -Wformat=2 \
@@ -46,10 +46,10 @@ CWARNS= $(CWARNSCPP) $(CWARNSC)
# -DLUA_USE_CTYPE -DLUA_USE_APICHECK
# ('-ftrapv' for runtime checks of integer overflows)
# -fsanitize=undefined -ftrapv -fno-inline
TESTS= -DLUA_USER_H='"ltests.h"' -O0
# TESTS= -DLUA_USER_H='"ltests.h"' -O0 -g
# LOCAL = $(TESTS) $(CWARNS) -g
LOCAL = $(TESTS) $(CWARNS)
# enable Linux goodies
@@ -106,8 +106,17 @@ $(LUA_T): $(LUA_O) $(CORE_T)
$(LUAC_T): $(LUAC_O) $(CORE_T)
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(CORE_T) $(LIBS) $(MYLIBS)
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:
rcsclean -u
$(RM) $(ALL_T) $(ALL_O)
depend:
+1
View File
@@ -324,6 +324,7 @@ N = function (s) return (string.gsub(s, " ", "&nbsp;")) end,
NE = id, -- tag"foreignphrase",
num = id,
["nil"] = fixed(Tag.b"nil"),
fail = fixed(Tag.b"fail"),
Open = fixed"{",
part = section("h1", true),
Pat = compose(verbfixed, prepos("'", "'")),
+253 -159
View File
@@ -223,7 +223,7 @@ In Lua, the global variable @Lid{_G} is initialized with this same value.
so changing its value will affect only your own code.)
When Lua loads a chunk,
the default value for its @id{_ENV} upvalue
the default value for its @id{_ENV} variable
is the global environment @seeF{load}.
Therefore, by default,
free names in Lua code refer to entries in the global environment
@@ -233,7 +233,7 @@ and some functions there operate on that environment.
You can use @Lid{load} (or @Lid{loadfile})
to load a chunk with a different environment.
(In C, you have to load the chunk and then change the value
of its first upvalue.)
of its first upvalue; see @See{lua_setupvalue}.)
}
@@ -295,9 +295,9 @@ although this behavior can be adapted from C @seeC{lua_setwarnf}.
Every value in Lua can have a @emph{metatable}.
This @def{metatable} is an ordinary Lua table
that defines the behavior of the original value
under certain special operations.
under certain events.
You can change several aspects of the behavior
of operations over a value by setting specific fields in its metatable.
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.
If it finds one,
@@ -306,7 +306,7 @@ Lua calls this function to perform the addition.
The key for each event in a metatable is a string
with the event name prefixed by two underscores;
the corresponding values are called @def{metamethods}.
In the previous example, the key is @St{__add}
In the previous example, the key is the string @St{__add}
and the metamethod is the function that performs the addition.
Unless stated otherwise,
metamethods should be function values.
@@ -328,22 +328,10 @@ one for all strings, etc.
By default, a value has no metatable,
but the string library sets a metatable for the string type @see{strlib}.
A metatable controls how an object behaves in
arithmetic operations, bitwise operations,
order comparisons, concatenation, length operation, calls, and indexing.
A metatable also can define a function to be called
when a userdata or a table is @link{GC|garbage collected}.
For the unary operators (negation, length, and bitwise NOT),
the metamethod is computed and called with a dummy second operand,
equal to the first one.
This extra operand is only to simplify Lua's internals
(by making these operators behave like a binary operation)
and may be removed in future versions.
(For most uses this extra operand is irrelevant.)
A detailed list of events controlled by metatables is given next.
Each operation is identified by its corresponding key.
A detailed list of operations controlled by metatables is given next.
Each event is identified by its corresponding key.
By convention, all metatable keys used by Lua are composed by
two underscores followed by lowercase Latin letters.
@description{
@@ -351,16 +339,16 @@ Each operation is identified by its corresponding key.
the addition (@T{+}) operation.
If any operand for an addition is not a number,
Lua will try to call a metamethod.
First, Lua will check the first operand (even if it is valid).
If that operand does not define a metamethod for @idx{__add},
It starts by checking the first operand (even if it is a number);
if that operand does not define a metamethod for @idx{__add},
then Lua will check the second operand.
If Lua can find a metamethod,
it calls the metamethod with the two operands as arguments,
and the result of the call
(adjusted to one value)
is the result of the operation.
Otherwise,
it raises an error.
Otherwise, if no metamethod is found,
Lua raises an error.
}
@item{@idx{__sub}|
@@ -467,7 +455,7 @@ the less than (@T{<}) operation.
Behavior similar to the addition operation,
except that Lua will try a metamethod only when the values
being compared are neither both numbers nor both strings.
The result of the call is always converted to a boolean.
Moreover, the result of the call is always converted to a boolean.
}
@item{@idx{__le}|
@@ -512,9 +500,9 @@ and therefore can trigger another metamethod.
Whenever there is a @idx{__newindex} metamethod,
Lua does not perform the primitive assignment.
(If necessary,
If needed,
the metamethod itself can call @Lid{rawset}
to do the assignment.)
to do the assignment.
}
@item{@idx{__call}|
@@ -526,16 +514,29 @@ If present,
the metamethod is called with @id{func} as its first argument,
followed by the arguments of the original call (@id{args}).
All results of the call
are the result of the operation.
are the results of the operation.
This is the only metamethod that allows multiple results.
}
}
It is a good practice to add all needed metamethods to a table
before setting it as a metatable of some object.
In particular, the @idx{__gc} metamethod works only when this order
is followed @see{finalizers}.
In addition to the previous list,
the interpreter also respects the following keys in metatables:
@idx{__gc} @see{finalizers},
@idx{__close} @see{to-be-closed},
@idx{__mode} @see{weak-table},
and @idx{__name}.
(The entry @idx{__name},
when it contains a string,
is used by some error-reporting functions to build error messages.)
For the unary operators (negation, length, and bitwise NOT),
the metamethod is computed and called with a dummy second operand,
equal to the first one.
This extra operand is only to simplify Lua's internals
(by making these operators behave like a binary operation)
and may be removed in future versions.
For most uses this extra operand is irrelevant.
Because metatables are regular tables,
they can contain arbitrary fields,
@@ -544,6 +545,13 @@ Some functions in the standard library
(e.g., @Lid{tostring})
use other fields in metatables for their own purposes.
It is a good practice to add all needed metamethods to a table
before setting it as a metatable of some object.
In particular, the @idx{__gc} metamethod works only when this order
is followed @see{finalizers}.
It is also a good practice to set the metatable of an object
right after its creation.
}
@sect2{GC| @title{Garbage Collection}
@@ -1012,7 +1020,7 @@ it must be expressed using exactly three digits.)
The @x{UTF-8} encoding of a @x{Unicode} character
can be inserted in a literal string with
the escape sequence @T{\u{@rep{XXX}}}
(with mandatory enclosing brackets),
(with mandatory enclosing braces),
where @rep{XXX} is a sequence of one or more hexadecimal digits
representing the character code point.
This code point can be any value less than @M{2@sp{31}}.
@@ -1224,7 +1232,7 @@ As such, chunks can define local variables,
receive arguments, and return values.
Moreover, such anonymous function is compiled as in the
scope of an external local variable called @id{_ENV} @see{globalenv}.
The resulting function always has @id{_ENV} as its only upvalue,
The resulting function always has @id{_ENV} as its only external variable,
even if it does not use that variable.
A chunk can be stored in a file or in a string inside the host program.
@@ -1399,23 +1407,30 @@ they must all result in numbers.
Their values are called respectively
the @emph{initial value}, the @emph{limit}, and the @emph{step}.
If the step is absent, it defaults @N{to 1}.
Then the loop body is repeated with the value of the control variable
If both the initial value and the step are integers,
the loop is done with integers;
note that the limit may not be an integer.
Otherwise, the loop is done with floats.
(Beware of floating-point accuracy in this case.)
After that initialization,
the loop body is repeated with the value of the control variable
going through an arithmetic progression,
starting at the initial value,
with a common difference given by the step,
until that value passes the limit.
with a common difference given by the step.
A negative step makes a decreasing sequence;
a step equal to zero raises an error.
The loop continues while the value is less than
or equal to the limit
(greater than or equal to for a negative step).
If the initial value is already greater than the limit
(or less than, if the step is negative),
the body is not executed.
If both the initial value and the step are integers,
the loop is done with integers;
in this case, the range of the control variable is clipped
by the range of integers.
Otherwise, the loop is done with floats.
(Beware of floating-point accuracy in this case.)
For integer loops,
the control variable never wraps around;
instead, the loop ends in case of an overflow.
You should not change the value of the control variable
during the loop.
@@ -1490,22 +1505,25 @@ Function calls are explained in @See{functioncall}.
@x{Local variables} can be declared anywhere inside a block.
The declaration can include an initialization:
@Produc{
@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}}
@producname{stat}@producbody{
@Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
}}
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
@producname{attnamelist}@producbody{
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
}
If present, an initial assignment has the same semantics
of a multiple assignment @see{assignment}.
Otherwise, all variables are initialized with @nil.
The second syntax declares a local with a given attribute,
which is the name between the angle brackets.
In this case, there must be an initialization.
Each variable name may be postfixed by an attribute
(a name between angle brackets):
@Produc{
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
}
There are two possible attributes:
@id{const}, which declares a @x{constant variable},
that is, a variable that cannot be assigned to
after its initialization;
and @id{toclose}, which declares a to-be-closed variable @see{to-be-closed}.
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
A list of variables can contain at most one to-be-closed variable.
A chunk is also a block @see{chunks},
and so local variables can be declared in a chunk outside any explicit block.
@@ -1516,12 +1534,6 @@ The visibility rules for local variables are explained in @See{visibility}.
@sect3{to-be-closed| @title{To-be-closed Variables}
A local variable can be declared as a @def{to-be-closed} variable,
using the identifier @id{toclose} as its attribute:
@Produc{
@producname{stat}@producbody{
@Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp
}}
A to-be-closed variable behaves like a constant local variable,
except that its value is @emph{closed} whenever the variable
goes out of scope, including normal block termination,
@@ -1530,37 +1542,42 @@ or exiting by an error.
Here, to @emph{close} a value means
to call its @idx{__close} metamethod.
If the value is @nil, it is ignored;
otherwise,
if it does not have a @idx{__close} metamethod,
an error is raised.
When calling the metamethod,
the value itself is passed as the first argument
and the error object (if any) is passed as a second argument;
and the error object that caused the exit (if any)
is passed as a second argument;
if there was no error, the second argument is @nil.
The value assigned to a to-be-closed variable
must have a @idx{__close} metamethod
or be a false value.
(@nil and @false are ignored as to-be-closed values.)
If several to-be-closed variables go out of scope at the same event,
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;
in particular,
the other pending closing methods will still be called.
After an error,
other errors in closing methods
interrupt the respective method,
but are otherwise ignored;
the error reported is the original one.
where the variable was defined.
However, Lua may call the method one more time.
If a coroutine yields inside a block and is never resumed again,
the variables visible at that block will never go out of scope,
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,
and therefore they will never be closed.
(These variables are the ones created inside the coroutine
and in scope at the point where the coroutine yielded.)
Similarly, if a coroutine ends with an error,
it does not unwind its stack,
so it does not close any variable.
In both cases,
you should either use finalizers
you can either use finalizers
or call @Lid{coroutine.close} to close the variables.
However, if the coroutine was created
through @Lid{coroutine.wrap},
@@ -2235,8 +2252,8 @@ and so the second @id{x} refers to the outside variable.
Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions
defined inside their scope.
A local variable used by an inner function is called
an @def{upvalue}, or @emphx{external local variable},
A local variable used by an inner function is called an @def{upvalue}
(or @emphx{external local variable}, or simply @emphx{external variable})
inside the inner function.
Notice that each execution of a @Rw{local} statement
@@ -2245,9 +2262,9 @@ Consider the following example:
@verbatim{
a = {}
local x = 20
for i=1,10 do
for i = 1, 10 do
local y = 0
a[i] = function () y=y+1; return x+y end
a[i] = function () y = y + 1; return x + y end
end
}
The loop creates ten closures
@@ -2910,8 +2927,9 @@ it is left unchanged.
@APIEntry{void lua_close (lua_State *L);|
@apii{0,0,-}
Destroys all objects in the given Lua state
(calling the corresponding garbage-collection metamethods, if any)
Close all active to-be-closed variables in the main thread,
release all objects in the given Lua state
(calling the corresponding garbage-collection metamethods, if any),
and frees all dynamic memory used by this state.
On several platforms, you may not need to call this function,
@@ -4048,12 +4066,15 @@ Returns 0 if the userdata does not have that value.
}
@APIEntry{void lua_setmetatable (lua_State *L, int index);|
@APIEntry{int lua_setmetatable (lua_State *L, int index);|
@apii{1,0,-}
Pops a table from the stack and
sets it as the new metatable for the value at the given index.
(For historical reasons, this function returns an @id{int},
which now is always 1.)
}
@APIEntry{void lua_settable (lua_State *L, int index);|
@@ -4179,7 +4200,7 @@ 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}.
This function should not be called for an index
that is equal to or below an already marked to-be-closed index.
that is equal to or below an active to-be-closed index.
This function can raise an out-of-memory error.
In that case, the value in the given index is immediately closed,
@@ -4360,6 +4381,8 @@ The third parameter is a boolean that
indicates whether the message is
to be continued by the message in the next call.
See @Lid{warn} for more details about warnings.
}
@APIEntry{
@@ -4370,6 +4393,8 @@ Emits a warning with the given message.
A message in a call with @id{tocont} true should be
continued in another call to this function.
See @Lid{warn} for more details about warnings.
}
@APIEntry{
@@ -4759,11 +4784,7 @@ and returns its name.
Returns @id{NULL} (and pushes nothing)
when the index @id{n} is greater than the number of upvalues.
For @N{C functions}, this function uses the empty string @T{""}
as a name for all upvalues.
(For Lua functions,
upvalues are the external local variables that the function uses,
and that are consequently included in its closure.)
See @Lid{debug.getupvalue} for more information about upvalues.
}
@@ -4801,6 +4822,20 @@ 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,-}
@@ -5509,7 +5544,6 @@ creates a new table to be used as a metatable for userdata,
adds to this new table the pair @T{__name = tname},
adds to the registry the pair @T{[tname] = new table},
and returns 1.
(The entry @idx{__name} is used by some error-reporting functions.)
In both cases,
the function pushes onto the stack the final value associated
@@ -5758,7 +5792,7 @@ that will be called to close the stream
when the handle is closed or collected;
this function receives the file handle as its sole argument and
must return either @true, in case of success,
or @nil plus an error message, in case of error.
or a false value plus an error message, in case of error.
Once Lua calls this field,
it changes the field value to @id{NULL}
to signal that the handle is closed.
@@ -5880,6 +5914,14 @@ to its expected parameters.
For instance, a function documented as @T{foo(arg)}
should not be called without an argument.
The notation @fail means a return value representing
some kind of failure or the absence of a better value to return.
Currently, @fail is equal to @nil,
but that may change in future versions.
The recommendation is to always test the success of these functions
with @T{(not status)}, instead of @T{(status == nil)}.
Currently, Lua has the following standard libraries:
@itemize{
@@ -6084,8 +6126,8 @@ with previous results.
A return of an empty string, @nil, or no value signals the end of the chunk.
If there are no syntactic errors,
returns the compiled chunk as a function;
otherwise, returns @nil plus the error message.
@id{load} returns the compiled chunk as a function;
otherwise, it returns @fail plus the error message.
When you load a main chunk,
the resulting function will always have exactly one upvalue,
@@ -6277,7 +6319,7 @@ When called with no @id{base},
If the argument is already a number or
a string convertible to a number,
then @id{tonumber} returns this number;
otherwise, it returns @nil.
otherwise, it returns @fail.
The conversion of strings can result in integers or floats,
according to the lexical conventions of Lua @see{lexical}.
@@ -6291,7 +6333,7 @@ In bases @N{above 10}, the letter @Char{A} (in either upper or lower case)
@N{represents 10}, @Char{B} @N{represents 11}, and so forth,
with @Char{Z} representing 35.
If the string @id{e} is not a valid numeral in the given base,
the function returns @nil.
the function returns @fail.
}
@@ -6335,6 +6377,16 @@ The current value of this variable is @St{Lua 5.4}.
Emits a warning with a message composed by the concatenation
of all its arguments (which should be strings).
By convention,
a one-piece message starting with @Char{@At}
is intended to be a @emph{control message},
which is a message to the warning system itself.
In particular, the standard warning function in Lua
recognizes the control messages @St{@At{}off},
to stop the emission of warnings,
and @St{@At{}on}, to (re)start the emission;
it ignores unknown control messages.
}
@LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
@@ -6728,7 +6780,7 @@ will try to open the files
Returns the resulting name of the first file that it can
open in read mode (after closing the file),
or @nil plus an error message if none succeeds.
or @fail plus an error message if none succeeds.
(This error message lists all file names it tried to open.)
}
@@ -6807,7 +6859,7 @@ Looks for the first match of
@id{pattern} @see{pm} in the string @id{s}.
If it finds a match, then @id{find} returns the indices @N{of @T{s}}
where this occurrence starts and ends;
otherwise, it returns @nil.
otherwise, it returns @fail.
A third, optional numeric argument @id{init} specifies
where to start the search;
its default value @N{is 1} and can be negative.
@@ -6815,7 +6867,6 @@ A value of @true as a fourth, optional argument @id{plain}
turns off the pattern matching facilities,
so the function does a plain @Q{find substring} operation,
with no characters in @id{pattern} being considered magic.
Note that if @id{plain} is given, then @id{init} must be given as well.
If the pattern has captures,
then in a successful match
@@ -7001,7 +7052,7 @@ Looks for the first @emph{match} of
the @id{pattern} @see{pm} in the string @id{s}.
If it finds one, then @id{match} returns
the captures from the pattern;
otherwise it returns @nil.
otherwise it returns @fail.
If @id{pattern} specifies no captures,
then the whole match is returned.
A third, optional numeric argument @id{init} specifies
@@ -7274,7 +7325,7 @@ stored as the first capture, and therefore has @N{number 1};
the character matching @St{.} is captured with @N{number 2},
and the part matching @St{%s*} has @N{number 3}.
As a special case, the empty capture @T{()} captures
As a special case, the capture @T{()} captures
the current string position (a number).
For instance, if we apply the pattern @T{"()aa()"} on the
string @T{"flaaap"}, there will be two captures: @N{3 and 5}.
@@ -7466,7 +7517,7 @@ Returns the number of UTF-8 characters in string @id{s}
that start between positions @id{i} and @id{j} (both inclusive).
The default for @id{i} is @num{1} and for @id{j} is @num{-1}.
If it finds any invalid byte sequence,
returns a false value plus the position of the first invalid byte.
returns @fail plus the position of the first invalid byte.
}
@@ -7482,7 +7533,7 @@ so that @T{utf8.offset(s, -n)} gets the offset of the
@id{n}-th character from the end of the string.
If the specified character is neither in the subject
nor right after its end,
the function returns @nil.
the function returns @fail.
As a special case,
when @id{n} is 0 the function returns the start of the encoding
@@ -7782,6 +7833,11 @@ The default for @id{y} is zero.
When called with no arguments,
Lua generates a seed with
a weak attempt for randomness.
This function returns the two seed components
that were effectively used,
so that setting them again repeats the sequence.
To ensure a required level of randomness to the initial state
(or contrarily, to have a deterministic sequence,
for instance when debugging a program),
@@ -7812,7 +7868,7 @@ Returns the tangent of @id{x} (assumed to be in radians).
If the value @id{x} is convertible to an integer,
returns that integer.
Otherwise, returns @nil.
Otherwise, returns @fail.
}
@@ -7820,7 +7876,7 @@ Otherwise, returns @nil.
Returns @St{integer} if @id{x} is an integer,
@St{float} if it is a float,
or @nil if @id{x} is not a number.
or @fail if @id{x} is not a number.
}
@@ -7834,7 +7890,6 @@ they are compared as @x{unsigned integers}.
}
@sect2{iolib| @title{Input and Output Facilities}
The I/O library provides two different styles for file manipulation.
@@ -7860,10 +7915,10 @@ three predefined file handles with their usual meanings from C:
The I/O library never closes these files.
Unless otherwise stated,
all I/O functions return @nil on failure,
all I/O functions return @fail on failure,
plus an error message as a second result and
a system-dependent error code as a third result,
and some value different from @nil on success.
and some non-false value on success.
On non-POSIX systems,
the computation of the error message and error code
in case of errors
@@ -7902,8 +7957,8 @@ instead of returning an error code.
Opens the given file name in read mode
and returns an iterator function that
works like @T{file:lines(@Cdots)} over the opened file.
When the iterator function detects the end of file,
it returns no values (to finish the loop) and automatically closes the file.
When the iterator function fails to read any value,
it automatically closes the file.
Besides the iterator function,
@id{io.lines} returns three other values:
two @nil values as placeholders,
@@ -7917,7 +7972,8 @@ to @T{io.input():lines("l")};
that is, it iterates over the lines of the default input file.
In this case, the iterator does not close the file when the loop ends.
In case of errors this function raises the error,
In case of errors opening the file,
this function raises the error,
instead of returning an error code.
}
@@ -7983,7 +8039,7 @@ and it is automatically removed when the program ends.
Checks whether @id{obj} is a valid file handle.
Returns the string @T{"file"} if @id{obj} is an open file handle,
@T{"closed file"} if @id{obj} is a closed file handle,
or @nil if @id{obj} is not a file handle.
or @fail if @id{obj} is not a file handle.
}
@@ -8029,9 +8085,6 @@ starting at the current position.
Unlike @Lid{io.lines}, this function does not close the file
when the loop ends.
In case of errors this function raises the error,
instead of returning an error code.
}
@LibEntry{file:read (@Cdots)|
@@ -8040,7 +8093,7 @@ Reads the file @id{file},
according to the given formats, which specify what to read.
For each format,
the function returns a string or a number with the characters read,
or @nil if it cannot read data with the specified format.
or @fail if it cannot read data with the specified format.
(In this latter case,
the function does not read subsequent formats.)
When called without arguments,
@@ -8059,31 +8112,32 @@ is a valid prefix for a numeral;
if that prefix does not form a valid numeral
(e.g., an empty string, @St{0x}, or @St{3.4e-})
or it is too long (more than 200 characters),
it is discarded and the format returns @nil.
it is discarded and the format returns @fail.
}
@item{@St{a}|
reads the whole file, starting at the current position.
On end of file, it returns the empty string.
On end of file, it returns the empty string;
this format never fails.
}
@item{@St{l}|
reads the next line skipping the end of line,
returning @nil on end of file.
returning @fail on end of file.
This is the default format.
}
@item{@St{L}|
reads the next line keeping the end-of-line character (if present),
returning @nil on end of file.
returning @fail on end of file.
}
@item{@emph{number}|
reads a string with up to this number of bytes,
returning @nil on end of file.
returning @fail on end of file.
If @id{number} is zero,
it reads nothing and returns an empty string,
or @nil on end of file.
or @fail on end of file.
}
}
@@ -8104,7 +8158,7 @@ specified by the string @id{whence}, as follows:
}
In case of success, @id{seek} returns the final file position,
measured in bytes from the beginning of the file.
If @id{seek} fails, it returns @nil,
If @id{seek} fails, it returns @fail,
plus a string describing the error.
The default value for @id{whence} is @T{"cur"},
@@ -8120,31 +8174,22 @@ end of the file, and returns its size.
@LibEntry{file:setvbuf (mode [, size])|
Sets the buffering mode for an output file.
Sets the buffering mode for a file.
There are three available modes:
@description{
@item{@St{no}|
no buffering; the result of any output operation appears immediately.
@item{@St{no}| no buffering.}
@item{@St{full}| full buffering.}
@item{@St{line}| line buffering.}
}
@item{@St{full}|
full buffering; output operation is performed only
when the buffer is full or when
you explicitly @T{flush} the file @seeF{io.flush}.
}
@item{@St{line}|
line buffering; output is buffered until a newline is output
or there is any input from some special files
(such as a terminal device).
}
}
For the last two cases,
@id{size} is a hint for the size of the buffer, in bytes.
The default is an appropriate size.
The specific behavior of each mode is non portable;
check the underlying @ANSI{setvbuf} in your platform for
more details.
}
@LibEntry{file:write (@Cdots)|
@@ -8153,7 +8198,6 @@ Writes the value of each of its arguments to @id{file}.
The arguments must be strings or numbers.
In case of success, this function returns @id{file}.
Otherwise it returns @nil plus a string describing the error.
}
@@ -8200,7 +8244,7 @@ then @id{date} returns the date as a string,
formatted according to the same rules as the @ANSI{strftime}.
If @id{format} is absent, it defaults to @St{%c},
which gives a reasonable date and time representation
which gives a human-readable date and time representation
using the current locale.
On non-POSIX systems,
@@ -8225,7 +8269,7 @@ This function is equivalent to the @ANSI{system}.
It passes @id{command} to be executed by an operating system shell.
Its first result is @true
if the command terminated successfully,
or @nil otherwise.
or @fail otherwise.
After this first result
the function returns a string plus a number,
as follows:
@@ -8267,7 +8311,7 @@ closes the Lua state before exiting.
@LibEntry{os.getenv (varname)|
Returns the value of the process environment variable @id{varname},
or @nil if the variable is not defined.
or @fail if the variable is not defined.
}
@@ -8275,7 +8319,7 @@ or @nil if the variable is not defined.
Deletes the file (or empty directory, on @x{POSIX} systems)
with the given name.
If this function fails, it returns @nil,
If this function fails, it returns @fail
plus a string describing the error and the error code.
Otherwise, it returns true.
@@ -8284,7 +8328,7 @@ Otherwise, it returns true.
@LibEntry{os.rename (oldname, newname)|
Renames the file or directory named @id{oldname} to @id{newname}.
If this function fails, it returns @nil,
If this function fails, it returns @fail,
plus a string describing the error and the error code.
Otherwise, it returns true.
@@ -8299,7 +8343,7 @@ Sets the current locale of the program.
@T{"monetary"}, @T{"numeric"}, or @T{"time"};
the default category is @T{"all"}.
The function returns the name of the new locale,
or @nil if the request cannot be honored.
or @fail if the request cannot be honored.
If @id{locale} is the empty string,
the current locale is set to an implementation-defined native locale.
@@ -8418,6 +8462,8 @@ the current hook function, the current hook mask,
and the current hook count,
as set by the @Lid{debug.sethook} function.
Returns @fail if there is no active hook.
}
@LibEntry{debug.getinfo ([thread,] f [, what])|
@@ -8432,7 +8478,7 @@ of the given thread:
(except for tail calls, which do not count on the stack);
and so on.
If @id{f} is a number greater than the number of active functions,
then @id{getinfo} returns @nil.
then @id{getinfo} returns @fail.
The returned table can contain all the fields returned by @Lid{lua_getinfo},
with the string @id{what} describing which fields to fill in.
@@ -8466,9 +8512,12 @@ The first parameter or local variable has @N{index 1}, and so on,
following the order that they are declared in the code,
counting only the variables that are active
in the current scope of the function.
Compile-time constants may not appear in this listing,
if they were optimized away by the compiler.
Negative indices refer to vararg arguments;
@num{-1} is the first vararg argument.
The function returns @nil if there is no variable with the given index,
The function returns @fail
if there is no variable with the given index,
and raises an error when called with a level out of range.
(You can call @Lid{debug.getinfo} to check whether the level is valid.)
@@ -8499,10 +8548,18 @@ Returns the registry table @see{registry}.
This function returns the name and the value of the upvalue
with index @id{up} of the function @id{f}.
The function returns @nil if there is no upvalue with the given index.
The function returns @fail
if there is no upvalue with the given index.
Variable names starting with @Char{(} (open parenthesis) @C{)}
represent variables with no known names
(For Lua functions,
upvalues are the external local variables that the function uses,
and that are consequently included in its closure.)
For @N{C functions}, this function uses the empty string @T{""}
as a name for all upvalues.
Variable name @Char{?} (interrogation mark)
represents variables with no known names
(variables from chunks saved without debug information).
}
@@ -8515,6 +8572,34 @@ 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 falsy value.
Otherwise,
the call returns the old limit.
}
@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
Sets the given function as the debug hook.
@@ -8552,7 +8637,7 @@ and @N{level 1} is the hook function.)
This function assigns the value @id{value} to the local variable
with index @id{local} of the function at level @id{level} of the stack.
The function returns @nil if there is no local
The function returns @fail if there is no local
variable with the given index,
and raises an error when called with a @id{level} out of range.
(You can call @id{getinfo} to check whether the level is valid.)
@@ -8575,10 +8660,12 @@ Returns @id{value}.
This function assigns the value @id{value} to the upvalue
with index @id{up} of the function @id{f}.
The function returns @nil if there is no upvalue
The function returns @fail if there is no upvalue
with the given index.
Otherwise, it returns the name of the upvalue.
See @Lid{debug.getupvalue} for more information about upvalues.
}
@LibEntry{debug.setuservalue (udata, value, n)|
@@ -8588,7 +8675,7 @@ the @id{n}-th user value associated to the given @id{udata}.
@id{udata} must be a full userdata.
Returns @id{udata},
or @nil if the userdata does not have that value.
or @fail if the userdata does not have that value.
}
@@ -8655,6 +8742,7 @@ The options are:
@item{@T{-i}| enters interactive mode after running @rep{script};}
@item{@T{-v}| prints version information;}
@item{@T{-E}| ignores environment variables;}
@item{@T{-W}| turn warnings on;}
@item{@T{--}| stops handling options;}
@item{@T{-}| executes @id{stdin} as a file and stops handling options.}
}
@@ -8680,12 +8768,13 @@ setting the values of
@Lid{package.path} and @Lid{package.cpath}
with the default paths defined in @id{luaconf.h}.
All options are handled in order, except @T{-i} and @T{-E}.
The options @T{-e}, @T{-l}, and @T{-W} are handled in
the order they appear.
For instance, an invocation like
@verbatim{
$ lua -e'a=1' -e 'print(a)' script.lua
$ lua -e 'a=1' -llib1 script.lua
}
will first set @id{a} to 1, then print the value of @id{a},
will first set @id{a} to 1, then require the library @id{lib1},
and finally run the file @id{script.lua} with no arguments.
(Here @T{$} is the shell prompt. Your prompt may be different.)
@@ -8745,7 +8834,8 @@ has a metamethod @idx{__tostring},
the interpreter calls this metamethod to produce the final message.
Otherwise, the interpreter converts the error object to a string
and adds a stack traceback to it.
Warnings are simply printed in the standard error output.
When warnings are on,
they are simply printed in the standard error output.
When finishing normally,
the interpreter closes its main Lua state
@@ -8979,10 +9069,14 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end}
@OrNL @Rw{function} funcname funcbody
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
}
@producname{attnamelist}@producbody{
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
@producname{retstat}@producbody{@Rw{return}
@bnfopt{explist} @bnfopt{@bnfter{;}}}
+29 -5
View File
@@ -5,8 +5,8 @@
local version = "Lua 5.4"
if _VERSION ~= version then
warn("This test suite is for ", version,
", not for ", _VERSION, "\nExiting tests")
io.stderr:write("This test suite is for ", version,
", not for ", _VERSION, "\nExiting tests")
return
end
@@ -37,8 +37,6 @@ end
-- tests should require debug when needed
debug = nil
require"bwcoercion"
if usertests then
T = nil -- no "internal" tests for user tests
@@ -46,7 +44,6 @@ else
T = rawget(_G, "T") -- avoid problems with 'strict' module
end
math.randomseed(0)
--[=[
example of a long [comment],
@@ -54,6 +51,14 @@ math.randomseed(0)
]=]
print("\n\tStarting Tests")
do
-- set random seed
local random_x, random_y = math.randomseed()
print(string.format("random seeds: %d, %d", random_x, random_y))
end
print("current path:\n****" .. package.path .. "****\n")
@@ -95,6 +100,8 @@ local function F (m)
end
end
local Cstacklevel
local showmem
if not T then
local max = 0
@@ -104,6 +111,7 @@ if not T then
print(format(" ---- total memory: %s, max memory: %s ----\n",
F(m), F(max)))
end
Cstacklevel = function () return 0 end -- no info about stack level
else
showmem = function ()
T.checkmemory()
@@ -117,9 +125,16 @@ else
T.totalmem"string", T.totalmem"table", T.totalmem"function",
T.totalmem"userdata", T.totalmem"thread"))
end
Cstacklevel = function ()
local _, _, ncalls, nci = T.stacklevel()
return ncalls + nci -- number of free slots in the C stack
end
end
local Cstack = Cstacklevel()
--
-- redefine dofile to run files through dump/undump
--
@@ -195,7 +210,12 @@ if #msgs > 0 then
end
print("(there should be two warnings now)")
warn("@on")
warn("#This is ", "an expected", " warning")
warn("@off")
warn("******** THIS WARNING SHOULD NOT APPEAR **********")
warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
warn("@on")
warn("#This is", " another one")
-- no test module should define 'debug'
@@ -211,6 +231,10 @@ debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
-- to survive outside block
_G.showmem = showmem
assert(Cstack == Cstacklevel(),
"should be at the same C-stack level it was when started the tests")
end --)
local _G, showmem, print, format, clock, time, difftime,
+83 -6
View File
@@ -241,6 +241,23 @@ assert(a == 20 and b == false)
a,b = T.testC("compare LE 5 -6, return 2", a1, 2, 2, a1, 2, 20)
assert(a == 20 and b == true)
do -- testing lessthan and lessequal with metamethods
local mt = {__lt = function (a,b) return a[1] < b[1] end,
__le = function (a,b) return a[1] <= b[1] end,
__eq = function (a,b) return a[1] == b[1] end}
local function O (x)
return setmetatable({x}, mt)
end
local a, b = T.testC("compare LT 2 3; pushint 10; return 2", O(1), O(2))
assert(a == true and b == 10)
local a, b = T.testC("compare LE 2 3; pushint 10; return 2", O(3), O(2))
assert(a == false and b == 10)
local a, b = T.testC("compare EQ 2 3; pushint 10; return 2", O(3), O(3))
assert(a == true and b == 10)
end
-- testing length
local t = setmetatable({x = 20}, {__len = function (t) return t.x end})
a,b,c = T.testC([[
@@ -354,8 +371,11 @@ assert(to("topointer", nil) == null)
assert(to("topointer", "abc") ~= null)
assert(to("topointer", string.rep("x", 10)) ==
to("topointer", string.rep("x", 10))) -- short strings
assert(to("topointer", string.rep("x", 300)) ~=
to("topointer", string.rep("x", 300))) -- long strings
do -- long strings
local s1 = string.rep("x", 300)
local s2 = string.rep("x", 300)
assert(to("topointer", s1) ~= to("topointer", s2))
end
assert(to("topointer", T.pushuserdata(20)) ~= null)
assert(to("topointer", io.read) ~= null) -- light C function
assert(to("topointer", hfunc) ~= null) -- "heavy" C function
@@ -498,7 +518,53 @@ do -- getp/setp
local a = {}
T.testC("rawsetp 2 1", a, 20)
assert(a[T.pushuserdata(1)] == 20)
assert(T.testC("rawgetp 2 1; return 1", a) == 20)
assert(T.testC("rawgetp -1 1; return 1", a) == 20)
end
do -- using the table itself as index
local a = {}
a[a] = 10
local prog = "gettable -1; return *"
local res = {T.testC(prog, a)}
assert(#res == 2 and res[1] == prog and res[2] == 10)
local prog = "settable -2; return *"
local res = {T.testC(prog, a, 20)}
assert(a[a] == 20)
assert(#res == 1 and res[1] == prog)
-- raw
a[a] = 10
local prog = "rawget -1; return *"
local res = {T.testC(prog, a)}
assert(#res == 2 and res[1] == prog and res[2] == 10)
local prog = "rawset -2; return *"
local res = {T.testC(prog, a, 20)}
assert(a[a] == 20)
assert(#res == 1 and res[1] == prog)
-- using the table as the value to set
local prog = "rawset -1; return *"
local res = {T.testC(prog, 30, a)}
assert(a[30] == a)
assert(#res == 1 and res[1] == prog)
local prog = "settable -1; return *"
local res = {T.testC(prog, 40, a)}
assert(a[40] == a)
assert(#res == 1 and res[1] == prog)
local prog = "rawseti -1 100; return *"
local res = {T.testC(prog, a)}
assert(a[100] == a)
assert(#res == 1 and res[1] == prog)
local prog = "seti -1 200; return *"
local res = {T.testC(prog, a)}
assert(a[200] == a)
assert(#res == 1 and res[1] == prog)
end
a = {x=0, y=12}
@@ -632,7 +698,7 @@ for k, v in ipairs(t) do
assert(v1 == v and p)
end
assert(debug.getuservalue(4) == nil)
assert(not debug.getuservalue(4))
debug.setuservalue(b, function () return 10 end, 10)
collectgarbage() -- function should not be collected
@@ -911,6 +977,7 @@ assert(t[7] == nil)
-------------------------------------------------------------------------
do -- testing errors during GC
warn("@off")
collectgarbage("stop")
local a = {}
for i=1,20 do
@@ -928,6 +995,7 @@ do -- testing errors during GC
collectgarbage()
assert(A == 10) -- number of normal collections
collectgarbage("restart")
warn("@on")
end
-------------------------------------------------------------------------
-- test for userdata vals
@@ -1030,7 +1098,7 @@ do
assert(type(a[1]) == "string" and a[2][1] == 11)
assert(#openresource == 0) -- was closed
-- error
-- closing by error
local a, b = pcall(T.makeCfunc[[
call 0 1 # create resource
toclose -1 # mark it to be closed
@@ -1039,6 +1107,15 @@ do
assert(a == false and b[1] == 11)
assert(#openresource == 0) -- was closed
-- non-closable value
local a, b = pcall(T.makeCfunc[[
newtable # create non-closable object
toclose -1 # mark it to be closed (shoud raise an error)
abort # will not be executed
]])
assert(a == false and
string.find(b, "non%-closable value"))
local function check (n)
assert(#openresource == n)
end
@@ -1137,7 +1214,7 @@ end)
testamem("to-be-closed variables", function()
local flag
do
local <toclose> x =
local x <close> =
setmetatable({}, {__close = function () flag = true end})
flag = false
local x = {}
+3 -3
View File
@@ -60,9 +60,9 @@ assert("1234.0" << "5.0" == 1234 * 32)
assert("0xffff.0" ~ "0xAAAA" == 0x5555)
assert(~"0x0.000p4" == -1)
assert("7" .. 3 << 1 == 146)
assert(10 >> 1 .. "9" == 0)
assert(10 | 1 .. "9" == 27)
assert(("7" .. 3) << 1 == 146)
assert(0xffffffff >> (1 .. "9") == 0x1fff)
assert(10 | (1 .. "9") == 27)
do
local st, msg = pcall(function () return 4 & "a" end)
+17
View File
@@ -151,6 +151,23 @@ end
print('+')
do -- testing chains of '__call'
local N = 20
local u = table.pack
for i = 1, N do
u = setmetatable({i}, {__call = u})
end
local Res = u("a", "b", "c")
assert(Res.n == N + 3)
for i = 1, N do
assert(Res[i][1] == i)
end
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
end
a = nil
(function (x) a=x end)(23)
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
+121 -62
View File
@@ -7,6 +7,23 @@ if T==nil then
end
print "testing code generation and optimizations"
-- to test constant propagation
local k0aux <const> = 0
local k0 <const> = k0aux
local k1 <const> = 1
local k3 <const> = 3
local k6 <const> = k3 + (k3 << k0)
local kFF0 <const> = 0xFF0
local k3_78 <const> = 3.78
local x, k3_78_4 <const> = 10, k3_78 / 4
assert(x == 10)
local kx <const> = "x"
local kTrue <const> = true
local kFalse <const> = false
local kNil <const> = nil
-- this code gave an error for the code checker
do
@@ -27,12 +44,12 @@ end
local function foo ()
local a
a = 3;
a = k3;
a = 0; a = 0.0; a = -7 + 7
a = 3.78/4; a = 3.78/4
a = -3.78/4; a = 3.78/4; a = -3.78/4
a = k3_78/4; a = k3_78_4
a = -k3_78/4; a = k3_78/4; a = -3.78/4
a = -3.79/4; a = 0.0; a = -0;
a = 3; a = 3.0; a = 3; a = 3.0
a = k3; a = 3.0; a = 3; a = 3.0
end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
@@ -77,19 +94,22 @@ end
-- some basic instructions
check(function () -- function does not create upvalues
(function () end){f()}
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0')
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
'SETLIST', 'CALL', 'RETURN0')
check(function (x) -- function creates upvalues
(function () return x end){f()}
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
'SETLIST', 'CALL', 'RETURN')
-- sequence of LOADNILs
check(function ()
local kNil <const> = nil
local a,b,c
local d; local e;
local f,g,h;
d = nil; d=nil; b=nil; a=nil; c=nil;
d = nil; d=nil; b=nil; a=kNil; c=nil;
end, 'LOADNIL', 'RETURN0')
check(function ()
@@ -109,7 +129,7 @@ check (function (a,b,c) return a end, 'RETURN1')
-- infinite loops
check(function () while true do local a = -1 end end,
check(function () while kTrue do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () while 1 do local a = -1 end end,
@@ -125,9 +145,9 @@ check(function (a,b,c,d) return a..b..c..d end,
-- not
check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
check(function () return not not false end, 'LOADBOOL', 'RETURN1')
check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
check(function () return not not true end, 'LOADBOOL', 'RETURN1')
check(function () return not not 1 end, 'LOADBOOL', 'RETURN1')
check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
-- direct access to locals
check(function ()
@@ -136,15 +156,16 @@ check(function ()
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
end,
'LOADNIL',
'MUL',
'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETFIELD', 'POW',
'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
'MUL', 'MMBIN',
'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
-- direct access to constants
check(function ()
local a,b
a.x = 3.2
local c = kNil
a[kx] = 3.2
a.x = b
a[b] = 'x'
end,
@@ -152,8 +173,9 @@ end,
-- "get/set table" with numeric indices
check(function (a)
local k255 <const> = 255
a[1] = a[100]
a[255] = a[256]
a[k255] = a[256]
a[256] = 5
end,
'GETI', 'SETI',
@@ -166,11 +188,11 @@ check(function ()
b = a/a
b = 5-4
end,
'LOADNIL', 'SUB', 'DIV', 'LOADI', 'RETURN0')
'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
check(function ()
local a,b
a[true] = false
a[kTrue] = false
end,
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
@@ -238,68 +260,81 @@ local function checkF (func, val)
end
checkF(function () return 0.0 end, 0.0)
checkI(function () return 0 end, 0)
checkI(function () return -0//1 end, 0)
checkI(function () return k0 end, 0)
checkI(function () return -k0//1 end, 0)
checkK(function () return 3^-1 end, 1/3)
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
checkI(function () return -3 % 5 end, 2)
checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
checkI(function () return -k3 % 5 end, 2)
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
checkI(function () return ~(~0xFF0 | 0xFF0) end, 0)
checkI(function () return ~(~kFF0 | kFF0) end, 0)
checkI(function () return ~~-1024.0 end, -1024)
checkI(function () return ((100 << 6) << -4) >> 2 end, 100)
checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
checkI(function () return 65535 end, sbx)
checkI(function () return -65535 end, -sbx)
checkI(function () return 65536 end, sbx + 1)
checkK(function () return 65537 end, sbx + 2)
checkK(function () return -65536 end, -(sbx + 1))
local border <const> = 65535
checkI(function () return border end, sbx)
checkI(function () return -border end, -sbx)
checkI(function () return border + 1 end, sbx + 1)
checkK(function () return border + 2 end, sbx + 2)
checkK(function () return -(border + 1) end, -(sbx + 1))
checkF(function () return 65535.0 end, sbx + 0.0)
checkF(function () return -65535.0 end, -sbx + 0.0)
checkF(function () return 65536.0 end, (sbx + 1.0))
checkK(function () return 65537.0 end, (sbx + 2.0))
checkK(function () return -65536.0 end, -(sbx + 1.0))
local border <const> = 65535.0
checkF(function () return border end, sbx + 0.0)
checkF(function () return -border end, -sbx + 0.0)
checkF(function () return border + 1 end, (sbx + 1.0))
checkK(function () return border + 2 end, (sbx + 2.0))
checkK(function () return -(border + 1) end, -(sbx + 1.0))
-- immediate operands
checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1')
checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWI', 'RETURN1')
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1')
checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'RETURN1')
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'RETURN1')
checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return 128 + x end, 0.0, 128.0,
'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return x * -127 end, -1.0, 127.0,
'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x // 1 end, 10.0, 10.0,
'IDIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x % (100 - 10) end, 91, 1,
'MODK', 'MMBINK', 'RETURN1')
checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
-- K operands in arithmetic operations
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'RETURN1')
-- check(function (x) return 128 + x end, 'ADDK', 'RETURN1')
checkR(function (x) return x * -10000 end, 2, -20000, 'MULK', 'RETURN1')
-- check(function (x) return 20 * x end, 'MULK', 'RETURN1')
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'RETURN1')
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'RETURN1')
checkR(function (x) return x // 10000 end, 10000, 1, 'IDIVK', 'RETURN1')
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0, 'MODK', 'RETURN1')
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
-- check(function (x) return 128 + x end, 'ADDK', 'MMBINK', 'RETURN1')
checkR(function (x) return x * -10000 end, 2, -20000,
'MULK', 'MMBINK', 'RETURN1')
-- check(function (x) return 20 * x end, 'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x // 10000 end, 10000, 1,
'IDIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
'MODK', 'MMBINK', 'RETURN1')
-- no foldings (and immediate operands)
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1')
check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'RETURN1')
check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
-- basic 'for' loops
check(function () for i = -10, 10.5 do end end,
@@ -335,7 +370,7 @@ end,
do -- tests for table access in upvalues
local t
check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP')
check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
check(function (a) t[a()] = t[a()] end,
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
'GETUPVAL', 'GETTABLE', 'SETTABLE')
@@ -355,7 +390,7 @@ check(function (a, b)
if b then break else a = a + 1 end
end
end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'JMP', 'RETURN0')
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
@@ -379,6 +414,30 @@ function (a)
end
)
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)
checkequal(function () return 6 and true or nil end,
function () return k6 and kTrue or kNil end)
do -- string constants
local k0 <const> = "00000000000000000000000000000000000000000000000000"
local function f1 ()
local k <const> = k0
return function ()
return function () return k end
end
end
local f2 = f1()
local f3 = f2()
assert(f3() == k0)
checkK(f3, k0)
-- string is not needed by other functions
assert(T.listk(f1)[1] == nil)
assert(T.listk(f2)[1] == nil)
end
print 'OK'
+27 -9
View File
@@ -211,15 +211,15 @@ assert(a==1 and b==nil)
print'+';
do -- testing constants
local <const> prog = [[local <XXX> x = 10]]
local prog <const> = [[local x <XXX> = 10]]
checkload(prog, "unknown attribute 'XXX'")
checkload([[local <const> xxx = 20; xxx = 10]],
checkload([[local xxx <const> = 20; xxx = 10]],
":1: attempt to assign to const variable 'xxx'")
checkload([[
local xx;
local <const> xxx = 20;
local xxx <const> = 20;
local yyy;
local function foo ()
local abc = xx + yyy + xxx;
@@ -228,7 +228,7 @@ do -- testing constants
]], ":6: attempt to assign to const variable 'xxx'")
checkload([[
local <toclose> x = nil
local x <close> = nil
x = io.open()
]], ":2: attempt to assign to const variable 'x'")
end
@@ -287,7 +287,7 @@ a,b = F(nil)==nil; assert(a == true and b == nil)
------------------------------------------------------------------
-- sometimes will be 0, sometimes will not...
_ENV.GLOB1 = math.floor(os.time()) % 2
_ENV.GLOB1 = math.random(0, 1)
-- basic expressions with their respective values
local basiccases = {
@@ -298,16 +298,36 @@ local basiccases = {
{"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
}
local prog
if _ENV.GLOB1 == 0 then
basiccases[2][1] = "F" -- constant false
prog = [[
local F <const> = false
if %s then IX = true end
return %s
]]
else
basiccases[4][1] = "k10" -- constant 10
prog = [[
local k10 <const> = 10
if %s then IX = true end
return %s
]]
end
print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
-- operators with their respective values
local <const> binops = {
local binops <const> = {
{" and ", function (a,b) if not a then return a else return b end end},
{" or ", function (a,b) if a then return a else return b end end},
}
local <const> cases = {}
local cases <const> = {}
-- creates all combinations of '(cases[i] op cases[n-i])' plus
-- 'not(cases[i] op cases[n-i])' (syntax + value)
@@ -337,8 +357,6 @@ cases[1] = basiccases
for i = 2, level do cases[i] = createcases(i) end
print("+")
local prog = [[if %s then IX = true end; return %s]]
local i = 0
for n = 1, level do
for _, v in pairs(cases[n]) do
+36 -6
View File
@@ -151,7 +151,7 @@ do
end
co = coroutine.create(function ()
local <toclose> x = func2close(function (self, err)
local x <close> = func2close(function (self, err)
assert(err == nil); X = false
end)
X = true
@@ -163,21 +163,30 @@ do
assert(not X and coroutine.status(co) == "dead")
-- error closing a coroutine
warn("@on")
local x = 0
co = coroutine.create(function()
local <toclose> y = func2close(function (self,err)
local y <close> = func2close(function (self,err)
if (err ~= 111) then os.exit(false) end -- should not happen
x = 200
error(200)
error("200")
end)
local <toclose> x = func2close(function (self, err)
local x <close> = func2close(function (self, err)
assert(err == nil); error(111)
end)
coroutine.yield()
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 = nil
warn("@normal")
end
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
assert(x == 200)
@@ -356,7 +365,7 @@ do
local X = false
A = coroutine.wrap(function()
local <toclose> _ = setmetatable({}, {__close = function () X = true end})
local _ <close> = setmetatable({}, {__close = function () X = true end})
return pcall(A, 1)
end)
st, res = A()
@@ -426,6 +435,10 @@ else
while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
if A==0 then turn = "A"; assert(T.resume(x)) end
if B==0 then turn = "B"; assert(T.resume(y)) end
-- check that traceback works correctly after yields inside hooks
debug.traceback(x)
debug.traceback(y)
end
assert(B // A == 7) -- fact(7) // fact(6)
@@ -711,6 +724,17 @@ assert(run(function () return a / b end, {"div"}) == 10/12)
assert(run(function () return a % b end, {"mod"}) == 10)
assert(run(function () return a // b end, {"idiv"}) == 0)
-- repeat tests with larger constants (to use 'K' opcodes)
local a1000 = new(1000)
assert(run(function () return a1000 + 1000 end, {"add"}) == 2000)
assert(run(function () return a1000 - 25000 end, {"sub"}) == -24000)
assert(run(function () return 2000 * a end, {"mul"}) == 20000)
assert(run(function () return a1000 / 1000 end, {"div"}) == 1)
assert(run(function () return a1000 % 600 end, {"mod"}) == 400)
assert(run(function () return a1000 // 500 end, {"idiv"}) == 2)
assert(run(function () return a % b end, {"mod"}) == 10)
@@ -723,6 +747,12 @@ assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
assert(run(function () return 10 & b end, {"band"}) == 10 & 12)
assert(run(function () return a | 2 end, {"bor"}) == 10 | 2)
assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2)
assert(run(function () return a >> 2 end, {"shr"}) == 10 >> 2)
assert(run(function () return 1 >> a end, {"shr"}) == 1 >> 10)
assert(run(function () return a << 2 end, {"shl"}) == 10 << 2)
assert(run(function () return 1 << a end, {"shl"}) == 1 << 10)
assert(run(function () return 2 ~ a end, {"bxor"}) == 2 ~ 10)
assert(run(function () return a..b end, {"concat"}) == "1012")
@@ -805,7 +835,7 @@ assert(run(function ()
-- tests for coroutine API
if T==nil then
(Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
return
print "OK"; return
end
print('testing coroutine API')
+65 -3
View File
@@ -1,12 +1,32 @@
-- $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 craches, see its file ('cstack.lua')"
-- Segmentation faults in these tests probably result from a C-stack
-- overflow. To avoid these errors, recompile Lua with a smaller
-- value for the constant 'LUAI_MAXCCALLS' or else ensure a larger
-- stack for the program.
-- 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.)
-- 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.
local origlimit = debug.setcstacklimit(400)
print("default stack limit: " .. origlimit)
-- change this value for different limits for this test suite
local currentlimit = origlimit
debug.setcstacklimit(currentlimit)
print("current stack limit: " .. currentlimit)
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
@@ -79,4 +99,46 @@ do print("testing stack-overflow in recursive 'gsub'")
print("\tfinal count: ", count)
end
do print("testing changes in C-stack limit")
assert(not debug.setcstacklimit(0)) -- limit too small
assert(not debug.setcstacklimit(50000)) -- limit too large
local co = coroutine.wrap (function ()
return debug.setcstacklimit(400)
end)
assert(co() == false) -- 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
end
assert(debug.setcstacklimit(400) == currentlimit)
local lim400 = check()
-- a very low limit (given that the several calls to arive here)
local lowlimit = 38
assert(debug.setcstacklimit(lowlimit) == 400)
assert(check() < lowlimit - 30)
assert(debug.setcstacklimit(600) == lowlimit)
local lim600 = check()
assert(lim600 == lim400 + 200)
-- 'setcstacklimit' works inside protected calls. (The new stack
-- limit is kept when 'pcall' returns.)
assert(pcall(function ()
assert(debug.setcstacklimit(400) == 600)
assert(check() <= lim400)
end))
assert(check() == lim400)
assert(debug.setcstacklimit(origlimit) == 400) -- restore original limit
end
print'OK'
+8 -5
View File
@@ -255,6 +255,10 @@ do -- test hook presence in debug info
end
-- hook table has weak keys
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
a = {}; L = nil
local glob = 1
local oldglob = glob
@@ -347,12 +351,12 @@ assert(g(0,0) == 30)
debug.sethook(nil);
assert(debug.gethook() == nil)
assert(not debug.gethook())
-- minimal tests for setuservalue/getuservalue
do
assert(debug.setuservalue(io.stdin, 10) == nil)
assert(not debug.setuservalue(io.stdin, 10))
local a, b = debug.getuservalue(io.stdin, 10)
assert(a == nil and not b)
end
@@ -410,7 +414,7 @@ end, "c")
a:f(1,2,3,4,5)
assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil)
assert(XX == 12)
assert(debug.gethook() == nil)
assert(not debug.gethook())
-- testing access to local variables in return hook (bug in 5.2)
@@ -802,8 +806,7 @@ assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and
-a == "unm" and #a == "len" and a&3 == "band")
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 == "shift" and
a>>1 == "shift")
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")
+15 -11
View File
@@ -18,7 +18,7 @@ end
local function doit (s)
local f, msg = load(s)
if f == nil then return msg end
if not f then return msg end
local cond, msg = pcall(f)
return (not cond) and msg
end
@@ -312,8 +312,8 @@ end
local function lineerror (s, l)
local err,msg = pcall(load(s))
local line = string.match(msg, ":(%d+):")
assert(tonumber(line) == l)
local line = tonumber(string.match(msg, ":(%d+):"))
assert(line == l or (not line and not l))
end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
@@ -359,7 +359,7 @@ local p = [[
g()
]]
X=3;lineerror((p), 3)
X=0;lineerror((p), nil)
X=0;lineerror((p), false)
X=1;lineerror((p), 2)
X=2;lineerror((p), 1)
@@ -510,7 +510,7 @@ checksyntax("a\1a = 1", "", "<\\1>", 1)
checksyntax("\255a = 1", "", "<\\255>", 1)
doit('I = load("a=9+"); a=3')
assert(a==3 and I == nil)
assert(a==3 and not I)
print('+')
lim = 1000
@@ -523,9 +523,13 @@ end
-- testing syntax limits
local function testrep (init, rep, close, repc)
local function testrep (init, rep, close, repc, finalresult)
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
assert(load(s)) -- 100 levels is OK
local res, msg = load(s)
assert(res) -- 100 levels is OK
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
@@ -534,14 +538,14 @@ end
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}")
testrep("local a; a=", "(", "2", ")")
testrep("local a; ", "a(", "2", ")")
testrep("return ", "(", "2", ")", 2)
testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
testrep("", "do ", "", " end")
testrep("", "while a do ", "", " end")
testrep("local a; ", "if a then else ", "", " end")
testrep("", "function foo () ", "", " end")
testrep("local a; a=", "a..", "a", "")
testrep("local a; a=", "a^", "a", "")
testrep("local a = ''; return ", "a..", "'a'", "", "a")
testrep("local a = 1; return ", "a^", "a", "", 1)
checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
+13 -2
View File
@@ -217,9 +217,16 @@ t.__le = function (a,b,c)
return a<=b, "dummy"
end
t.__eq = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.x end
if type(b) == 'table' then b = b.x end
return a == b, "dummy"
end
function Op(x) return setmetatable({x=x}, t) end
local function test ()
local function test (a, b, c)
assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
@@ -232,9 +239,13 @@ local function test ()
assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
assert(Op(1) == Op(1) and Op(1) ~= Op(2))
assert(Op('a') == Op('a') and Op('a') ~= Op('b'))
assert(a == a and a ~= b)
assert(Op(3) == c)
end
test()
test(Op(1), Op(2), Op(3))
-- test `partial order'
+79 -38
View File
@@ -125,7 +125,7 @@ do
-- closing file by scope
local F = nil
do
local <toclose> f = assert(io.open(file, "w"))
local f <close> = assert(io.open(file, "w"))
F = f
end
assert(tostring(F) == "file (closed)")
@@ -135,7 +135,7 @@ assert(os.remove(file))
do
-- test writing/reading numbers
local <toclose> f = assert(io.open(file, "w"))
local f <close> = assert(io.open(file, "w"))
f:write(maxint, '\n')
f:write(string.format("0X%x\n", maxint))
f:write("0xABCp-3", '\n')
@@ -144,7 +144,7 @@ do
f:write(string.format("0x%X\n", -maxint))
f:write("-0xABCp-3", '\n')
assert(f:close())
local <toclose> f = assert(io.open(file, "r"))
local f <close> = assert(io.open(file, "r"))
assert(f:read("n") == maxint)
assert(f:read("n") == maxint)
assert(f:read("n") == 0xABCp-3)
@@ -158,7 +158,7 @@ assert(os.remove(file))
-- testing multiple arguments to io.read
do
local <toclose> f = assert(io.open(file, "w"))
local f <close> = assert(io.open(file, "w"))
f:write[[
a line
another line
@@ -170,21 +170,21 @@ three
]]
local l1, l2, l3, l4, n1, n2, c, dummy
assert(f:close())
local <toclose> f = assert(io.open(file, "r"))
local f <close> = assert(io.open(file, "r"))
l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
assert(l1 == "a line" and l2 == "another line\n" and
n1 == 1234 and n2 == 3.45 and dummy == nil)
assert(f:close())
local <toclose> f = assert(io.open(file, "r"))
local f <close> = assert(io.open(file, "r"))
l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
and dummy == nil)
assert(f:close())
local <toclose> f = assert(io.open(file, "r"))
local f <close> = assert(io.open(file, "r"))
-- second item failing
l1, n1, n2, dummy = f:read("l", "n", "n", "l")
assert(l1 == "a line" and n1 == nil)
assert(l1 == "a line" and not n1)
end
assert(os.remove(file))
@@ -228,7 +228,7 @@ assert(f:read("n") == 0Xdeadbeefdeadbeef); assert(f:read(2) == "x\n")
assert(f:read("n") == 0x1.13aP3); assert(f:read(1) == "e")
do -- attempt to read too long number
assert(f:read("n") == nil) -- fails
assert(not f:read("n")) -- fails
local s = f:read("L") -- read rest of line
assert(string.find(s, "^00*\n$")) -- lots of 0's left
end
@@ -314,13 +314,13 @@ assert(io.read() == "fourth_line")
assert(io.read() == "") -- empty line
assert(io.read('n') == 3450)
assert(io.read(1) == '\n')
assert(io.read(0) == nil) -- end of file
assert(io.read(1) == nil) -- end of file
assert(io.read(30000) == nil) -- end of file
assert(not io.read(0)) -- end of file
assert(not io.read(1)) -- end of file
assert(not io.read(30000)) -- end of file
assert(({io.read(1)})[2] == undef)
assert(io.read() == nil) -- end of file
assert(not io.read()) -- end of file
assert(({io.read()})[2] == undef)
assert(io.read('n') == nil) -- end of file
assert(not io.read('n')) -- end of file
assert(({io.read('n')})[2] == undef)
assert(io.read('a') == '') -- end of file (OK for 'a')
assert(io.read('a') == '') -- end of file (OK for 'a')
@@ -356,7 +356,7 @@ assert(io.read(string.len(t)) == t)
assert(io.read(1) == ' ')
assert(io.read(0))
assert(io.read('a') == ';end of file\n')
assert(io.read(0) == nil)
assert(not io.read(0))
assert(io.close(io.input()))
@@ -364,7 +364,7 @@ assert(io.close(io.input()))
do
local function ismsg (m)
-- error message is not a code number
return (type(m) == "string" and tonumber(m) == nil)
return (type(m) == "string" and not tonumber(m))
end
-- read
@@ -393,7 +393,7 @@ assert(io.read"L" == "\n")
assert(io.read"L" == "\n")
assert(io.read"L" == "line\n")
assert(io.read"L" == "other")
assert(io.read"L" == nil)
assert(not io.read"L")
io.input():close()
local f = assert(io.open(file))
@@ -427,10 +427,12 @@ do -- testing closing file in line iteration
-- get the to-be-closed variable from a loop
local function gettoclose (lv)
lv = lv + 1
for i = 1, math.maxinteger do
local stvar = 0 -- to-be-closed is 4th state variable in the loop
for i = 1, 1000 do
local n, v = debug.getlocal(lv, i)
if n == "(for toclose)" then
return v
if n == "(for state)" then
stvar = stvar + 1
if stvar == 4 then return v end
end
end
end
@@ -460,7 +462,7 @@ end
-- test for multipe arguments in 'lines'
io.output(file); io.write"0123456789\n":close()
for a,b in io.lines(file, 1, 1) do
if a == "\n" then assert(b == nil)
if a == "\n" then assert(not b)
else assert(tonumber(a) == tonumber(b) - 1)
end
end
@@ -471,13 +473,13 @@ end
for a,b,c in io.lines(file, "a", 0, 1) do
if a == "" then break end
assert(a == "0123456789\n" and b == nil and c == nil)
assert(a == "0123456789\n" and not b and not c)
end
collectgarbage() -- to close file in previous iteration
io.output(file); io.write"00\n10\n20\n30\n40\n":close()
for a, b in io.lines(file, "n", "n") do
if a == 40 then assert(b == nil)
if a == 40 then assert(not b)
else assert(a == b - 10)
end
end
@@ -652,7 +654,7 @@ and the rest of the file
io.input(file)
local _,a,b,c,d,e,h,__ = io.read(1, 'n', 'n', 'l', 'l', 'l', 'a', 10)
assert(io.close(io.input()))
assert(_ == ' ' and __ == nil)
assert(_ == ' ' and not __)
assert(type(a) == 'number' and a==123.4 and b==-56e-2)
assert(d=='second line' and e=='third line')
assert(h==[[
@@ -704,7 +706,7 @@ if not _soft then
io.input():seek('set', 0)
y = io.read() -- huge line
assert(x == y..'\n'..io.read())
assert(io.read() == nil)
assert(not io.read())
io.close(io.input())
assert(os.remove(file))
x = nil; y = nil
@@ -773,11 +775,24 @@ assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
local t = os.time()
D = os.date("*t", t)
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
D.hour==%H and D.min==%M and D.sec==%S and
D.wday==%w+1 and D.yday==%j)]], t))()
local function checkDateTable (t)
_G.D = os.date("*t", t)
assert(os.time(D) == t)
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
D.hour==%H and D.min==%M and D.sec==%S and
D.wday==%w+1 and D.yday==%j)]], t))()
_G.D = nil
end
checkDateTable(os.time())
if not _port then
-- assume that time_t can represent these values
checkDateTable(0)
checkDateTable(1)
checkDateTable(1000)
checkDateTable(0x7fffffff)
checkDateTable(0x80000000)
end
checkerr("invalid conversion specifier", os.date, "%")
checkerr("invalid conversion specifier", os.date, "%9")
@@ -791,11 +806,24 @@ checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
checkerr("missing", os.time, {hour = 12}) -- missing date
if string.packsize("i") == 4 then -- 4-byte ints
checkerr("field 'year' is out-of-bound", os.time,
{year = -(1 << 31) + 1899, month = 1, day = 1})
end
if not _port then
-- test Posix-specific modifiers
assert(type(os.date("%Ex")) == 'string')
assert(type(os.date("%Oy")) == 'string')
-- test large dates (assume at least 4-byte ints and time_t)
local t0 = os.time{year = 1970, month = 1, day = 0}
local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1}
assert(t1 - t0 == (1 << 31) - 1)
t0 = os.time{year = 1970, month = 1, day = 1}
t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)}
assert(t1 - t0 == -(1 << 31))
-- test out-of-range dates (at least for Unix)
if maxint >= 2^62 then -- cannot do these tests in Small Lua
@@ -810,25 +838,37 @@ if not _port then
-- time_t has 8 bytes; an int year cannot represent a huge time
print(" 8-byte time_t")
checkerr("cannot be represented", os.date, "%Y", 2^60)
-- it should have no problems with year 4000
assert(tonumber(os.time{year=4000, month=1, day=1}))
-- this is the maximum year
assert(tonumber(os.time
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59}))
-- this is too much
checkerr("represented", os.time,
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60})
end
-- internal 'int' fields cannot hold these values
checkerr("field 'day' is out-of-bound", os.time,
{year = 0, month = 1, day = 2^32})
checkerr("field 'month' is out-of-bound", os.time,
{year = 0, month = -((1 << 31) + 1), day = 1})
checkerr("field 'year' is out-of-bound", os.time,
{year = (1 << 31) + 1900, month = 1, day = 1})
else -- 8-byte ints
-- assume time_t has 8 bytes too
print(" 8-byte time_t")
assert(tonumber(os.date("%Y", 2^60)))
-- but still cannot represent a huge year
checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
end
end
end
D = os.date("!*t", t)
load(os.date([[!assert(D.year==%Y and D.month==%m and D.day==%d and
D.hour==%H and D.min==%M and D.sec==%S and
D.wday==%w+1 and D.yday==%j)]], t))()
do
local D = os.date("*t")
local t = os.time(D)
@@ -842,6 +882,7 @@ do
assert(t == t1) -- if isdst is absent uses correct default
end
local D = os.date("*t")
t = os.time(D)
D.year = D.year-1;
local t1 = os.time(D)
+15 -17
View File
@@ -99,35 +99,28 @@ local function GC() GC1(); GC2() end
do
print("creating many objects")
local contCreate = 0
local limit = 5000
while contCreate <= limit do
for i = 1, limit do
local a = {}; a = nil
contCreate = contCreate+1
end
local a = "a"
contCreate = 0
while contCreate <= limit do
a = contCreate .. "b";
for i = 1, limit do
a = i .. "b";
a = string.gsub(a, '(%d%d*)', "%1 %1")
a = "a"
contCreate = contCreate+1
end
contCreate = 0
a = {}
function a:test ()
while contCreate <= limit do
load(string.format("function temp(a) return 'a%d' end", contCreate), "")()
assert(temp() == string.format('a%d', contCreate))
contCreate = contCreate+1
for i = 1, limit do
load(string.format("function temp(a) return 'a%d' end", i), "")()
assert(temp() == string.format('a%d', i))
end
end
@@ -166,9 +159,8 @@ print('long strings')
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
assert(string.len(x)==80)
s = ''
n = 0
k = math.min(300, (math.maxinteger // 80) // 2)
while n < k do s = s..x; n=n+1; j=tostring(n) end
for n = 1, k do s = s..x; j=tostring(n) end
assert(string.len(s) == k*80)
s = string.sub(s, 1, 10000)
s, i = string.gsub(s, '(%d%d%d%d)', '')
@@ -377,9 +369,10 @@ if T then
s[n] = i
end
warn("@on"); warn("@store")
collectgarbage()
assert(string.find(_WARN, "error in __gc metamethod"))
assert(string.match(_WARN, "@(.-)@") == "expected")
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = nil
for i = 8, 10 do assert(s[i]) end
for i = 1, 5 do
@@ -391,6 +384,7 @@ if T then
for i = 1, 10 do assert(s[i]) end
getmetatable(u).__gc = nil
warn("@normal")
end
print '+'
@@ -483,9 +477,12 @@ end
-- errors during collection
if T then
warn("@store")
u = setmetatable({}, {__gc = function () error "@expected error" end})
u = nil
collectgarbage()
assert(string.find(_WARN, "@expected error")); _WARN = nil
warn("@normal")
end
@@ -653,7 +650,7 @@ end
-- create several objects to raise errors when collected while closing state
if T then
local error, assert, find = error, assert, string.find
local error, assert, find, warn = error, assert, string.find, warn
local n = 0
local lastmsg
local mt = {__gc = function (o)
@@ -667,6 +664,7 @@ if T then
else
assert(lastmsg == _WARN) -- subsequent error messages are equal
end
warn("@store"); _WARN = nil
error"@expected warning"
end}
for i = 10, 1, -1 do
+1 -1
View File
@@ -258,7 +258,7 @@ do
::L2:: goto L3
::L1:: do
local <toclose> a = setmetatable({}, {__close = function () X = true end})
local a <close> = setmetatable({}, {__close = function () X = true end})
assert(X == nil)
if a then goto L2 end -- jumping back out of scope of 'a'
end
+5 -5
View File
@@ -11,17 +11,17 @@ CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
touch all
lib1.so: lib1.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
lib1.so: lib1.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib1.so lib1.c
lib11.so: lib11.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib11.so lib11.c
lib2.so: lib2.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib2.so lib2.c
lib21.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib21.so lib21.c
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c
+1 -1
View File
@@ -281,7 +281,7 @@ if os.setlocale("pt_BR") or os.setlocale("ptb") then
assert(" 0x.1 " + " 0x,1" + "-0X.1\t" == 0x0.1)
assert(tonumber"inf" == nil and tonumber"NAN" == nil)
assert(not tonumber"inf" and not tonumber"NAN")
assert(assert(load(string.format("return %q", 4.51)))() == 4.51)
+240 -89
View File
@@ -82,7 +82,7 @@ assert(c.a == nil)
f()
assert(c.a == 3)
-- old test for limits for special instructions (now just a generic test)
-- old test for limits for special instructions
do
local i = 2
local p = 4 -- p == 2^i
@@ -173,24 +173,57 @@ end
assert(x==20)
do -- constants
local a<const>, b, c<const> = 10, 20, 30
b = a + c + b -- 'b' is not constant
assert(a == 10 and b == 60 and c == 30)
local function checkro (name, code)
local st, msg = load(code)
local gab = string.format("attempt to assign to const variable '%s'", name)
assert(not st and string.find(msg, gab))
end
checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12")
checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11")
checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11")
checkro("z", [[
local a, z <const>, b = 10;
function foo() a = 20; z = 32; end
]])
checkro("var1", [[
local a, var1 <const> = 10;
function foo() a = 20; z = function () var1 = 12; end end
]])
end
print"testing to-be-closed variables"
local function stack(n) n = ((n == 0) or stack(n - 1)) end
local function func2close (f)
return setmetatable({}, {__close = f})
local function func2close (f, x, y)
local obj = setmetatable({}, {__close = f})
if x then
return x, obj, y
else
return obj
end
end
do
local a = {}
do
local <toclose> x = setmetatable({"x"}, {__close = function (self)
local b <close> = false -- not to be closed
local x <close> = setmetatable({"x"}, {__close = function (self)
a[#a + 1] = self[1] end})
local <toclose> y = func2close(function (self, err)
assert(err == nil); a[#a + 1] = "y"
end)
local w, y <close>, z = func2close(function (self, err)
assert(err == nil); a[#a + 1] = "y"
end, 10, 20)
local c <close> = nil -- not to be closed
a[#a + 1] = "in"
assert(w == 10 and z == 20)
end
a[#a + 1] = "out"
assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
@@ -199,11 +232,12 @@ end
do
local X = false
local closescope = func2close(function () stack(10); X = true end)
local x, closescope = func2close(function () stack(10); X = true end, 100)
assert(x == 100); x = 101; -- 'x' is not read-only
-- closing functions do not corrupt returning values
local function foo (x)
local <toclose> _ = closescope
local _ <close> = closescope
return x, X, 23
end
@@ -212,7 +246,7 @@ do
X = false
foo = function (x)
local <toclose> _ = closescope
local _<close> = closescope
local y = 15
return y
end
@@ -221,7 +255,7 @@ do
X = false
foo = function ()
local <toclose> x = closescope
local x <close> = closescope
return x
end
@@ -234,13 +268,13 @@ do
-- calls cannot be tail in the scope of to-be-closed variables
local X, Y
local function foo ()
local <toclose> _ = func2close(function () Y = 10 end)
local _ <close> = func2close(function () Y = 10 end)
assert(X == true and Y == nil) -- 'X' not closed yet
return 1,2,3
end
local function bar ()
local <toclose> _ = func2close(function () X = false end)
local _ <close> = func2close(function () X = false end)
X = true
do
return foo() -- not a tail call!
@@ -252,69 +286,187 @@ do
end
do -- errors in __close
local log = {}
local function foo (err)
local <toclose> x =
func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
local <toclose> x1 =
func2close(function (self, msg) log[#log + 1] = msg; end)
local <toclose> gc = func2close(function () collectgarbage() end)
local <toclose> y =
func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
local <toclose> z =
func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end)
if err then error(4) 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
local stat, msg = pcall(foo, false)
assert(msg == 3)
assert(log[1] == 10 and log[2] == 3 and log[3] == 3 and log[4] == 3
and #log == 4)
log = {}
local stat, msg = pcall(foo, true)
assert(msg == 4)
assert(log[1] == 4 and log[2] == 4 and log[3] == 4 and log[4] == 4
and #log == 4)
-- error in toclose in vararg function
function foo (...)
local <toclose> x123 = 10
end
local st, msg = pcall(foo)
assert(string.find(msg, "'x123'"))
end
do
local function endwarn ()
if not T then
warn("@on") -- back to normal
else
assert(_WARN == nil)
warn("@normal")
end
end
-- errors due to non-closable values
local function checkwarn (msg)
if T then
assert(string.find(_WARN, msg))
_WARN = nil -- 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 <toclose> x = 34
local x <close> =
func2close(function (self, msg)
assert(string.find(msg, "@z"))
error("@x")
end)
local x1 <close> =
func2close(function (self, msg)
checkwarn("@y")
assert(string.find(msg, "@z"))
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
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
error("@z")
end)
return 200
end
local stat, msg = pcall(foo, false)
assert(string.find(msg, "@z"))
checkwarn("@x")
-- original error not in __close
local function foo ()
local x <close> =
func2close(function (self, msg)
assert(msg == 4)
end)
local x1 <close> =
func2close(function (self, msg)
checkwarn("@y")
assert(msg == 4)
error("@x1")
end)
local gc <close> = func2close(function () collectgarbage() end)
local y <close> =
func2close(function (self, msg)
assert(msg == 4) -- error in body
checkwarn("@z")
error("@y")
end)
local first = true
local z <close> =
func2close(function (self, msg)
-- 'z' close is called once
assert(first and msg == 4)
first = false
error("@z")
end)
error(4) -- original error
end
local stat, msg = pcall(foo, true)
assert(msg == 4)
checkwarn("@x1") -- last error
-- error leaving a block
local function foo (...)
do
local x1 <close> =
func2close(function ()
checkwarn("@X")
error("@Y")
end)
local x123 <close> =
func2close(function ()
error("@X")
end)
end
os.exit(false) -- should not run
end
local st, msg = xpcall(foo, debug.traceback)
assert(string.match(msg, "^[^ ]* @X"))
assert(string.find(msg, "in metamethod 'close'"))
checkwarn("@Y")
-- error in toclose in vararg function
local function foo (...)
local x123 <close> = func2close(function () error("@x123") end)
end
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
do -- errors due to non-closable values
local function foo ()
local x <close> = {}
os.exit(false) -- should not run
end
local stat, msg = pcall(foo)
assert(not stat and string.find(msg, "variable 'x'"))
assert(not stat and
string.find(msg, "variable 'x' got a non%-closable value"))
-- with other errors, non-closable values are ignored
local function foo ()
local <toclose> x = 34
local <toclose> y = func2close(function () error(32) end)
local xyz <close> = setmetatable({}, {__close = print})
getmetatable(xyz).__close = nil -- remove metamethod
end
local stat, msg = pcall(foo)
assert(not stat and msg == 32)
assert(not stat and
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
end
if rawget(_G, "T") then
warn("@off")
-- memory error inside closing function
local function foo ()
local <toclose> y = func2close(function () T.alloccount() end)
local <toclose> x = setmetatable({}, {__close = function ()
local y <close> = func2close(function () T.alloccount() end)
local x <close> = setmetatable({}, {__close = function ()
T.alloccount(0); local x = {} -- force a memory error
end})
error(1000) -- common error inside the function's body
@@ -340,7 +492,7 @@ if rawget(_G, "T") then
end
local function test ()
local <toclose> x = enter(0) -- set a memory limit
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
assert(false) -- should not run
end
@@ -355,14 +507,14 @@ if rawget(_G, "T") then
-- repeat test with extra closing upvalues
local function test ()
local <toclose> xxx = func2close(function (self, msg)
local xxx <close> = func2close(function (self, msg)
assert(msg == "not enough memory");
error(1000) -- raise another error
end)
local <toclose> xx = func2close(function (self, msg)
local xx <close> = func2close(function (self, msg)
assert(msg == "not enough memory");
end)
local <toclose> x = enter(0) -- set a memory limit
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
os.exit(false) -- should not run
end
@@ -387,7 +539,7 @@ if rawget(_G, "T") then
local s = string.rep("a", lim)
-- concat this table needs two buffer resizes (one for each 's')
-- concat this table needs two buffer resizes (one for each 's')
local a = {s, s}
collectgarbage()
@@ -422,6 +574,8 @@ if rawget(_G, "T") then
print'+'
end
warn("@on")
end
@@ -432,9 +586,9 @@ do
local x = false
local y = false
local co = coroutine.wrap(function ()
local <toclose> xv = func2close(function () x = true end)
local xv <close> = func2close(function () x = true end)
do
local <toclose> yv = func2close(function () y = true end)
local yv <close> = func2close(function () y = true end)
coroutine.yield(100) -- yield doesn't close variable
end
coroutine.yield(200) -- yield doesn't close variable
@@ -451,36 +605,45 @@ end
do
prepwarn()
-- error in a wrapped coroutine raising errors when closing a variable
local x = 0
local co = coroutine.wrap(function ()
local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
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")
x = 0
local x = 0
local y = 0
co = coroutine.wrap(function ()
local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
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
end)
assert(co() == 100); assert(x == 0)
local st, msg = pcall(co); assert(x == 2)
local st, msg = pcall(co)
assert(x == 2 and y == 1) -- first close is called twice
-- should get first error raised
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
checkwarn("YYY")
endwarn()
end
-- a suspended coroutine should not close its variables when collected
local co
co = coroutine.wrap(function()
local <toclose> x = function () os.exit(false) end -- should not run
-- should not run
local x <close> = func2close(function () os.exit(false) end)
co = nil
coroutine.yield()
end)
@@ -517,27 +680,15 @@ do
end
assert(s == 35 and numopen == 0)
-- repeat test with '__open' metamethod instead of a function
local function open (x)
numopen = numopen + 1
local state = setmetatable({x},
{__close = function () numopen = numopen - 1 end})
return
function (t) -- iteraction function
t[1] = t[1] - 1
if t[1] > 0 then return t[1] end
end,
state,
nil,
state -- to-be-closed
end
local s = 0
for i in open(10) do
if (i < 5) then break end
s = s + i
for j in open(10) do
if i + j < 5 then goto endloop end
s = s + i
end
end
assert(s == 35 and numopen == 0)
::endloop::
assert(s == 375 and numopen == 0)
end
print('OK')
+54 -8
View File
@@ -43,6 +43,8 @@ local function getoutput ()
end
local function checkprogout (s)
-- expected result must end with new line
assert(string.sub(s, -1) == "\n")
local t = getoutput()
for line in string.gmatch(s, ".-\n") do
assert(string.find(t, line, 1, true))
@@ -219,6 +221,42 @@ assert(string.find(getoutput(), "error calling 'print'"))
RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
checkout("lua_debug> 1000lua_debug> ")
print("testing warnings")
-- no warnings by default
RUN('echo "io.stderr:write(1); warn[[XXX]]" | lua 2> %s', out)
checkout("1")
prepfile[[
warn("@allow") -- unknown control, ignored
warn("@off", "XXX", "@off") -- these are not control messages
warn("@off") -- this one is
warn("@on", "YYY", "@on") -- not control, but warn is off
warn("@off") -- keep it off
warn("@on") -- restart warnings
warn("", "@on") -- again, no control, real warning
warn("@on") -- keep it "started"
warn("Z", "Z", "Z") -- common warning
]]
RUN('lua -W %s 2> %s', prog, out)
checkout[[
Lua warning: @offXXX@off
Lua warning: @on
Lua warning: ZZZ
]]
prepfile[[
warn("@allow")
-- create two objects to be finalized when closing state
-- the errors in the finalizers must generate warnings
u1 = setmetatable({}, {__gc = function () error("XYZ") end})
u2 = setmetatable({}, {__gc = function () error("ZYX") end})
]]
RUN('lua -W %s 2> %s', prog, out)
checkprogout("ZYX)\nXYZ)\n")
-- test many arguments
prepfile[[print(({...})[30])]]
RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
@@ -292,7 +330,7 @@ debug = require"debug"
print(debug.getinfo(1).currentline)
]]
RUN('lua %s > %s', prog, out)
checkprogout('3')
checkprogout('3\n')
-- close Lua with an open file
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
@@ -320,15 +358,16 @@ NoRun("", "lua %s", prog) -- no message
-- to-be-closed variables in main chunk
prepfile[[
local <toclose> x = function (err)
assert(err == 120)
print("Ok")
end
local <toclose> e1 = function () error(120) end
local x <close> = setmetatable({},
{__close = function (self, err)
assert(err == nil)
print("Ok")
end})
local e1 <close> = setmetatable({}, {__close = function () print(120) end})
os.exit(true, true)
]]
RUN('lua %s > %s', prog, out)
checkprogout("Ok")
checkprogout("120\nOk\n")
-- remove temporary files
@@ -352,8 +391,15 @@ if T then -- test library?
NoRun("not enough memory", "env MEMLIMIT=100 lua")
-- testing 'warn'
warn("@store")
warn("@123", "456", "789")
assert(_WARN == "@123456789")
assert(_WARN == "@123456789"); _WARN = nil
warn("zip", "", " ", "zap")
assert(_WARN == "zip zap"); _WARN = nil
warn("ZIP", "", " ", "ZAP")
assert(_WARN == "ZIP ZAP"); _WARN = nil
warn("@normal")
end
do
+73 -62
View File
@@ -3,10 +3,10 @@
print("testing numbers and math lib")
local <const> minint = math.mininteger
local <const> maxint = math.maxinteger
local minint <const> = math.mininteger
local maxint <const> = math.maxinteger
local <const> intbits = math.floor(math.log(maxint, 2) + 0.5) + 1
local intbits <const> = math.floor(math.log(maxint, 2) + 0.5) + 1
assert((1 << intbits) == 0)
assert(minint == 1 << (intbits - 1))
@@ -39,7 +39,7 @@ do
end
assert(math.type(0) == "integer" and math.type(0.0) == "float"
and math.type("10") == nil)
and not math.type("10"))
local function checkerror (msg, f, ...)
@@ -270,7 +270,7 @@ else
end
do
local NaN = 0/0
local NaN <const> = 0/0
assert(not (NaN < 0))
assert(not (NaN > minint))
assert(not (NaN <= -9))
@@ -381,17 +381,17 @@ assert(tonumber(1/0) == 1/0)
-- 'tonumber' with strings
assert(tonumber("0") == 0)
assert(tonumber("") == nil)
assert(tonumber(" ") == nil)
assert(tonumber("-") == nil)
assert(tonumber(" -0x ") == nil)
assert(tonumber{} == nil)
assert(not tonumber(""))
assert(not tonumber(" "))
assert(not tonumber("-"))
assert(not tonumber(" -0x "))
assert(not tonumber{})
assert(tonumber'+0.01' == 1/100 and tonumber'+.01' == 0.01 and
tonumber'.01' == 0.01 and tonumber'-1.' == -1 and
tonumber'+1.' == 1)
assert(tonumber'+ 0.01' == nil and tonumber'+.e1' == nil and
tonumber'1e' == nil and tonumber'1.0e+' == nil and
tonumber'.' == nil)
assert(not tonumber'+ 0.01' and not tonumber'+.e1' and
not tonumber'1e' and not tonumber'1.0e+' and
not tonumber'.')
assert(tonumber('-012') == -010-2)
assert(tonumber('-1.2e2') == - - -120)
@@ -445,45 +445,45 @@ local function f (...)
end
end
assert(f(tonumber('fFfa', 15)) == nil)
assert(f(tonumber('099', 8)) == nil)
assert(f(tonumber('1\0', 2)) == nil)
assert(f(tonumber('', 8)) == nil)
assert(f(tonumber(' ', 9)) == nil)
assert(f(tonumber(' ', 9)) == nil)
assert(f(tonumber('0xf', 10)) == nil)
assert(not f(tonumber('fFfa', 15)))
assert(not f(tonumber('099', 8)))
assert(not f(tonumber('1\0', 2)))
assert(not f(tonumber('', 8)))
assert(not f(tonumber(' ', 9)))
assert(not f(tonumber(' ', 9)))
assert(not f(tonumber('0xf', 10)))
assert(f(tonumber('inf')) == nil)
assert(f(tonumber(' INF ')) == nil)
assert(f(tonumber('Nan')) == nil)
assert(f(tonumber('nan')) == nil)
assert(not f(tonumber('inf')))
assert(not f(tonumber(' INF ')))
assert(not f(tonumber('Nan')))
assert(not f(tonumber('nan')))
assert(f(tonumber(' ')) == nil)
assert(f(tonumber('')) == nil)
assert(f(tonumber('1 a')) == nil)
assert(f(tonumber('1 a', 2)) == nil)
assert(f(tonumber('1\0')) == nil)
assert(f(tonumber('1 \0')) == nil)
assert(f(tonumber('1\0 ')) == nil)
assert(f(tonumber('e1')) == nil)
assert(f(tonumber('e 1')) == nil)
assert(f(tonumber(' 3.4.5 ')) == nil)
assert(not f(tonumber(' ')))
assert(not f(tonumber('')))
assert(not f(tonumber('1 a')))
assert(not f(tonumber('1 a', 2)))
assert(not f(tonumber('1\0')))
assert(not f(tonumber('1 \0')))
assert(not f(tonumber('1\0 ')))
assert(not f(tonumber('e1')))
assert(not f(tonumber('e 1')))
assert(not f(tonumber(' 3.4.5 ')))
-- testing 'tonumber' for invalid hexadecimal formats
assert(tonumber('0x') == nil)
assert(tonumber('x') == nil)
assert(tonumber('x3') == nil)
assert(tonumber('0x3.3.3') == nil) -- two decimal points
assert(tonumber('00x2') == nil)
assert(tonumber('0x 2') == nil)
assert(tonumber('0 x2') == nil)
assert(tonumber('23x') == nil)
assert(tonumber('- 0xaa') == nil)
assert(tonumber('-0xaaP ') == nil) -- no exponent
assert(tonumber('0x0.51p') == nil)
assert(tonumber('0x5p+-2') == nil)
assert(not tonumber('0x'))
assert(not tonumber('x'))
assert(not tonumber('x3'))
assert(not tonumber('0x3.3.3')) -- two decimal points
assert(not tonumber('00x2'))
assert(not tonumber('0x 2'))
assert(not tonumber('0 x2'))
assert(not tonumber('23x'))
assert(not tonumber('- 0xaa'))
assert(not tonumber('-0xaaP ')) -- no exponent
assert(not tonumber('0x0.51p'))
assert(not tonumber('0x5p+-2'))
-- testing hexadecimal numerals
@@ -705,19 +705,19 @@ do -- testing floor & ceil
assert(eqT(math.tointeger(maxint), maxint))
assert(eqT(math.tointeger(maxint .. ""), maxint))
assert(eqT(math.tointeger(minint + 0.0), minint))
assert(math.tointeger(0.0 - minint) == nil)
assert(math.tointeger(math.pi) == nil)
assert(math.tointeger(-math.pi) == nil)
assert(not math.tointeger(0.0 - minint))
assert(not math.tointeger(math.pi))
assert(not math.tointeger(-math.pi))
assert(math.floor(math.huge) == math.huge)
assert(math.ceil(math.huge) == math.huge)
assert(math.tointeger(math.huge) == nil)
assert(not math.tointeger(math.huge))
assert(math.floor(-math.huge) == -math.huge)
assert(math.ceil(-math.huge) == -math.huge)
assert(math.tointeger(-math.huge) == nil)
assert(not math.tointeger(-math.huge))
assert(math.tointeger("34.0") == 34)
assert(math.tointeger("34.3") == nil)
assert(math.tointeger({}) == nil)
assert(math.tointeger(0/0) == nil) -- NaN
assert(not math.tointeger("34.3"))
assert(not math.tointeger({}))
assert(not math.tointeger(0/0)) -- NaN
end
@@ -767,7 +767,8 @@ assert(a == '10' and b == '20')
do
print("testing -0 and NaN")
local mz, z = -0.0, 0.0
local mz <const> = -0.0
local z <const> = 0.0
assert(mz == z)
assert(1/mz < 0 and 0 < 1/z)
local a = {[mz] = 1}
@@ -775,17 +776,18 @@ do
a[z] = 2
assert(a[z] == 2 and a[mz] == 2)
local inf = math.huge * 2 + 1
mz, z = -1/inf, 1/inf
local mz <const> = -1/inf
local z <const> = 1/inf
assert(mz == z)
assert(1/mz < 0 and 0 < 1/z)
local NaN = inf - inf
local NaN <const> = inf - inf
assert(NaN ~= NaN)
assert(not (NaN < NaN))
assert(not (NaN <= NaN))
assert(not (NaN > NaN))
assert(not (NaN >= NaN))
assert(not (0 < NaN) and not (NaN < 0))
local NaN1 = 0/0
local NaN1 <const> = 0/0
assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
local a = {}
assert(not pcall(rawset, a, NaN, 1))
@@ -813,9 +815,9 @@ end
-- low-level!! For the current implementation of random in Lua,
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6
do
-- all computations assume at most 32-bit integers
local h = 0x7a7040a5 -- higher half
local l = 0xa323c9d6 -- lower half
-- all computations should work with 32-bit integers
local h <const> = 0x7a7040a5 -- higher half
local l <const> = 0xa323c9d6 -- lower half
math.randomseed(1007)
-- get the low 'intbits' of the 64-bit expected result
@@ -838,7 +840,16 @@ do
assert(rand * 2^floatbits == res)
end
math.randomseed()
do
-- testing return of 'randomseed'
local x, y = math.randomseed()
local res = math.random(0)
x, y = math.randomseed(x, y) -- should repeat the state
assert(math.random(0) == res)
math.randomseed(x, y) -- again should repeat the state
assert(math.random(0) == res)
-- keep the random seed for following tests
end
do -- test random for floats
local randbits = math.min(floatbits, 64) -- at most 64 random bits
+35 -40
View File
@@ -49,33 +49,13 @@ if not T then
else --[
-- testing table sizes
local function log2 (x) return math.log(x, 2) end
local function mp2 (n) -- minimum power of 2 >= n
local mp = 2^math.ceil(log2(n))
local mp = 2^math.ceil(math.log(n, 2))
assert(n == 0 or (mp/2 < n and n <= mp))
return mp
end
local function fb (n)
local r, nn = T.int2fb(n)
assert(r < 256)
return nn
end
-- test fb function
for a = 1, 10000 do -- all numbers up to 10^4
local n = fb(a)
assert(a <= n and n <= a*1.125)
end
local a = 1024 -- plus a few up to 2 ^30
local lim = 2^30
while a < lim do
local n = fb(a)
assert(a <= n and n <= a*1.125)
a = math.ceil(a*1.3)
end
local function check (t, na, nh)
local a, h = T.querytab(t)
@@ -95,30 +75,44 @@ end
-- testing constructor sizes
local lim = 40
local s = 'return {'
for i=1,lim do
s = s..i..','
local s = s
for k=0,lim do
local t = load(s..'}', '')()
assert(#t == i)
check(t, fb(i), mp2(k))
s = string.format('%sa%d=%d,', s, k, k)
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
30, 31, 32, 33, 34, 500, 1000}
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
local arr = {"return {"}
for i = 1, sa do arr[1 + i] = "1," end -- build array part
for _, sh in ipairs(sizes) do -- 'sh' is size of the hash part
for j = 1, sh do -- build hash part
arr[1 + sa + j] = string.format('k%x=%d,', j, j)
end
arr[1 + sa + sh + 1] = "}"
local prog = table.concat(arr)
local f = assert(load(prog))
f() -- call once to ensure stack space
-- make sure table is not resized after being created
if sa == 0 or sh == 0 then
T.alloccount(2); -- header + array or hash part
else
T.alloccount(3); -- header + array part + hash part
end
local t = f()
T.alloccount();
assert(#t == sa)
check(t, sa, mp2(sh))
end
end
-- tests with unknown number of elements
local a = {}
for i=1,lim do a[i] = i end -- build auxiliary table
for k=0,lim do
local a = {table.unpack(a,1,k)}
assert(#a == k)
check(a, k, 0)
a = {1,2,3,table.unpack(a,1,k)}
check(a, k+3, 0)
assert(#a == k + 3)
for i=1,sizes[#sizes] do a[i] = i end -- build auxiliary table
for k in ipairs(sizes) do
local t = {table.unpack(a,1,k)}
assert(#t == k)
check(t, k, 0)
t = {1,2,3,table.unpack(a,1,k)}
check(t, k+3, 0)
assert(#t == k + 3)
end
@@ -677,7 +671,8 @@ collectgarbage()
local function f (n, p)
local t = {}; for i=1,p do t[i] = i*10 end
return function (_,n)
return function (_, n, ...)
assert(select("#", ...) == 0) -- no extra arguments
if n > 0 then
n = n-1
return n, table.unpack(t)
+54
View File
@@ -0,0 +1,54 @@
NAME=$1"-tests"
ln -s . $NAME
ln -s .. ltests
tar --create --gzip --no-recursion --file=$NAME.tar.gz \
$NAME/all.lua \
$NAME/api.lua \
$NAME/attrib.lua \
$NAME/big.lua \
$NAME/bitwise.lua \
$NAME/bwcoercion.lua \
$NAME/calls.lua \
$NAME/closure.lua \
$NAME/code.lua \
$NAME/constructs.lua \
$NAME/coroutine.lua \
$NAME/cstack.lua \
$NAME/db.lua \
$NAME/errors.lua \
$NAME/events.lua \
$NAME/files.lua \
$NAME/gc.lua \
$NAME/gengc.lua \
$NAME/goto.lua \
$NAME/heavy.lua \
$NAME/literals.lua \
$NAME/locals.lua \
$NAME/main.lua \
$NAME/math.lua \
$NAME/nextvar.lua \
$NAME/pm.lua \
$NAME/sort.lua \
$NAME/strings.lua \
$NAME/tpack.lua \
$NAME/utf8.lua \
$NAME/vararg.lua \
$NAME/verybig.lua \
$NAME/libs/makefile \
$NAME/libs/P1 \
$NAME/libs/lib1.c \
$NAME/libs/lib11.c \
$NAME/libs/lib2.c \
$NAME/libs/lib21.c \
$NAME/libs/lib22.c \
$NAME/ltests/ltests.h \
$NAME/ltests/ltests.c
\rm -I $NAME
\rm -I ltests
echo ${NAME}.tar.gz" created"
+11 -11
View File
@@ -28,10 +28,10 @@ a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end
assert(a == 9 and b == 11);
a,b = string.find('a\0a\0a\0a\0\0ab', 'b') -- last position
assert(a == 11 and b == 11)
assert(string.find('a\0a\0a\0a\0\0ab', 'b\0') == nil) -- check ending
assert(string.find('', '\0') == nil)
assert(not string.find('a\0a\0a\0a\0\0ab', 'b\0')) -- check ending
assert(not string.find('', '\0'))
assert(string.find('alo123alo', '12') == 4)
assert(string.find('alo123alo', '^12') == nil)
assert(not string.find('alo123alo', '^12'))
assert(string.match("aaab", ".*b") == "aaab")
assert(string.match("aaa", ".*a") == "aaa")
@@ -57,17 +57,17 @@ assert(f('aaa', 'ab*a') == 'aa')
assert(f('aba', 'ab*a') == 'aba')
assert(f('aaab', 'a+') == 'aaa')
assert(f('aaa', '^.+$') == 'aaa')
assert(f('aaa', 'b+') == nil)
assert(f('aaa', 'ab+a') == nil)
assert(not f('aaa', 'b+'))
assert(not f('aaa', 'ab+a'))
assert(f('aba', 'ab+a') == 'aba')
assert(f('a$a', '.$') == 'a')
assert(f('a$a', '.%$') == 'a$')
assert(f('a$a', '.$.') == 'a$a')
assert(f('a$a', '$$') == nil)
assert(f('a$b', 'a$') == nil)
assert(not f('a$a', '$$'))
assert(not f('a$b', 'a$'))
assert(f('a$a', '$') == '')
assert(f('', 'b*') == '')
assert(f('aaa', 'bb*') == nil)
assert(not f('aaa', 'bb*'))
assert(f('aaab', 'a-') == '')
assert(f('aaa', '^.-$') == 'aaa')
assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab')
@@ -101,7 +101,7 @@ end
assert(f1('alo alx 123 b\0o b\0o', '(..*) %1') == "b\0o b\0o")
assert(f1('axz123= 4= 4 34', '(.+)=(.*)=%2 %1') == '3= 4= 4 3')
assert(f1('=======', '^(=*)=%1$') == '=======')
assert(string.match('==========', '^([=]*)=%1$') == nil)
assert(not string.match('==========', '^([=]*)=%1$'))
local function range (i, j)
if i <= j then
@@ -135,7 +135,7 @@ print('+');
assert(string.match("alo xyzK", "(%w+)K") == "xyz")
assert(string.match("254 K", "(%d*)K") == "")
assert(string.match("alo ", "(%w*)$") == "")
assert(string.match("alo ", "(%w+)$") == nil)
assert(not string.match("alo ", "(%w+)$"))
assert(string.find("(álo)", "%(á") == 1)
local a, b, c, d, e = string.match("âlo alo", "^(((.).).* (%w*))$")
assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil)
@@ -209,7 +209,7 @@ assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4)
function isbalanced (s)
return string.find(string.gsub(s, "%b()", ""), "[()]") == nil
return not string.find(string.gsub(s, "%b()", ""), "[()]")
end
assert(isbalanced("(9 ((8))(\0) 7) \0\0 a b ()(c)() a"))
+24 -10
View File
@@ -3,8 +3,8 @@
print('testing strings and string library')
local <const> maxi = math.maxinteger
local <const> mini = math.mininteger
local maxi <const> = math.maxinteger
local mini <const> = math.mininteger
local function checkerror (msg, f, ...)
@@ -56,13 +56,13 @@ a,b = string.find("123456789", "345")
assert(string.sub("123456789", a, b) == "345")
assert(string.find("1234567890123456789", "345", 3) == 3)
assert(string.find("1234567890123456789", "345", 4) == 13)
assert(string.find("1234567890123456789", "346", 4) == nil)
assert(not string.find("1234567890123456789", "346", 4))
assert(string.find("1234567890123456789", ".45", -9) == 13)
assert(string.find("abcdefg", "\0", 5, 1) == nil)
assert(not string.find("abcdefg", "\0", 5, 1))
assert(string.find("", "") == 1)
assert(string.find("", "", 1) == 1)
assert(not string.find("", "", 2))
assert(string.find('', 'aaa', 1) == nil)
assert(not string.find('', 'aaa', 1))
assert(('alo(.)alo'):find('(.)', 1, 1) == 4)
assert(string.len("") == 0)
@@ -163,11 +163,19 @@ do -- tests for '%p' format
assert(string.format("%p", 4) == null)
assert(string.format("%p", print) ~= null)
assert(string.format("%p", coroutine.running()) ~= null)
assert(string.format("%p", {}) ~= string.format("%p", {}))
assert(string.format("%p", string.rep("a", 10)) ==
string.format("%p", string.rep("a", 10))) -- short strings
assert(string.format("%p", string.rep("a", 300)) ~=
string.format("%p", string.rep("a", 300))) -- long strings
do
local t1 = {}; local t2 = {}
assert(string.format("%p", t1) ~= string.format("%p", t2))
end
do -- short strings
local s1 = string.rep("a", 10)
local s2 = string.rep("a", 10)
assert(string.format("%p", s1) == string.format("%p", s2))
end
do -- long strings
local s1 = string.rep("a", 300); local s2 = string.rep("a", 300)
assert(string.format("%p", s1) ~= string.format("%p", s2))
end
assert(#string.format("%90p", {}) == 90)
end
@@ -250,6 +258,12 @@ do -- longest number that can be formatted
local s = string.format('%.99f', -(10^i))
assert(string.len(s) >= i + 101)
assert(tonumber(s) == -(10^i))
-- limit for floats
assert(10^38 < math.huge)
local s = string.format('%.99f', -(10^38))
assert(string.len(s) >= 38 + 101)
assert(tonumber(s) == -(10^38))
end
+2 -2
View File
@@ -30,8 +30,8 @@ local function checksyntax (s, t)
assert(assert(load(ts))() == s)
end
assert(utf8.offset("alo", 5) == nil)
assert(utf8.offset("alo", -4) == nil)
assert(not utf8.offset("alo", 5))
assert(not utf8.offset("alo", -4))
-- 'check' makes several tests over the validity of string 's'.
-- 't' is the list of codepoints of 's'.