Compare commits

...

202 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
Roberto Ierusalimschy
f39e8c06d6 Updated the documentation for the API function 'lua_gc' 2019-06-06 12:51:41 -03:00
Roberto Ierusalimschy
6aeaeb5656 Detail in makefile 2019-06-05 13:21:16 -03:00
Roberto Ierusalimschy
b4d5dff8ec Multiple errors in '__toclose' report the first one
When there are multiple errors when closing objects, the error
reported by the protected call is the first one, for two reasons:
First, other errors may be caused by this one;
second, the first error is handled in the original execution context,
and therefore has the full traceback.
2019-06-05 13:16:25 -03:00
Roberto Ierusalimschy
14edd364c3 Function 'warn' is vararg
Instead of a 'tocont' flag, the function 'warn' in Lua now receives all
message pieces as multiple arguments in a single call. Besides being
simpler to use, this implementation ensures that Lua code cannot create
unfinished warnings.
2019-06-04 11:22:21 -03:00
Roberto Ierusalimschy
514d942748 'coroutine.kill' renamed 'coroutine.close' 2019-06-03 13:11:20 -03:00
Roberto Ierusalimschy
4a3fd8488d bug in 5.4 alpha rc1: to-be-closed x vararg functions
Closing methods must be run before correcting 'ci->func' when exiting
a vararg function, to get correct debug information (e.g., in case of
errors).
2019-06-03 12:13:13 -03:00
Roberto Ierusalimschy
2c68e66570 Details
Several small changes from feedback on 5.4 alhpa rc1 (warnings,
typos in the manual, and the like)
2019-06-03 11:36:42 -03:00
Roberto Ierusalimschy
7d0f41df41 Improvements in 'testes/cstack.lua'
- tests show progress in real time, so that we can see maximum
stack levels even if test crashes.
- new test for recursion continuing into message handler.
2019-06-03 11:34:32 -03:00
Roberto Ierusalimschy
2b8b53864c Improvements in 'luaL_traceback'
'luaL_traceback' changed to use an aux buffer instead of concats.
This should reduce the quantity of garbage it generates (in the
form of intermediate strings) while producing a trackback.
It also added information about the number of levels skipped when
skipping levels in a trace.
2019-05-28 15:50:40 -03:00
Roberto Ierusalimschy
b293ae0577 Details
- new error message for "attempt to assign to const variable"
- note in the manual about compatibility options
- comments
- small changes in 'read_line' and 'pushstr'
2019-05-28 15:46:49 -03:00
Roberto Ierusalimschy
d9f40e3f6f First implementation for 'const' variables
A variable can be declared const, which means it cannot be assigned to,
with the syntax 'local <const> name = exp'.
2019-05-17 11:11:44 -03:00
Roberto Ierusalimschy
347d6961ac Define LUA_MAXUNSIGNED as a preprocessor constant
The previous definition of LUA_MAXUNSIGNED used a typecast,
making it unsuitable for constant expressions in the preprocessor.
2019-05-14 11:10:24 -03:00
Roberto Ierusalimschy
0b63d79b36 Details
- 'luaL_setfuncs' avoids creating closures for placeholders.
- Fixed some warnings about unused values in comma expressions.
- Comments.
2019-05-13 16:20:40 -03:00
Roberto Ierusalimschy
279c3a6961 A few changes in tests about number of bits in integers
- The preprocessor must work with at least 'long', and therefore must
do shifts of up to 31 bits correctly.
- Whenever possible, use unsigned types in shifts.
2019-05-13 16:17:21 -03:00
Roberto Ierusalimschy
49c42f3615 Some improvements in 'luaconf.h'
Added '#if !defined' in some definitions to allow external definitions;
more comments; other small changes.
2019-05-13 14:24:10 -03:00
Roberto Ierusalimschy
d881325c2f Flag for to-be-closed variables changed to '<toclose>'
The flag for to-be-closed variables was changed from '*toclose'
to '<toclose>'. Several people found confusing the old syntax and
the new one has a clear terminator, making it more flexible for
future changes.
2019-05-09 12:10:31 -03:00
Roberto Ierusalimschy
3f253f116e Test for dead coroutine moved to 'lua_resume'
The test for dead coroutines done in the 'coro' library was moved
to 'lua_resume', in the kernel, which already does other similar
tests.
2019-05-09 11:32:20 -03:00
Roberto Ierusalimschy
389116d8ab Coroutines do not unwind the stack in case of errors
Back to how it was, a coroutine does not unwind its stack in case of
errors (and therefore do not close its to-be-closed variables). This
allows the stack to be examined after the error. The program can
use 'coroutine.kill' to close the variables.

The function created by 'coroutine.wrap', however, closes the
coroutine's variables in case of errors, as it is impossible to examine
the stack any way.
2019-05-09 11:13:45 -03:00
Roberto Ierusalimschy
01bded3d8c File 'lib2-v2.so' generated from its own source
Instead of being a copy of 'lib2.so', 'lib2-v2.so' has its own source
file ('lib22.c'), so that the test can distinguish both libraries.
2019-05-03 10:36:19 -03:00
Roberto Ierusalimschy
7c5786479c A few more improvements in 'luaO_pushvfstring'
- 'L' added to the 'BuffFS' structure
- '%c' does not handle control characters (it is not its business.
This now is done by the lexer, who is the one in charge of that
kind of errors.)
- avoid the direct use of 'l_sprintf' in the Lua kernel
2019-05-03 10:18:44 -03:00
Roberto Ierusalimschy
b14609032c Avoid the creation of too many strings in 'package'
Both when setting a path and searching for a file ('searchpath'),
this commit reduces the number of intermediate strings created
in Lua.
(For setting a path the change is not relevant, because this is
done only twice when loading the module. Anyway, it is a nice example
of how to use auxlib buffers to manipulate strings in the C API.)
2019-05-03 10:14:25 -03:00
Roberto Ierusalimschy
b36e26f51b Some more small improvements to 'luaO_pushvfstring'
Details:
- counter 'pushed' moved to the struct 'BuffFS'
- new auxiliar function 'getbuff' to build strings directly on
the buffer.
2019-04-26 11:24:39 -03:00
Roberto Ierusalimschy
969b8c1f14 Fixed bug with to-be-closed variables in base C level
To-be-closed variables in C use 'ci.nresults' to code that there is
a variable to be closed in that function. The intialization of the
base C level (the one "running" when calling API functions outside
any Lua call) did not initialize 'ci.nresults', creating (correct)
warnings in valgrind.
2019-04-26 11:13:01 -03:00
Roberto Ierusalimschy
c65605151c New function 'luaL_addgsub'
Added a new function 'luaL_addgsub', similar to 'luaL_gsub' but that
adds its result directly to a preexisting buffer, avoiding the creation
of one extra intermediate string. Also added two simple macros,
'luaL_bufflen' and 'luaL_buffaddr', to query the current length
and the contents address of a buffer.
2019-04-24 14:41:41 -03:00
Roberto Ierusalimschy
3da34a5fa7 Revamp of 'lua_pushfstring' / 'luaO_pushvfstring'
The function 'luaO_pushvfstring' now uses an internal buffer to
concatenate small strings, instead of pushing all pieces on the
stack. This avoids the creation of several small Lua strings for each
piece of the result. (For instance, a format like "n: '%d'" used to
create three intermediate strings: "n: '", the numeral, and "'".
Now it creates none.)
2019-04-24 14:01:20 -03:00
Roberto Ierusalimschy
20b161e285 Small correction in test about 'isdst'
The field 'isdst' can be false, so we cannot test its absence with
'if not D.isdst'; we must compare with nil for a correct test.
2019-04-22 12:31:29 -03:00
Roberto Ierusalimschy
ed2872cd3b 'require' returns where module was found
The function 'require' returns the *loader data* as a second result.
For file searchers, this data is the path where they found the module.
2019-04-17 14:57:29 -03:00
Roberto Ierusalimschy
2d3f095448 Avoid using large buffers in 'string.format'
The result of "string.format("%.99f", -1e308) is 410 characters long,
but all other formats have much smaller limits (at most 99 plus a fex
extras). This commit avoids 'string.format' asking for a buffer
~400 chars large when ~100 will do.
2019-04-12 11:48:24 -03:00
Roberto Ierusalimschy
b0810c51c3 Small optimizations in 'string.gsub'
Avoid creating extra strings when possible:

- avoid creating new resulting string when subject was not modified
(instead, return the subject itself);

- avoid creating strings representing the captured substrings when
handling replacements like '%1' (instead, add the substring directly
to the buffer).
2019-04-11 11:29:16 -03:00
Roberto Ierusalimschy
a93e014447 Added an optional parameter to 'coroutine.isyieldable' 2019-04-10 13:23:14 -03:00
Roberto Ierusalimschy
8ba4523ccc 'print' does not call 'tostring' to format its arguments 2019-04-10 12:58:14 -03:00
Roberto Ierusalimschy
979ad95eb1 Thorough revision of the reference manual 2019-04-10 12:41:56 -03:00
Roberto Ierusalimschy
0f028b9008 Corrected tests around non-portable 'isdst' in dates
The field 'isdst' in date tables may not be present; portable tests
should not assume it is.
2019-04-09 18:44:13 -03:00
Roberto Ierusalimschy
65d1aa7a77 Syntax should not allow numbers touching identifiers
Code like 'a = 1print()' should not be accepted.
2019-04-09 18:40:39 -03:00
Roberto Ierusalimschy
8004798b03 Fixed wrong error message in 'return math.seed(0)'
Bug introduced in commit 28d829c8: OP_TAILCALL might raise an
error without saving 'pc'. (This commit also fixes a detail in
'testes/uf8.lua'.)
2019-04-04 16:31:24 -03:00
Roberto Ierusalimschy
5ca1075b71 Added field 'srclen' to structure 'lua_Debug'
This new field gets the length of 'source' in the same structure.
Unlike the other strings in that structure, 'source' can be
relatively large, and Lua already has its length readily available.
2019-04-04 11:45:26 -03:00
Roberto Ierusalimschy
38425e0692 Avoid moving the collector while in 'GCSenteratomic' state
The 'GCSenteratomic' is just an auxiliary state for transitioning
to 'GCSatomic'. All GC traversals should be done either on the
'GCSpropagate' state or the 'GCSatomic' state.
2019-04-01 14:22:07 -03:00
Roberto Ierusalimschy
d12262068d Small optimizations in range checks
Checks of the form '1 <= x && x <= M' were rewritten in the form
'(unsigned)x - 1 < (unsigned)M', which is usually more efficient.
(Other similar checks have similar translations.) Although
some compilers do these optimizations, that does not happen
for all compilers or all cases.
2019-03-27 14:56:10 -03:00
Roberto Ierusalimschy
0443ad9e28 LUAI_MAXCCALLS renamed LUAI_MAXCSTACK
The limit LUAI_MAXCCALLS was renamed LUAI_MAXCSTACK, which better
represents its meaning. Moreover, its definition was moved to
'luaconf.h', given its importance now that Lua does not use
a "stackless" implementation.
2019-03-25 14:12:06 -03:00
Roberto Ierusalimschy
f9b0cf0e2e Year in copyright notice updated to 2019 2019-03-25 14:00:09 -03:00
Roberto Ierusalimschy
7ceb2154ed Fixed small bugs/issues
- In 'readutf8esc' (llex.c), the overflow check must be done before
shifting the accumulator. It was working because tests were using
64-bit longs. Failed with 32-bit longs.

- In OP_FORPREP (lvm.c), avoid negating an unsigned value. Visual
Studio gives a warning for that operation, despite being well
defined in ISO C.

- In 'luaV_execute' (lvm.c), 'cond' can be defined only when needed,
like all other variables.
2019-03-25 10:38:56 -03:00
Roberto Ierusalimschy
23e6bac8a0 Keep correct type for immediate operands in comparisons
When calling metamethods for things like 'a < 3.0', which generates
the opcode OP_LTI, the C register tells that the operand was
converted to an integer, so that it can be corrected to float when
calling a metamethod.

This commit also includes some other stuff:
- file 'onelua.c' added to the project
- opcode OP_PREPVARARG renamed to OP_VARARGPREP
- comparison opcodes rewritten through macros
2019-03-22 13:37:17 -03:00
Roberto Ierusalimschy
682054920d Details in the implementation of the integer 'for' loop
Changed some implementation details; in particular, it is back using
an internal variable to keep the index, with the control variable
being only a copy of that internal variable. (The direct use of
the control variable demands a check of its type for each access,
which offsets the gains from the use of a single variable.)
2019-03-21 16:01:55 -03:00
Roberto Ierusalimschy
f53eabeed8 Small changes in the header of binary files
- LUAC_VERSION is equal to LUA_VERSION_NUM, and it is stored
as an int.

- 'sizeof(int)' and 'sizeof(size_t)' removed from the header, as
the binary format does not depend on these sizes. (It uses its
own serialization for unsigned integer values.)
2019-03-19 15:31:08 -03:00
Roberto Ierusalimschy
39bb3cf242 Name 'nonstrict' in the UTF-8 library changed to 'lax'
It is not a good idea to use negative words to describe boolean
values. (When we negate that boolean we create a double negative...)
2019-03-19 11:15:49 -03:00
Roberto Ierusalimschy
9b37a4695e New semantics for the integer 'for' loop
The numerical 'for' loop over integers now uses a precomputed counter
to control its number of iteractions. This change eliminates several
weird cases caused by overflows (wrap-around) in the control variable.
(It also ensures that every integer loop halts.)

Also, the special opcodes for the usual case of step==1 were removed.
(The new code is already somewhat complex for the usual case,
but efficient.)
2019-03-19 10:53:18 -03:00
Roberto Ierusalimschy
1e0c73d5b6 Changes in the validation of UTF-8
All UTF-8 encoding functionality (including the escape
sequence '\u') accepts all values from the original UTF-8
specification (with sequences of up to six bytes).

By default, the decoding functions in the UTF-8 library do not
accept invalid Unicode code points, such as surrogates. A new
parameter 'nonstrict' makes them accept all code points up to
(2^31)-1, as in the original UTF-8 specification.
2019-03-15 13:14:17 -03:00
Roberto Ierusalimschy
8fa4f1380b Finalizers must be callable
Non-function __gc metamethods are not ignored; if present, the
metamethod will be called even if it is not a function.
2019-03-14 15:53:42 -03:00
Roberto Ierusalimschy
b56d4e570a Changes in the warning system
- The warning functions get an extra parameter that tells whether
message is to be continued (instead of using end-of-lines as a signal).

- The user data for the warning function is a regular value, instead
of a writable slot inside the Lua state.
2019-03-14 15:30:54 -03:00
Roberto Ierusalimschy
9eca305e75 'math.randomseed()' sets a somewhat random seed
When called with no arguments, 'math.randomseed' uses time and ASLR
to generate a somewhat random seed. the initial seed when Lua starts
is generated this way.
2019-03-13 14:47:48 -03:00
Roberto Ierusalimschy
c5feac2b5e Strings inside Lua are not fully aligned
Removed code to ensure that strings inside Lua (as returned by
'lua_tolstring') always start in fully aligned addresses.
Since version 5.3 the documentation does not ensure that.
2019-03-13 14:14:40 -03:00
Roberto Ierusalimschy
dfebe439db New conversion specifier '%p' for 'string.format'
The call 'string.format("%p", val)' gives a Lua equivalent to the
C API function 'lua_topointer'.
2019-03-13 14:04:01 -03:00
Roberto Ierusalimschy
cf71a5ddc7 Details
Several small improvements (code style, warnings, comments, more tests),
in particular:

- 'lua_topointer' extended to handle strings
- raises an error in 'string.format("%10q")' ('%q' with modifiers)
- in the manual for 'string.format', the term "option" replaced by
  "conversion specifier" (the term used by the C standard)
2019-03-13 13:16:53 -03:00
Roberto Ierusalimschy
2c32bff609 After a "bad collections", avoid switching back back to generational
After a major bad collection (one that collects too few objects),
next collection will be major again. In that case, avoid switching
back to generational mode (as it will have to switch again to
incremental to do next major collection).
2019-01-30 11:44:42 -02:00
Roberto Ierusalimschy
264659bd53 Optional 'init' argument to 'string.gmatch'
The function 'string.gmatch' now has an optional 'init' argument,
similar to 'string.find' and 'string.match'. Moreover, there was
some reorganization in the manipulation of indices in the string
library.
This commit also includes small janitorial work in the manual
and in comments in the interpreter loop.
2019-01-08 14:22:32 -02:00
Roberto Ierusalimschy
4ace93ca65 No more to-be-closed functions
To-be-closed variables must contain objects with '__toclose'
metamethods (or nil). Functions were removed for several reasons:

* Functions interact badly with sandboxes. If a sandbox raises
an error to interrupt a script, a to-be-closed function still
can hijack control and continue running arbitrary sandboxed code.

* Functions interact badly with coroutines. If a coroutine yields
and is never resumed again, its to-be-closed functions will never
run. To-be-closed objects, on the other hand, will still be closed,
provided they have appropriate finalizers.

* If you really need a function, it is easy to create a dummy
object to run that function in its '__toclose' metamethod.

This comit also adds closing of variables in case of panic.
2019-01-04 13:09:47 -02:00
Roberto Ierusalimschy
c6f7181e91 No more LUA_ERRGCMM errors
Errors in finalizers (__gc metamethods) are never propagated.
Instead, they generate a warning.
2019-01-01 12:14:56 -02:00
Roberto Ierusalimschy
437a5b07d4 Added a warning system to Lua
The warning system is just a way for Lua to emit warnings, messages
to the programmer that do not interfere with the running program.
2018-12-28 15:42:34 -02:00
Roberto Ierusalimschy
ba7da13ec5 Changes in the control of C-stack overflow
* unification of the 'nny' and 'nCcalls' counters;
  * external C functions ('lua_CFunction') count more "slots" in
    the C stack (to allow for their possible use of buffers)
  * added a new test script specific for C-stack overflows. (Most
    of those tests were already present, but concentrating them
    in a single script easies the task of checking whether
    'LUAI_MAXCCALLS' is adequate in a system.)
2018-12-27 14:32:29 -02:00
Roberto Ierusalimschy
da37ac9c78 Detail
Slightly better error message for invalid conversions in 'string.format'.
2018-12-27 14:19:53 -02:00
Roberto Ierusalimschy
662506476b 'all' script automatically 'make's everything
The script 'all', to run all tests, automatically ensures that the
Lua interpreter and the test C libraries (in 'testes/libs/') are
updated with any changes in 'luaconf.h'.
2018-12-19 13:15:14 -02:00
Roberto Ierusalimschy
5d7dec5520 Added directory 'testes/libs/P1' to the repository
This directory is used for some tests. As standard Lua has no command
to create directories, it must be present before running tests.
2018-12-17 15:19:56 -02:00
Roberto Ierusalimschy
af6d9f3116 Details
A few details in the makefile and in the manual. (In particular,
it updates the dependency lists in the makefile.)
2018-12-17 13:56:22 -02:00
Roberto Ierusalimschy
2258f3133b Added file 'testes/heavy.lua'
This file is not part of the regular tests. It tests error conditions
that demand too much memory or too much time to create:

* string with too many characters
* control structure with body too large
* chunk with too many lines
* identifier with too many characters
* chunks with too many instructions
* function with too many constants
* too many strings internalized
* table with too many entries

In machines with limited memory (less than 150 GB), many tests run up
to a "not enough memory" error. We need some memory (~256 GB) to
run all tests up to their intrinsic limits.
2018-12-14 13:49:02 -02:00
Roberto Ierusalimschy
57f5b81da9 Bug: Long brackets with a huge number of '=' causes overflow
A long bracket with too many equal signs can overflow the 'int' used for
the counting and some arithmetic done on the value. Changing the counter
to 'size_t' avoids that. (Because what is counted goes to a buffer, an
overflow in the counter will first raise a buffer-overflow error.)
2018-12-14 13:12:01 -02:00
Roberto Ierusalimschy
fdc25a1ebf New functions 'lua_resetthread' and 'coroutine.kill'
New functions to reset/kill a thread/coroutine, mainly (only?) to
close any pending to-be-closed variable. ('lua_resetthread' also
allows a thread to be reused...)
2018-12-13 13:07:53 -02:00
Roberto Ierusalimschy
3b06f983ae Details
- in 'luaB_tonumber', do not need to "checkany" when argument
is a number.

- in 'lua_resume', the call to 'luaD_rawrunprotected' cannot return
a status equal to -1.
2018-12-11 11:54:14 -02:00
Roberto Ierusalimschy
51316f9df7 'math.rand()' uses higher bits to produce float value
The call 'math.rand()' converts the higher bits of the internal unsigned
integer random to a float, instead of its lower bits. That ensures that
Lua compiled with different float precisions always generates equal (up
to the available precision) random numbers when given the same seed.
2018-12-11 11:34:47 -02:00
Roberto Ierusalimschy
46beca5bed Better error messages for some polymorphic functions
New auxiliary functions/macros 'luaL_argexpected'/'luaL_typeerror'
ease the creation of error messages such as

  bad argument #2 to 'setmetatable' (nil or table expected, got boolean)

(The novelty being the "got boolean" part...)
2018-12-10 13:46:03 -02:00
Roberto Ierusalimschy
28d829c867 Calls cannot be tail in the scope of a to-be-closed variable
A to-be-closed variable must be closed when a block ends, so even
a 'return foo()' cannot directly returns the results of 'foo'; the
function must close the scope before returning.
2018-12-04 15:01:42 -02:00
Roberto Ierusalimschy
6d04537ea6 A to-be-closed variable must have a closable value (or be nil)
It is an error for a to-be-closed variable to have a non-closable
non-nil value when it is being closed. This situation does not seem to
be useful and often hints to an error. (Particularly in the C API, it is
easy to change a to-be-closed index by mistake.)
2018-11-29 16:02:44 -02:00
Roberto Ierusalimschy
7696c6474f Auxiliary buffer cannot close box with 'lua_remove'
To remove a to-be-closed variable from the stack in the C API a
function must use 'lua_settop' or 'lua_pop'. Previous implementation of
'luaL_pushresult' was not closing the box. (This commit also added
tests to check that box is being closed "as soon as possible".)
2018-11-26 14:16:17 -02:00
Roberto Ierusalimschy
7e63d3da02 Some bugs with stack reallocation by 'luaF_close'
(Long time without testing with '-DHARDSTACKTESTS'...)
With the introduction of to-be-closed variables, calls to 'luaF_close'
can move the stack, but some call sites where keeping pointers to the
stack without correcting them.
2018-11-24 11:59:15 -02:00
Roberto Ierusalimschy
84e32ad2eb Added opcodes for arithmetic with K operands
Added opcodes for all seven arithmetic operators with K operands
(that is, operands that are numbers in the array of constants of
the function). They cover the cases of constant float operands
(e.g., 'x + .0.0', 'x^0.5') and large integer operands (e.g.,
'x % 10000').
2018-11-23 12:23:45 -02:00
Roberto Ierusalimschy
35296e1fde Details
comments and other janitorial work.
2018-11-22 13:56:04 -02:00
Roberto Ierusalimschy
bb0185b196 Documentation for to-be-closed variables 2018-11-13 14:33:14 -02:00
Roberto Ierusalimschy
d40cd315f5 Visibility of non-API functions changed to "internal"
The visibility for functions marked as LUAI_FUNC was changed from
"hidden" to "internal". These functions cannot be called from
outside the Lua kernel, and "internal" visibility offers more
chances for optimizations.
2018-11-13 13:58:46 -02:00
Roberto Ierusalimschy
8cb84210ab String buffer using to-be-closed variable
The string buffers in the C API now mark their boxes as to-be-closed
variables, to release their buffers in case of errors.
2018-11-13 13:50:33 -02:00
Roberto Ierusalimschy
5fda30b4f9 'lua_toclose' gets the index to be closed as an argument
Sometimes it is useful to mark to-be-closed an index that is not
at the top of the stack (e.g., if the value to be closed came from
a function call returning multiple values).
2018-11-12 14:15:50 -02:00
Roberto Ierusalimschy
9eafe9c053 New implementation for 'luaL_addvalue'
The function 'luaL_addvalue' (from the buffer system) was rewritten
so that it does not change the position of the box (if present)
in the stack.
2018-11-09 17:03:52 -02:00
Roberto Ierusalimschy
7f6f70853c To-be-closed variable in 'for' loop separated from the state
The variable to be closed in a generic 'for' loop now is the
4th value produced in the loop initialization, instead of being
the loop state (the 2nd value produced). That allows a loop to
use a state with a '__toclose' metamethod but do not close it.
(As an example, 'f:lines()' might use the file 'f' as a state
for the loop, but it should not close the file when the loop ends.)
2018-11-07 14:42:05 -02:00
Roberto Ierusalimschy
b8fed93215 New syntax for to-be-closed variables
The new syntax is <local *toclose x = f()>. The mark '*' allows other
attributes to be added later without the need of new keywords; it
also allows better error messages.  The API function was also renamed
('lua_tobeclosed' -> 'lua_toclose').
2018-11-07 10:03:05 -02:00
Roberto Ierusalimschy
5e76a4fd31 New macros for arithmetic/bitwise operations in 'luaV_execute'
The repetitive code of the arithmetic and bitwise operators in
the main iterpreter loop was moved to appropriate macros.
(As a detail, the function 'luaV_div' was renamed 'luaV_idiv',
as it does an "integer division" (floor division).
2018-11-05 16:10:42 -02:00
Roberto Ierusalimschy
e8c779736f Removed internal cache for closures
The mechanism of "caching the last closure created for a prototype to
try to reuse it the next time a closure for that prototype is created"
was removed. There are several reasons:

- It is hard to find a natural example where this cache has a measurable
impact on performance.

- Programmers already perceive closure creation as something slow,
so they tend to avoid it inside hot paths. (Any case where the cache
could reuse a closure can be rewritten predefining the closure in some
variable and using that variable.)

- The implementation was somewhat complex, due to a bad interaction
with the generational collector. (Typically, new closures are new,
while prototypes are old. So, the cache breaks the invariant that
old objects should not point to new ones.)
2018-11-01 13:21:00 -03:00
Roberto Ierusalimschy
2fc6b55dae Removed resource-related "emergency collections"
New to-be-closed variables is a better way to ensure the proper release
of resources.
2018-10-31 16:25:29 -03:00
Roberto Ierusalimschy
947a372f58 State in generic 'for' acts as a to-be-closed variable
The implicit variable 'state' in a generic 'for' is marked as a
to-be-closed variable, so that the state will be closed as soon
as the loop ends, no matter how.

Taking advantage of this new facility, the call 'io.lines(filename)'
now returns the open file as a second result. Therefore,
an iteraction like 'for l in io.lines(name)...' will close the
file even when the loop ends with a break or an error.
2018-10-31 14:54:45 -03:00
Roberto Ierusalimschy
e073cbc2e5 Better error messages for invalid operands in numeric 'for'
"Better" and similar to error messages for invalid function arguments.
  *old message: 'for' limit must be a number
  *new message: bad 'for' limit (number expected, got table)
2018-10-30 15:46:56 -03:00
Roberto Ierusalimschy
2316ec4c24 Back with optimization for 'if cond then goto'
Statements like 'if cond then goto label' generate code so that the
jump in the 'if' goes directly to the given label. This optimization
cannot be done when the jump is backwards leaving the scope of some
variable, as it cannot add the needed 'close' instruction. (The jumps
were already generated by the 'if'.)

This commit also added 'likely'/'unlikely' for tests for errors in
the parser, and it changed the way breaks outside loops are detected.
(Now they are detected like other goto's with undefined labels.)
2018-10-30 15:04:19 -03:00
Roberto Ierusalimschy
a006514ea1 Big revamp in the implmentation of labels/gotos
Added restriction that, when a label is created, there cannot be
another label with the same name visible. That allows backward goto's
to be resolved when they are read. Backward goto's get a close if
they jump out of the scope of some variable; labels get a close only
if previous goto to it jumps out of the scope of some upvalue.
2018-10-29 14:26:48 -03:00
Roberto Ierusalimschy
6e9b719694 More uniformity in code generation for 'for' loops
Added new instruction 'OP_TFORPREP' to prepare a generic for loop.
Currently it is equivalent to a jump (but with a format 'iABx',
similar to other for-loop preparing instructions), but soon it will
be the place to create upvalues for closing loop states.
2018-10-26 10:38:50 -03:00
Roberto Ierusalimschy
34840301b5 To-be-closed variables in the C API 2018-10-25 15:30:15 -03:00
Roberto Ierusalimschy
41c800b352 Closing methods should not interfere with returning values
A closing method cannot be called in its own stack slot, as there may
be returning values in the stack after that slot, and the call would
corrupt those values. Instead, the closing method must be copied to the
top of the stack to be called.

Moreover, even when a function returns no value, its return istruction
still has to have its position (which will set the stack top) after
the local variables, otherwise a closing method might corrupt another
not-yet-called closing method.
2018-10-25 12:50:20 -03:00
Roberto Ierusalimschy
0a9aca56ca Added a '__close' metamethod to file handles 2018-10-23 13:57:25 -03:00
Roberto Ierusalimschy
ea1322ef54 Detail: bad assertion in 'luaM_free_' 2018-10-23 12:58:38 -03:00
Roberto Ierusalimschy
6a4b9bb2b4 Removed extra information from RCS keyword strings in tests
Version numbers and dates (mostly wrong) from RCS keyword strings
removed from all test files; only the file name are kept.
2018-10-22 15:20:07 -03:00
Roberto Ierusalimschy
7c8146d556 Small improvements in the manual 2018-10-22 15:02:09 -03:00
Roberto Ierusalimschy
c90176f969 Complete implementation of to-be-closed variables 2018-10-22 14:55:51 -03:00
Roberto Ierusalimschy
3c7dc52909 Handling of memory errors when creating to-be-closed upvalues 2018-10-18 16:15:09 -03:00
Roberto Ierusalimschy
bd96330d03 First "complete" implementation of to-be-closed variables
Still missing:
- handling of memory errors when creating upvalue (must run closing
method all the same)
- interaction with coroutines
2018-10-17 10:44:42 -03:00
Roberto Ierusalimschy
4cd1f4aac0 Towards "to closed" local variables
Start of the implementation of "scoped variables" or "to be closed"
variables, local variables whose '__close' (or themselves) are called
when they go out of scope. This commit implements the syntax, the
opcode, and the creation of the corresponding upvalue, but it still
does not call the finalizations when the variable goes out of scope
(the most important part).

Currently, the syntax is 'local scoped name = exp', but that will
probably change.
2018-10-08 10:42:07 -03:00
Roberto Ierusalimschy
b114c7d487 Added "cost" for the use of C stack by a coroutine invocation.
Resuming a coroutine uses more C stack than other operations (such as
function calls or recursive syntax). So, to avoid stack overflow
in recursive coroutine invocations, either LUAI_MAXCCALLS must be
too small or a coroutine invocation must "pay" a higher price.
New constant LUAL_COROCSTK ("COROutine C STaK") defines how much
is this price.
2018-09-11 14:24:14 -03:00
Roberto Ierusalimschy
9cbf17b0f1 Details (comments) 2018-09-11 08:39:12 -03:00
Roberto Ierusalimschy
5382a22e0e Corrections in the implementation of '%' for floats.
The multiplication (m*b) used to test whether 'm' is non-zero and
'm' and 'b' have different signs can underflow for very small numbers,
giving a wrong result. The use of explicit comparisons solves this
problem. This commit also adds several new tests for '%' (both for
floats and for integers) to exercise more corner cases, such as
very large and very small values.
2018-08-28 12:36:58 -03:00
Roberto Ierusalimschy
8c8a91f2ef Deprecated the emulation of '__le' using '__lt'
As hinted in the manual for Lua 5.3, the emulation of the metamethod
for '__le' using '__le' has been deprecated. It is slow, complicates
the logic, and it is easy to avoid this emulation by defining a proper
'__le' function.

Moreover, often this emulation was used wrongly, with a programmer
assuming that an order is total when it is not (e.g., NaN in
floating-point numbers).
2018-08-24 10:17:54 -03:00
Roberto Ierusalimschy
f99509581e Removed extra information from RCS keyword strings
Version numbers and dates (mostly wrong) from RCS keyword strings
removed from all source files; only the file name are kept.
2018-08-23 14:26:12 -03:00
Roberto Ierusalimschy
3dcd04ad61 details
Minor optimizations in 'lvm.c'. (Not exactly optimizations, but more
chances for optimizations.)
2018-08-17 15:53:39 -03:00
Roberto Ierusalimschy
faaf7e481f Removed use of 'rl_inhibit_completion' in 'lua.c'
Some old systems (e.g., Mac OS X 10.4) do not define
'rl_inhibit_completion', even when line history is available.
Anyway, the user can configure this option externally, using '~/.inputrc'.
2018-08-16 14:38:05 -03:00
Roberto Ierusalimschy
3d838f635c Added "emergency collection" to 'io.tmpfile' and 'os.tmpname'
These operations also can give errors for lack of resources, so they
also will try "emergency collections" in case of resource errors.
Because there are now two libraries with that kind of handling,
'resourcetryagain' was moved to the auxiliary library to be shared
by the libraries.
2018-07-27 15:50:53 -03:00
Roberto Ierusalimschy
aa4c5cf190 Added directory to test file names in '$Id:'
From the point of view of 'git', all names are relative to the root
directory of the project. So, file names in '$Id:' also should be
relative to that directory: the proper name for test file 'all.lua'
is 'testes/all.lua'.
2018-07-25 15:31:04 -03:00
Roberto Ierusalimschy
b47f2cd068 Small improvements in the manual 2018-07-25 14:56:42 -03:00
Roberto Ierusalimschy
e885dee5ab File operations try an "emergency collection" when failing
If a file operation fails do to lack of resources (too many open
files or not enough memory), it does a full garbage collection and
tries the operation again. Lack of resources are "too many open
files" (process wise and system wise) and "not enough memory".
The code is full of '#if's because error codes are not part
of the standard ISO C.
2018-07-25 11:44:46 -03:00
Roberto Ierusalimschy
ccae0f5aad Comments about OLD0/OLD1 ages
Improved the comments in file 'lgc.c' explaining the roles of "ages"
OLD0 and OLD1 in the generacional collector.
2018-07-18 11:43:45 -03:00
Roberto Ierusalimschy
2e297d6ab3 Fixed bug in generational collection of userdata
During generational collection, a userdatum must become gray and
go to a gray list after being traversed (like tables), so that
'correctgraylist' can handle it to its next stage.

This commit also added minimum tests for the generational collector,
including one that would detect this bug.
2018-07-13 15:43:02 -03:00
Roberto Ierusalimschy
fb18346ddd Avoid using 'int' for UTF-8 values
An 'int' may have only 16 bits, so it may not be big enough for UTF-8
values. The new type 'utfint' (in the utf8 library) ensures at least
21 bits for those values.
2018-07-12 15:56:44 -03:00
Roberto Ierusalimschy
96f9643f33 Bug: wrong 'nCcalls' when resuming a coroutine
The counter 'nCcalls' now includes the number of CallInfo structures
pre-allocated (so that these "potential" C calls can be made without
checking 'nCcalls'). So, when copying this value from a thread to
another, in 'lua_resume', it must be corrected to the number of
CallInfo structures in the thread being resumed.
2018-07-11 16:11:50 -03:00
Roberto Ierusalimschy
84058b1506 Added definition for LUA_VERSION_RELEASE_NUM
LUA_VERSION_RELEASE_NUM is set to the release number of the Lua
interpreter (e.g., 5.4.0 becomes the integer 50400).
2018-07-11 13:17:46 -03:00
Roberto Ierusalimschy
4d5de1c1fb Fixed bug in line info. when using 'not' operator
When creating code for a jump on a 'not' condition, the code generator
was removing an instruction (the OP_NOT) without adjusting its
corresponding line information.

This fix also added tests for this case and extra functionality in
the test library to debug line info. structures.
2018-07-11 12:53:23 -03:00
Roberto Ierusalimschy
9a825f6bb9 In tests of opcodes, avoid coercion in bitwise operation 2018-07-10 14:55:16 -03:00
Roberto Ierusalimschy
941b189d98 Improvements in the manual
- More precise use of 'argument' x 'parameter'.
- Clarification about what the lexer considers 'letter', 'space',
and 'digit'.
2018-07-10 13:48:19 -03:00
Roberto Ierusalimschy
21f663d29f Added missing $Id$ to file 'ljumptab.h' 2018-07-10 13:40:30 -03:00
Roberto Ierusalimschy
626cf0581b Generational mode may wait longer after a major collection
When Lua is building large long-duration structures, frequent small
minor collections just waste time. Trying to avoid this, the
collector will do a larger pause after a major collection when it
does not collect enough garbage (which is a hint that memory is
being used for long-lasting objects).
2018-07-09 14:22:09 -03:00
Roberto Ierusalimschy
ccf6d098f6 'searchpath' creates less temporary strings
When creating error messages, package loaders may create dozens of
temporary strings (one or more for each tried template). This change
reduces the number of these strings, and avoid creating some of
them if the search is successful.
2018-07-09 13:29:08 -03:00
Roberto Ierusalimschy
de2caf7ee4 Bit-library file removed from the project (as it was deprecated)
This commit only removed the file 'lbitlib.c' from the project; the
makefile already was not using it.
2018-07-09 12:54:51 -03:00
Roberto Ierusalimschy
b08c9079c5 Opcode names moved to a new header file
The array with the names of the opcodes was moved to a header file
('lopnames.h'), as it is not used by the Lua kernel. Files that need
that array ('luac.c' and 'ltests.c') include the header file to get
a private (static) copy.
2018-07-09 12:50:51 -03:00
Roberto Ierusalimschy
06e08c6d05 Fixed bug in OP_IDIVI
Opocode was using 'luai_numdiv' (float division) instead of
'luai_numidiv' (integer division).
2018-07-09 12:41:24 -03:00
Roberto Ierusalimschy
7c519dfbd0 Added manual and tests for version 5.4-w2 2018-07-09 12:33:01 -03:00
109 changed files with 29201 additions and 2526 deletions

9
all Executable file
View File

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

19
bugs
View File

@@ -4017,6 +4017,25 @@ patch = [[
--[=[
Bug{
what = [[Long brackets with a huge number of '=' overflow some
internal buffer arithmetic]],
report = [[Marco, 2018/12/12]],
since = [[5.1]],
fix = nil,
example = [[
local eqs = string.rep("=", 0x3ffffffe)
local code = "return [" .. eqs .. "[a]" .. eqs .. "]"
print(#assert(load(code))())
]],
patch = [[
]]
}
]=]
--[=[
Bug{

110
lapi.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lapi.c,v 2.294 2018/06/15 19:31:22 roberto Exp roberto $
** $Id: lapi.c $
** Lua API
** See Copyright Notice in lua.h
*/
@@ -38,7 +38,11 @@ const char lua_ident[] =
/* test for a valid index */
/*
** Test for a valid index.
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
** However, it covers the most common cases in a faster way.
*/
#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue)
@@ -166,18 +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) {
api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
while (L->top < (func + 1) + idx)
setnilvalue(s2v(L->top++));
L->top = (func + 1) + idx;
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) */
}
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);
}
@@ -408,8 +417,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
}
LUA_API void *lua_touserdata (lua_State *L, int idx) {
const TValue *o = index2value(L, idx);
static void *touserdata (const TValue *o) {
switch (ttype(o)) {
case LUA_TUSERDATA: return getudatamem(uvalue(o));
case LUA_TLIGHTUSERDATA: return pvalue(o);
@@ -418,23 +426,37 @@ LUA_API void *lua_touserdata (lua_State *L, int idx) {
}
LUA_API void *lua_touserdata (lua_State *L, int idx) {
const TValue *o = index2value(L, idx);
return touserdata(o);
}
LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
const TValue *o = index2value(L, idx);
return (!ttisthread(o)) ? NULL : thvalue(o);
}
/*
** Returns a pointer to the internal representation of an object.
** Note that ANSI C does not allow the conversion of a pointer to
** function to a 'void*', so the conversion here goes through
** a 'size_t'. (As the returned pointer is only informative, this
** conversion should not be a problem.)
*/
LUA_API const void *lua_topointer (lua_State *L, int idx) {
const TValue *o = index2value(L, idx);
switch (ttypetag(o)) {
case LUA_TTABLE: return hvalue(o);
case LUA_TLCL: return clLvalue(o);
case LUA_TCCL: return clCvalue(o);
case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o)));
case LUA_TTHREAD: return thvalue(o);
case LUA_TUSERDATA: return getudatamem(uvalue(o));
case LUA_TLIGHTUSERDATA: return pvalue(o);
default: return NULL;
case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA:
return touserdata(o);
default: {
if (iscollectable(o))
return gcvalue(o);
else
return NULL;
}
}
}
@@ -917,8 +939,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
api_checknelems(L, 1);
o = index2value(L, idx);
api_check(L, ttisfulluserdata(o), "full userdata expected");
if (!(0 < n && n <= uvalue(o)->nuvalue))
res = 0;
if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
else {
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1));
luaC_barrierback(L, gcvalue(o), s2v(L->top - 1));
@@ -950,7 +972,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
checkresults(L, nargs, nresults);
func = L->top - (nargs+1);
if (k != NULL && L->nny == 0) { /* need to prepare continuation? */
if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
L->ci->u.c.k = k; /* save continuation */
L->ci->u.c.ctx = ctx; /* save context */
luaD_call(L, func, nresults); /* do the call */
@@ -998,7 +1020,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
func = savestack(L, o);
}
c.func = L->top - (nargs+1); /* function to be called */
if (k == NULL || L->nny > 0) { /* no continuation or no yieldable? */
if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
c.nresults = nresults; /* do a 'conventional' protected call */
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
}
@@ -1135,22 +1157,21 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
break;
}
case LUA_GCGEN: {
int oldmode = g->gckind;
int minormul = va_arg(argp, int);
int majormul = va_arg(argp, int);
res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
if (minormul != 0)
g->genminormul = minormul;
if (majormul != 0)
setgcparam(g->genmajormul, majormul);
luaC_changemode(L, KGC_GEN);
res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC;
break;
}
case LUA_GCINC: {
int oldmode = g->gckind;
int pause = va_arg(argp, int);
int stepmul = va_arg(argp, int);
int stepsize = va_arg(argp, int);
res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
if (pause != 0)
setgcparam(g->gcpause, pause);
if (stepmul != 0)
@@ -1158,7 +1179,6 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
if (stepsize != 0)
g->gcstepsize = stepsize;
luaC_changemode(L, KGC_INC);
res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC;
break;
}
default: res = -1; /* invalid option */
@@ -1201,6 +1221,22 @@ LUA_API int lua_next (lua_State *L, int idx) {
}
LUA_API void lua_toclose (lua_State *L, int idx) {
int nresults;
StkId o;
lua_lock(L);
o = index2stack(L, idx);
nresults = L->ci->nresults;
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
"marked index below or equal new one");
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
L->ci->nresults = codeNresults(nresults); /* mark it */
lua_assert(hastocloseCfunc(L->ci->nresults));
lua_unlock(L);
}
LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L);
api_checknelems(L, n);
@@ -1245,6 +1281,22 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
}
void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
lua_lock(L);
G(L)->ud_warn = ud;
G(L)->warnf = f;
lua_unlock(L);
}
void lua_warning (lua_State *L, const char *msg, int tocont) {
lua_lock(L);
luaE_warning(L, msg, tocont);
lua_unlock(L);
}
LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
Udata *u;
lua_lock(L);
@@ -1264,7 +1316,8 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
switch (ttypetag(fi)) {
case LUA_TCCL: { /* C closure */
CClosure *f = clCvalue(fi);
if (!(1 <= n && n <= f->nupvalues)) return NULL;
if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues)))
return NULL; /* 'n' not in [1, f->nupvalues] */
*val = &f->upvalue[n-1];
if (owner) *owner = obj2gco(f);
return "";
@@ -1273,11 +1326,12 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
LClosure *f = clLvalue(fi);
TString *name;
Proto *p = f->p;
if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
return NULL; /* 'n' not in [1, p->sizeupvalues] */
*val = f->upvals[n-1]->v;
if (owner) *owner = obj2gco(f->upvals[n - 1]);
name = p->upvalues[n-1].name;
return (name == NULL) ? "(*no name)" : getstr(name);
return (name == NULL) ? "(no name)" : getstr(name);
}
default: return NULL; /* not a closure */
}

29
lapi.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lapi.h,v 2.10 2017/11/01 18:20:48 roberto Exp $
** $Id: lapi.h $
** Auxiliary functions from Lua API
** See Copyright Notice in lua.h
*/
@@ -11,14 +11,37 @@
#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");}
#define adjustresults(L,nres) \
{ if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
/*
** 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")
/*
** To reduce the overhead of returning from C functions, the presence of
** to-be-closed variables in these functions is coded in the CallInfo's
** field 'nresults', in a way that functions with no to-be-closed variables
** with zero, one, or "all" wanted results have no overhead. Functions
** with other number of wanted results, as well as functions with
** variables to be closed, have an extra check.
*/
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
#define codeNresults(n) (-(n) - 3)
#endif

245
lauxlib.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lauxlib.c,v 1.294 2018/02/27 18:47:32 roberto Exp roberto $
** $Id: lauxlib.c $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
@@ -27,6 +27,12 @@
#include "lauxlib.h"
#if !defined(MAX_SIZET)
/* maximum value for size_t */
#define MAX_SIZET ((size_t)(~(size_t)0))
#endif
/*
** {======================================================
** Traceback
@@ -40,8 +46,8 @@
/*
** search for 'objidx' in table at index -1.
** return 1 + string at top if find a good name.
** Search for 'objidx' in table at index -1. ('objidx' must be an
** absolute index.) Return 1 + string at top if it found a good name.
*/
static int findfield (lua_State *L, int objidx, int level) {
if (level == 0 || !lua_istable(L, -1))
@@ -54,10 +60,10 @@ static int findfield (lua_State *L, int objidx, int level) {
return 1;
}
else if (findfield(L, objidx, level - 1)) { /* try recursively */
lua_remove(L, -2); /* remove table (but keep name) */
lua_pushliteral(L, ".");
lua_insert(L, -2); /* place '.' between the two names */
lua_concat(L, 3);
/* stack: lib_name, lib_table, field_name (top) */
lua_pushliteral(L, "."); /* place '.' between the two names */
lua_replace(L, -3); /* (in the slot occupied by table) */
lua_concat(L, 3); /* lib_name.field_name */
return 1;
}
}
@@ -80,8 +86,8 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
lua_pushstring(L, name + 3); /* push name without prefix */
lua_remove(L, -2); /* remove original name */
}
lua_copy(L, -1, top + 1); /* move name to proper place */
lua_pop(L, 2); /* remove pushed values */
lua_copy(L, -1, top + 1); /* copy name to proper place */
lua_settop(L, top + 1); /* remove table "loaded" an name copy */
return 1;
}
else {
@@ -124,32 +130,37 @@ static int lastlevel (lua_State *L) {
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
const char *msg, int level) {
luaL_Buffer b;
lua_Debug ar;
int top = lua_gettop(L);
int last = lastlevel(L1);
int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
if (msg)
lua_pushfstring(L, "%s\n", msg);
luaL_checkstack(L, 10, NULL);
lua_pushliteral(L, "stack traceback:");
int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
luaL_buffinit(L, &b);
if (msg) {
luaL_addstring(&b, msg);
luaL_addchar(&b, '\n');
}
luaL_addstring(&b, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (n1-- == 0) { /* too many levels? */
lua_pushliteral(L, "\n\t..."); /* add a '...' */
level = last - LEVELS2 + 1; /* and skip to last ones */
if (limit2show-- == 0) { /* too many levels? */
int n = last - level - LEVELS2 + 1; /* number of levels to skip */
lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n);
luaL_addvalue(&b); /* add warning about skip */
level += n; /* and skip to last levels */
}
else {
lua_getinfo(L1, "Slnt", &ar);
lua_pushfstring(L, "\n\t%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
lua_pushliteral(L, " in ");
if (ar.currentline <= 0)
lua_pushfstring(L, "\n\t%s: in ", ar.short_src);
else
lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline);
luaL_addvalue(&b);
pushfuncname(L, &ar);
luaL_addvalue(&b);
if (ar.istailcall)
lua_pushliteral(L, "\n\t(...tail calls...)");
lua_concat(L, lua_gettop(L) - top);
luaL_addstring(&b, "\n\t(...tail calls...)");
}
}
lua_concat(L, lua_gettop(L) - top);
luaL_pushresult(&b);
}
/* }====================================================== */
@@ -179,7 +190,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
}
static int typeerror (lua_State *L, int arg, const char *tname) {
int luaL_typeerror (lua_State *L, int arg, const char *tname) {
const char *msg;
const char *typearg; /* name for the type of the actual argument */
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
@@ -194,7 +205,7 @@ static int typeerror (lua_State *L, int arg, const char *tname) {
static void tag_error (lua_State *L, int arg, int tag) {
typeerror(L, arg, lua_typename(L, tag));
luaL_typeerror(L, arg, lua_typename(L, tag));
}
@@ -238,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
@@ -280,16 +291,17 @@ 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 */
}
}
/* }====================================================== */
/*
** {======================================================
** Userdata's metatable manipulation
@@ -332,7 +344,7 @@ LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {
LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
void *p = luaL_testudata(L, ud, tname);
if (p == NULL) typeerror(L, ud, tname);
luaL_argexpected(L, p != NULL, ud, tname);
return p;
}
@@ -463,10 +475,8 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
lua_Alloc allocf = lua_getallocf(L, &ud);
UBox *box = (UBox *)lua_touserdata(L, idx);
void *temp = allocf(ud, box->box, box->bsize, newsize);
if (temp == NULL && newsize > 0) { /* allocation error? */
resizebox(L, idx, 0); /* free buffer */
if (temp == NULL && newsize > 0) /* allocation error? */
luaL_error(L, "not enough memory for buffer allocation");
}
box->box = temp;
box->bsize = newsize;
return temp;
@@ -479,16 +489,20 @@ static int boxgc (lua_State *L) {
}
static void *newbox (lua_State *L, size_t newsize) {
static const luaL_Reg boxmt[] = { /* box metamethods */
{"__gc", boxgc},
{"__close", boxgc},
{NULL, NULL}
};
static void newbox (lua_State *L) {
UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
box->box = NULL;
box->bsize = 0;
if (luaL_newmetatable(L, "_UBOX*")) { /* creating metatable? */
lua_pushcfunction(L, boxgc);
lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */
}
if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */
luaL_setfuncs(L, boxmt, 0); /* set its metamethods */
lua_setmetatable(L, -2);
return resizebox(L, -1, newsize);
}
@@ -500,34 +514,60 @@ static void *newbox (lua_State *L, size_t newsize) {
/*
** returns a pointer to a free area with at least 'sz' bytes
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
** bytes.
*/
LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
lua_State *L = B->L;
if (B->size - B->n < sz) { /* not enough space? */
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
size_t newsize = B->size * 2; /* double buffer size */
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
return luaL_error(B->L, "buffer too large");
if (newsize < B->n + sz) /* double is not big enough? */
newsize = B->n + sz;
return newsize;
}
/*
** Returns a pointer to a free area with at least 'sz' bytes in buffer
** 'B'. 'boxidx' is the relative position in the stack where the
** buffer's box is or should be.
*/
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
if (B->size - B->n >= sz) /* enough space? */
return B->b + B->n;
else {
lua_State *L = B->L;
char *newbuff;
size_t newsize = B->size * 2; /* double buffer size */
if (newsize - B->n < sz) /* not big enough? */
newsize = B->n + sz;
if (newsize < B->n || newsize - B->n < sz)
luaL_error(L, "buffer too large");
size_t newsize = newbuffsize(B, sz);
/* create larger buffer */
if (buffonstack(B))
newbuff = (char *)resizebox(L, -1, newsize);
else { /* no buffer yet */
newbuff = (char *)newbox(L, newsize);
if (buffonstack(B)) /* buffer already has a box? */
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
else { /* no box yet */
lua_pushnil(L); /* reserve slot for final result */
newbox(L); /* create a new box */
/* move box (and slot) to its intended position */
lua_rotate(L, boxidx - 1, 2);
lua_toclose(L, boxidx);
newbuff = (char *)resizebox(L, boxidx, newsize);
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
}
B->b = newbuff;
B->size = newsize;
return newbuff + B->n;
}
return &B->b[B->n];
}
/*
** returns a pointer to a free area with at least 'sz' bytes
*/
LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
return prepbuffsize(B, sz, -1);
}
LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */
char *b = luaL_prepbuffsize(B, l);
char *b = prepbuffsize(B, l, -1);
memcpy(b, s, l * sizeof(char));
luaL_addsize(B, l);
}
@@ -543,8 +583,8 @@ LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
lua_State *L = B->L;
lua_pushlstring(L, B->b, B->n);
if (buffonstack(B)) {
resizebox(L, -2, 0); /* delete old buffer */
lua_remove(L, -2); /* remove its header from the stack */
lua_copy(L, -1, -3); /* move string to reserved slot */
lua_pop(L, 2); /* pop string and box (closing the box) */
}
}
@@ -555,14 +595,23 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
}
/*
** 'luaL_addvalue' is the only function in the Buffer system where the
** box (if existent) is not on the top of the stack. So, instead of
** calling 'luaL_addlstring', it replicates the code using -2 as the
** last argument to 'prepbuffsize', signaling that the box is (or will
** be) bellow the string being added to the buffer. (Box creation can
** trigger an emergency GC, so we should not remove the string from the
** stack before we have the space guaranteed.)
*/
LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
lua_State *L = B->L;
size_t l;
const char *s = lua_tolstring(L, -1, &l);
if (buffonstack(B))
lua_insert(L, -2); /* put value below buffer */
luaL_addlstring(B, s, l);
lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */
size_t len;
const char *s = lua_tolstring(L, -1, &len);
char *b = prepbuffsize(B, len, -2);
memcpy(b, s, len * sizeof(char));
luaL_addsize(B, len);
lua_pop(L, 1); /* pop string */
}
@@ -576,7 +625,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
luaL_buffinit(L, B);
return luaL_prepbuffsize(B, sz);
return prepbuffsize(B, sz, -1);
}
/* }====================================================== */
@@ -854,9 +903,13 @@ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
if (l->func == NULL) /* place holder? */
lua_pushboolean(L, 0);
else {
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
}
lua_setfield(L, -(nup + 2), l->name);
}
lua_pop(L, nup); /* remove upvalues */
@@ -907,18 +960,24 @@ LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
}
LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
const char *r) {
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
const char *p, const char *r) {
const char *wild;
size_t l = strlen(p);
luaL_Buffer b;
luaL_buffinit(L, &b);
while ((wild = strstr(s, p)) != NULL) {
luaL_addlstring(&b, s, wild - s); /* push prefix */
luaL_addstring(&b, r); /* push replacement in place of pattern */
luaL_addlstring(b, s, wild - s); /* push prefix */
luaL_addstring(b, r); /* push replacement in place of pattern */
s = wild + l; /* continue after 'p' */
}
luaL_addstring(&b, s); /* push last suffix */
luaL_addstring(b, s); /* push last suffix */
}
LUALIB_API const char *luaL_gsub (lua_State *L, const char *s,
const char *p, const char *r) {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addgsub(&b, s, p, r);
luaL_pushresult(&b);
return lua_tostring(L, -1);
}
@@ -942,9 +1001,45 @@ static int panic (lua_State *L) {
}
/*
** Emit a warning. '*warnstate' means:
** 0 - warning system is off;
** 1 - ready to start a new message;
** 2 - previous message is to be continued.
*/
static void warnf (void *ud, const char *message, int tocont) {
int *warnstate = (int *)ud;
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */
if (strcmp(message, "@off") == 0)
*warnstate = 0;
else if (strcmp(message, "@on") == 0)
*warnstate = 1;
return;
}
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) /* not the last part? */
*warnstate = 2; /* to be continued */
else { /* last part */
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
*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) lua_atpanic(L, &panic);
if (L) {
int *warnstate; /* space for warning state */
lua_atpanic(L, &panic);
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*warnstate = 0; /* default is warnings off */
lua_setwarnf(L, warnf, warnstate);
}
return L;
}

View File

@@ -1,5 +1,5 @@
/*
** $Id: lauxlib.h,v 1.133 2017/06/27 18:32:49 roberto Exp roberto $
** $Id: lauxlib.h $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
@@ -19,6 +19,8 @@
#define LUA_GNAME "_G"
typedef struct luaL_Buffer luaL_Buffer;
/* extra error code for 'luaL_loadfilex' */
#define LUA_ERRFILE (LUA_ERRERR+1)
@@ -48,6 +50,7 @@ LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
@@ -77,6 +80,7 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
/* predefined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
@@ -97,8 +101,10 @@ LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r);
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
const char *p, const char *r);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
const char *p, const char *r);
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
@@ -125,6 +131,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
#define luaL_argcheck(L, cond,arg,extramsg) \
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
#define luaL_argexpected(L,cond,arg,tname) \
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
@@ -143,13 +153,17 @@ 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
** =======================================================
*/
typedef struct luaL_Buffer {
struct luaL_Buffer {
char *b; /* buffer address */
size_t size; /* buffer size */
size_t n; /* number of characters in buffer */
@@ -158,7 +172,11 @@ typedef struct luaL_Buffer {
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
char b[LUAL_BUFFERSIZE]; /* initial buffer */
} init;
} luaL_Buffer;
};
#define luaL_bufflen(bf) ((bf)->n)
#define luaL_buffaddr(bf) ((bf)->b)
#define luaL_addchar(B,c) \

View File

@@ -1,5 +1,5 @@
/*
** $Id: lbaselib.c,v 1.322 2018/02/27 18:47:32 roberto Exp roberto $
** $Id: lbaselib.c $
** Basic library
** See Copyright Notice in lua.h
*/
@@ -24,18 +24,12 @@
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
for (i = 1; i <= n; i++) { /* for each argument */
size_t l;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, "'tostring' must return a string to 'print'");
if (i>1) lua_writestring("\t", 1);
lua_writestring(s, l);
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
if (i > 1) /* not the first element? */
lua_writestring("\t", 1); /* add a tab before it */
lua_writestring(s, l); /* print it */
lua_pop(L, 1); /* pop result */
}
lua_writeline();
@@ -43,6 +37,24 @@ static int luaB_print (lua_State *L) {
}
/*
** Creates a warning with all given arguments.
** Check first for errors; otherwise an error may interrupt
** the composition of a warning, leaving it unfinished.
*/
static int luaB_warn (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
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 */
lua_warning(L, lua_tostring(L, i), 1);
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
return 0;
}
#define SPACECHARS " \f\n\r\t\v"
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
@@ -68,7 +80,6 @@ static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
static int luaB_tonumber (lua_State *L) {
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
luaL_checkany(L, 1);
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
lua_settop(L, 1); /* yes; return it */
return 1;
@@ -79,6 +90,7 @@ static int luaB_tonumber (lua_State *L) {
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
return 1; /* successful conversion to number */
/* else not a number */
luaL_checkany(L, 1); /* (but there must be some parameter) */
}
}
else {
@@ -94,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;
}
@@ -125,8 +137,7 @@ static int luaB_getmetatable (lua_State *L) {
static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
return luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
@@ -145,8 +156,8 @@ static int luaB_rawequal (lua_State *L) {
static int luaB_rawlen (lua_State *L) {
int t = lua_type(L, 1);
luaL_argcheck(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
"table or string expected");
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
"table or string");
lua_pushinteger(L, lua_rawlen(L, 1));
return 1;
}
@@ -297,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 */
}
}
@@ -483,6 +494,7 @@ static const luaL_Reg base_funcs[] = {
{"pairs", luaB_pairs},
{"pcall", luaB_pcall},
{"print", luaB_print},
{"warn", luaB_warn},
{"rawequal", luaB_rawequal},
{"rawlen", luaB_rawlen},
{"rawget", luaB_rawget},

View File

@@ -1,7 +0,0 @@
/*
** $Id: lbitlib.c,v 1.31 2017/11/16 13:19:06 roberto Exp roberto $
** Standard library for bitwise operations
** See Copyright Notice in lua.h
*/
Deprecated module.

612
lcode.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lcode.c,v 2.160 2018/03/16 14:22:09 roberto Exp roberto $
** $Id: lcode.c $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
@@ -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
@@ -179,8 +215,8 @@ void luaK_ret (FuncState *fs, int first, int nret) {
** Code a "conditional jump", that is, a test or comparison opcode
** followed by a jump. Return jump position.
*/
static int condjump (FuncState *fs, OpCode op, int A, int B, int k) {
luaK_codeABCk(fs, op, A, B, 0, k);
static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) {
luaK_codeABCk(fs, op, A, B, C, k);
return luaK_jump(fs);
}
@@ -276,43 +312,18 @@ void luaK_patchtohere (FuncState *fs, int list) {
/*
** Correct a jump list to jump to 'target'. If 'hasclose' is true,
** 'target' contains an OP_CLOSE instruction (see first assert).
** Only the jumps with ('m' == true) need that close; other jumps
** avoid it jumping to the next instruction.
** MAXimum number of successive Instructions WiTHout ABSolute line
** information.
*/
void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose) {
lua_assert(!hasclose || GET_OPCODE(fs->f->code[target]) == OP_CLOSE);
while (list != NO_JUMP) {
int next = getjump(fs, list);
lua_assert(!GETARG_m(fs->f->code[list]) || hasclose);
patchtestreg(fs, list, NO_REG); /* do not generate values */
if (!hasclose || GETARG_m(fs->f->code[list]))
fixjump(fs, list, target);
else /* there is a CLOSE instruction but jump does not need it */
fixjump(fs, list, target + 1); /* avoid CLOSE instruction */
list = next;
}
}
/*
** Mark (using the 'm' arg) all jumps in 'list' to close upvalues. Mark
** will instruct 'luaK_patchgoto' to make these jumps go to OP_CLOSE
** instructions.
*/
void luaK_patchclose (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list)) {
lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP);
SETARG_m(fs->f->code[list], 1);
}
}
#if !defined(MAXIWTHABS)
#define MAXIWTHABS 120
#endif
/* limit for difference between lines in relative line info. */
#define LIMLINEDIFF 0x80
/*
** Save line info for a new instruction. If difference from last line
** does not fit in a byte, of after that many instructions, save a new
@@ -320,14 +331,15 @@ void luaK_patchclose (FuncState *fs, int list) {
** in 'lineinfo' signals the existence of this absolute information.)
** Otherwise, store the difference from last line in 'lineinfo'.
*/
static void savelineinfo (FuncState *fs, Proto *f, int pc, int line) {
static void savelineinfo (FuncState *fs, Proto *f, int line) {
int linedif = line - fs->previousline;
if (abs(linedif) >= 0x80 || fs->iwthabs++ > MAXIWTHABS) {
int pc = fs->pc - 1; /* last instruction coded */
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
f->abslineinfo[fs->nabslineinfo].pc = pc;
f->abslineinfo[fs->nabslineinfo++].line = line;
linedif = ABSLINEINFO; /* signal there is absolute information */
linedif = ABSLINEINFO; /* signal that there is absolute information */
fs->iwthabs = 0; /* restart counter */
}
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
@@ -337,18 +349,49 @@ static void savelineinfo (FuncState *fs, Proto *f, int pc, int line) {
}
/*
** Remove line information from the last instruction.
** If line information for that instruction is absolute, set 'iwthabs'
** above its max to force the new (replacing) instruction to have
** absolute line info, too.
*/
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]; /* 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 */
fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
}
}
/*
** Remove the last instruction created, correcting line information
** accordingly.
*/
static void removelastinstruction (FuncState *fs) {
removelastlineinfo(fs);
fs->pc--;
}
/*
** 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,
MAX_INT, "opcodes");
f->code[fs->pc] = i;
savelineinfo(fs, f, fs->pc, fs->ls->lastline);
return fs->pc++;
f->code[fs->pc++] = i;
savelineinfo(fs, f, fs->ls->lastline);
return fs->pc - 1; /* index of new instruction */
}
@@ -364,10 +407,6 @@ int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
}
#define codeABsC(fs,o,a,b,c,k) luaK_codeABCk(fs,o,a,b,((c) + OFFSET_sC),k)
/*
** Format and emit an 'iABx' instruction.
*/
@@ -455,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);
}
@@ -534,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 */
@@ -587,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));
}
@@ -612,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)
@@ -644,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
@@ -669,12 +738,17 @@ 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.sidx;
e->k = VNONRELOC; /* becomes a non-relocatable value */
break;
}
@@ -731,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;
@@ -796,7 +873,7 @@ static int need_value (FuncState *fs, int list) {
/*
** Ensures final expression result (which includes results from its
** jump ** lists) is in register 'reg'.
** jump lists) is in register 'reg'.
** If expression has jumps, need to patch these jumps either to
** its final position or to "load" instructions (for those tests
** that do not produce values).
@@ -811,8 +888,9 @@ static void exp2reg (FuncState *fs, expdesc *e, int reg) {
int p_t = NO_JUMP; /* position of an eventual LOAD true */
if (need_value(fs, e->t) || need_value(fs, e->f)) {
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
p_f = code_loadbool(fs, reg, 0, 1);
p_t = code_loadbool(fs, reg, 1, 0);
p_f = code_loadbool(fs, reg, 0, 1); /* load false and skip next i. */
p_t = code_loadbool(fs, reg, 1, 0); /* load true */
/* jump around these booleans if 'e' is not a test */
luaK_patchtohere(fs, fj);
}
final = luaK_getlabel(fs);
@@ -845,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;
}
@@ -877,31 +955,47 @@ void luaK_exp2val (FuncState *fs, expdesc *e) {
}
/*
** Try to make 'e' a K expression with an index in the range of R/K
** indices. Return true iff succeeded.
*/
static int luaK_exp2K (FuncState *fs, expdesc *e) {
if (!hasjumps(e)) {
int info;
switch (e->k) { /* move constants to 'k' */
case VTRUE: info = boolK(fs, 1); break;
case VFALSE: info = boolK(fs, 0); break;
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 */
}
if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */
e->k = VK; /* make expression a 'K' expression */
e->u.info = info;
return 1;
}
}
/* else, expression doesn't fit; leave it unchanged */
return 0;
}
/*
** Ensures final expression result is in a valid R/K index
** (that is, it is either in a register or in 'k' with an index
** in the range of R/K indices).
** Returns 1 if expression is K, 0 otherwise.
** Returns 1 iff expression is K.
*/
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) { /* move constants to 'k' */
case VTRUE: e->u.info = boolK(fs, 1); goto vk;
case VFALSE: e->u.info = boolK(fs, 0); goto vk;
case VNIL: e->u.info = nilK(fs); goto vk;
case VKINT: e->u.info = luaK_intK(fs, e->u.ival); goto vk;
case VKFLT: e->u.info = luaK_numberK(fs, e->u.nval); goto vk;
case VK:
vk:
e->k = VK;
if (e->u.info <= MAXINDEXRK) /* constant fits in 'argC'? */
return 1;
else break;
default: break;
if (luaK_exp2K(fs, e))
return 1;
else { /* not a constant in the right range: put it in a register */
luaK_exp2anyreg(fs, e);
return 0;
}
/* not a constant in the right range: put it in a register */
luaK_exp2anyreg(fs, e);
return 0;
}
@@ -919,7 +1013,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.info); /* compute 'ex' into proper place */
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
return;
}
case VUPVAL: {
@@ -986,14 +1080,14 @@ static int jumponcond (FuncState *fs, expdesc *e, int cond) {
if (e->k == VRELOC) {
Instruction ie = getinstruction(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
fs->pc--; /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
removelastinstruction(fs); /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond);
}
/* else go through */
}
discharge2anyreg(fs, e);
freeexp(fs, e);
return condjump(fs, OP_TESTSET, NO_REG, e->u.info, cond);
return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond);
}
@@ -1009,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;
}
@@ -1054,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;
}
@@ -1121,15 +1214,18 @@ static int isSCint (expdesc *e) {
/*
** Check whether expression 'e' is a literal integer or float in
** proper range to fit in register sC
** proper range to fit in a register (sB or sC).
*/
static int isSCnumber (expdesc *e, lua_Integer *i) {
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)))
i = e->u.ival;
else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, 0))
*isfloat = 1;
else
return 0; /* not a number */
if (!hasjumps(e) && fitsC(*i)) {
*i += OFFSET_sC;
if (!hasjumps(e) && fitsC(i)) {
*pi = int2sC(cast_int(i));
return 1;
}
else
@@ -1144,25 +1240,32 @@ static int isSCnumber (expdesc *e, lua_Integer *i) {
** values in registers.
*/
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
lua_assert(!hasjumps(t) && (vkisinreg(t->k) || t->k == VUPVAL));
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
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 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */
t->u.ind.t = t->u.info; /* register or upvalue index */
if (t->k == VUPVAL) {
t->u.ind.t = t->u.info; /* upvalue index */
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXUP;
}
else if (isKstr(fs, k)) {
t->u.ind.idx = k->u.info; /* literal string */
t->k = VINDEXSTR;
}
else if (isCint(k)) {
t->u.ind.idx = cast_int(k->u.ival); /* integer constant in proper range */
t->k = VINDEXI;
}
else {
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
t->k = VINDEXED;
/* register index of the table */
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;
}
else if (isCint(k)) {
t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
t->k = VINDEXI;
}
else {
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
t->k = VINDEXED;
}
}
}
@@ -1191,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 */
@@ -1225,56 +1328,70 @@ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
}
static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
int pc, int line) {
freeexps(fs, e1, e2);
e1->u.info = pc;
e1->k = VRELOC; /* all those operations are relocatable */
luaK_fixline(fs, line);
}
/*
** Emit code for binary expressions that "produce values"
** (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 flip, int line,
OpCode mmop, TMS event) {
int v1 = luaK_exp2anyreg(fs, e1);
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);
}
/*
** Emit code for binary expressions that "produce values" over
** two registers.
*/
static void codebinexpval (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int line) {
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
int v1 = luaK_exp2anyreg(fs, e1);
int pc = luaK_codeABC(fs, op, 0, v1, v2); /* generate opcode */
finishbinexpval(fs, e1, e2, pc, 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); /* immediate operand */
int v1 = luaK_exp2anyreg(fs, e1);
int pc = codeABsC(fs, op, 0, v1, v2, k); /* generate opcode */
finishbinexpval(fs, e1, e2, pc, 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);
}
/*
** Code arithmetic operators ('+', '-', ...). If second operand is a
** constant in the proper range, use variant opcodes with immediate
** operands.
/* Try to code a binary operator negating its second operand.
** For the metamethod, 2nd operand must keep its original value.
*/
static void codearith (FuncState *fs, OpCode op,
expdesc *e1, expdesc *e2, int flip, int line) {
if (!isSCint(e2))
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
else /* use immediate operators */
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
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 */
}
}
}
@@ -1284,17 +1401,42 @@ static void swapexps (expdesc *e1, expdesc *e2) {
/*
** Code commutative operators ('+', '*'). If first operand is a
** constant, change order of operands to use immediate operator.
** Code arithmetic operators ('+', '-', ...). If second operand is a
** constant in the proper range, use variant opcodes with K operands.
*/
static void codecommutative (FuncState *fs, OpCode op,
static void codearith (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
TMS event = cast(TMS, opr + TM_ADD);
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
int v2 = e2->u.info; /* K index */
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 */
}
}
/*
** Code commutative operators ('+', '*'). If first operand is a
** numeric constant, change order of operands to try to use an
** immediate or K operator.
*/
static void codecommutative (FuncState *fs, BinOpr op,
expdesc *e1, expdesc *e2, int line) {
int flip = 0;
if (isSCint(e1)) {
swapexps(e1, e2);
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);
}
@@ -1304,65 +1446,44 @@ static void codecommutative (FuncState *fs, OpCode op,
*/
static void codebitwise (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) {
int inv = 0;
int v1, v2, pc;
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;
}
v1 = luaK_exp2anyreg(fs, e1);
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]));
pc = luaK_codeABCk(fs, op, 0, v1, v2, inv);
finishbinexpval(fs, e1, e2, pc, line);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
cast(TMS, opr + TM_ADD));
}
/*
** 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);
}
/*
** Emit code for order comparisons.
** When the first operand A is an integral value in the proper range,
** change (A < B) to (B > A) and (A <= B) to (B >= A) so that
** it can use an immediate operand.
** Emit code for order comparisons. When using an immediate operand,
** 'isfloat' tells whether the original value was a float.
*/
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
int r1, r2;
lua_Integer im;
if (isSCnumber(e2, &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)) {
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 */
@@ -1370,7 +1491,7 @@ static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
r2 = luaK_exp2anyreg(fs, e2);
}
freeexps(fs, e1, e2);
e1->u.info = condjump(fs, op, r1, r2, 1);
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
e1->k = VJMP;
}
@@ -1381,16 +1502,17 @@ 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) {
lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT);
swapexps(e1, e2);
}
r1 = luaK_exp2anyreg(fs, e1); /* 1nd expression must be in register */
if (isSCnumber(e2, &im)) {
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;
@@ -1401,7 +1523,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
r2 = luaK_exp2anyreg(fs, e2);
}
freeexps(fs, e1, e2);
e1->u.info = condjump(fs, op, r1, r2, (opr == OPR_EQ));
e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ));
e1->k = VJMP;
}
@@ -1411,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;
@@ -1430,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 */
@@ -1461,8 +1585,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
}
case OPR_LT: case OPR_LE:
case OPR_GT: case OPR_GE: {
lua_Integer dummy;
if (!isSCnumber(v, &dummy))
int dummy, dummy2;
if (!isSCnumber(v, &dummy, &dummy2))
luaK_exp2anyreg(fs, v);
/* else keep numeral, which may be an immediate operand */
break;
@@ -1498,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;
@@ -1519,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: {
@@ -1572,23 +1701,28 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
/*
** Change line information associated with current position. If that
** information is absolute, just change it and correct 'previousline'.
** Otherwise, restore 'previousline' to its value before saving the
** current position and than saves the line information again, with the
** new line.
** Change line information associated with current position, by removing
** previous info and adding it again with new line.
*/
void luaK_fixline (FuncState *fs, int line) {
Proto *f = fs->f;
if (f->lineinfo[fs->pc - 1] == ABSLINEINFO) {
lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == fs->pc - 1);
f->abslineinfo[fs->nabslineinfo - 1].line = line;
fs->previousline = line;
}
else {
fs->previousline -= f->lineinfo[fs->pc - 1]; /* undo previous info. */
savelineinfo(fs, f, fs->pc - 1, line); /* redo it */
removelastlineinfo(fs);
savelineinfo(fs, fs->f, 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);
}
@@ -1600,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 */
}
@@ -1643,16 +1777,16 @@ void luaK_finish (FuncState *fs) {
lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
switch (GET_OPCODE(*pc)) {
case OP_RETURN0: case OP_RETURN1: {
if (p->sizep == 0 && !p->is_vararg)
if (!(fs->needclose || p->is_vararg))
break; /* no extra work */
/* else use OP_RETURN to do the extra work */
SET_OPCODE(*pc, OP_RETURN);
} /* FALLTHROUGH */
case OP_RETURN: case OP_TAILCALL: {
if (p->sizep > 0 || 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: {

22
lcode.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lcode.h,v 1.72 2018/03/19 20:03:44 roberto Exp roberto $
** $Id: lcode.h $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
@@ -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);
@@ -78,16 +87,15 @@ LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_jump (FuncState *fs);
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
LUAI_FUNC void luaK_patchgoto (FuncState *fs, int list, int target,
int hasclose);
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
LUAI_FUNC void luaK_patchclose (FuncState *fs, int list);
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
LUAI_FUNC int luaK_getlabel (FuncState *fs);
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);

View File

@@ -1,5 +1,5 @@
/*
** $Id: lcorolib.c,v 1.10 2016/04/11 19:19:55 roberto Exp roberto $
** $Id: lcorolib.c $
** Coroutine Library
** See Copyright Notice in lua.h
*/
@@ -20,21 +20,21 @@
static lua_State *getco (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argcheck(L, co, 1, "thread expected");
luaL_argexpected(L, co, 1, "thread");
return co;
}
/*
** Resumes a coroutine. Returns the number of results for non-error
** cases or -1 for errors.
*/
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status, nres;
if (!lua_checkstack(co, narg)) {
lua_pushliteral(L, "too many arguments to resume");
return -1; /* error flag */
}
if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) {
lua_pushliteral(L, "cannot resume dead coroutine");
return -1; /* error flag */
}
lua_xmove(L, co, narg);
status = lua_resume(co, L, narg, &nres);
if (status == LUA_OK || status == LUA_YIELD) {
@@ -74,8 +74,11 @@ static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD)
lua_resetthread(co); /* close variables in case of errors */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
luaL_where(L, 1); /* add extra info */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);
lua_concat(L, 2);
}
@@ -107,35 +110,48 @@ static int luaB_yield (lua_State *L) {
}
static int luaB_costatus (lua_State *L) {
lua_State *co = getco(L);
if (L == co) lua_pushliteral(L, "running");
#define COS_RUN 0
#define COS_DEAD 1
#define COS_YIELD 2
#define COS_NORM 3
static const char *const statname[] =
{"running", "dead", "suspended", "normal"};
static int auxstatus (lua_State *L, lua_State *co) {
if (L == co) return COS_RUN;
else {
switch (lua_status(co)) {
case LUA_YIELD:
lua_pushliteral(L, "suspended");
break;
return COS_YIELD;
case LUA_OK: {
lua_Debug ar;
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
lua_pushliteral(L, "normal"); /* it is running */
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
return COS_NORM; /* it is running */
else if (lua_gettop(co) == 0)
lua_pushliteral(L, "dead");
return COS_DEAD;
else
lua_pushliteral(L, "suspended"); /* initial state */
break;
return COS_YIELD; /* initial state */
}
default: /* some error occurred */
lua_pushliteral(L, "dead");
break;
return COS_DEAD;
}
}
}
static int luaB_costatus (lua_State *L) {
lua_State *co = getco(L);
lua_pushstring(L, statname[auxstatus(L, co)]);
return 1;
}
static int luaB_yieldable (lua_State *L) {
lua_pushboolean(L, lua_isyieldable(L));
lua_State *co = lua_isnone(L, 1) ? L : getco(L);
lua_pushboolean(L, lua_isyieldable(co));
return 1;
}
@@ -147,6 +163,28 @@ static int luaB_corunning (lua_State *L) {
}
static int luaB_close (lua_State *L) {
lua_State *co = getco(L);
int status = auxstatus(L, co);
switch (status) {
case COS_DEAD: case COS_YIELD: {
status = lua_resetthread(co);
if (status == LUA_OK) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushboolean(L, 0);
lua_xmove(co, L, 1); /* copy error message */
return 2;
}
}
default: /* normal or running coroutine */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
}
}
static const luaL_Reg co_funcs[] = {
{"create", luaB_cocreate},
{"resume", luaB_coresume},
@@ -155,6 +193,7 @@ static const luaL_Reg co_funcs[] = {
{"wrap", luaB_cowrap},
{"yield", luaB_yield},
{"isyieldable", luaB_yieldable},
{"close", luaB_close},
{NULL, NULL}
};

View File

@@ -1,5 +1,5 @@
/*
** $Id: lctype.c,v 1.11 2011/10/03 16:19:23 roberto Exp roberto $
** $Id: lctype.c $
** 'ctype' functions for Lua
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp roberto $
** $Id: lctype.h $
** 'ctype' functions for Lua
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: ldblib.c,v 1.154 2018/03/05 14:15:04 roberto Exp roberto $
** $Id: ldblib.c $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
@@ -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";
/*
@@ -55,8 +55,7 @@ static int db_getmetatable (lua_State *L) {
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
lua_settop(L, 2);
lua_setmetatable(L, 1);
return 1; /* return 1st argument */
@@ -66,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;
@@ -81,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;
}
@@ -160,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;
}
}
@@ -168,7 +167,8 @@ static int db_getinfo (lua_State *L) {
return luaL_argerror(L, arg+2, "invalid option");
lua_newtable(L); /* table to collect results */
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
lua_pushlstring(L, ar.source, ar.srclen);
lua_setfield(L, -2, "source");
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
@@ -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}
};

View File

@@ -1,5 +1,5 @@
/*
** $Id: ldebug.c,v 2.157 2018/05/02 18:17:59 roberto Exp roberto $
** $Id: ldebug.c $
** Debug Interface
** See Copyright Notice in lua.h
*/
@@ -192,15 +192,14 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
int nextra = ci->u.l.nextraargs;
if (n <= nextra) {
*pos = ci->func - nextra + (n - 1);
return "(*vararg)"; /* generic name for any vararg */
return "(vararg)"; /* generic name for any vararg */
}
}
return NULL; /* no such vararg */
}
static const char *findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos) {
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
StkId base = ci->func + 1;
const char *name = NULL;
if (isLua(ci)) {
@@ -211,12 +210,15 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
}
if (name == NULL) { /* no 'standard' name? */
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
name = "(*temporary)"; /* generic name for any valid slot */
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
/* generic name for any valid slot */
name = isLua(ci) ? "(temporary)" : "(C temporary)";
}
else
return NULL; /* no name */
}
*pos = base + (n - 1);
if (pos)
*pos = base + (n - 1);
return name;
}
@@ -232,7 +234,7 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
}
else { /* active function; get information through 'ar' */
StkId pos = NULL; /* to avoid warnings */
name = findlocal(L, ar->i_ci, n, &pos);
name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) {
setobjs2s(L, L->top, pos);
api_incr_top(L);
@@ -247,7 +249,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
StkId pos = NULL; /* to avoid warnings */
const char *name;
lua_lock(L);
name = findlocal(L, ar->i_ci, n, &pos);
name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) {
setobjs2s(L, pos, L->top - 1);
L->top--; /* pop value */
@@ -260,18 +262,26 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) {
if (noLuaClosure(cl)) {
ar->source = "=[C]";
ar->srclen = LL("=[C]");
ar->linedefined = -1;
ar->lastlinedefined = -1;
ar->what = "C";
}
else {
const Proto *p = cl->l.p;
ar->source = p->source ? getstr(p->source) : "=?";
if (p->source) {
ar->source = getstr(p->source);
ar->srclen = tsslen(p->source);
}
else {
ar->source = "=?";
ar->srclen = LL("=?");
}
ar->linedefined = p->linedefined;
ar->lastlinedefined = p->lastlinedefined;
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
}
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
luaO_chunkid(ar->short_src, ar->source, ar->srclen);
}
@@ -363,6 +373,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
ar->ftransfer = ci->u2.transferinfo.ftransfer;
ar->ntransfer = ci->u2.transferinfo.ntransfer;
}
break;
}
case 'L':
case 'f': /* handled by lua_getinfo */
@@ -454,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);
@@ -515,8 +528,8 @@ static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
}
const char *getobjname (const Proto *p, int lastpc, int reg,
const char **name) {
static const char *getobjname (const Proto *p, int lastpc, int reg,
const char **name) {
int pc;
*name = luaF_getlocalname(p, reg + 1, lastpc);
if (*name) /* is a local? */
@@ -609,18 +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:
case OP_BANDK: case OP_BORK: case OP_BXORK: {
int offset = GET_OPCODE(i) - OP_ADDI; /* 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;
@@ -631,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 */
@@ -696,6 +699,12 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
}
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
what, luaT_objtypename(L, o));
}
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
luaG_typeerror(L, p1, "concatenate");
@@ -736,7 +745,7 @@ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
int line) {
char buff[LUA_IDSIZE];
if (src)
luaO_chunkid(buff, getstr(src), LUA_IDSIZE);
luaO_chunkid(buff, getstr(src), tsslen(src));
else { /* no source available; use "?" instead */
buff[0] = '?'; buff[1] = '\0';
}

View File

@@ -1,5 +1,5 @@
/*
** $Id: ldebug.h,v 2.17 2018/05/02 18:17:59 roberto Exp roberto $
** $Id: ldebug.h $
** Auxiliary functions from Debug Interface module
** See Copyright Notice in lua.h
*/
@@ -22,8 +22,12 @@
#define ABSLINEINFO (-0x80)
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos);
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
const char *what);
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,

160
ldo.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: ldo.c,v 2.201 2018/05/22 12:02:36 roberto Exp roberto $
** $Id: ldo.c $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@@ -88,17 +88,20 @@ struct lua_longjmp {
};
static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) {
case LUA_ERRMEM: { /* memory error? */
TString *memerrmsg = luaS_newliteral(L, MEMERRMSG);
setsvalue2s(L, oldtop, memerrmsg); /* reuse preregistered msg. */
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
break;
}
case LUA_ERRERR: {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case CLOSEPROTECT: {
setnilvalue(s2v(oldtop)); /* no error message */
break;
}
default: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
@@ -115,6 +118,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
}
else { /* thread has no error handler */
global_State *g = G(L);
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
L->status = cast_byte(errcode); /* mark it as dead */
if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
@@ -122,7 +126,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
}
else { /* no handler at all; abort */
if (g->panic) { /* panic function? */
seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
if (L->ci->top < L->top)
L->ci->top = L->top; /* pushing msg. can break this invariant */
lua_unlock(L);
@@ -135,7 +139,8 @@ l_noret luaD_throw (lua_State *L, int errcode) {
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
unsigned short oldnCcalls = L->nCcalls - L->nci;
global_State *g = G(L);
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
struct lua_longjmp lj;
lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */
@@ -144,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;
}
@@ -343,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 */
}
@@ -366,32 +371,40 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
** separated.
*/
static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
StkId firstresult;
int i;
switch (wanted) { /* handle typical cases separately */
case 0: /* no values needed */
L->top = res;
break;
return;
case 1: /* one value needed */
if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */
else
setobjs2s(L, res, L->top - nres); /* move it to proper place */
L->top = res + 1;
break;
return;
case LUA_MULTRET:
wanted = nres; /* we want all results */
/* FALLTHROUGH */
default: { /* multiple results */
StkId firstresult = L->top - nres; /* index of first result */
int i;
/* move all results to correct place */
for (i = 0; i < nres && i < wanted; i++)
setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i));
L->top = res + wanted; /* top points after the last result */
break;
}
default: /* multiple results (or to-be-closed variables) */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
ptrdiff_t savedres = savestack(L, res);
luaF_close(L, res, LUA_OK); /* may change the stack */
res = restorestack(L, savedres);
wanted = codeNresults(wanted); /* correct value */
if (wanted == LUA_MULTRET)
wanted = nres;
}
break;
}
firstresult = L->top - nres; /* index of first result */
/* move all results to correct place */
for (i = 0; i < nres && i < wanted; i++)
setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i));
L->top = res + wanted; /* top points after the last result */
}
@@ -444,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;
@@ -474,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 */
@@ -492,21 +505,26 @@ 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 */
}
}
}
/*
** Similar to 'luaD_call', but does not allow yields during the call
** Similar to 'luaD_call', but does not allow yields during the call.
** If there is a stack overflow, freeing all CI structures will
** force the subsequent call to invoke 'luaE_extendCI', which then
** will raise any errors.
*/
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
L->nny++;
incXCcalls(L);
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
luaE_freeCI(L);
luaD_call(L, func, nResults);
L->nny--;
decXCcalls(L);
}
@@ -518,7 +536,7 @@ static void finishCcall (lua_State *L, int status) {
CallInfo *ci = L->ci;
int n;
/* must have a continuation and must be able to call it */
lua_assert(ci->u.c.k != NULL && L->nny == 0);
lua_assert(ci->u.c.k != NULL && yieldable(L));
/* error status can only happen in a protected call */
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
@@ -584,11 +602,11 @@ static int recover (lua_State *L, int status) {
if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u2.funcidx);
luaF_close(L, oldtop);
seterrorobj(L, status, oldtop);
luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
L->ci = ci;
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
L->nny = 0; /* should be zero to be yieldable */
luaD_shrinkstack(L);
L->errfunc = ci->u.c.old_errfunc;
return 1; /* continue running the coroutine */
@@ -641,50 +659,48 @@ static void resume (lua_State *L, void *ud) {
}
}
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
int *nresults) {
int status;
unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */
lua_lock(L);
if (L->status == LUA_OK) { /* may be starting a coroutine */
if (L->ci != &L->base_ci) /* not in base level? */
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
else if (L->top - (L->ci->func + 1) == nargs) /* no function? */
return resume_error(L, "cannot resume dead coroutine", nargs);
}
else if (L->status != LUA_YIELD)
else if (L->status != LUA_YIELD) /* ended with errors? */
return resume_error(L, "cannot resume dead coroutine", nargs);
L->nCcalls = (from) ? from->nCcalls + 1 : 1;
if (L->nCcalls >= LUAI_MAXCCALLS)
if (from == NULL)
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
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);
L->nny = 0; /* allow yields */
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs);
if (unlikely(status == -1)) /* error calling 'lua_resume'? */
status = LUA_ERRRUN;
else { /* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (unlikely(errorstatus(status))) { /* unrecoverable error? */
L->status = cast_byte(status); /* mark thread as 'dead' */
seterrorobj(L, status, L->top); /* push error message */
L->ci->top = L->top;
}
else lua_assert(status == L->status); /* normal end or yield */
/* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (likely(!errorstatus(status)))
lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */
L->status = cast_byte(status); /* mark thread as 'dead' */
luaD_seterrorobj(L, status, L->top); /* push error message */
L->ci->top = L->top;
}
*nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
: cast_int(L->top - (L->ci->func + 1));
L->nny = oldnny; /* restore 'nny' */
L->nCcalls--;
lua_unlock(L);
return status;
}
LUA_API int lua_isyieldable (lua_State *L) {
return (L->nny == 0);
return yieldable(L);
}
@@ -694,7 +710,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
luai_userstateyield(L, nresults);
lua_lock(L);
api_checknelems(L, nresults);
if (unlikely(L->nny > 0)) {
if (unlikely(!yieldable(L))) {
if (L != G(L)->mainthread)
luaG_runerror(L, "attempt to yield across a C-call boundary");
else
@@ -718,22 +734,26 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
}
/*
** Call the C function 'func' in protected mode, restoring basic
** thread information ('allowhook', etc.) and in particular
** its stack level in case of errors.
*/
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef) {
int status;
CallInfo *old_ci = L->ci;
lu_byte old_allowhooks = L->allowhook;
unsigned short old_nny = L->nny;
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u);
if (unlikely(status != LUA_OK)) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
luaF_close(L, oldtop); /* close possible pending closures */
seterrorobj(L, status, oldtop);
L->ci = old_ci;
L->allowhook = old_allowhooks;
L->nny = old_nny;
status = luaF_close(L, oldtop, status);
oldtop = restorestack(L, old_top); /* previous call may change stack */
luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L);
}
L->errfunc = old_errfunc;
@@ -784,7 +804,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode) {
struct SParser p;
int status;
L->nny++; /* cannot yield during parsing */
incnny(L); /* cannot yield during parsing */
p.z = z; p.name = name; p.mode = mode;
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
@@ -795,7 +815,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
L->nny--;
decnny(L);
return status;
}

3
ldo.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: ldo.h,v 2.43 2018/02/17 19:29:29 roberto Exp roberto $
** $Id: ldo.h $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@@ -50,6 +50,7 @@
/* type of protected functions, to be ran by 'runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode);
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,

View File

@@ -1,5 +1,5 @@
/*
** $Id: ldump.c,v 2.40 2017/11/28 11:19:07 roberto Exp roberto $
** $Id: ldump.c $
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -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);
}
}
@@ -198,11 +199,9 @@ static void DumpFunction (const Proto *f, TString *psource, DumpState *D) {
static void DumpHeader (DumpState *D) {
DumpLiteral(LUA_SIGNATURE, D);
DumpByte(LUAC_VERSION, D);
DumpInt(LUAC_VERSION, D);
DumpByte(LUAC_FORMAT, D);
DumpLiteral(LUAC_DATA, D);
DumpByte(sizeof(int), D);
DumpByte(sizeof(size_t), D);
DumpByte(sizeof(Instruction), D);
DumpByte(sizeof(lua_Integer), D);
DumpByte(sizeof(lua_Number), D);

184
lfunc.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lfunc.c,v 2.50 2017/06/27 11:35:31 roberto Exp roberto $
** $Id: lfunc.c $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
@@ -14,6 +14,8 @@
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
@@ -39,6 +41,7 @@ LClosure *luaF_newLclosure (lua_State *L, int n) {
return c;
}
/*
** fill a closure with new closed upvalues
*/
@@ -55,26 +58,21 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
}
UpVal *luaF_findupval (lua_State *L, StkId level) {
UpVal **pp = &L->openupval;
GCObject *o;
UpVal *p;
UpVal *uv;
lua_assert(isintwups(L) || L->openupval == NULL);
while ((p = *pp) != NULL && uplevel(p) >= level) {
if (uplevel(p) == level && !isdead(G(L), p)) /* corresponding upvalue? */
return p; /* return it */
pp = &p->u.open.next;
}
/* not found: create a new upvalue between 'pp' and 'p' */
o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
uv = gco2upv(o);
uv->u.open.next = p; /* link it to list of open upvalues */
uv->u.open.previous = pp;
if (p)
p->u.open.previous = &uv->u.open.next;
*pp = uv;
/*
** 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 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)
next->u.open.previous = &uv->u.open.next;
*prev = uv;
if (!isintwups(L)) { /* thread not in list of threads with upvalues? */
L->twups = G(L)->twups; /* link it to the list */
G(L)->twups = L;
@@ -83,6 +81,137 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
}
/*
** Find and reuse, or create if it does not exist, an upvalue
** at the given level.
*/
UpVal *luaF_findupval (lua_State *L, StkId level) {
UpVal **pp = &L->openupval;
UpVal *p;
lua_assert(isintwups(L) || L->openupval == NULL);
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
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, 0, level, pp);
}
static void callclose (lua_State *L, void *ud) {
UNUSED(ud);
luaD_callnoyield(L, L->top - 3, 0);
}
/*
** Prepare closing method plus its arguments for object 'obj' with
** error message 'err'. (This function assumes EXTRA_STACK.)
*/
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
StkId top = L->top;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
return 0; /* nothing to call */
setobj2s(L, top, tm); /* will call metamethod... */
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
L->top = top + 3; /* add function and arguments */
return 1;
}
/*
** 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 the original
** protected call, and so the call to the closing method must be
** protected here. (A status == CLOSEPROTECT behaves like a previous
** error, to also run the closing method in protected mode).
** If status is OK, the call to the closing method will be pushed
** at the top of the stack. Otherwise, values are pushed after
** the 'level' of the upvalue being closed, as everything after
** that won't be used again.
*/
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 (!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;
level++; /* space for error message */
oldtop = savestack(L, level + 1); /* top will be after that */
luaD_seterrorobj(L, status, level); /* set error message */
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
status = newstatus; /* this will be the new error */
else {
if (newstatus != LUA_OK) /* 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 */
}
return status;
}
/*
** Try to create a to-be-closed upvalue
** (can raise a memory-allocation error)
*/
static void trynewtbcupval (lua_State *L, void *ud) {
newupval(L, 1, cast(StkId, ud), &L->openupval);
}
/*
** Create a to-be-closed upvalue. If there is a memory error
** when creating the upvalue, the closing method must be called here,
** as there is no upvalue to call it later.
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
TValue *obj = s2v(level);
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
int status;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
if (ttisnil(tm)) /* no metamethod? */
varerror(L, level, "variable '%s' got a non-closable value");
status = luaD_rawrunprotected(L, trynewtbcupval, level);
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
lua_assert(status == LUA_ERRMEM);
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
/* next call must succeed, as object is closable */
prepclosingmethod(L, s2v(level), s2v(level + 1));
callclose(L, NULL); /* call closing method */
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
}
}
}
void luaF_unlinkupval (UpVal *uv) {
lua_assert(upisopen(uv));
*uv->u.open.previous = uv->u.open.next;
@@ -91,11 +220,17 @@ void luaF_unlinkupval (UpVal *uv) {
}
void luaF_close (lua_State *L, StkId level) {
int luaF_close (lua_State *L, StkId level, int status) {
UpVal *uv;
while (L->openupval != NULL &&
(uv = L->openupval, uplevel(uv) >= level)) {
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top);
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 */
@@ -103,6 +238,7 @@ void luaF_close (lua_State *L, StkId level) {
gray2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot);
}
return status;
}
@@ -114,8 +250,6 @@ Proto *luaF_newproto (lua_State *L) {
f->p = NULL;
f->sizep = 0;
f->code = NULL;
f->cache = NULL;
f->cachemiss = 0;
f->sizecode = 0;
f->lineinfo = NULL;
f->sizelineinfo = 0;

16
lfunc.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lfunc.h,v 2.19 2018/01/28 15:13:26 roberto Exp roberto $
** $Id: lfunc.h $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
@@ -42,12 +42,24 @@
#define MAXMISS 10
/*
** Special "status" for 'luaF_close'
*/
/* close upvalues without running their closing methods */
#define NOCLOSINGMETH (-1)
/* close upvalues running all closing methods in protected mode */
#define CLOSEPROTECT (-2)
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,

343
lgc.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lgc.c,v 2.253 2018/03/16 14:22:09 roberto Exp roberto $
** $Id: lgc.c $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -12,6 +12,7 @@
#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "ldebug.h"
@@ -100,6 +101,7 @@
static void reallymarkobject (global_State *g, GCObject *o);
static lu_mem atomic (lua_State *L);
static void entersweep (lua_State *L);
/*
@@ -180,9 +182,13 @@ static int iscleared (global_State *g, const GCObject *o) {
/*
** barrier that moves collector forward, that is, mark the white object
** being pointed by a black object. (If in sweep phase, clear the black
** object to white [sweep it] to avoid other barrier calls for this
** same object.)
** 'v' being pointed by the black object 'o'. (If in sweep phase, clear
** the black object to white [sweep it] to avoid other barrier calls for
** this same object.) In the generational mode, 'v' must also become
** old, if 'o' is old; however, it cannot be changed directly to OLD,
** because it may still point to non-old objects. So, it is marked as
** OLD0. In the next cycle it will become OLD1, and in the next it
** will finally become OLD (regular old).
*/
void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L);
@@ -211,31 +217,11 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1));
if (getage(o) != G_TOUCHED2) /* not already in gray list? */
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */
black2gray(o); /* make table gray (again) */
black2gray(o); /* make object gray (again) */
setage(o, G_TOUCHED1); /* touched in current cycle */
}
/*
** Barrier for prototype's cache of closures. For an 'old1'
** object, making it gray stops it from being visited by 'markold',
** so it is linked in the 'grayagain' list to ensure it will be
** visited. Otherwise, it goes to 'protogray', as only its 'cache' field
** needs to be revisited. (A prototype to be in this barrier must be
** already finished, so its other fields cannot change and do not need
** to be revisited.)
*/
LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p) {
global_State *g = G(L);
lua_assert(g->gckind != KGC_GEN || isold(p));
if (getage(p) == G_OLD1) /* still need to be visited? */
linkgclist(p, g->grayagain); /* link it in 'grayagain' */
else
linkgclist(p, g->protogray); /* link it in 'protogray' */
black2gray(p); /* make prototype gray (to avoid other barriers) */
}
void luaC_fix (lua_State *L, GCObject *o) {
global_State *g = G(L);
lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
@@ -372,7 +358,7 @@ static int remarkupvals (global_State *g) {
*/
static void restartcollection (global_State *g) {
g->gray = g->grayagain = NULL;
g->weak = g->allweak = g->ephemeron = g->protogray = NULL;
g->weak = g->allweak = g->ephemeron = NULL;
markobject(g, g->mainthread);
markvalue(g, &g->l_registry);
markmt(g);
@@ -427,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])) {
@@ -441,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)? */
@@ -497,14 +485,14 @@ static lu_mem traversetable (global_State *g, Table *h) {
const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
markobjectN(g, h->metatable);
if (mode && ttisstring(mode) && /* is there a weak mode? */
((weakkey = strchr(svalue(mode), 'k')),
(weakvalue = strchr(svalue(mode), 'v')),
(cast_void(weakkey = strchr(svalue(mode), 'k')),
cast_void(weakvalue = strchr(svalue(mode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */
black2gray(h); /* keep table gray */
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 */
}
@@ -514,29 +502,16 @@ static lu_mem traversetable (global_State *g, Table *h) {
}
/*
** Check the cache of a prototype, to keep invariants. If the
** cache is white, clear it. (A cache should not prevent the
** collection of its reference.) Otherwise, if in generational
** mode, check the generational invariant. If the cache is old,
** everything is ok. If the prototype is 'old0', everything
** is ok too. (It will naturally be visited again.) If the
** prototype is older than 'old0', then its cache (which is new)
** must be visited again in the next collection, so the prototype
** goes to the 'protogray' list. (If the prototype has a cache,
** it is already immutable and does not need other barriers;
** then, it can become gray without problems for its other fields.)
*/
static void checkprotocache (global_State *g, Proto *p) {
if (p->cache) {
if (iswhite(p->cache))
p->cache = NULL; /* allow cache to be collected */
else if (g->gckind == KGC_GEN && !isold(p->cache) && getage(p) >= G_OLD1) {
linkgclist(p, g->protogray); /* link it in 'protogray' */
black2gray(p); /* make prototype gray */
}
static int traverseudata (global_State *g, Udata *u) {
int i;
markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv);
if (g->gckind == KGC_GEN) {
linkgclist(u, g->grayagain); /* keep it in some gray list */
black2gray(u);
}
p->cachemiss = 0; /* restart counting */
return 1 + u->nuvalue;
}
@@ -547,7 +522,6 @@ static void checkprotocache (global_State *g, Proto *p) {
*/
static int traverseproto (global_State *g, Proto *f) {
int i;
checkprotocache(g, f);
markobjectN(g, f->source);
for (i = 0; i < f->sizek; i++) /* mark literals */
markvalue(g, &f->k[i]);
@@ -589,6 +563,7 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
** That ensures that the entire stack have valid (non-dead) objects.
*/
static int traversethread (global_State *g, lua_State *th) {
UpVal *uv;
StkId o = th->stack;
if (o == NULL)
return 1; /* stack not completely built yet */
@@ -596,6 +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)
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 */
@@ -612,15 +589,6 @@ static int traversethread (global_State *g, lua_State *th) {
}
static int traverseudata (global_State *g, Udata *u) {
int i;
markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv);
return 1 + u->nuvalue;
}
/*
** traverse one gray object, turning it to black (except for threads,
** which are always gray).
@@ -654,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 */
}
/* }====================================================== */
@@ -680,19 +657,6 @@ static void convergeephemerons (global_State *g) {
** =======================================================
*/
static void clearprotolist (global_State *g) {
GCObject *p = g->protogray;
g->protogray = NULL;
while (p != NULL) {
Proto *pp = gco2p(p);
GCObject *next = pp->gclist;
lua_assert(isgray(pp) && (pp->cache != NULL || pp->cachemiss >= MAXMISS));
gray2black(pp);
checkprotocache(g, pp);
p = next;
}
}
/*
** clear entries with unmarked keys from all weaktables in list 'l'
@@ -837,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 */
}
}
}
@@ -868,36 +833,29 @@ static void dothecall (lua_State *L, void *ud) {
}
static void GCTM (lua_State *L, int propagateerrors) {
static void GCTM (lua_State *L) {
global_State *g = G(L);
const TValue *tm;
TValue v;
lua_assert(!g->gcemergency);
setgcovalue(L, &v, udata2finalize(g));
tm = luaT_gettmbyobj(L, &v, TM_GC);
if (tm != NULL && ttisfunction(tm)) { /* is there a finalizer? */
if (!notm(tm)) { /* is there a finalizer? */
int status;
lu_byte oldah = L->allowhook;
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 && propagateerrors) { /* error while running __gc? */
if (status == LUA_ERRRUN) { /* is there an error object? */
const char *msg = (ttisstring(s2v(L->top - 1)))
? svalue(s2v(L->top - 1))
: "no message";
luaO_pushfstring(L, "error in __gc metamethod (%s)", msg);
status = LUA_ERRGCMM; /* error in __gc metamethod */
}
luaD_throw(L, status); /* re-throw error */
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
luaE_warnerror(L, "__gc metamethod");
L->top--; /* pops error object */
}
}
}
@@ -910,7 +868,7 @@ static int runafewfinalizers (lua_State *L, int n) {
global_State *g = G(L);
int i;
for (i = 0; i < n && g->tobefnz; i++)
GCTM(L, 1); /* call one finalizer */
GCTM(L); /* call one finalizer */
return i;
}
@@ -918,10 +876,10 @@ static int runafewfinalizers (lua_State *L, int n) {
/*
** call all pending finalizers
*/
static void callallpendingfinalizers (lua_State *L, int propagateerrors) {
static void callallpendingfinalizers (lua_State *L) {
global_State *g = G(L);
while (g->tobefnz)
GCTM(L, propagateerrors);
GCTM(L);
}
@@ -1004,6 +962,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
** =======================================================
*/
static void setpause (global_State *g);
/* mask to erase all color bits, not changing gen-related stuff */
#define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS))
@@ -1038,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 */
@@ -1078,9 +1038,9 @@ static void whitelist (global_State *g, GCObject *p) {
/*
** Correct a list of gray objects. Because this correction is
** done after sweeping, young objects can be white and still
** be in the list. They are only removed.
** Correct a list of gray objects.
** Because this correction is done after sweeping, young objects might
** be turned white and still be in the list. They are only removed.
** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2'
** objects become regular old and are removed from the list.
** For threads, just remove white ones from the list.
@@ -1097,13 +1057,14 @@ static GCObject **correctgraylist (GCObject **p) {
changeage(curr, G_TOUCHED1, G_TOUCHED2);
p = next; /* go to next element */
}
else {
if (!iswhite(curr)) {
else { /* not touched in this cycle */
if (!iswhite(curr)) { /* not white? */
lua_assert(isold(curr));
if (getage(curr) == G_TOUCHED2)
changeage(curr, G_TOUCHED2, G_OLD);
if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */
changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */
gray2black(curr); /* make it black */
}
/* else, object is white: just remove it from this list */
*p = *next; /* remove 'curr' from gray list */
}
break;
@@ -1139,7 +1100,7 @@ static void correctgraylists (global_State *g) {
/*
** Mark 'old1' objects when starting a new young collection.
** Mark 'OLD1' objects when starting a new young collection.
** Gray objects are already in some gray list, and so will be visited
** in the atomic step.
*/
@@ -1165,14 +1126,14 @@ static void finishgencycle (lua_State *L, global_State *g) {
checkSizes(L, g);
g->gcstate = GCSpropagate; /* skip restart */
if (!g->gcemergency)
callallpendingfinalizers(L, 1);
callallpendingfinalizers(L);
}
/*
** Does a young collection. First, mark 'old1' objects. (Only survival
** and "recent old" lists can contain 'old1' objects. New lists cannot
** contain 'old1' objects, at most 'old0' objects that were already
** Does a young collection. First, mark 'OLD1' objects. (Only survival
** and "recent old" lists can contain 'OLD1' objects. New lists cannot
** contain 'OLD1' objects, at most 'OLD0' objects that were already
** visited when marked old.) Then does the atomic step. Then,
** sweep all lists and advance pointers. Finally, finish the collection.
*/
@@ -1205,15 +1166,7 @@ static void youngcollection (lua_State *L, global_State *g) {
}
/*
** Enter generational mode. Must go until the end of an atomic cycle
** to ensure that all threads are in the gray list. Then, turn all
** objects into old and finishes the collection.
*/
static void entergen (lua_State *L, global_State *g) {
luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
atomic(L);
static void atomic2gen (lua_State *L, global_State *g) {
/* sweep all elements making them old */
sweep2old(L, &g->allgc);
/* everything alive now is old */
@@ -1226,15 +1179,31 @@ static void entergen (lua_State *L, global_State *g) {
sweep2old(L, &g->tobefnz);
g->gckind = KGC_GEN;
g->lastatomic = 0;
g->GCestimate = gettotalbytes(g); /* base for memory control */
finishgencycle(L, g);
}
/*
** Enter generational mode. Must go until the end of an atomic cycle
** to ensure that all threads and weak tables are in the gray lists.
** Then, turn all objects into old and finishes the collection.
*/
static lu_mem entergen (lua_State *L, global_State *g) {
lu_mem numobjs;
luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
numobjs = atomic(L); /* propagates all and then do the atomic stuff */
atomic2gen(L, g);
return numobjs;
}
/*
** Enter incremental mode. Turn all objects white, make all
** intermediate lists point to NULL (to avoid invalid pointers),
** and go to pause state.
** and go to the pause state.
*/
static void enterinc (global_State *g) {
whitelist(g, g->allgc);
@@ -1244,6 +1213,7 @@ static void enterinc (global_State *g) {
g->finobjrold = g->finobjold = g->finobjsur = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_INC;
g->lastatomic = 0;
}
@@ -1258,40 +1228,114 @@ void luaC_changemode (lua_State *L, int newmode) {
else
enterinc(g); /* entering incremental mode */
}
g->lastatomic = 0;
}
/*
** Does a full collection in generational mode.
*/
static void fullgen (lua_State *L, global_State *g) {
static lu_mem fullgen (lua_State *L, global_State *g) {
enterinc(g);
entergen(L, g);
return entergen(L, g);
}
/*
** Does a generational "step". If memory grows 'genmajormul'% larger
** than last major collection (kept in 'g->GCestimate'), does a major
** collection. Otherwise, does a minor collection and set debt to make
** another collection when memory grows 'genminormul'% larger.
** Set debt for the next minor collection, which will happen when
** memory grows 'genminormul'%.
*/
static void setminordebt (global_State *g) {
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
}
/*
** Does a major collection after last collection was a "bad collection".
**
** 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
** collection that frees too few objects. To avoid the cost of switching
** between generational mode and the incremental mode needed for full
** (major) collections, the collector tries to stay in incremental mode
** after a bad collection, and to switch back to generational mode only
** after a "good" collection (one that traverses less than 9/8 objects
** of the previous one).
** The collector must choose whether to stay in incremental mode or to
** switch back to generational mode before sweeping. At this point, it
** does not know the real memory in use, so it cannot use memory to
** decide whether to return to generational mode. Instead, it uses the
** number of objects traversed (returned by 'atomic') as a proxy. The
** field 'g->lastatomic' keeps this count from the last collection.
** ('g->lastatomic != 0' also means that the last collection was bad.)
*/
static void stepgenfull (lua_State *L, global_State *g) {
lu_mem newatomic; /* count of traversed objects */
lu_mem lastatomic = g->lastatomic; /* count from last collection */
if (g->gckind == KGC_GEN) /* still in generational mode? */
enterinc(g); /* enter incremental mode */
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
newatomic = atomic(L); /* mark everybody */
if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */
atomic2gen(L, g); /* return to generational mode */
setminordebt(g);
}
else { /* another bad collection; stay in incremental mode */
g->GCestimate = gettotalbytes(g); /* first estimate */;
entersweep(L);
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
setpause(g);
g->lastatomic = newatomic;
}
}
/*
** Does a generational "step".
** Usually, this means doing a minor collection and setting the debt to
** make another collection when memory grows 'genminormul'% larger.
**
** However, there are exceptions. If memory grows 'genmajormul'%
** larger than it was at the end of the last major collection (kept
** in 'g->GCestimate'), the function does a major collection. At the
** end, it checks whether the major collection was able to free a
** decent amount of memory (at least half the growth in memory since
** previous major collection). If so, the collector keeps its state,
** and the next collection will probably be minor again. Otherwise,
** we have what we call a "bad collection". In that case, set the field
** 'g->lastatomic' to signal that fact, so that the next collection will
** go to 'stepgenfull'.
**
** 'GCdebt <= 0' means an explicit call to GC step with "size" zero;
** in that case, always do a minor collection.
** in that case, do a minor collection.
*/
static void genstep (lua_State *L, global_State *g) {
lu_mem majorbase = g->GCestimate;
int majormul = getgcparam(g->genmajormul);
if (g->GCdebt > 0 &&
gettotalbytes(g) > (majorbase / 100) * (100 + majormul)) {
fullgen(L, g);
}
if (g->lastatomic != 0) /* last collection was a bad one? */
stepgenfull(L, g); /* do a full step */
else {
lu_mem mem;
youngcollection(L, g);
mem = gettotalbytes(g);
luaE_setdebt(g, -(cast(l_mem, (mem / 100)) * g->genminormul));
g->GCestimate = majorbase; /* preserve base value */
lu_mem majorbase = g->GCestimate; /* memory after last major collection */
lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul);
if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) {
lu_mem numobjs = fullgen(L, g); /* do a major collection */
if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
/* collected at least half of memory growth since last major
collection; keep doing minor collections */
setminordebt(g);
}
else { /* bad collection */
g->lastatomic = numobjs; /* signal that last collection was bad */
setpause(g); /* do a long wait for next (major) collection */
}
}
else { /* regular case; do a minor collection */
youngcollection(L, g);
setminordebt(g);
g->GCestimate = majorbase; /* preserve base value */
}
}
lua_assert(isdecGCmodegen(g));
}
/* }====================================================== */
@@ -1361,7 +1405,7 @@ void luaC_freeallobjects (lua_State *L) {
luaC_changemode(L, KGC_INC);
separatetobefnz(g, 1); /* separate all objects with finalizers */
lua_assert(g->finobj == NULL);
callallpendingfinalizers(L, 0);
callallpendingfinalizers(L);
deletelist(L, g->allgc, obj2gco(g->mainthread));
deletelist(L, g->finobj, NULL);
deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
@@ -1382,6 +1426,7 @@ static lu_mem atomic (lua_State *L) {
/* registry and global metatables may be changed by API */
markvalue(g, &g->l_registry);
markmt(g); /* mark global metatables */
work += propagateall(g); /* empties 'gray' list */
/* remark occasional upvalues of (maybe) dead threads */
work += remarkupvals(g);
work += propagateall(g); /* propagate changes */
@@ -1405,7 +1450,6 @@ static lu_mem atomic (lua_State *L) {
clearbyvalues(g, g->weak, origweak);
clearbyvalues(g, g->allweak, origall);
luaS_clearcache(g);
clearprotolist(g);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
lua_assert(g->gray == NULL);
return work; /* estimate of slots marked by 'atomic' */
@@ -1446,8 +1490,7 @@ static lu_mem singlestep (lua_State *L) {
return propagatemark(g); /* traverse one gray object */
}
case GCSenteratomic: {
lu_mem work = propagateall(g); /* make sure gray list is empty */
work += atomic(L); /* work is what was traversed by 'atomic' */
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L);
g->GCestimate = gettotalbytes(g); /* first estimate */;
return work;
@@ -1523,10 +1566,10 @@ static void incstep (lua_State *L, global_State *g) {
void luaC_step (lua_State *L) {
global_State *g = G(L);
if (g->gcrunning) { /* running? */
if (g->gckind == KGC_INC)
incstep(L, g);
else
if(isdecGCmodegen(g))
genstep(L, g);
else
incstep(L, g);
}
}

17
lgc.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lgc.h,v 2.102 2018/02/19 20:06:56 roberto Exp roberto $
** $Id: lgc.h $
** Garbage Collector
** See Copyright Notice in lua.h
*/
@@ -123,11 +123,11 @@
#define LUAI_GENMINORMUL 20
/* wait memory to double before starting new cycle */
#define LUAI_GCPAUSE 200 /* 200% */
#define LUAI_GCPAUSE 200
/*
** some gc parameters are stored divided by 4 to allow a maximum value
** larger than 1000 in a 'lu_byte'.
** up to 1023 in a 'lu_byte'.
*/
#define getgcparam(p) ((p) * 4)
#define setgcparam(p,v) ((p) = (v) / 4)
@@ -138,6 +138,13 @@
#define LUAI_GCSTEPSIZE 13 /* 8 KB */
/*
** Check whether the declared GC mode is generational. While in
** generational mode, the collector can go temporarily to incremental
** mode to improve performance. This is signaled by 'g->lastatomic != 0'.
*/
#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
/*
** Does one step of collection when debt becomes positive. 'pre'/'pos'
** allows some adjustments to be done only when needed. macro
@@ -164,9 +171,6 @@
(isblack(p) && iswhite(o)) ? \
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
#define luaC_protobarrier(L,p,o) \
(isblack(p) ? luaC_protobarrier_(L,p) : cast_void(0))
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);
@@ -175,7 +179,6 @@ LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p);
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);

View File

@@ -1,5 +1,5 @@
/*
** $Id: linit.c,v 1.40 2017/06/27 18:32:49 roberto Exp roberto $
** $Id: linit.c $
** Initialization of libraries for lua.c and other clients
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: liolib.c,v 2.155 2018/02/21 13:48:44 roberto Exp roberto $
** $Id: liolib.c $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
@@ -39,7 +39,7 @@
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
static int l_checkmode (const char *mode) {
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
(*mode != '+' || (++mode, 1)) && /* skip if char is '+' */
(*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
}
@@ -68,7 +68,7 @@ static int l_checkmode (const char *mode) {
/* ISO C definitions */
#define l_popen(L,c,m) \
((void)((void)c, m), \
((void)c, (void)m, \
luaL_error(L, "'popen' not supported"), \
(FILE*)0)
#define l_pclose(L,file) ((void)L, (void)file, -1)
@@ -133,6 +133,7 @@ static int l_checkmode (const char *mode) {
/* }====================================================== */
#define IO_PREFIX "_IO_"
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
#define IO_INPUT (IO_PREFIX "input")
@@ -152,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
@@ -336,12 +337,22 @@ static int io_readline (lua_State *L);
*/
#define MAXARGLINE 250
/*
** 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)
** 2) the number of arguments to read
** 3) a boolean, true iff file has to be closed when finished ('toclose')
** *) a variable number of format arguments (rest of the stack)
*/
static void aux_lines (lua_State *L, int toclose) {
int n = lua_gettop(L) - 1; /* number of arguments to read */
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
lua_pushvalue(L, 1); /* file */
lua_pushinteger(L, n); /* number of arguments to read */
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */
lua_rotate(L, 2, 3); /* move the three values to their positions */
lua_pushcclosure(L, io_readline, 3 + n);
}
@@ -353,6 +364,11 @@ static int f_lines (lua_State *L) {
}
/*
** Return an iteration function for 'io.lines'. If file has to be
** closed, also returns the file itself as a second result (to be
** closed as the state at the exit of a generic for).
*/
static int io_lines (lua_State *L) {
int toclose;
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
@@ -368,8 +384,15 @@ static int io_lines (lua_State *L) {
lua_replace(L, 1); /* put file at index 1 */
toclose = 1; /* close it after iteration */
}
aux_lines(L, toclose);
return 1;
aux_lines(L, toclose); /* push iteration function */
if (toclose) {
lua_pushnil(L); /* state */
lua_pushnil(L); /* control */
lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */
return 4;
}
else
return 1;
}
@@ -435,7 +458,7 @@ static int readdigits (RN *rn, int hex) {
/*
** Read a number: first reads a valid prefix of a numeral into a buffer.
** Then it calls 'lua_stringtonumber' to check whether the format is
** correct and to convert it to a Lua number
** correct and to convert it to a Lua number.
*/
static int read_number (lua_State *L, FILE *f) {
RN rn;
@@ -481,17 +504,17 @@ static int test_eof (lua_State *L, FILE *f) {
static int read_line (lua_State *L, FILE *f, int chop) {
luaL_Buffer b;
int c = '\0';
int c;
luaL_buffinit(L, &b);
while (c != EOF && c != '\n') { /* repeat until end of line */
char *buff = luaL_prepbuffer(&b); /* preallocate buffer */
do { /* may need to read several chunks to get whole line */
char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */
int i = 0;
l_lockfile(f); /* no memory errors can happen inside the lock */
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
buff[i++] = c;
buff[i++] = c; /* read up to end of line or buffer limit */
l_unlockfile(f);
luaL_addsize(&b, i);
}
} while (c != EOF && c != '\n'); /* repeat until end of line */
if (!chop && c == '\n') /* want a newline and have one? */
luaL_addchar(&b, c); /* add ending newline to result */
luaL_pushresult(&b); /* close buffer */
@@ -570,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;
}
@@ -586,6 +609,9 @@ static int f_read (lua_State *L) {
}
/*
** Iteration function for 'lines'.
*/
static int io_readline (lua_State *L) {
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
int i;
@@ -600,14 +626,14 @@ 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));
}
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
lua_settop(L, 0); /* clear stack */
lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */
aux_close(L); /* close it */
}
return 0;
@@ -716,26 +742,37 @@ 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},
{NULL, NULL}
};
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 */
}
@@ -745,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;
}

View File

@@ -1,3 +1,10 @@
/*
** $Id: ljumptab.h $
** Jump Table for the Lua interpreter
** See Copyright Notice in lua.h
*/
#undef vmdispatch
#undef vmcase
#undef vmbreak
@@ -9,7 +16,7 @@
#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
static void *disptab[] = {
static void *disptab[NUM_OPCODES] = {
#if 0
** you can update the following list with this command:
@@ -38,12 +45,13 @@ static void *disptab[] = {
&&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,
&&L_OP_MODK,
&&L_OP_POWK,
&&L_OP_DIVK,
&&L_OP_IDIVK,
&&L_OP_BANDK,
&&L_OP_BORK,
&&L_OP_BXORK,
@@ -61,12 +69,16 @@ static void *disptab[] = {
&&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,
&&L_OP_LEN,
&&L_OP_CONCAT,
&&L_OP_CLOSE,
&&L_OP_TBC,
&&L_OP_JMP,
&&L_OP_EQ,
&&L_OP_LT,
@@ -84,16 +96,15 @@ static void *disptab[] = {
&&L_OP_RETURN,
&&L_OP_RETURN0,
&&L_OP_RETURN1,
&&L_OP_FORLOOP1,
&&L_OP_FORPREP1,
&&L_OP_FORLOOP,
&&L_OP_FORPREP,
&&L_OP_TFORPREP,
&&L_OP_TFORCALL,
&&L_OP_TFORLOOP,
&&L_OP_SETLIST,
&&L_OP_CLOSURE,
&&L_OP_VARARG,
&&L_OP_PREPVARARG,
&&L_OP_VARARGPREP,
&&L_OP_EXTRAARG
};

63
llex.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: llex.c,v 2.101 2018/03/07 15:55:38 roberto Exp roberto $
** $Id: llex.c $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
@@ -29,7 +29,7 @@
#define next(ls) (ls->current = zgetc(ls->z))
#define next(ls) (ls->current = zgetc(ls->z))
@@ -82,7 +82,10 @@ void luaX_init (lua_State *L) {
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) { /* single-byte symbols? */
lua_assert(token == cast_uchar(token));
return luaO_pushfstring(ls->L, "'%c'", token);
if (lisprint(token))
return luaO_pushfstring(ls->L, "'%c'", token);
else /* control character */
return luaO_pushfstring(ls->L, "'<\\%d>'", token);
}
else {
const char *s = luaX_tokens[token - FIRST_RESERVED];
@@ -208,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;
@@ -220,14 +231,14 @@ 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 (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? */
lexerror(ls, "malformed number", TK_FLT);
@@ -244,12 +255,12 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
/*
** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return
** its number of '='s; otherwise, return a negative number (-1 iff there
** are no '='s after initial bracket)
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
** If sequence is well formed, return its number of '='s + 2; otherwise,
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
*/
static int skip_sep (LexState *ls) {
int count = 0;
static size_t skip_sep (LexState *ls) {
size_t count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
@@ -257,11 +268,13 @@ static int skip_sep (LexState *ls) {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
return (ls->current == s) ? count + 2
: (count == 0) ? 1
: 0;
}
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
int line = ls->linenumber; /* initial line (for error message) */
save_and_next(ls); /* skip 2nd '[' */
if (currIsNewline(ls)) /* string starts with a newline? */
@@ -295,8 +308,8 @@ static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
}
} endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
luaZ_bufflen(ls->buff) - 2*(2 + sep));
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
luaZ_bufflen(ls->buff) - 2 * sep);
}
@@ -330,10 +343,10 @@ static unsigned long readutf8esc (LexState *ls) {
save_and_next(ls); /* skip 'u' */
esccheck(ls, ls->current == '{', "missing '{'");
r = gethexa(ls); /* must have at least one digit */
while ((save_and_next(ls), lisxdigit(ls->current))) {
while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) {
i++;
esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large");
r = (r << 4) + luaO_hexavalue(ls->current);
esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large");
}
esccheck(ls, ls->current == '}', "missing '}'");
next(ls); /* skip '}' */
@@ -444,9 +457,9 @@ static int llex (LexState *ls, SemInfo *seminfo) {
/* else is a comment */
next(ls);
if (ls->current == '[') { /* long comment? */
int sep = skip_sep(ls);
size_t sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
if (sep >= 0) {
if (sep >= 2) {
read_long_string(ls, NULL, sep); /* skip long comment */
luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
break;
@@ -458,12 +471,12 @@ static int llex (LexState *ls, SemInfo *seminfo) {
break;
}
case '[': { /* long string or simply '[' */
int sep = skip_sep(ls);
if (sep >= 0) {
size_t sep = skip_sep(ls);
if (sep >= 2) {
read_long_string(ls, seminfo, sep);
return TK_STRING;
}
else if (sep != -1) /* '[=...' missing second bracket */
else if (sep == 0) /* '[=...' missing second bracket? */
lexerror(ls, "invalid long string delimiter", TK_STRING);
return '[';
}

2
llex.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: llex.h,v 1.81 2018/03/07 15:55:38 roberto Exp roberto $
** $Id: llex.h $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: llimits.h,v 1.150 2018/05/30 14:25:52 roberto Exp roberto $
** $Id: llimits.h $
** Limits, basic types, and some other 'installation-dependent' definitions
** See Copyright Notice in lua.h
*/
@@ -14,6 +14,7 @@
#include "lua.h"
/*
** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count
** the total memory used by Lua (in bytes). Usually, 'size_t' and
@@ -22,7 +23,7 @@
#if defined(LUAI_MEM) /* { external definitions? */
typedef LUAI_UMEM lu_mem;
typedef LUAI_MEM l_mem;
#elif LUAI_BITSINT >= 32 /* }{ */
#elif LUAI_IS32INT /* }{ */
typedef size_t lu_mem;
typedef ptrdiff_t l_mem;
#else /* 16-bit ints */ /* }{ */
@@ -39,7 +40,7 @@ typedef signed char ls_byte;
/* maximum value for size_t */
#define MAX_SIZET ((size_t)(~(size_t)0))
/* maximum size visible for Lua (must be representable in a lua_Integer */
/* maximum size visible for Lua (must be representable in a lua_Integer) */
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: (size_t)(LUA_MAXINTEGER))
@@ -65,6 +66,10 @@ typedef signed char ls_byte;
#define ispow2(x) (((x) & ((x) - 1)) == 0)
/* number of chars of a literal string without the ending \0 */
#define LL(x) (sizeof(x)/sizeof(char) - 1)
/*
** conversion of pointer to unsigned integer:
** this is for hashing only; there is no problem if the integer
@@ -168,27 +173,18 @@ typedef LUAI_UACINT l_uacInt;
#endif
/*
** maximum depth for nested C calls and syntactical nested non-terminals
** in a program. (Value must fit in an unsigned short int. It must also
** be compatible with the size of the C stack.)
*/
#if !defined(LUAI_MAXCCALLS)
#define LUAI_MAXCCALLS 2200
#endif
/*
** type for virtual-machine instructions;
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
*/
#if LUAI_BITSINT >= 32
typedef unsigned int Instruction;
#if LUAI_IS32INT
typedef unsigned int l_uint32;
#else
typedef unsigned long Instruction;
typedef unsigned long l_uint32;
#endif
typedef l_uint32 Instruction;
/*
@@ -293,15 +289,20 @@ typedef unsigned long Instruction;
#endif
/*
** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when
** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of
** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b)
** ~= floor(a/b)'. That happens when the division has a non-integer
** negative result, which is equivalent to the test below.
** modulo: defined as 'a - floor(a/b)*b'; the direct computation
** using this definition has several problems with rounding errors,
** so it is better to use 'fmod'. 'fmod' gives the result of
** 'a - trunc(a/b)*b', and therefore must be corrected when
** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a
** non-integer negative result: non-integer result is equivalent to
** a non-zero remainder 'm'; negative result is equivalent to 'a' and
** 'b' with different signs, or 'm' and 'b' with different signs
** (as the result 'm' of 'fmod' has the same sign of 'a').
*/
#if !defined(luai_nummod)
#define luai_nummod(L,a,b,m) \
{ (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); }
{ (void)L; (m) = l_mathop(fmod)(a,b); \
if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); }
#endif
/* exponentiation */
@@ -318,6 +319,8 @@ typedef unsigned long Instruction;
#define luai_numeq(a,b) ((a)==(b))
#define luai_numlt(a,b) ((a)<(b))
#define luai_numle(a,b) ((a)<=(b))
#define luai_numgt(a,b) ((a)>(b))
#define luai_numge(a,b) ((a)>=(b))
#define luai_numisnan(a) (!luai_numeq((a), (a)))
#endif

View File

@@ -1,5 +1,5 @@
/*
** $Id: lmathlib.c,v 1.134 2018/05/16 11:27:59 roberto Exp roberto $
** $Id: lmathlib.c $
** Standard mathematical library
** See Copyright Notice in lua.h
*/
@@ -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;
}
@@ -266,7 +266,7 @@ static int math_type (lua_State *L) {
/* try to find an integer type with at least 64 bits */
#if (LONG_MAX >> 31 >> 31) >= 1
#if (ULONG_MAX >> 31 >> 31) >= 3
/* 'long' has at least 64 bits */
#define Rand64 unsigned long
@@ -276,7 +276,7 @@ static int math_type (lua_State *L) {
/* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long
#elif (LUA_MAXINTEGER >> 31 >> 31) >= 1
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
/* 'lua_Integer' has at least 64 bits */
#define Rand64 lua_Unsigned
@@ -301,7 +301,7 @@ static int math_type (lua_State *L) {
/* rotate left 'x' by 'n' bits */
static Rand64 rotl (Rand64 x, int n) {
return (x << n) | (trim64(x) >> (64 - n));
return (x << n) | (trim64(x) >> (64 - n));
}
static Rand64 nextrand (Rand64 *state) {
@@ -323,14 +323,18 @@ static Rand64 nextrand (Rand64 *state) {
/*
** Convert bits from a random integer into a float in the
** interval [0,1).
** interval [0,1), getting the higher FIG bits from the
** random unsigned integer and converting that to a float.
*/
#define maskFIG (~(~(Rand64)1 << (FIGS - 1))) /* use FIGS bits */
#define shiftFIG \
(l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) /* 2^(-FIGS) */
/* must throw out the extra (64 - FIGS) bits */
#define shift64_FIG (64 - FIGS)
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
static lua_Number I2d (Rand64 x) {
return (lua_Number)(x & maskFIG) * shiftFIG;
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
}
/* convert a 'Rand64' to a 'lua_Unsigned' */
@@ -343,7 +347,7 @@ static lua_Number I2d (Rand64 x) {
#else /* no 'Rand64' }{ */
/* get an integer with at least 32 bits */
#if (INT_MAX >> 30) >= 1
#if LUAI_IS32INT
typedef unsigned int lu_int32;
#else
typedef unsigned long lu_int32;
@@ -449,35 +453,49 @@ static Rand64 nextrand (Rand64 *state) {
/* an unsigned 1 with proper type */
#define UONE ((lu_int32)1)
#if FIGS <= 32
#define maskHI 0 /* do not need bits from higher half */
#define maskLOW (~(~UONE << (FIGS - 1))) /* use FIGS bits */
#define shiftFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) /* 2^(-FIGS) */
/* 2^(-FIGS) */
#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
/*
** get up to 32 bits from higher half, shifting right to
** throw out the extra bits.
*/
static lua_Number I2d (Rand64 x) {
lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
return h * scaleFIG;
}
#else /* 32 < FIGS <= 64 */
/* must take care to not shift stuff by more than 31 slots */
/* use FIGS - 32 bits from higher half */
#define maskHI (~(~UONE << (FIGS - 33)))
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
#define scaleFIG \
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
/* use 32 bits from lower half */
#define maskLOW (~(~UONE << 31))
/*
** use FIGS - 32 bits from lower half, throwing out the other
** (32 - (FIGS - 32)) = (64 - FIGS) bits
*/
#define shiftLOW (64 - FIGS)
/* 2^(-FIGS) == (1 / 2^33) / 2^(FIGS-33) */
#define shiftFIG ((lua_Number)(1.0 / 8589934592.0) / (UONE << (FIGS - 33)))
/*
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
*/
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
#endif
#define twoto32 l_mathop(4294967296.0) /* 2^32 */
static lua_Number I2d (Rand64 x) {
lua_Number h = (lua_Number)(x.h & maskHI);
lua_Number l = (lua_Number)(x.l & maskLOW);
return (h * twoto32 + l) * shiftFIG;
lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
return (h + l) * scaleFIG;
}
#endif
/* convert a 'Rand64' to a 'lua_Unsigned' */
static lua_Unsigned I2UInt (Rand64 x) {
@@ -520,7 +538,7 @@ static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
lim |= (lim >> 4);
lim |= (lim >> 8);
lim |= (lim >> 16);
#if (LUA_MAXINTEGER >> 30 >> 1) > 0
#if (LUA_MAXUNSIGNED >> 31) >= 3
lim |= (lim >> 32); /* integer type has more than 32 bits */
#endif
}
@@ -568,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 */
@@ -576,15 +595,34 @@ 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);
}
/*
** Set a "random" seed. To get some randomness, use the current time
** and the address of 'L' (in case the machine does address space layout
** randomization).
*/
static void randseed (lua_State *L, RanState *state) {
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
setseed(L, state->s, seed1, seed2);
}
static int math_randomseed (lua_State *L) {
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
lua_Integer n1 = luaL_checkinteger(L, 1);
lua_Integer n2 = luaL_optinteger(L, 2, 0);
setseed(state->s, n1, n2);
return 0;
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(L, state->s, n1, n2);
}
return 2; /* return seeds */
}
@@ -597,15 +635,11 @@ static const luaL_Reg randfuncs[] = {
/*
** Register the random functions and initialize their state.
** To give some "randomness" to the initial seed, use the current time
** and the address of 'L' (in case the machine does address space layout
** randomization).
*/
static void setrandfunc (lua_State *L) {
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
setseed(state->s, seed1, seed2);
randseed(L, state); /* initialize with a "random" seed */
lua_pop(L, 2); /* remove pushed seeds */
luaL_setfuncs(L, randfuncs, 1);
}

89
lmem.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lmem.c,v 1.97 2018/05/30 14:25:52 roberto Exp roberto $
** $Id: lmem.c $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
@@ -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");
@@ -108,7 +131,7 @@ l_noret luaM_toobig (lua_State *L) {
*/
void luaM_free_ (lua_State *L, void *block, size_t osize) {
global_State *g = G(L);
lua_assert((block == 0) == (block == NULL));
lua_assert((osize == 0) == (block == NULL));
(*g->frealloc)(g->ud, block, osize, 0);
g->GCdebt -= osize;
}
@@ -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)

2
lmem.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lmem.h,v 1.46 2017/12/08 17:28:25 roberto Exp roberto $
** $Id: lmem.h $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/

145
loadlib.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: loadlib.c,v 1.131 2017/12/13 12:51:42 roberto Exp roberto $
** $Id: loadlib.c $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
@@ -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"
@@ -290,22 +290,33 @@ static int noenv (lua_State *L) {
static void setpath (lua_State *L, const char *fieldname,
const char *envname,
const char *dft) {
const char *dftmark;
const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
const char *path = getenv(nver); /* use versioned name */
if (path == NULL) /* no environment variable? */
const char *path = getenv(nver); /* try versioned name */
if (path == NULL) /* no versioned environment variable? */
path = getenv(envname); /* try unversioned name */
if (path == NULL || noenv(L)) /* no environment variable? */
lua_pushstring(L, dft); /* use default */
else {
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP,
LUA_PATH_SEP AUXMARK LUA_PATH_SEP);
luaL_gsub(L, path, AUXMARK, dft);
lua_remove(L, -2); /* remove result from 1st 'gsub' */
else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
lua_pushstring(L, path); /* nothing to change */
else { /* path contains a ";;": insert default path in its place */
size_t len = strlen(path);
luaL_Buffer b;
luaL_buffinit(L, &b);
if (path < dftmark) { /* is there a prefix before ';;'? */
luaL_addlstring(&b, path, dftmark - path); /* add it */
luaL_addchar(&b, *LUA_PATH_SEP);
}
luaL_addstring(&b, dft); /* add default */
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);
}
luaL_pushresult(&b);
}
setprogdir(L);
lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */
lua_pop(L, 1); /* pop versioned variable name */
lua_pop(L, 1); /* pop versioned variable name ('nver') */
}
/* }================================================================== */
@@ -316,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' */
@@ -329,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 */
@@ -397,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 */
}
}
@@ -421,14 +432,42 @@ static int readable (const char *filename) {
}
static const char *pushnexttemplate (lua_State *L, const char *path) {
const char *l;
while (*path == *LUA_PATH_SEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATH_SEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, l - path); /* template */
return l;
/*
** Get the next name in '*path' = 'name1;name2;name3;...', changing
** the ending ';' to '\0' to create a zero-terminated string. Return
** NULL when list ends.
*/
static const char *getnextfilename (char **path, char *end) {
char *sep;
char *name = *path;
if (name == end)
return NULL; /* no more names */
else if (*name == '\0') { /* from previous iteration? */
*name = *LUA_PATH_SEP; /* restore separator */
name++; /* skip it */
}
sep = strchr(name, *LUA_PATH_SEP); /* find next separator */
if (sep == NULL) /* separator not found? */
sep = end; /* name goes until the end */
*sep = '\0'; /* finish file name */
*path = sep; /* will start next search from here */
return name;
}
/*
** Given a path such as ";blabla.so;blublu.so", pushes the string
**
** no file 'blabla.so'
** no file 'blublu.so'
*/
static void pusherrornotfound (lua_State *L, const char *path) {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addstring(&b, "\n\tno file '");
luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
luaL_addstring(&b, "'");
luaL_pushresult(&b);
}
@@ -436,21 +475,25 @@ static const char *searchpath (lua_State *L, const char *name,
const char *path,
const char *sep,
const char *dirsep) {
luaL_Buffer msg; /* to build error message */
if (*sep != '\0') /* non-empty separator? */
luaL_Buffer buff;
char *pathname; /* path with name inserted */
char *endpathname; /* its end */
const char *filename;
/* separator is non-empty and appears in 'name'? */
if (*sep != '\0' && strchr(name, *sep) != NULL)
name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
luaL_buffinit(L, &msg);
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename = luaL_gsub(L, lua_tostring(L, -1),
LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
luaL_buffinit(L, &buff);
/* add path to the buffer, replacing marks ('?') with the file name */
luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
luaL_addchar(&buff, '\0');
pathname = luaL_buffaddr(&buff); /* writable list of file names */
endpathname = pathname + luaL_bufflen(&buff) - 1;
while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file '%s'", filename);
lua_remove(L, -2); /* remove file name */
luaL_addvalue(&msg); /* concatenate error msg. entry */
return lua_pushstring(L, filename); /* save and return name */
}
luaL_pushresult(&msg); /* create error message */
luaL_pushresult(&buff); /* push path to create error message */
pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */
return NULL; /* not found */
}
@@ -462,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 */
}
}
@@ -560,9 +603,14 @@ static int searcher_Croot (lua_State *L) {
static int searcher_preload (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
if (lua_getfield(L, -1, name) == LUA_TNIL) /* not found? */
if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
return 1;
}
else {
lua_pushliteral(L, ":preload:");
return 2;
}
}
@@ -604,17 +652,23 @@ static int ll_require (lua_State *L) {
/* else must load package */
lua_pop(L, 1); /* remove 'getfield' result */
findloader(L, name);
lua_pushstring(L, name); /* pass name as argument to module loader */
lua_insert(L, -2); /* name is 1st argument (before search data) */
lua_rotate(L, -2, 1); /* function <-> loader data */
lua_pushvalue(L, 1); /* name is 1st argument to module loader */
lua_pushvalue(L, -3); /* loader data is 2nd argument */
/* stack: ...; loader data; loader function; mod. name; loader data */
lua_call(L, 2, 1); /* run loader to load module */
/* stack: ...; loader data; result from loader */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* LOADED[name] = returned value */
else
lua_pop(L, 1); /* pop nil */
if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_copy(L, -1, -2); /* replace loader result */
lua_setfield(L, 2, name); /* LOADED[name] = true */
}
return 1;
lua_rotate(L, -2, 1); /* loader data <-> module result */
return 2; /* return module result and loader data */
}
/* }====================================================== */
@@ -662,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 */
}

251
lobject.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lobject.c,v 2.125 2018/04/25 16:26:20 roberto Exp roberto $
** $Id: lobject.c $
** Some generic functions over Lua objects
** See Copyright Notice in lua.h
*/
@@ -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))
*/
@@ -83,7 +57,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
case LUA_OPSUB:return intop(-, v1, v2);
case LUA_OPMUL:return intop(*, v1, v2);
case LUA_OPMOD: return luaV_mod(L, v1, v2);
case LUA_OPIDIV: return luaV_div(L, v1, v2);
case LUA_OPIDIV: return luaV_idiv(L, v1, v2);
case LUA_OPBAND: return intop(&, v1, v2);
case LUA_OPBOR: return intop(|, v1, v2);
case LUA_OPBXOR: return intop(^, v1, v2);
@@ -106,11 +80,7 @@ static lua_Number numarith (lua_State *L, int op, lua_Number v1,
case LUA_OPPOW: return luai_numpow(L, v1, v2);
case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
case LUA_OPUNM: return luai_numunm(L, v1);
case LUA_OPMOD: {
lua_Number m;
luai_nummod(L, v1, v2, m);
return m;
}
case LUA_OPMOD: return luaV_modf(L, v1, v2);
default: lua_assert(0); return 0;
}
}
@@ -347,7 +317,7 @@ size_t luaO_str2num (const char *s, TValue *o) {
int luaO_utf8esc (char *buff, unsigned long x) {
int n = 1; /* number of bytes put in buffer (backwards) */
lua_assert(x <= 0x10FFFF);
lua_assert(x <= 0x7FFFFFFFu);
if (x < 0x80) /* ascii? */
buff[UTF8BUFFSZ - 1] = cast_char(x);
else { /* need continuation bytes */
@@ -368,85 +338,178 @@ int luaO_utf8esc (char *buff, unsigned long x) {
/*
** Convert a number object to a string
** Convert a number object to a string, adding it to a buffer
*/
void luaO_tostring (lua_State *L, TValue *obj) {
char buff[MAXNUMBER2STR];
size_t len;
static int tostringbuff (TValue *obj, char *buff) {
int len;
lua_assert(ttisnumber(obj));
if (ttisinteger(obj))
len = lua_integer2str(buff, sizeof(buff), ivalue(obj));
len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
else {
len = lua_number2str(buff, sizeof(buff), fltvalue(obj));
len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
buff[len++] = lua_getlocaledecpoint();
buff[len++] = '0'; /* adds '.0' to result */
}
}
setsvalue(L, obj, luaS_newlstr(L, buff, len));
}
static void pushstr (lua_State *L, const char *str, size_t l) {
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
L->top++;
return len;
}
/*
** this function handles only '%d', '%c', '%f', '%p', and '%s'
** Convert a number object to a Lua string, replacing the value at 'obj'
*/
void luaO_tostring (lua_State *L, TValue *obj) {
char buff[MAXNUMBER2STR];
int len = tostringbuff(obj, buff);
setsvalue(L, obj, luaS_newlstr(L, buff, len));
}
/*
** {==================================================================
** 'luaO_pushvfstring'
** ===================================================================
*/
/* size for buffer space used by 'luaO_pushvfstring' */
#define BUFVFS 400
/* buffer used by 'luaO_pushvfstring' */
typedef struct BuffFS {
lua_State *L;
int pushed; /* number of string pieces already on the stack */
int blen; /* length of partial string in 'space' */
char space[BUFVFS]; /* holds last part of the result */
} BuffFS;
/*
** Push given string to the stack, as part of the buffer. If the stack
** is almost full, join all partial strings in the stack into one.
*/
static void pushstr (BuffFS *buff, const char *str, size_t l) {
lua_State *L = buff->L;
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
L->top++; /* may use one extra slot */
buff->pushed++;
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) {
luaV_concat(L, buff->pushed); /* join all partial results into one */
buff->pushed = 1;
}
}
/*
** empty the buffer space into the stack
*/
static void clearbuff (BuffFS *buff) {
pushstr(buff, buff->space, buff->blen); /* push buffer contents */
buff->blen = 0; /* space now is empty */
}
/*
** Get a space of size 'sz' in the buffer. If buffer has not enough
** space, empty it. 'sz' must fit in an empty buffer.
*/
static char *getbuff (BuffFS *buff, int sz) {
lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
if (sz > BUFVFS - buff->blen) /* not enough space? */
clearbuff(buff);
return buff->space + buff->blen;
}
#define addsize(b,sz) ((b)->blen += (sz))
/*
** Add 'str' to the buffer. If string is larger than the buffer space,
** push the string directly to the stack.
*/
static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
if (slen <= BUFVFS) { /* does string fit into buffer? */
char *bf = getbuff(buff, cast_int(slen));
memcpy(bf, str, slen); /* add string to buffer */
addsize(buff, cast_int(slen));
}
else { /* string larger than buffer */
clearbuff(buff); /* string comes after buffer's content */
pushstr(buff, str, slen); /* push string */
}
}
/*
** Add a number to the buffer.
*/
static void addnum2buff (BuffFS *buff, TValue *num) {
char *numbuff = getbuff(buff, MAXNUMBER2STR);
int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
addsize(buff, len);
}
/*
** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%'
conventional formats, plus Lua-specific '%I' and '%U'
*/
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
int n = 0; /* number of strings in the stack to concatenate */
const char *e; /* points to next conversion specifier */
BuffFS buff; /* holds last part of the result */
const char *e; /* points to next '%' */
buff.pushed = buff.blen = 0;
buff.L = L;
while ((e = strchr(fmt, '%')) != NULL) {
pushstr(L, fmt, e - fmt); /* string up to conversion specifier */
switch (*(e+1)) {
addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
switch (*(e + 1)) { /* conversion specifier */
case 's': { /* zero-terminated string */
const char *s = va_arg(argp, char *);
if (s == NULL) s = "(null)";
pushstr(L, s, strlen(s));
addstr2buff(&buff, s, strlen(s));
break;
}
case 'c': { /* an 'int' as a character */
char buff = cast_char(va_arg(argp, int));
if (lisprint(cast_uchar(buff)))
pushstr(L, &buff, 1);
else /* non-printable character; print its code */
luaO_pushfstring(L, "<\\%d>", cast_uchar(buff));
char c = cast_uchar(va_arg(argp, int));
addstr2buff(&buff, &c, sizeof(char));
break;
}
case 'd': { /* an 'int' */
setivalue(s2v(L->top), va_arg(argp, int));
goto top2str;
TValue num;
setivalue(&num, va_arg(argp, int));
addnum2buff(&buff, &num);
break;
}
case 'I': { /* a 'lua_Integer' */
setivalue(s2v(L->top), cast(lua_Integer, va_arg(argp, l_uacInt)));
goto top2str;
TValue num;
setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
addnum2buff(&buff, &num);
break;
}
case 'f': { /* a 'lua_Number' */
setfltvalue(s2v(L->top), cast_num(va_arg(argp, l_uacNumber)));
top2str: /* convert the top element to a string */
L->top++;
luaO_tostring(L, s2v(L->top - 1));
TValue num;
setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
addnum2buff(&buff, &num);
break;
}
case 'p': { /* a pointer */
char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */
const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
char *bf = getbuff(&buff, sz);
void *p = va_arg(argp, void *);
int l = lua_pointer2str(buff, sizeof(buff), p);
pushstr(L, buff, l);
int len = lua_pointer2str(bf, sz, p);
addsize(&buff, len);
break;
}
case 'U': { /* an 'int' as a UTF-8 sequence */
char buff[UTF8BUFFSZ];
int l = luaO_utf8esc(buff, cast(long, va_arg(argp, long)));
pushstr(L, buff + UTF8BUFFSZ - l, l);
case 'U': { /* a 'long' as a UTF-8 sequence */
char bf[UTF8BUFFSZ];
int len = luaO_utf8esc(bf, va_arg(argp, long));
addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
break;
}
case '%': {
pushstr(L, "%", 1);
addstr2buff(&buff, "%", 1);
break;
}
default: {
@@ -454,15 +517,12 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
*(e + 1));
}
}
n += 2;
if (L->top + 2 > L->stack_last) { /* no free stack space? */
luaV_concat(L, n);
n = 1;
}
fmt = e + 2;
fmt = e + 2; /* skip '%' and the specifier */
}
pushstr(L, fmt, strlen(fmt));
if (n > 0) luaV_concat(L, n + 1);
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */
if (buff.pushed > 1)
luaV_concat(L, buff.pushed); /* join all partial results */
return svalue(s2v(L->top - 1));
}
@@ -476,9 +536,8 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
return msg;
}
/* }================================================================== */
/* number of chars of a literal string without the ending \0 */
#define LL(x) (sizeof(x)/sizeof(char) - 1)
#define RETS "..."
#define PRE "[string \""
@@ -486,36 +545,36 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
void luaO_chunkid (char *out, const char *source, size_t bufflen) {
size_t l = strlen(source);
void luaO_chunkid (char *out, const char *source, size_t srclen) {
size_t bufflen = LUA_IDSIZE; /* free space in buffer */
if (*source == '=') { /* 'literal' source */
if (l <= bufflen) /* small enough? */
memcpy(out, source + 1, l * sizeof(char));
if (srclen <= bufflen) /* small enough? */
memcpy(out, source + 1, srclen * sizeof(char));
else { /* truncate it */
addstr(out, source + 1, bufflen - 1);
*out = '\0';
}
}
else if (*source == '@') { /* file name */
if (l <= bufflen) /* small enough? */
memcpy(out, source + 1, l * sizeof(char));
if (srclen <= bufflen) /* small enough? */
memcpy(out, source + 1, srclen * sizeof(char));
else { /* add '...' before rest of name */
addstr(out, RETS, LL(RETS));
bufflen -= LL(RETS);
memcpy(out, source + 1 + l - bufflen, bufflen * sizeof(char));
memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char));
}
}
else { /* string; format as [string "source"] */
const char *nl = strchr(source, '\n'); /* find first new line (if any) */
addstr(out, PRE, LL(PRE)); /* add prefix */
bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
if (l < bufflen && nl == NULL) { /* small one-line source? */
addstr(out, source, l); /* keep it */
if (srclen < bufflen && nl == NULL) { /* small one-line source? */
addstr(out, source, srclen); /* keep it */
}
else {
if (nl != NULL) l = nl - source; /* stop at first newline */
if (l > bufflen) l = bufflen;
addstr(out, source, l);
if (nl != NULL) srclen = nl - source; /* stop at first newline */
if (srclen > bufflen) srclen = bufflen;
addstr(out, source, srclen);
addstr(out, RETS, LL(RETS));
}
memcpy(out, POS, (LL(POS) + 1) * sizeof(char));

View File

@@ -1,5 +1,5 @@
/*
** $Id: lobject.h,v 2.145 2018/06/15 14:14:20 roberto Exp roberto $
** $Id: lobject.h $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
@@ -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
@@ -350,21 +350,13 @@ typedef struct TString {
} TString;
/*
** Ensures that address after this type is always fully aligned.
*/
typedef union UTString {
LUAI_MAXALIGN; /* ensures maximum alignment for strings */
TString tsv;
} UTString;
/*
** Get the actual string (array of bytes) from a 'TString'.
** (Access to 'extra' ensures that value is really a 'TString'.)
*/
#define getstr(ts) \
check_exp(sizeof((ts)->extra), cast_charp((ts)) + sizeof(UTString))
check_exp(sizeof((ts)->extra), cast_charp((ts)) + sizeof(TString))
/* get the actual string (array of bytes) from a Lua value */
@@ -468,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;
@@ -505,7 +498,6 @@ typedef struct Proto {
lu_byte numparams; /* number of fixed (named) parameters */
lu_byte is_vararg;
lu_byte maxstacksize; /* number of registers needed by this function */
lu_byte cachemiss; /* count for successive misses for 'cache' field */
int sizeupvalues; /* size of 'upvalues' */
int sizek; /* size of 'k' */
int sizecode;
@@ -516,7 +508,6 @@ typedef struct Proto {
int linedefined; /* debug information */
int lastlinedefined; /* debug information */
TValue *k; /* constants used by the function */
struct LClosure *cache; /* last-created closure with this prototype */
Instruction *code; /* opcodes */
struct Proto **p; /* functions defined inside the function */
Upvaldesc *upvalues; /* upvalue information */
@@ -577,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) */
@@ -588,6 +580,7 @@ typedef struct UpVal {
} UpVal;
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist
@@ -656,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_); }
/*
@@ -739,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,
@@ -753,7 +744,7 @@ LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj);
LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
va_list argp);
LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen);
#endif

View File

@@ -1,5 +1,5 @@
/*
** $Id: lopcodes.c,v 1.81 2018/04/04 14:23:41 roberto Exp roberto $
** $Id: lopcodes.c $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
@@ -17,170 +17,88 @@
/* ORDER OP */
#if defined(LUAI_DEFOPNAMES)
LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = {
"MOVE",
"LOADI",
"LOADF",
"LOADK",
"LOADKX",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"SETUPVAL",
"GETTABUP",
"GETTABLE",
"GETI",
"GETFIELD",
"SETTABUP",
"SETTABLE",
"SETI",
"SETFIELD",
"NEWTABLE",
"SELF",
"ADDI",
"SUBI",
"MULI",
"MODI",
"POWI",
"DIVI",
"IDIVI",
"BANDK",
"BORK",
"BXORK",
"SHRI",
"SHLI",
"ADD",
"SUB",
"MUL",
"MOD",
"POW",
"DIV",
"IDIV",
"BAND",
"BOR",
"BXOR",
"SHL",
"SHR",
"UNM",
"BNOT",
"NOT",
"LEN",
"CONCAT",
"CLOSE",
"JMP",
"EQ",
"LT",
"LE",
"EQK",
"EQI",
"LTI",
"LEI",
"GTI",
"GEI",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"RETURN0",
"RETURN1",
"FORLOOP1",
"FORPREP1",
"FORLOOP",
"FORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"PREPVARARG",
"EXTRAARG",
NULL
};
#endif
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_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, 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_FORLOOP1 */
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP1 */
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP */
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP */
,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_PREPVARARG */
,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 */
};

View File

@@ -1,5 +1,5 @@
/*
** $Id: lopcodes.h,v 1.192 2018/06/08 19:07:27 roberto Exp roberto $
** $Id: lopcodes.h $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
@@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) |
iABx Bx(17) | A(8) | Op(7) |
iAsB sBx (signed)(17) | A(8) | Op(7) |
iAx Ax(25) | Op(7) |
isJ sJ(24) |m| Op(7) |
isJ sJ(25) | Op(7) |
A signed argument is represented in excess K: the represented value is
the written unsigned value minus K, where K is half the maximum for the
@@ -40,7 +40,7 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define SIZE_Bx (SIZE_C + SIZE_B + 1)
#define SIZE_A 8
#define SIZE_Ax (SIZE_Bx + SIZE_A)
#define SIZE_sJ (SIZE_Bx + SIZE_A - 1)
#define SIZE_sJ (SIZE_Bx + SIZE_A)
#define SIZE_OP 7
@@ -55,15 +55,20 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define POS_Ax POS_A
#define POS_m POS_A
#define POS_sJ (POS_A + 1)
#define POS_sJ POS_A
/*
** limits for opcode arguments.
** we use (signed) int to manipulate most arguments,
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
** we use (signed) 'int' to manipulate most arguments,
** so they must fit in ints.
*/
#if SIZE_Bx < LUAI_BITSINT-1
/* Check whether type 'int' has at least 'b' bits ('b' < 32) */
#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1)
#if L_INTHASBITS(SIZE_Bx)
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
#else
#define MAXARG_Bx MAX_INT
@@ -72,13 +77,13 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
#if SIZE_Ax < LUAI_BITSINT-1
#if L_INTHASBITS(SIZE_Ax)
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
#else
#define MAXARG_Ax MAX_INT
#endif
#if SIZE_sJ < LUAI_BITSINT-1
#if L_INTHASBITS(SIZE_sJ)
#define MAXARG_sJ ((1 << SIZE_sJ) - 1)
#else
#define MAXARG_sJ MAX_INT
@@ -91,7 +96,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
#define MAXARG_B ((1<<SIZE_B)-1)
#define MAXARG_C ((1<<SIZE_C)-1)
#define OFFSET_sC (MAXARG_C >> 1)
#define MAXARG_Cx ((1<<(SIZE_C + 1))-1)
#define int2sC(i) ((i) + OFFSET_sC)
#define sC2int(i) ((i) - OFFSET_sC)
/* creates a mask with 'n' 1 bits at position 'p' */
@@ -119,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))))
@@ -144,8 +151,6 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
#define SETARG_sJ(i,j) \
setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
#define GETARG_m(i) check_exp(checkopm(i, isJ), getarg(i, POS_m, 1))
#define SETARG_m(i,m) setarg(i, m, POS_m, 1)
#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
@@ -212,24 +217,26 @@ 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) */
OP_MULK,/* A B C R(A) := R(B) * K(C) */
OP_MODK,/* A B C R(A) := R(B) % K(C) */
OP_POWK,/* A B C R(A) := R(B) ^ K(C) */
OP_DIVK,/* A B C R(A) := R(B) / K(C) */
OP_IDIVK,/* A B C R(A) := R(B) // K(C) */
OP_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 C R(A) := R(B) >> C */
OP_SHLI,/* A B C 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) */
@@ -238,11 +245,17 @@ OP_MOD,/* A B C R(A) := R(B) % R(C) */
OP_POW,/* A B C R(A) := R(B) ^ R(C) */
OP_DIV,/* A B C R(A) := R(B) / R(C) */
OP_IDIV,/* A B C R(A) := R(B) // R(C) */
OP_BAND,/* A B C R(A) := R(B) & R(C) */
OP_BOR,/* A B C R(A) := R(B) | R(C) */
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) */
@@ -251,6 +264,7 @@ OP_LEN,/* A B R(A) := length of R(B) */
OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */
OP_CLOSE,/* A close all upvalues >= R(A) */
OP_TBC,/* A mark variable A "to be closed" */
OP_JMP,/* k sJ pc += sJ (k is used in code generation) */
OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */
OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */
@@ -264,7 +278,7 @@ OP_GTI,/* A sB if ((R(A) > sB) ~= k) then pc++ */
OP_GEI,/* A sB if ((R(A) >= sB) ~= k) then pc++ */
OP_TEST,/* A if (not R(A) == k) then pc++ */
OP_TESTSET,/* A B if (not R(B) == k) then R(A) := R(B) else pc++ */
OP_TESTSET,/* A B if (not R(B) == k) then pc++ else R(A) := R(B) */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
@@ -273,16 +287,13 @@ OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
OP_RETURN0,/* return */
OP_RETURN1,/* A return R(A) */
OP_FORLOOP1,/* A Bx R(A)++;
if R(A) <= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
OP_FORPREP1,/* A Bx R(A)--; 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_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_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
OP_TFORLOOP,/* A Bx if R(A+1) ~= nil then { R(A)=R(A+1); pc -= Bx } */
OP_TFORPREP,/* A Bx create upvalue for R(A + 3); pc+=Bx */
OP_TFORCALL,/* A C R(A+4), ... ,R(A+3+C) := R(A)(R(A+1), R(A+2)); */
OP_TFORLOOP,/* A Bx if R(A+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
@@ -290,31 +301,37 @@ OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
OP_VARARG,/* A C R(A), R(A+1), ..., R(A+C-2) = vararg */
OP_PREPVARARG,/*A (adjust vararg parameters) */
OP_VARARGPREP,/*A (adjust vararg parameters) */
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode;
#define NUM_OPCODES (cast_int(OP_EXTRAARG) + 1)
#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1)
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then 'top' is
set to last_result+1, so next open instruction (OP_CALL, OP_RETURN*,
OP_SETLIST) may use 'top'.
(*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
'top' is set to last_result+1, so next open instruction (OP_CALL,
OP_RETURN*, OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) 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).
@@ -322,10 +339,12 @@ 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.
===========================================================================*/
@@ -337,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];)
@@ -346,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) \
@@ -355,14 +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))
LUAI_DDEC(const char *const luaP_opnames[NUM_OPCODES+1];) /* opcode names */
#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

98
lopnames.h Normal file
View File

@@ -0,0 +1,98 @@
/*
** $Id: lopnames.h $
** Opcode names
** See Copyright Notice in lua.h
*/
#if !defined(lopnames_h)
#define lopnames_h
/* ORDER OP */
static const char *const opnames[] = {
"MOVE",
"LOADI",
"LOADF",
"LOADK",
"LOADKX",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"SETUPVAL",
"GETTABUP",
"GETTABLE",
"GETI",
"GETFIELD",
"SETTABUP",
"SETTABLE",
"SETI",
"SETFIELD",
"NEWTABLE",
"SELF",
"ADDI",
"ADDK",
"SUBK",
"MULK",
"MODK",
"POWK",
"DIVK",
"IDIVK",
"BANDK",
"BORK",
"BXORK",
"SHRI",
"SHLI",
"ADD",
"SUB",
"MUL",
"MOD",
"POW",
"DIV",
"IDIV",
"BAND",
"BOR",
"BXOR",
"SHL",
"SHR",
"MMBIN",
"MMBINI",
"MMBINK",
"UNM",
"BNOT",
"NOT",
"LEN",
"CONCAT",
"CLOSE",
"TBC",
"JMP",
"EQ",
"LT",
"LE",
"EQK",
"EQI",
"LTI",
"LEI",
"GTI",
"GEI",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"RETURN0",
"RETURN1",
"FORLOOP",
"FORPREP",
"TFORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"VARARGPREP",
"EXTRAARG",
NULL
};
#endif

View File

@@ -1,5 +1,5 @@
/*
** $Id: loslib.c,v 1.65 2016/07/18 17:58:58 roberto Exp roberto $
** $Id: loslib.c $
** Standard Operating System library
** See Copyright Notice in lua.h
*/
@@ -10,7 +10,6 @@
#include "lprefix.h"
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
@@ -59,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 /* } */
@@ -194,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 */
@@ -211,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);
}
@@ -231,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 */
@@ -248,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;
}
@@ -276,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
@@ -294,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);
@@ -330,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 */

907
lparser.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
** $Id: lparser.h,v 1.81 2018/03/07 15:55:38 roberto Exp roberto $
** $Id: lparser.h $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -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; info = local register */
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 */
@@ -58,36 +62,56 @@ typedef enum {
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL)
typedef struct expdesc {
expkind k;
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 */
lu_byte sidx; /* index in the stack */
unsigned short vidx; /* index in 'actvar.arr' */
} var;
} u;
int t; /* patch list of 'exit when true' */
int f; /* patch list of 'exit when false' */
} expdesc;
/* description of active local variable */
typedef struct Vardesc {
short idx; /* variable index in stack */
/* 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 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;
@@ -128,14 +152,17 @@ typedef struct FuncState {
int np; /* number of elements in 'p' */
int nabslineinfo; /* number of elements in 'abslineinfo' */
int firstlocal; /* index of first local var (in Dyndata array) */
short nlocvars; /* number of elements in 'f->locvars' */
int firstlabel; /* index of first label (in 'dyd->label->arr') */
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 */
lu_byte iwthabs; /* instructions issued since last absolute line info */
lu_byte needclose; /* function needs to close upvalues when returning */
} 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);

View File

@@ -1,5 +1,5 @@
/*
** $Id: lprefix.h,v 1.1 2014/11/03 15:12:44 roberto Exp roberto $
** $Id: lprefix.h $
** Definitions for Lua code that must come before any other header file
** See Copyright Notice in lua.h
*/

138
lstate.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lstate.c,v 2.154 2018/06/15 19:31:22 roberto Exp roberto $
** $Id: lstate.c $
** Global State
** See Copyright Notice in lua.h
*/
@@ -96,27 +96,67 @@ 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 'nCalls' is larger than
** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but
** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to
** allow overflow handling to work)
** 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_incCcalls (lua_State *L) {
if (++L->nCcalls >= LUAI_MAXCCALLS) {
if (L->nCcalls == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
void luaE_enterCcall (lua_State *L) {
int ncalls = getCcalls(L);
L->nCcalls--;
if (ncalls <= CSTACKERR) { /* possible overflow? */
luaE_freeCI(L); /* release unused CIs */
ncalls = getCcalls(L); /* update call count */
if (ncalls <= CSTACKERR) { /* still overflow? */
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
else if (ncalls >= CSTACKMARK) {
/* not in error-handling zone; raise the error now */
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
luaG_runerror(L, "C stack overflow");
}
/* else stack is in the error-handling zone;
allow message handler to work */
}
}
}
CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *ci;
luaE_incCcalls(L);
lua_assert(L->ci->next == NULL);
luaE_enterCcall(L);
ci = luaM_new(L, CallInfo);
lua_assert(L->ci->next == NULL);
L->ci->next = ci;
@@ -135,13 +175,13 @@ void luaE_freeCI (lua_State *L) {
CallInfo *ci = L->ci;
CallInfo *next = ci->next;
ci->next = NULL;
L->nCcalls -= L->nci; /* to 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; /* to subtract removed elements from 'nCcalls' */
L->nCcalls -= L->nci; /* adjust result */
}
@@ -151,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; /* to 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 */
@@ -160,7 +200,7 @@ void luaE_shrinkCI (lua_State *L) {
next2->previous = ci;
ci = next2; /* keep next's next */
}
L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */
L->nCcalls -= L->nci; /* adjust result */
}
@@ -178,6 +218,8 @@ static void stack_init (lua_State *L1, lua_State *L) {
ci->next = ci->previous = NULL;
ci->callstatus = CIST_C;
ci->func = L1->top;
ci->u.c.k = NULL;
ci->nresults = 0;
setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */
L1->top++;
ci->top = L1->top + LUA_MINSTACK;
@@ -215,7 +257,8 @@ static void init_registry (lua_State *L, global_State *g) {
/*
** open parts of the state that may cause memory-allocation errors.
** ('ttisnil(&g->nilvalue)'' flags that the state was completely build)
** ('g->nilvalue' being a nil value flags that the state was completely
** build.)
*/
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
@@ -243,14 +286,13 @@ 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;
L->allowhook = 1;
resethookcount(L);
L->openupval = NULL;
L->nny = 1;
L->status = LUA_OK;
L->errfunc = 0;
}
@@ -258,7 +300,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
static void close_state (lua_State *L) {
global_State *g = G(L);
luaF_close(L, L->stack); /* close all upvalues for this thread */
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
luaC_freeallobjects(L); /* collect all objects */
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
luai_userstateclose(L);
@@ -301,7 +343,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1);
luaF_close(L1, L1->stack); /* close all upvalues for this thread */
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
lua_assert(L1->openupval == NULL);
luai_userstatefree(L, L1);
freestack(L1);
@@ -309,6 +351,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
}
int lua_resetthread (lua_State *L) {
CallInfo *ci;
int status;
lua_lock(L);
ci = &L->base_ci;
status = luaF_close(L, L->stack, CLOSEPROTECT);
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
if (status != CLOSEPROTECT) /* real errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else {
status = LUA_OK;
L->top = L->stack + 1;
}
ci->callstatus = CIST_C;
ci->func = L->stack;
ci->top = L->top + LUA_MINSTACK;
L->ci = ci;
L->status = status;
lua_unlock(L);
return status;
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
@@ -323,8 +388,11 @@ 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;
g->ud_warn = NULL;
g->mainthread = L;
g->seed = luai_makeseed(L);
g->gcrunning = 0; /* no GC while building state */
@@ -340,10 +408,11 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->finobjsur = g->finobjold = g->finobjrold = NULL;
g->sweepgc = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = g->protogray = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
g->twups = NULL;
g->totalbytes = sizeof(LG);
g->GCdebt = 0;
g->lastatomic = 0;
setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */
setgcparam(g->gcpause, LUAI_GCPAUSE);
setgcparam(g->gcstepmul, LUAI_GCMUL);
@@ -367,3 +436,26 @@ LUA_API void lua_close (lua_State *L) {
}
void luaE_warning (lua_State *L, const char *msg, int tocont) {
lua_WarnFunction wf = G(L)->warnf;
if (wf != NULL)
wf(G(L)->ud_warn, msg, 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);
}

126
lstate.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lstate.h,v 2.159 2018/06/15 19:31:22 roberto Exp roberto $
** $Id: lstate.h $
** Global State
** See Copyright Notice in lua.h
*/
@@ -15,7 +15,6 @@
/*
** Some notes about garbage-collected objects: All objects in Lua must
** be kept somehow accessible until being freed, so all objects always
** belong to one (and only one) of these lists, using field 'next' of
@@ -27,6 +26,22 @@
** 'fixedgc': all objects that are not to be collected (currently
** only small strings, such as reserved words).
**
** For the generational collector, some of these lists have marks for
** generations. Each mark points to the first element in the list for
** that particular generation; that generation goes until the next mark.
**
** 'allgc' -> 'survival': new objects;
** 'survival' -> 'old': objects that survived one collection;
** 'old' -> 'reallyold': objects that became old in last collection;
** 'reallyold' -> NULL: objects old for more than one cycle.
**
** 'finobj' -> 'finobjsur': new objects marked for finalization;
** 'finobjsur' -> 'finobjold': survived """";
** 'finobjold' -> 'finobjrold': just old """";
** 'finobjrold' -> NULL: really old """".
*/
/*
** Moreover, there is another set of lists that control gray objects.
** These lists are linked by fields 'gclist'. (All objects that
** can become gray have such a field. The field is not the same
@@ -43,12 +58,79 @@
** 'weak': tables with weak values to be cleared;
** 'ephemeron': ephemeron tables with white->white entries;
** 'allweak': tables with weak keys and/or weak values to be cleared.
** There is also a list 'protogray' for prototypes that need to have
** their caches cleared.
*/
/*
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
** how many "C calls" it still can do in the C stack, to avoid C-stack
** overflow. This count is very rough approximation; it considers only
** recursive functions inside the interpreter, as non-recursive calls
** can be considered using a fixed (although unknown) amount of stack
** space.
**
** The count has two parts: the lower part is the count itself; the
** higher part counts the number of non-yieldable calls in the stack.
** (They are together so that we can change both with one instruction.)
**
** Because calls to external C functions can use an unknown amount
** of space (e.g., functions using an auxiliary buffer), calls
** to these functions add more than one to the count (see CSTACKCF).
**
** The proper count excludes the number of CallInfo structures allocated
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
** (and "consumes" one CallInfo), it needs neither to decrement nor to
** check 'nCcalls', as its use of C stack is already accounted for.
*/
/* 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)
/* real number of C calls */
#define getCcalls(L) ((L)->nCcalls & 0xffff)
/* Increment the number of non-yieldable calls */
#define incnny(L) ((L)->nCcalls += 0x10000)
/* Decrement the number of non-yieldable calls */
#define decnny(L) ((L)->nCcalls -= 0x10000)
/* Increment the number of non-yieldable calls and decrement nCcalls */
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
/* Decrement the number of non-yieldable calls and increment nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
struct lua_longjmp; /* defined in ldo.c */
@@ -103,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 */
@@ -122,9 +204,11 @@ typedef struct CallInfo {
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
#define CIST_TAIL (1<<4) /* call was tail called */
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
#define CIST_LEQ (1<<6) /* using __lt for __le */
#define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
#define CIST_FIN (1<<6) /* call is running a finalizer */
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
#if defined(LUA_COMPAT_LT_LE)
#define CIST_LEQ (1<<8) /* using __lt for __le */
#endif
/* active function is a Lua function */
#define isLua(ci) (!((ci)->callstatus & CIST_C))
@@ -146,6 +230,7 @@ typedef struct global_State {
l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
stringtable strt; /* hash table for strings */
TValue l_registry;
TValue nilvalue; /* a nil value */
@@ -168,7 +253,6 @@ typedef struct global_State {
GCObject *weak; /* list of tables with weak values */
GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
GCObject *allweak; /* list of all-weak tables */
GCObject *protogray; /* list of prototypes with "new" caches */
GCObject *tobefnz; /* list of userdata to be GC */
GCObject *fixedgc; /* list of objects not to be collected */
/* fields for generational collector */
@@ -185,6 +269,9 @@ typedef struct global_State {
TString *tmname[TM_N]; /* array with tag-method names */
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
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;
@@ -193,8 +280,9 @@ typedef struct global_State {
*/
struct lua_State {
CommonHeader;
unsigned short nci; /* number of items in 'ci' list */
lu_byte status;
lu_byte allowhook;
unsigned short nci; /* number of items in 'ci' list */
StkId top; /* first free slot in the stack */
global_State *l_G;
CallInfo *ci; /* call info for current function */
@@ -208,13 +296,11 @@ struct lua_State {
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
int stacksize;
int basehookcount;
int hookcount;
unsigned short nny; /* number of non-yieldable calls in stack */
unsigned short nCcalls; /* number of nested C calls */
l_signalT hookmask;
lu_byte allowhook;
};
@@ -249,7 +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((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
#define gco2upv(o) check_exp((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
/*
@@ -267,8 +353,12 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_incCcalls (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++)
#endif

View File

@@ -1,5 +1,5 @@
/*
** $Id: lstring.c,v 2.65 2018/02/20 16:52:50 roberto Exp roberto $
** $Id: lstring.c $
** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h
*/
@@ -121,8 +121,8 @@ void luaS_clearcache (global_State *g) {
int i, j;
for (i = 0; i < STRCACHE_N; i++)
for (j = 0; j < STRCACHE_M; j++) {
if (iswhite(g->strcache[i][j])) /* will entry be collected? */
g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */
if (iswhite(g->strcache[i][j])) /* will entry be collected? */
g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */
}
}

View File

@@ -1,5 +1,5 @@
/*
** $Id: lstring.h,v 1.63 2017/11/23 19:29:04 roberto Exp roberto $
** $Id: lstring.h $
** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h
*/
@@ -19,7 +19,7 @@
#define MEMERRMSG "not enough memory"
#define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char))
#define sizelstring(l) (sizeof(TString) + ((l) + 1) * sizeof(char))
#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
(sizeof(s)/sizeof(char))-1))

283
lstrlib.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lstrlib.c,v 1.262 2018/02/21 17:48:31 roberto Exp roberto $
** $Id: lstrlib.c $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
@@ -60,23 +60,50 @@ static int str_len (lua_State *L) {
}
/* translate a relative string position: negative means back from end */
static lua_Integer posrelat (lua_Integer pos, size_t len) {
if (pos >= 0) return pos;
else if (0u - (size_t)pos > len) return 0;
else return (lua_Integer)len + pos + 1;
/*
** translate a relative initial string position
** (negative means back from end): clip result to [1, inf).
** The length of any string in Lua must fit in a lua_Integer,
** so there are no overflows in the casts.
** The inverted comparison avoids a possible overflow
** computing '-pos'.
*/
static size_t posrelatI (lua_Integer pos, size_t len) {
if (pos > 0)
return (size_t)pos;
else if (pos == 0)
return 1;
else if (pos < -(lua_Integer)len) /* inverted comparison */
return 1; /* clip to 1 */
else return len + (size_t)pos + 1;
}
/*
** Gets an optional ending string position from argument 'arg',
** with default value 'def'.
** Negative means back from end: clip result to [0, len]
*/
static size_t getendpos (lua_State *L, int arg, lua_Integer def,
size_t len) {
lua_Integer pos = luaL_optinteger(L, arg, def);
if (pos > (lua_Integer)len)
return len;
else if (pos >= 0)
return (size_t)pos;
else if (pos < -(lua_Integer)len)
return 0;
else return len + (size_t)pos + 1;
}
static int str_sub (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
lua_Integer start = posrelat(luaL_checkinteger(L, 2), l);
lua_Integer end = posrelat(luaL_optinteger(L, 3, -1), l);
if (start < 1) start = 1;
if (end > (lua_Integer)l) end = l;
size_t start = posrelatI(luaL_checkinteger(L, 2), l);
size_t end = getendpos(L, 3, -1, l);
if (start <= end)
lua_pushlstring(L, s + start - 1, (size_t)(end - start) + 1);
lua_pushlstring(L, s + start - 1, (end - start) + 1);
else lua_pushliteral(L, "");
return 1;
}
@@ -149,13 +176,12 @@ static int str_rep (lua_State *L) {
static int str_byte (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
lua_Integer posi = posrelat(luaL_optinteger(L, 2, 1), l);
lua_Integer pose = posrelat(luaL_optinteger(L, 3, posi), l);
lua_Integer pi = luaL_optinteger(L, 2, 1);
size_t posi = posrelatI(pi, l);
size_t pose = getendpos(L, 3, pi, l);
int n, i;
if (posi < 1) posi = 1;
if (pose > (lua_Integer)l) pose = l;
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= INT_MAX) /* arithmetic overflow? */
if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
return luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1;
luaL_checkstack(L, n, "string slice too long");
@@ -171,8 +197,8 @@ static int str_char (lua_State *L) {
luaL_Buffer b;
char *p = luaL_buffinitsize(L, &b, n);
for (i=1; i<=n; i++) {
lua_Integer c = luaL_checkinteger(L, i);
luaL_argcheck(L, uchar(c) == c, i, "value out of range");
lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i);
luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range");
p[i - 1] = uchar(c);
}
luaL_pushresultsize(&b, n);
@@ -207,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);
@@ -285,6 +322,8 @@ static const luaL_Reg stringmetamethods[] = {
{NULL, NULL}
};
#endif /* } */
/* }====================================================== */
/*
@@ -634,25 +673,46 @@ static const char *lmemfind (const char *s1, size_t l1,
}
static void push_onecapture (MatchState *ms, int i, const char *s,
const char *e) {
/*
** get information about the i-th capture. If there are no captures
** and 'i==0', return information about the whole match, which
** is the range 's'..'e'. If the capture is a string, return
** its length and put its address in '*cap'. If it is an integer
** (a position), push it on the stack and return CAP_POSITION.
*/
static size_t get_onecapture (MatchState *ms, int i, const char *s,
const char *e, const char **cap) {
if (i >= ms->level) {
if (i == 0) /* ms->level == 0, too */
lua_pushlstring(ms->L, s, e - s); /* add whole match */
else
if (i != 0)
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
*cap = s;
return e - s;
}
else {
ptrdiff_t l = ms->capture[i].len;
if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
if (l == CAP_POSITION)
ptrdiff_t capl = ms->capture[i].len;
*cap = ms->capture[i].init;
if (capl == CAP_UNFINISHED)
luaL_error(ms->L, "unfinished capture");
else if (capl == CAP_POSITION)
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
else
lua_pushlstring(ms->L, ms->capture[i].init, l);
return capl;
}
}
/*
** Push the i-th capture on the stack.
*/
static void push_onecapture (MatchState *ms, int i, const char *s,
const char *e) {
const char *cap;
ptrdiff_t l = get_onecapture(ms, i, s, e, &cap);
if (l != CAP_POSITION)
lua_pushlstring(ms->L, cap, l);
/* else position was already pushed */
}
static int push_captures (MatchState *ms, const char *s, const char *e) {
int i;
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
@@ -695,16 +755,15 @@ static int str_find_aux (lua_State *L, int find) {
size_t ls, lp;
const char *s = luaL_checklstring(L, 1, &ls);
const char *p = luaL_checklstring(L, 2, &lp);
lua_Integer init = posrelat(luaL_optinteger(L, 3, 1), ls);
if (init < 1) init = 1;
else if (init > (lua_Integer)ls + 1) { /* start after string's end? */
lua_pushnil(L); /* cannot find anything */
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
if (init > ls) { /* start after string's end? */
luaL_pushfail(L); /* cannot find anything */
return 1;
}
/* explicit request or no special characters? */
if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) {
/* do a plain search */
const char *s2 = lmemfind(s + init - 1, ls - (size_t)init + 1, p, lp);
const char *s2 = lmemfind(s + init, ls - init, p, lp);
if (s2) {
lua_pushinteger(L, (s2 - s) + 1);
lua_pushinteger(L, (s2 - s) + lp);
@@ -713,7 +772,7 @@ static int str_find_aux (lua_State *L, int find) {
}
else {
MatchState ms;
const char *s1 = s + init - 1;
const char *s1 = s + init;
int anchor = (*p == '^');
if (anchor) {
p++; lp--; /* skip anchor character */
@@ -733,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;
}
@@ -777,11 +836,14 @@ static int gmatch (lua_State *L) {
size_t ls, lp;
const char *s = luaL_checklstring(L, 1, &ls);
const char *p = luaL_checklstring(L, 2, &lp);
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
GMatchState *gm;
lua_settop(L, 2); /* keep them on closure to avoid being collected */
lua_settop(L, 2); /* keep strings on closure to avoid being collected */
gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0);
if (init > ls) /* start after string's end? */
init = ls + 1; /* avoid overflows in 's + init' */
prepstate(&gm->ms, L, s, ls, p, lp);
gm->src = s; gm->p = p; gm->lastmatch = NULL;
gm->src = s + init; gm->p = p; gm->lastmatch = NULL;
lua_pushcclosure(L, gmatch_aux, 3);
return 1;
}
@@ -789,60 +851,72 @@ static int gmatch (lua_State *L) {
static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e) {
size_t l, i;
size_t l;
lua_State *L = ms->L;
const char *news = lua_tolstring(L, 3, &l);
for (i = 0; i < l; i++) {
if (news[i] != L_ESC)
luaL_addchar(b, news[i]);
else {
i++; /* skip ESC */
if (!isdigit(uchar(news[i]))) {
if (news[i] != L_ESC)
luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
luaL_addchar(b, news[i]);
}
else if (news[i] == '0')
luaL_addlstring(b, s, e - s);
else {
push_onecapture(ms, news[i] - '1', s, e);
luaL_tolstring(L, -1, NULL); /* if number, convert it to string */
lua_remove(L, -2); /* remove original value */
luaL_addvalue(b); /* add capture to accumulated result */
}
const char *p;
while ((p = (char *)memchr(news, L_ESC, l)) != NULL) {
luaL_addlstring(b, news, p - news);
p++; /* skip ESC */
if (*p == L_ESC) /* '%%' */
luaL_addchar(b, *p);
else if (*p == '0') /* '%0' */
luaL_addlstring(b, s, e - s);
else if (isdigit(uchar(*p))) { /* '%n' */
const char *cap;
ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap);
if (resl == CAP_POSITION)
luaL_addvalue(b); /* add position to accumulated result */
else
luaL_addlstring(b, cap, resl);
}
else
luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
l -= p + 1 - news;
news = p + 1;
}
luaL_addlstring(b, news, l);
}
static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e, int tr) {
/*
** Add the replacement value to the string buffer 'b'.
** Return true if the original string was changed. (Function calls and
** table indexing resulting in nil or false do not change the subject.)
*/
static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
const char *e, int tr) {
lua_State *L = ms->L;
switch (tr) {
case LUA_TFUNCTION: {
case LUA_TFUNCTION: { /* call the function */
int n;
lua_pushvalue(L, 3);
n = push_captures(ms, s, e);
lua_call(L, n, 1);
lua_pushvalue(L, 3); /* push the function */
n = push_captures(ms, s, e); /* all captures as arguments */
lua_call(L, n, 1); /* call it */
break;
}
case LUA_TTABLE: {
push_onecapture(ms, 0, s, e);
case LUA_TTABLE: { /* index the table */
push_onecapture(ms, 0, s, e); /* first capture is the index */
lua_gettable(L, 3);
break;
}
default: { /* LUA_TNUMBER or LUA_TSTRING */
add_s(ms, b, s, e);
return;
add_s(ms, b, s, e); /* add value to the buffer */
return 1; /* something changed */
}
}
if (!lua_toboolean(L, -1)) { /* nil or false? */
lua_pop(L, 1);
lua_pushlstring(L, s, e - s); /* keep original text */
lua_pop(L, 1); /* remove value */
luaL_addlstring(b, s, e - s); /* keep original text */
return 0; /* no changes */
}
else if (!lua_isstring(L, -1))
luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
luaL_addvalue(b); /* add result to accumulator */
return luaL_error(L, "invalid replacement value (a %s)",
luaL_typename(L, -1));
else {
luaL_addvalue(b); /* add result to accumulator */
return 1; /* something changed */
}
}
@@ -855,11 +929,12 @@ static int str_gsub (lua_State *L) {
lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
int anchor = (*p == '^');
lua_Integer n = 0; /* replacement count */
int changed = 0; /* change flag */
MatchState ms;
luaL_Buffer b;
luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
"string/function/table expected");
"string/function/table");
luaL_buffinit(L, &b);
if (anchor) {
p++; lp--; /* skip anchor character */
@@ -870,7 +945,7 @@ static int str_gsub (lua_State *L) {
reprepstate(&ms); /* (re)prepare state for new match */
if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
n++;
add_value(&ms, &b, src, e, tr); /* add replacement to buffer */
changed = add_value(&ms, &b, src, e, tr) | changed;
src = lastmatch = e;
}
else if (src < ms.src_end) /* otherwise, skip one character */
@@ -878,8 +953,12 @@ static int str_gsub (lua_State *L) {
else break; /* end of subject */
if (anchor) break;
}
luaL_addlstring(&b, src, ms.src_end-src);
luaL_pushresult(&b);
if (!changed) /* no changes? */
lua_pushvalue(L, 1); /* return original string */
else { /* something changed */
luaL_addlstring(&b, src, ms.src_end-src);
luaL_pushresult(&b); /* create and return new string */
}
lua_pushinteger(L, n); /* number of substitutions */
return 2;
}
@@ -972,13 +1051,23 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
/*
** Maximum size of each formatted item. This maximum size is produced
** Maximum size for items formatted with '%f'. This size is produced
** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
** and '\0') + number of decimal digits to represent maxfloat (which
** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra
** expenses", such as locale-dependent stuff)
** is maximum exponent + 1). (99+3+1, adding some extra, 110)
*/
#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP))
#define MAX_ITEMF (110 + l_mathlim(MAX_10_EXP))
/*
** All formats except '%f' do not need that large limit. The other
** float formats use exponents, so that they fit in the 99 limit for
** significant digits; 's' for large strings and 'q' add items directly
** to the buffer; all integer formats also fit in the 99 limit. The
** worst case are floats: they may need 99 significant digits, plus
** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120.
*/
#define MAX_ITEM 120
/* valid flags in a format specification */
@@ -1128,36 +1217,47 @@ static int str_format (lua_State *L) {
luaL_addchar(&b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format ('%...') */
char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */
int maxitem = MAX_ITEM;
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
int nb = 0; /* number of bytes in added item */
if (++arg > top)
luaL_argerror(L, arg, "no value");
return luaL_argerror(L, arg, "no value");
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg));
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
break;
}
case 'd': case 'i':
case 'o': case 'u': case 'x': case 'X': {
lua_Integer n = luaL_checkinteger(L, arg);
addlenmod(form, LUA_INTEGER_FRMLEN);
nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACINT)n);
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
break;
}
case 'a': case 'A':
addlenmod(form, LUA_NUMBER_FRMLEN);
nb = lua_number2strx(L, buff, MAX_ITEM, form,
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);
addlenmod(form, LUA_NUMBER_FRMLEN);
nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACNUMBER)n);
nb = snprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
break;
}
case 'p': {
const void *p = lua_topointer(L, arg);
nb = l_sprintf(buff, maxitem, form, p);
break;
}
case 'q': {
if (form[2] != '\0') /* modifiers? */
return luaL_error(L, "specifier '%%q' cannot have modifiers");
addliteral(L, &b, arg);
break;
}
@@ -1173,18 +1273,17 @@ static int str_format (lua_State *L) {
luaL_addvalue(&b); /* keep entire string */
}
else { /* format the string into 'buff' */
nb = l_sprintf(buff, MAX_ITEM, form, s);
nb = l_sprintf(buff, maxitem, form, s);
lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
}
}
break;
}
default: { /* also treat cases 'pnLlh' */
return luaL_error(L, "invalid option '%%%c' to 'format'",
*(strfrmt - 1));
return luaL_error(L, "invalid conversion '%s' to 'format'", form);
}
}
lua_assert(nb < MAX_ITEM);
lua_assert(nb < maxitem);
luaL_addsize(&b, nb);
}
}
@@ -1573,7 +1672,7 @@ static int str_unpack (lua_State *L) {
const char *fmt = luaL_checkstring(L, 1);
size_t ld;
const char *data = luaL_checklstring(L, 2, &ld);
size_t pos = (size_t)posrelat(luaL_optinteger(L, 3, 1), ld) - 1;
size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1;
int n = 0; /* number of results */
luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
initheader(L, &h);

View File

@@ -1,5 +1,5 @@
/*
** $Id: ltable.c,v 2.139 2018/06/15 14:14:20 roberto Exp roberto $
** $Id: ltable.c $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
@@ -48,8 +48,8 @@
/*
** MAXASIZE is the maximum size of the array part. It is the minimum
** between 2^MAXABITS and the maximum size such that, measured in bytes,
** it fits in a 'size_t'.
** between 2^MAXABITS and the maximum size that, measured in bytes,
** fits in a 'size_t'.
*/
#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue)
@@ -214,8 +214,8 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
size |= (size >> 4);
size |= (size >> 8);
size |= (size >> 16);
#if (INT_MAX >> 30 >> 1) > 0
size |= (size >> 32); /* int has more than 32 bits */
#if (UINT_MAX >> 30) > 3
size |= (size >> 32); /* unsigned int has more than 32 bits */
#endif
size++;
lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
@@ -269,7 +269,7 @@ static const TValue *getgeneric (Table *t, const TValue *key) {
** the array part of a table, 0 otherwise.
*/
static unsigned int arrayindex (lua_Integer k) {
if (0 < k && l_castS2U(k) <= MAXASIZE)
if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */
return cast_uint(k); /* 'key' is an appropriate array index */
else
return 0;
@@ -286,7 +286,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
unsigned int i;
if (ttisnil(key)) return 0; /* first iteration */
i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0;
if (i != 0 && i <= asize) /* is 'key' inside array part? */
if (i - 1u < asize) /* is 'key' inside array part? */
return i; /* yes; that's the index */
else {
const TValue *n = getgeneric(t, key);
@@ -678,7 +678,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
** changing the real size of the array).
*/
const TValue *luaH_getint (Table *t, lua_Integer key) {
if (l_castS2U(key) - 1u < t->alimit) /* (1 <= key && key <= t->alimit)? */
if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */
return &t->array[key - 1];
else if (!limitequalsasize(t) && /* key still may be in the array part? */
(l_castS2U(key) == t->alimit + 1 ||
@@ -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);
}

View File

@@ -1,5 +1,5 @@
/*
** $Id: ltable.h,v 2.27 2018/06/01 16:51:34 roberto Exp roberto $
** $Id: ltable.h $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: ltablib.c,v 1.96 2018/03/16 14:18:18 roberto Exp roberto $
** $Id: ltablib.c $
** Library for Table Manipulation
** See Copyright Notice in lua.h
*/
@@ -69,7 +69,9 @@ static int tinsert (lua_State *L) {
case 3: {
lua_Integer i;
pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */
luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
/* check whether 'pos' is in [1, e] */
luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2,
"position out of bounds");
for (i = e; i > pos; i--) { /* move up elements */
lua_geti(L, 1, i - 1);
lua_seti(L, 1, i); /* t[i] = t[i - 1] */
@@ -89,7 +91,9 @@ static int tremove (lua_State *L) {
lua_Integer size = aux_getn(L, 1, TAB_RW);
lua_Integer pos = luaL_optinteger(L, 2, size);
if (pos != size) /* validate 'pos' if given */
luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
/* check whether 'pos' is in [1, size + 1] */
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1,
"position out of bounds");
lua_geti(L, 1, pos); /* result = t[pos] */
for ( ; pos < size; pos++) {
lua_geti(L, 1, pos + 1);
@@ -295,14 +299,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
/* loop invariant: a[lo .. i] <= P <= a[j .. up] */
for (;;) {
/* next loop: repeat ++i while a[i] < P */
while (lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */
}
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
/* next loop: repeat --j while P < a[j] */
while (lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j < i) /* j < i but a[j] > P ?? */
luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */
@@ -334,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) {

232
ltests.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: ltests.c,v 2.244 2018/06/11 14:19:50 roberto Exp roberto $
** $Id: ltests.c $
** Internal Module for Debugging of the Lua Implementation
** See Copyright Notice in lua.h
*/
@@ -27,6 +27,7 @@
#include "lfunc.h"
#include "lmem.h"
#include "lopcodes.h"
#include "lopnames.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
@@ -50,9 +51,8 @@ static int runC (lua_State *L, lua_State *L1, const char *pc);
static void setnameval (lua_State *L, const char *name, int val) {
lua_pushstring(L, name);
lua_pushinteger(L, val);
lua_settable(L, -3);
lua_setfield(L, -2, name);
}
@@ -62,10 +62,93 @@ static void pushobject (lua_State *L, const TValue *o) {
}
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);
}
static int tpanic (lua_State *L) {
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
return (exit(EXIT_FAILURE), 0); /* do not return to Lua */
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
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, 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);
** - 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("warnf-buffer overflow (%s)\n", msg, buff);
strcat(buff, msg); /* add new message to current warning */
if (!tocont) { /* message finished? */
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;
}
}
buff[0] = '\0'; /* prepare buffer for next warning */
}
}
@@ -102,7 +185,7 @@ typedef union Header {
Memcontrol l_memcontrol =
{0L, 0L, 0L, 0L, (~0L), {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}};
{0UL, 0UL, 0UL, 0UL, (~0UL), {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
static void freeblock (Memcontrol *mc, Header *block) {
@@ -141,7 +224,7 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
freeblock(mc, block);
return NULL;
}
if (mc->countlimit != ~0UL && size > 0) { /* count limit in use? */
if (mc->countlimit != ~0UL && size != oldsize) { /* count limit in use? */
if (mc->countlimit == 0)
return NULL; /* fake a memory allocation error */
mc->countlimit--;
@@ -155,7 +238,8 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
size_t realsize = sizeof(Header) + size + MARKSIZE;
if (realsize < size) return NULL; /* arithmetic overflow! */
newblock = cast(Header *, malloc(realsize)); /* alloc a new block */
if (newblock == NULL) return NULL; /* really out of memory? */
if (newblock == NULL)
return NULL; /* really out of memory? */
if (block) {
memcpy(newblock + 1, block + 1, commonsize); /* copy old contents */
freeblock(mc, block); /* erase (and check) old copy */
@@ -282,7 +366,6 @@ static void checkudata (global_State *g, Udata *u) {
static void checkproto (global_State *g, Proto *f) {
int i;
GCObject *fgc = obj2gco(f);
checkobjref(g, fgc, f->cache);
checkobjref(g, fgc, f->source);
for (i=0; i<f->sizek; i++) {
if (ttisstring(f->k + i))
@@ -415,8 +498,6 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
getage(o) == G_TOUCHED1 ||
getage(o) == G_OLD0 ||
o->tt == LUA_TTHREAD ||
(o->tt == LUA_TPROTO &&
(gco2p(o)->cache != NULL || gco2p(o)->cachemiss >= MAXMISS)) ||
(o->tt == LUA_TUPVAL && upisopen(gco2upv(o))));
}
}
@@ -450,7 +531,6 @@ static void checkgrays (global_State *g) {
checkgraylist(g, g->grayagain);
checkgraylist(g, g->weak);
checkgraylist(g, g->ephemeron);
checkgraylist(g, g->protogray);
}
@@ -521,34 +601,37 @@ int lua_checkmemory (lua_State *L) {
static char *buildop (Proto *p, int pc, char *buff) {
char *obuff = buff;
Instruction i = p->code[pc];
OpCode o = GET_OPCODE(i);
const char *name = luaP_opnames[o];
const char *name = opnames[o];
int line = luaG_getfuncline(p, pc);
sprintf(buff, "(%4d) %4d - ", line, pc);
int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0;
if (lineinfo == ABSLINEINFO)
buff += sprintf(buff, "(__");
else
buff += sprintf(buff, "(%2d", lineinfo);
buff += sprintf(buff, " - %4d) %4d - ", line, pc);
switch (getOpMode(o)) {
case iABC:
sprintf(buff+strlen(buff), "%-12s%4d %4d %4d%s", name,
sprintf(buff, "%-12s%4d %4d %4d%s", name,
GETARG_A(i), GETARG_B(i), GETARG_C(i),
GETARG_k(i) ? " (k)" : "");
break;
case iABx:
sprintf(buff+strlen(buff), "%-12s%4d %4d", name, GETARG_A(i),
GETARG_Bx(i));
sprintf(buff, "%-12s%4d %4d", name, GETARG_A(i), GETARG_Bx(i));
break;
case iAsBx:
sprintf(buff+strlen(buff), "%-12s%4d %4d", name, GETARG_A(i),
GETARG_sBx(i));
sprintf(buff, "%-12s%4d %4d", name, GETARG_A(i), GETARG_sBx(i));
break;
case iAx:
sprintf(buff+strlen(buff), "%-12s%4d", name, GETARG_Ax(i));
sprintf(buff, "%-12s%4d", name, GETARG_Ax(i));
break;
case isJ:
sprintf(buff+strlen(buff), "%-12s%4d (%1d)", name, GETARG_sJ(i),
!!GETARG_m(i));
sprintf(buff, "%-12s%4d", name, GETARG_sJ(i));
break;
}
return buff;
return obuff;
}
@@ -599,7 +682,7 @@ static int printcode (lua_State *L) {
printf("numparams: %d\n", p->numparams);
for (pc=0; pc<p->sizecode; pc++) {
char buff[100];
printf("%d\t%s\n", pc + 1, buildop(p, pc, buff));
printf("%s\n", buildop(p, pc, buff));
}
return 0;
}
@@ -620,6 +703,24 @@ static int listk (lua_State *L) {
}
static int listabslineinfo (lua_State *L) {
Proto *p;
int i;
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
1, "Lua function expected");
p = getproto(obj_at(L, 1));
luaL_argcheck(L, p->abslineinfo != NULL, 1, "function has no debug info");
lua_createtable(L, 2 * p->sizeabslineinfo, 0);
for (i=0; i < p->sizeabslineinfo; i++) {
lua_pushinteger(L, p->abslineinfo[i].pc);
lua_rawseti(L, -2, 2 * i + 1);
lua_pushinteger(L, p->abslineinfo[i].line);
lua_rawseti(L, -2, 2 * i + 2);
}
return 1;
}
static int listlocals (lua_State *L) {
Proto *p;
int pc = cast_int(luaL_checkinteger(L, 2)) - 1;
@@ -649,12 +750,11 @@ static void printstack (lua_State *L) {
static int get_limits (lua_State *L) {
lua_createtable(L, 0, 5);
setnameval(L, "BITS_INT", LUAI_BITSINT);
lua_createtable(L, 0, 6);
setnameval(L, "IS32INT", LUAI_IS32INT);
setnameval(L, "MAXARG_Ax", MAXARG_Ax);
setnameval(L, "MAXARG_Bx", MAXARG_Bx);
setnameval(L, "OFFSET_sBx", OFFSET_sBx);
setnameval(L, "BITS_INT", LUAI_BITSINT);
setnameval(L, "LFPF", LFIELDS_PER_FLUSH);
setnameval(L, "NUM_OPCODES", NUM_OPCODES);
return 1;
@@ -1044,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));
@@ -1120,8 +1212,8 @@ static const char *const delimits = " \t\n,;";
static void skip (const char **pc) {
for (;;) {
if (**pc != '\0' && strchr(delimits, **pc)) (*pc)++;
else if (**pc == '#') {
while (**pc != '\n' && **pc != '\0') (*pc)++;
else if (**pc == '#') { /* comment? */
while (**pc != '\n' && **pc != '\0') (*pc)++; /* until end-of-line */
}
else break;
}
@@ -1185,7 +1277,7 @@ static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) {
static void pushcode (lua_State *L, int code) {
static const char *const codes[] = {"OK", "YIELD", "ERRRUN",
"ERRSYNTAX", "ERRMEM", "ERRGCMM", "ERRERR"};
"ERRSYNTAX", MEMERRMSG, "ERRGCMM", "ERRERR"};
lua_pushstring(L, codes[code]);
}
@@ -1347,6 +1439,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
else if EQ("newthread") {
lua_newthread(L1);
}
else if EQ("resetthread") {
lua_pushinteger(L1, lua_resetthread(L1));
}
else if EQ("newuserdata") {
lua_newuserdata(L1, getnum);
}
@@ -1382,6 +1477,14 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
const char *msg = getstring;
printf("%s\n", msg);
}
else if EQ("warningC") {
const char *msg = getstring;
lua_warning(L1, msg, 1);
}
else if EQ("warning") {
const char *msg = getstring;
lua_warning(L1, msg, 0);
}
else if EQ("pushbool") {
lua_pushboolean(L1, getnum);
}
@@ -1409,6 +1512,19 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
else if EQ("pushvalue") {
lua_pushvalue(L1, getindex);
}
else if EQ("pushfstringI") {
lua_pushfstring(L1, lua_tostring(L, -2), (int)lua_tointeger(L, -1));
}
else if EQ("pushfstringS") {
lua_pushfstring(L1, lua_tostring(L, -2), lua_tostring(L, -1));
}
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);
@@ -1417,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)));
@@ -1456,18 +1580,26 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
}
else if EQ("setfield") {
int t = getindex;
lua_setfield(L1, t, getstring);
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") {
lua_setglobal(L1, getstring);
const char *s = getstring;
lua_setglobal(L1, s);
}
else if EQ("sethook") {
int mask = getnum;
int count = getnum;
sethookaux(L1, mask, count, getstring);
const char *s = getstring;
sethookaux(L1, mask, count, s);
}
else if EQ("setmetatable") {
lua_setmetatable(L1, getindex);
int idx = getindex;
lua_setmetatable(L1, idx);
}
else if EQ("settable") {
lua_settable(L1, getindex);
@@ -1482,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;
@@ -1504,7 +1639,10 @@ static struct X { int x; } x;
lua_pushnumber(L1, lua_tonumber(L1, getindex));
}
else if EQ("topointer") {
lua_pushnumber(L1, cast_sizet(lua_topointer(L1, getindex)));
lua_pushlightuserdata(L1, cast_voidp(lua_topointer(L1, getindex)));
}
else if EQ("touserdata") {
lua_pushlightuserdata(L1, lua_touserdata(L1, getindex));
}
else if EQ("tostring") {
const char *s = lua_tostring(L1, getindex);
@@ -1531,6 +1669,9 @@ static struct X { int x; } x;
int i = getindex;
return lua_yieldk(L1, nres, i, Cfunck);
}
else if EQ("toclose") {
lua_toclose(L1, getnum);
}
else luaL_error(L, "unknown instruction %s", buff);
}
return 0;
@@ -1675,12 +1816,12 @@ 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},
{"printcode", printcode},
{"listk", listk},
{"listabslineinfo", listabslineinfo},
{"listlocals", listlocals},
{"loadlib", loadlib},
{"checkpanic", checkpanic},
@@ -1716,6 +1857,7 @@ static void checkfinalmem (void) {
int luaB_opentests (lua_State *L) {
void *ud;
lua_atpanic(L, &tpanic);
lua_setwarnf(L, &warnf, L);
atexit(checkfinalmem);
lua_assert(lua_getallocf(L, &ud) == debug_realloc);
lua_assert(ud == cast_voidp(&l_memcontrol));

View File

@@ -1,5 +1,5 @@
/*
** $Id: ltests.h,v 2.58 2018/04/19 15:42:41 roberto Exp roberto $
** $Id: ltests.h $
** Internal Header for Debugging of the Lua Implementation
** See Copyright Notice in lua.h
*/
@@ -13,6 +13,7 @@
/* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB
#define LUA_COMPAT_LT_LE
#define LUA_DEBUG
@@ -29,8 +30,8 @@
/* compiled with -O0, Lua uses a lot of C stack space... */
#undef LUAI_MAXCCALLS
#define LUAI_MAXCCALLS 200
#undef LUAI_MAXCSTACK
#define LUAI_MAXCSTACK 400
/* to avoid warnings, and to make sure value is really unused */
#define UNUSED(x) (x=0, (void)(x))
@@ -118,7 +119,6 @@ LUA_API void *debug_realloc (void *ud, void *block,
#undef LUAL_BUFFERSIZE
#define LUAL_BUFFERSIZE 23
#define MINSTRTABSIZE 2
#define MAXINDEXRK 1
#define MAXIWTHABS 3

36
ltm.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: ltm.c,v 2.69 2018/06/08 19:06:59 roberto Exp roberto $
** $Id: ltm.c $
** Tag methods
** See Copyright Notice in lua.h
*/
@@ -43,7 +43,7 @@ void luaT_init (lua_State *L) {
"__div", "__idiv",
"__band", "__bor", "__bxor", "__shl", "__shr",
"__unm", "__bnot", "__lt", "__le",
"__concat", "__call"
"__concat", "__call", "__close"
};
int i;
for (i=0; i<TM_N; i++) {
@@ -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,20 +164,27 @@ 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);
}
void luaT_trybiniTM (lua_State *L, const TValue *p1, int i2,
int inv, StkId res, TMS event) {
void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
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);
}
@@ -188,6 +192,7 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) {
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
return !l_isfalse(s2v(L->top));
#if defined(LUA_COMPAT_LT_LE)
else if (event == TM_LE) {
/* try '!(p2 < p1)' for '(p1 <= p2)' */
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
@@ -197,16 +202,21 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
}
/* else error will remove this 'ci'; no need to clear mark */
}
#endif
luaG_ordererror(L, p1, p2); /* no metamethod found */
return 0; /* to avoid warnings */
}
int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, TMS event) {
int flip, int isfloat, TMS event) {
TValue aux; const TValue *p2;
setivalue(&aux, v2);
if (inv) { /* arguments were exchanged? */
if (isfloat) {
setfltvalue(&aux, cast_num(v2));
}
else
setivalue(&aux, v2);
if (flip) { /* arguments were exchanged? */
p2 = p1; p1 = &aux; /* correct them */
}
else

10
ltm.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: ltm.h,v 2.38 2018/06/08 19:06:59 roberto Exp roberto $
** $Id: ltm.h $
** Tag methods
** See Copyright Notice in lua.h
*/
@@ -40,6 +40,7 @@ typedef enum {
TM_LE,
TM_CONCAT,
TM_CALL,
TM_CLOSE,
TM_N /* number of elements in the enum */
} TMS;
@@ -74,14 +75,15 @@ 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);
LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, int i2,
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,
const TValue *p2, TMS event);
LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, TMS event);
int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
struct CallInfo *ci, const Proto *p);

41
lua.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lua.c,v 1.234 2018/03/06 20:30:17 roberto Exp roberto $
** $Id: lua.c $
** Lua stand-alone interpreter
** See Copyright Notice in lua.h
*/
@@ -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;
@@ -383,8 +395,7 @@ static int handle_luainit (lua_State *L) {
#include <readline/readline.h>
#include <readline/history.h>
#define lua_initreadline(L) \
((void)L, rl_readline_name="lua", rl_inhibit_completion=1)
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,line) ((void)L, add_history(line))
#define lua_freeline(L,b) ((void)L, free(b))

32
lua.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lua.h,v 1.346 2018/04/04 14:23:41 roberto Exp roberto $
** $Id: lua.h $
** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
@@ -18,12 +18,14 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE "0"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2018 Lua.org, PUC-Rio"
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2019 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -49,8 +51,7 @@
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
#define LUA_ERRMEM 4
#define LUA_ERRGCMM 5
#define LUA_ERRERR 6
#define LUA_ERRERR 5
typedef struct lua_State lua_State;
@@ -124,6 +125,13 @@ typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/*
** Type for warning functions
*/
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
/*
** generic extra include file
@@ -145,6 +153,7 @@ extern const char lua_ident[];
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int (lua_resetthread) (lua_State *L);
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
@@ -296,6 +305,13 @@ LUA_API int (lua_isyieldable) (lua_State *L);
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
/*
** Warning-related functions
*/
LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
/*
** garbage-collection function and options
*/
@@ -331,6 +347,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
LUA_API void (lua_toclose) (lua_State *L, int idx);
/*
** {==============================================================
@@ -444,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;
@@ -451,6 +470,7 @@ struct lua_Debug {
const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
const char *source; /* (S) */
size_t srclen; /* (S) */
int currentline; /* (l) */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
@@ -469,7 +489,7 @@ struct lua_Debug {
/******************************************************************************
* Copyright (C) 1994-2018 Lua.org, PUC-Rio.
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the

144
luaconf.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: luaconf.h,v 1.270 2018/06/18 12:51:05 roberto Exp roberto $
** $Id: luaconf.h $
** Configuration file for Lua
** See Copyright Notice in lua.h
*/
@@ -14,6 +14,16 @@
/*
** ===================================================================
** General Configuration File for Lua
**
** Some definitions here can be changed externally, through the
** compiler (e.g., with '-D' options). Those are protected by
** '#if !defined' guards. However, several other definitions should
** be changed directly here, either because they affect the Lua
** ABI (by making the changes here, you ensure that all software
** connected to Lua, such as C libraries, will be compiled with the
** same configuration); or because they are seldom changed.
**
** Search for "@@" to find all configurable definitions.
** ===================================================================
*/
@@ -22,18 +32,23 @@
/*
** {====================================================================
** System Configuration: macros to adapt (if needed) Lua to some
** particular platform, for instance compiling it with 32-bit numbers or
** restricting it to C89.
** particular platform, for instance restricting it to C89.
** =====================================================================
*/
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You
** can also define LUA_32BITS in the make file, but changing here you
** ensure that all software connected to Lua will be compiled with the
** same configuration.
@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and
** also limits the maximum depth of other recursive algorithms in
** the implementation, such as syntactic analysis. A value too
** large may allow the interpreter to crash (C-stack overflow).
** The default value seems ok for regular machines, but may be
** too high for restricted hardware.
** The test file 'cstack.lua' may help finding a good limit.
** (It will crash with a limit too high.)
*/
/* #define LUA_32BITS */
#if !defined(LUAI_MAXCSTACK)
#define LUAI_MAXCSTACK 2000
#endif
/*
@@ -70,6 +85,27 @@
#endif
/*
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
*/
#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3)
/* }================================================================== */
/*
** {==================================================================
** Configuration for Number types.
** ===================================================================
*/
/*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/
/* #define LUA_32BITS */
/*
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
** C89 ('long' and 'double'); Windows always has '__int64', so it does
@@ -80,24 +116,11 @@
#endif
/*
@@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'.
*/
/* avoid undefined shifts */
#if ((INT_MAX >> 15) >> 15) >= 1
#define LUAI_BITSINT 32
#else
/* 'int' always must have at least 16 bits */
#define LUAI_BITSINT 16
#endif
/*
@@ LUA_INT_TYPE defines the type for Lua integers.
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
** Lua should work fine with any mix of these options (if supported
** by your C compiler). The usual configurations are 64-bit integers
** Lua should work fine with any mix of these options supported
** by your C compiler. The usual configurations are 64-bit integers
** and 'double' (the default), 32-bit integers and 'float' (for
** restricted platforms), and 'long'/'double' (for C compilers not
** compliant with C99, which may not have support for 'long long').
@@ -117,7 +140,7 @@
/*
** 32-bit integers and 'float'
*/
#if LUAI_BITSINT >= 32 /* use 'int' if big enough */
#if LUAI_IS32INT /* use 'int' if big enough */
#define LUA_INT_TYPE LUA_INT_INT
#else /* otherwise use 'long' */
#define LUA_INT_TYPE LUA_INT_LONG
@@ -149,7 +172,6 @@
/*
** {==================================================================
** Configuration for Paths.
@@ -177,6 +199,7 @@
** hierarchy or if you want to install your libraries in
** non-conventional directories.
*/
#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#if defined(_WIN32) /* { */
/*
@@ -186,27 +209,40 @@
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
#if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
".\\?.lua;" ".\\?\\init.lua"
#endif
#if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \
LUA_CDIR"?.dll;" \
LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
LUA_CDIR"loadall.dll;" ".\\?.dll"
#endif
#else /* }{ */
#define LUA_ROOT "/usr/local/"
#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
#if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \
LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
"./?.lua;" "./?/init.lua"
#endif
#if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \
LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
#endif
#endif /* } */
@@ -215,12 +251,16 @@
** CHANGE it if your machine does not use "/" as the directory separator
** and is not Windows. (On Windows Lua automatically uses "\".)
*/
#if !defined(LUA_DIRSEP)
#if defined(_WIN32)
#define LUA_DIRSEP "\\"
#else
#define LUA_DIRSEP "/"
#endif
#endif
/* }================================================================== */
@@ -277,7 +317,7 @@
*/
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
defined(__ELF__) /* { */
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
#define LUAI_FUNC __attribute__((visibility("internal"))) extern
#else /* }{ */
#define LUAI_FUNC extern
#endif /* } */
@@ -295,7 +335,7 @@
*/
/*
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.2.
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
** You can define it to get all options, or change specific options
** to fit your specific needs.
*/
@@ -304,8 +344,8 @@
/*
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
** functions in the mathematical library.
** (These functions were already officially removed in 5.3, but
** nevertheless they are available by default there.)
** (These functions were already officially removed in 5.3;
** nevertheless they are still available here.)
*/
#define LUA_COMPAT_MATHLIB
@@ -313,17 +353,25 @@
@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
** luaL_checkint, luaL_checklong, etc.)
** (These macros were also officially removed in 5.3, but they are still
** available here.)
*/
#define LUA_COMPAT_APIINTCASTS
#endif /* } */
/*
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
** using '__lt'.
*/
#define LUA_COMPAT_LT_LE
/*
@@ The following macros supply trivial compatibility for some
** changes in the API. The macros themselves document how to
** change your code to avoid using them.
** (Once more, these macros were officially removed in 5.3, but they are
** still available here.)
*/
#define lua_strlen(L,i) lua_rawlen(L, (i))
@@ -332,6 +380,8 @@
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#endif /* } */
/* }================================================================== */
@@ -355,7 +405,7 @@
@@ lua_number2str converts a float to a string.
@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
@@ l_floor takes the floor of a float.
@@ lua_str2number converts a decimal numeric string to a number.
@@ lua_str2number converts a decimal numeral to a number.
*/
@@ -443,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.
@@ -469,7 +519,6 @@
*/
#define LUA_UNSIGNED unsigned LUAI_UACINT
#define LUA_MAXUNSIGNED (~(lua_Unsigned)0)
#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT)
@@ -484,6 +533,8 @@
#define LUA_MAXINTEGER INT_MAX
#define LUA_MININTEGER INT_MIN
#define LUA_MAXUNSIGNED UINT_MAX
#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
#define LUA_INTEGER long
@@ -492,6 +543,8 @@
#define LUA_MAXINTEGER LONG_MAX
#define LUA_MININTEGER LONG_MIN
#define LUA_MAXUNSIGNED ULONG_MAX
#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
/* use presence of macro LLONG_MAX as proxy for C99 compliance */
@@ -504,6 +557,8 @@
#define LUA_MAXINTEGER LLONG_MAX
#define LUA_MININTEGER LLONG_MIN
#define LUA_MAXUNSIGNED ULLONG_MAX
#elif defined(LUA_USE_WINDOWS) /* }{ */
/* in Windows, can use specific Windows types */
@@ -513,6 +568,8 @@
#define LUA_MAXINTEGER _I64_MAX
#define LUA_MININTEGER _I64_MIN
#define LUA_MAXUNSIGNED _UI64_MAX
#else /* }{ */
#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
@@ -547,7 +604,7 @@
/*
@@ lua_strx2number converts a hexadecimal numeric string to a number.
@@ lua_strx2number converts a hexadecimal numeral to a number.
** In C99, 'strtod' does that conversion. Otherwise, you can
** leave 'lua_strx2number' undefined and Lua will provide its own
** implementation.
@@ -565,7 +622,7 @@
/*
@@ lua_number2strx converts a float to a hexadecimal numeric string.
@@ lua_number2strx converts a float to a hexadecimal numeral.
** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
** provide its own implementation.
@@ -611,7 +668,7 @@
/*
@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
** Change that if you do not want to use C locales. (Code using this
** macro must include header 'locale.h'.)
** macro must include the header 'locale.h'.)
*/
#if !defined(lua_getlocaledecpoint)
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
@@ -652,7 +709,7 @@
** {==================================================================
** Macros that affect the API and must be stable (that is, must be the
** same when you compile Lua and when you compile code that links to
** Lua). You probably do not want/need to change them.
** Lua).
** =====================================================================
*/
@@ -663,7 +720,7 @@
** space (and to reserve some numbers for pseudo-indices).
** (It must fit into max(size_t)/32.)
*/
#if LUAI_BITSINT >= 32
#if LUAI_IS32INT
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK 15000
@@ -688,16 +745,9 @@
/*
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
** CHANGE it if it uses too much C-stack space. (For long double,
** 'string.format("%.99f", -1e4932)' needs 5034 bytes, so a
** smaller buffer would force a memory allocation for each call to
** 'string.format'.)
*/
#if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE
#define LUAL_BUFFERSIZE 8192
#else
#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer)))
#endif
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
/*
@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure

View File

@@ -1,5 +1,5 @@
/*
** $Id: lualib.h,v 1.45 2017/01/12 17:14:26 roberto Exp roberto $
** $Id: lualib.h $
** Lua standard libraries
** See Copyright Notice in lua.h
*/

View File

@@ -1,5 +1,5 @@
/*
** $Id: lundump.c,v 2.49 2017/12/07 18:59:52 roberto Exp roberto $
** $Id: lundump.c $
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -10,6 +10,7 @@
#include "lprefix.h"
#include <limits.h>
#include <string.h>
#include "lua.h"
@@ -37,7 +38,7 @@ typedef struct {
static l_noret error (LoadState *S, const char *why) {
luaO_pushfstring(S->L, "%s: %s precompiled chunk", S->name, why);
luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why);
luaD_throw(S->L, LUA_ERRSYNTAX);
}
@@ -50,7 +51,7 @@ static l_noret error (LoadState *S, const char *why) {
static void LoadBlock (LoadState *S, void *b, size_t size) {
if (luaZ_read(S->Z, b, size) != 0)
error(S, "truncated");
error(S, "truncated chunk");
}
@@ -60,24 +61,32 @@ static void LoadBlock (LoadState *S, void *b, size_t size) {
static lu_byte LoadByte (LoadState *S) {
int b = zgetc(S->Z);
if (b == EOZ)
error(S, "truncated");
error(S, "truncated chunk");
return cast_byte(b);
}
static size_t LoadSize (LoadState *S) {
static size_t LoadUnsigned (LoadState *S, size_t limit) {
size_t x = 0;
int b;
limit >>= 7;
do {
b = LoadByte(S);
if (x >= limit)
error(S, "integer overflow");
x = (x << 7) | (b & 0x7f);
} while ((b & 0x80) == 0);
return x;
}
static size_t LoadSize (LoadState *S) {
return LoadUnsigned(S, ~(size_t)0);
}
static int LoadInt (LoadState *S) {
return cast_int(LoadSize(S));
return cast_int(LoadUnsigned(S, INT_MAX));
}
@@ -189,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);
}
}
@@ -255,28 +264,27 @@ static void checkliteral (LoadState *S, const char *s, const char *msg) {
static void fchecksize (LoadState *S, size_t size, const char *tname) {
if (LoadByte(S) != size)
error(S, luaO_pushfstring(S->L, "%s size mismatch in", tname));
error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
}
#define checksize(S,t) fchecksize(S,sizeof(t),#t)
static void checkHeader (LoadState *S) {
checkliteral(S, LUA_SIGNATURE + 1, "not a"); /* 1st char already checked */
if (LoadByte(S) != LUAC_VERSION)
error(S, "version mismatch in");
/* skip 1st char (already read and checked) */
checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
if (LoadInt(S) != LUAC_VERSION)
error(S, "version mismatch");
if (LoadByte(S) != LUAC_FORMAT)
error(S, "format mismatch in");
checkliteral(S, LUAC_DATA, "corrupted");
checksize(S, int);
checksize(S, size_t);
error(S, "format mismatch");
checkliteral(S, LUAC_DATA, "corrupted chunk");
checksize(S, Instruction);
checksize(S, lua_Integer);
checksize(S, lua_Number);
if (LoadInteger(S) != LUAC_INT)
error(S, "endianness mismatch in");
error(S, "integer format mismatch");
if (LoadNumber(S) != LUAC_NUM)
error(S, "float format mismatch in");
error(S, "float format mismatch");
}

View File

@@ -1,5 +1,5 @@
/*
** $Id: lundump.h,v 1.44 2014/06/19 18:27:20 roberto Exp roberto $
** $Id: lundump.h $
** load precompiled Lua chunks
** See Copyright Notice in lua.h
*/
@@ -18,8 +18,7 @@
#define LUAC_INT 0x5678
#define LUAC_NUM cast_num(370.5)
#define MYINT(s) (s[0]-'0')
#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
#define LUAC_VERSION LUA_VERSION_NUM
#define LUAC_FORMAT 0 /* this is the official format */
/* load one chunk; from lundump.c */

View File

@@ -1,5 +1,5 @@
/*
** $Id: lutf8lib.c,v 1.16 2016/12/22 13:08:50 roberto Exp roberto $
** $Id: lutf8lib.c $
** Standard library for UTF-8 manipulation
** See Copyright Notice in lua.h
*/
@@ -20,7 +20,20 @@
#include "lauxlib.h"
#include "lualib.h"
#define MAXUNICODE 0x10FFFF
#define MAXUNICODE 0x10FFFFu
#define MAXUTF 0x7FFFFFFFu
/*
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
*/
#if (UINT_MAX >> 30) >= 1
typedef unsigned int utfint;
#else
typedef unsigned long utfint;
#endif
#define iscont(p) ((*(p) & 0xC0) == 0x80)
@@ -35,53 +48,62 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
/*
** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
** Decode one UTF-8 sequence, returning NULL if byte sequence is
** invalid. The array 'limits' stores the minimum value for each
** sequence length, to check for overlong representations. Its first
** entry forces an error for non-ascii bytes with no continuation
** bytes (count == 0).
*/
static const char *utf8_decode (const char *o, int *val) {
static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
const unsigned char *s = (const unsigned char *)o;
unsigned int c = s[0];
unsigned int res = 0; /* final result */
static const char *utf8_decode (const char *s, utfint *val, int strict) {
static const utfint limits[] =
{~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
unsigned int c = (unsigned char)s[0];
utfint res = 0; /* final result */
if (c < 0x80) /* ascii? */
res = c;
else {
int count = 0; /* to count number of continuation bytes */
while (c & 0x40) { /* still have continuation bytes? */
int cc = s[++count]; /* read next byte */
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
unsigned int cc = (unsigned char)s[++count]; /* read next byte */
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
c <<= 1; /* to test next bit */
}
res |= ((c & 0x7F) << (count * 5)); /* add first byte */
if (count > 3 || res > MAXUNICODE || res <= limits[count])
res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
if (count > 5 || res > MAXUTF || res < limits[count])
return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */
}
if (strict) {
/* check for invalid code points; too large or surrogates */
if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu))
return NULL;
}
if (val) *val = res;
return (const char *)s + 1; /* +1 to include first byte */
return s + 1; /* +1 to include first byte */
}
/*
** utf8len(s [, i [, j]]) --> number of characters that start in the
** range [i,j], or nil + current position if 's' is not well formed in
** that interval
** utf8len(s [, i [, j [, lax]]]) --> number of characters that
** start in the range [i,j], or nil + current position if 's' is not
** well formed in that interval
*/
static int utflen (lua_State *L) {
int n = 0;
size_t len;
lua_Integer n = 0; /* counter for the number of characters */
size_t len; /* string length in bytes */
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
int lax = lua_toboolean(L, 4);
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
"initial position out of string");
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
"final position out of string");
while (posi <= posj) {
const char *s1 = utf8_decode(s + posi, NULL);
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;
}
@@ -94,14 +116,15 @@ static int utflen (lua_State *L) {
/*
** codepoint(s, [i, [j]]) -> returns codepoints for all characters
** that start in the range [i,j]
** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all
** characters that start in the range [i,j]
*/
static int codepoint (lua_State *L) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
int lax = lua_toboolean(L, 4);
int n;
const char *se;
luaL_argcheck(L, posi >= 1, 2, "out of range");
@@ -109,13 +132,13 @@ static int codepoint (lua_State *L) {
if (posi > pose) return 0; /* empty interval; return no values */
if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
return luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1;
n = (int)(pose - posi) + 1; /* upper bound for number of returns */
luaL_checkstack(L, n, "string slice too long");
n = 0;
se = s + pose;
n = 0; /* count the number of returns */
se = s + pose; /* string end */
for (s += posi - 1; s < se;) {
int code;
s = utf8_decode(s, &code);
utfint code;
s = utf8_decode(s, &code, !lax);
if (s == NULL)
return luaL_error(L, "invalid UTF-8 code");
lua_pushinteger(L, code);
@@ -126,8 +149,8 @@ static int codepoint (lua_State *L) {
static void pushutfchar (lua_State *L, int arg) {
lua_Integer code = luaL_checkinteger(L, arg);
luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range");
lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
lua_pushfstring(L, "%U", (long)code);
}
@@ -193,12 +216,12 @@ 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;
}
static int iter_aux (lua_State *L) {
static int iter_aux (lua_State *L, int strict) {
size_t len;
const char *s = luaL_checklstring(L, 1, &len);
lua_Integer n = lua_tointeger(L, 2) - 1;
@@ -211,9 +234,9 @@ static int iter_aux (lua_State *L) {
if (n >= (lua_Integer)len)
return 0; /* no more codepoints */
else {
int code;
const char *next = utf8_decode(s + n, &code);
if (next == NULL || iscont(next))
utfint code;
const char *next = utf8_decode(s + n, &code, strict);
if (next == NULL)
return luaL_error(L, "invalid UTF-8 code");
lua_pushinteger(L, n + 1);
lua_pushinteger(L, code);
@@ -222,9 +245,19 @@ static int iter_aux (lua_State *L) {
}
static int iter_auxstrict (lua_State *L) {
return iter_aux(L, 1);
}
static int iter_auxlax (lua_State *L) {
return iter_aux(L, 0);
}
static int iter_codes (lua_State *L) {
int lax = lua_toboolean(L, 2);
luaL_checkstring(L, 1);
lua_pushcfunction(L, iter_aux);
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
lua_pushvalue(L, 1);
lua_pushinteger(L, 0);
return 3;
@@ -232,7 +265,7 @@ static int iter_codes (lua_State *L) {
/* pattern to match a single UTF-8 character */
#define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*"
static const luaL_Reg funcs[] = {

1014
lvm.c

File diff suppressed because it is too large Load Diff

5
lvm.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lvm.h,v 2.51 2018/02/23 13:13:31 roberto Exp roberto $
** $Id: lvm.h $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@@ -114,8 +114,9 @@ LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
LUAI_FUNC void luaV_finishOp (lua_State *L);
LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci);
LUAI_FUNC void luaV_concat (lua_State *L, int total);
LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y);
LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y);
LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y);
LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y);
LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y);
LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);

2
lzio.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: lzio.c,v 1.36 2014/11/02 19:19:04 roberto Exp roberto $
** $Id: lzio.c $
** Buffered streams
** See Copyright Notice in lua.h
*/

2
lzio.h
View File

@@ -1,5 +1,5 @@
/*
** $Id: lzio.h,v 1.30 2014/12/19 17:26:14 roberto Exp roberto $
** $Id: lzio.h $
** Buffered streams
** See Copyright Notice in lua.h
*/

View File

@@ -6,6 +6,7 @@
# Warnings valid for both C and C++
CWARNSCPP= \
-fmax-errors=5 \
-Wextra \
-Wshadow \
-Wsign-compare \
@@ -14,13 +15,13 @@ CWARNSCPP= \
-Wredundant-decls \
-Wdisabled-optimization \
-Wdouble-promotion \
-Wstrict-aliasing=3 \
-Wno-aggressive-loop-optimizations \
-Wlogical-op \
-Werror \
-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 \
@@ -39,16 +40,16 @@ CWARNS= $(CWARNSCPP) $(CWARNSC)
# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"'
# -DMAXINDEXRK=1 -DLUA_COMPAT_5_3
# -g -DLUA_USER_H='"ltests.h"'
# -pg -malign-double
# -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
@@ -58,7 +59,7 @@ MYLIBS= -ldl -lreadline
CC= gcc
CFLAGS= -Wall -O2 $(MYCFLAGS) -Wfatal-errors -fno-stack-protector -fno-common
CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -march=native
AR= ar rc
RANLIB= ranlib
RM= rm -f
@@ -89,6 +90,7 @@ ALL_O= $(CORE_O) $(LUA_O) $(LUAC_O) $(AUX_O) $(LIB_O)
ALL_A= $(CORE_T)
all: $(ALL_T)
touch all
o: $(ALL_O)
@@ -104,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:
@@ -122,7 +133,7 @@ echo:
@echo "MYLIBS = $(MYLIBS)"
@echo "DL = $(DL)"
$(ALL_O): makefile
$(ALL_O): makefile ltests.h
# DO NOT EDIT
# automatically made with 'gcc -MM l*.c'
@@ -146,8 +157,8 @@ ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lparser.h lstring.h ltable.h lundump.h lvm.h
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
ltm.h lzio.h lmem.h lundump.h
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h lfunc.h lobject.h llimits.h \
lgc.h lstate.h ltm.h lzio.h lmem.h
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
@@ -178,8 +189,8 @@ ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
ltests.o: ltests.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
lobject.h ltm.h lzio.h lmem.h lauxlib.h lcode.h llex.h lopcodes.h \
lparser.h lctype.h ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h \
lualib.h
lparser.h lctype.h ldebug.h ldo.h lfunc.h lopnames.h lstring.h lgc.h \
ltable.h lualib.h
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h

519
manual/2html Executable file
View File

@@ -0,0 +1,519 @@
#!/usr/bin/env lua5.3
-- special marks:
-- \1 - paragraph (empty line)
-- \4 - remove spaces around it
-- \3 - ref (followed by label|)
---------------------------------------------------------------
header = [[
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Lua 5.4 Reference Manual</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link rel="stylesheet" href="lua.css">
<link rel="stylesheet" href="manual.css">
</head>
<body bgcolor="#FFFFFF">
<hr>
<h1>
<a href="http://www.lua.org/home.html"><img src="logo.gif" alt="[Lua logo]" border="0"></a>
Lua 5.4 Reference Manual
</h1>
by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<p>
<small>
<a href="http://www.lua.org/copyright.html">Copyright</a>
&copy; 2019 Lua.org, PUC-Rio. All rights reserved.
</small>
<hr>
<!-- ====================================================================== -->
<p>
]]
footer = "\n\n</body></html>\n\n"
local seefmt = '(see %s)'
if arg[1] == 'port' then
seefmt = '(ver %s)'
header = string.gsub(header, "by (.-)\n",
"%1\n<p>Tradu&ccedil;&atilde;o: S&eacute;rgio Queiroz de Medeiros", 1)
header = string.gsub(header, "Lua (%d+.%d+) Reference Manual",
"Manual de Refer&ecirc;ncia de Lua %1")
header = string.gsub(header, "All rights reserved",
"Todos os direitos reservados")
end
---------------------------------------------------------------
local function compose (f,g)
assert(f and g)
return function (s) return g(f(s)) end
end
local function concat (f, g)
assert(f and g)
return function (s) return f(s) .. g(s) end
end
local Tag = {}
setmetatable(Tag, {
__index = function (t, tag)
local v = function (n, att)
local e = ""
if type(att) == "table" then
for k,v in pairs(att) do e = string.format('%s %s="%s"', e, k, v) end
end
if n then
return string.format("<%s%s>%s</%s>", tag, e, n, tag)
else
return string.format("<%s%s>", tag, e)
end
end
t[tag] = v
return v
end
})
---------------------------------------------------------------
local labels = {}
local function anchor (text, label, link, textlink)
if labels[label] then
error("label " .. label .. " already defined")
end
labels[label] = {text = textlink, link = link}
return Tag.a(text, {name=link})
end
local function makeref (label)
assert(not string.find(label, "|"))
return string.format("\3%s\3", label)
end
local function ref (label)
local l = labels[label]
if not l then
io.stderr:write("label ", label, " undefined\n")
return "@@@@@@@"
else
return Tag.a(l.text, {href="#"..l.link})
end
end
---------------------------------------------------------------
local function nopara (t)
t = string.gsub(t, "\1", "\n\n")
t = string.gsub(t, "<p>%s*</p>", "")
return t
end
local function fixpara (t)
t = string.gsub(t, "\1", "\n</p>\n\n<p>\n")
t = string.gsub(t, "<p>%s*</p>", "")
return t
end
local function antipara (t)
return "</p>\n" .. t .. "<p>"
end
Tag.pre = compose(Tag.pre, antipara)
Tag.ul = compose(Tag.ul, antipara)
---------------------------------------------------------------
local Gfoots = 0
local footnotes = {}
local line = Tag.hr(nil)
local function dischargefoots ()
if #footnotes == 0 then return "" end
local fn = table.concat(footnotes)
footnotes = {}
return line .. Tag.h3"footnotes:" .. fn .. line
end
local Glists = 0
local listings = {}
local function dischargelist ()
if #listings == 0 then return "" end
local l = listings
listings = {}
return line .. table.concat(l, line..line) .. line
end
---------------------------------------------------------------
local counters = {
h1 = {val = 1},
h2 = {father = "h1", val = 1},
h3 = {father = "h2", val = 1},
listing = {father = "h1", val = 1},
}
local function inccounter (count)
counters[count].val = counters[count].val + 1
for c, v in pairs(counters) do
if v.father == count then v.val = 1 end
end
end
local function getcounter (count)
local c = counters[count]
if c.father then
return getcounter(c.father) .. "." .. c.val
else
return c.val .. ""
end
end
---------------------------------------------------------------
local function fixed (x)
return function () return x end
end
local function id (x) return x end
local function prepos (x, y)
assert(x and y)
return function (s) return string.format("%s%s%s", x, s, y) end
end
local rw = Tag.b
local function LuaName (name)
return Tag.code(name)
end
local function getparam (s)
local i, e = string.find(s, "^[^%s@|]+|")
if not i then return nil, s
else return string.sub(s, i, e - 1), string.sub(s, e + 1)
end
end
local function gettitle (h)
local title, p = assert(string.match(h, "<title>(.-)</title>()"))
return title, string.sub(h, p)
end
local function getparamtitle (what, h, nonum)
local label, title, c, count
label, h = getparam(h)
title, h = gettitle(h)
if not nonum then
count = getcounter(what)
inccounter(what)
c = string.format("%s &ndash; ", count)
else
c = ""
end
label = label or count
if label then
title = anchor(title, label, count, "&sect;"..count)
end
title = string.format("%s%s", c, title)
return title, h
end
local function section (what, nonum)
return function (h)
local title
title, h = getparamtitle(what, h, nonum)
local fn = what == "h1" and dischargefoots() or ""
h = fixpara(Tag.p(h))
return "</p>\n" .. Tag[what](title) .. h .. fn ..
dischargelist() .. "<p>"
end
end
local function verbatim (s)
s = nopara(s)
s = string.gsub(s, "\n", "\n ")
s = string.gsub(s, "\n%s*$", "\n")
return Tag.pre(s)
end
local function verb (s)
return Tag.code(s)
end
local function lua2link (e)
return string.find(e, "luaL?_") and e or "pdf-"..e
end
local verbfixed = verb
local Tex = {
ANSI = function (func)
return "ISO&nbsp;C function " .. Tag.code(func)
end,
At = fixed"@",
B = Tag.b,
bigskip = fixed"",
bignum = id,
C = fixed"",
Ci = prepos("<!-- ", " -->"),
CId = function (func)
return "C&nbsp;function " .. Tag.code(func)
end,
chapter = section"h1",
Char = compose(verbfixed, prepos("'", "'")),
Cdots = fixed"&middot;&middot;&middot;",
Close = fixed"}",
col = Tag.td,
defid = function (name)
local l = lua2link(name)
local c = Tag.code(name)
return anchor(c, l, l, c)
end,
def = Tag.em,
description = compose(nopara, Tag.ul),
Em = fixed("\4" .. "&mdash;" .. "\4"),
emph = Tag.em,
emphx = Tag.em, -- emphasis plus index (if there was an index)
En = fixed("&ndash;"),
format = fixed"",
["false"] = fixed(Tag.b"false"),
id = Tag.code,
idx = Tag.code,
index = fixed"",
Lidx = fixed"", -- Tag.code,
ldots = fixed"...",
x = id,
itemize = compose(nopara, Tag.ul),
leq = fixed"&le;",
Lid = function (s)
return makeref(lua2link(s))
end,
M = Tag.em,
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("'", "'")),
preface = section("h1", true),
psect = section("h2", true),
Q = prepos('"', '"'),
refchp = makeref,
refcode = makeref,
refsec = makeref,
pi = fixed"&pi;",
rep = Tag.em, -- compose(prepos("&lt;", "&gt;"), Tag.em),
Rw = rw,
rw = rw,
sb = Tag.sub,
sp = Tag.sup,
St = compose(verbfixed, prepos('"', '"')),
sect1 = section"h1",
sect2 = section"h2",
sect3 = section"h3",
sect4 = section("h4", true),
simplesect = id,
Tab2 = function (s) return Tag.table(s, {border=1}) end,
row = Tag.tr,
title = Tag.title,
todo = Tag.todo,
["true"] = fixed(Tag.b"true"),
T = verb,
item = function (s)
local t, p = string.match(s, "^([^\n|]+)|()")
if t then
s = string.sub(s, p)
s = Tag.b(t..": ") .. s
end
return Tag.li(fixpara(s))
end,
verbatim = verbatim,
manual = id,
-- for the manual
link =function (s)
local l, t = getparam(s)
assert(l)
return string.format("%s (%s)", t, makeref(l))
end,
see = function (s) return string.format(seefmt, makeref(s)) end,
See = makeref,
seeC = function (s)
return string.format(seefmt, makeref(s))
end,
seeF = function (s)
return string.format(seefmt, makeref(lua2link(s)))
end,
APIEntry = function (e)
local h, name
h, e = string.match(e, "^%s*(.-)%s*|(.*)$")
name = string.match(h, "(luaL?_[%w_]+)%)? +%(") or
string.match(h, "luaL?_[%w_]+")
local a = anchor(Tag.code(name), name, name, Tag.code(name))
local apiicmd, ne = string.match(e, "^(.-</span>)(.*)")
--io.stderr:write(e)
if not apiicmd then
return antipara(Tag.hr() .. Tag.h3(a)) .. Tag.pre(h) .. e
else
return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd .. Tag.pre(h) .. ne
end
end,
LibEntry = function (e)
local h, name
h, e = string.match(e, "^(.-)|(.*)$")
name = string.gsub(h, " (.+", "")
local l = lua2link(name)
local a = anchor(Tag.code(h), l, l, Tag.code(name))
return Tag.hr() .. Tag.h3(a) .. e
end,
Produc = compose(nopara, Tag.pre),
producname = prepos("\t", " ::= "),
Or = fixed" | ",
VerBar = fixed"&#124;", -- vertical bar
OrNL = fixed" | \4",
bnfNter = prepos("", ""),
bnfopt = prepos("[", "]"),
bnfrep = prepos("{", "}"),
bnfter = compose(Tag.b, prepos("&lsquo;", "&rsquo;")),
producbody = function (s)
s = string.gsub(s, "%s+", " ")
s = string.gsub(s, "\4", "\n\t\t")
return s
end,
apii = function (s)
local pop,push,err = string.match(s, "^(.-),(.-),(.*)$")
if pop ~= "?" and string.find(pop, "%W") then
pop = "(" .. pop .. ")"
end
if push ~= "?" and string.find(push, "%W") then
push = "(" .. push .. ")"
end
err = (err == "-") and "&ndash;" or Tag.em(err)
return Tag.span(
string.format("[-%s, +%s, %s]", pop, push, err),
{class="apii"}
)
end,
}
local others = prepos("?? "," ??")
local function trata (t)
t = string.gsub(t, "@(%w+)(%b{})", function (w, f)
f = trata(string.sub(f, 2, -2))
if type(Tex[w]) ~= "function" then
io.stderr:write(w .. "\n")
return others(f)
else
return Tex[w](f, w)
end
end)
return t
end
---------------------------------------------------------------------
---------------------------------------------------------------------
-- read whole book
t = io.read"*a"
t = string.gsub(t, "[<>&\128-\255]",
{["<"] = "&lt;",
[">"] = "&gt;",
["&"] = "&amp;",
["\170"] = "&ordf;",
["\186"] = "&ordm;",
["\192"] = "&Agrave;",
["\193"] = "&Aacute;",
["\194"] = "&Acirc;",
["\195"] = "&Atilde;",
["\199"] = "&Ccedil;",
["\201"] = "&Eacute;",
["\202"] = "&Ecirc;",
["\205"] = "&Iacute;",
["\211"] = "&Oacute;",
["\212"] = "&Ocirc;",
["\218"] = "&Uacute;",
["\224"] = "&agrave;",
["\225"] = "&aacute;",
["\226"] = "&acirc;",
["\227"] = "&atilde;",
["\231"] = "&ccedil;",
["\233"] = "&eacute;",
["\234"] = "&ecirc;",
["\237"] = "&iacute;",
["\243"] = "&oacute;",
["\244"] = "&ocirc;",
["\245"] = "&otilde;",
["\250"] = "&uacute;",
["\252"] = "&uuml;"
})
t = string.gsub(t, "\n\n+", "\1")
-- complete macros with no arguments
t = string.gsub(t, "(@%w+)([^{%w])", "%1{}%2")
t = trata(t)
-- correct references
t = string.gsub(t, "\3(.-)\3", ref)
-- remove extra space (??)
t = string.gsub(t, "%s*\4%s*", "")
t = nopara(t)
-- HTML 3.2 does not need </p> (but complains when it is in wrong places :)
t = string.gsub(t, "</p>", "")
io.write(header, t, footer)

9165
manual/manual.of Normal file

File diff suppressed because it is too large Load Diff

107
onelua.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* one.c -- Lua core, libraries, and interpreter in a single file
*/
/* default is to build the full interpreter */
#ifndef MAKE_LIB
#ifndef MAKE_LUAC
#ifndef MAKE_LUA
#define MAKE_LUA
#endif
#endif
#endif
/* choose suitable platform-specific features */
/* some of these may need extra libraries such as -ldl -lreadline -lncurses */
#if 0
#define LUA_USE_LINUX
#define LUA_USE_MACOSX
#define LUA_USE_POSIX
#define LUA_ANSI
#endif
/* no need to change anything below this line ----------------------------- */
#include "lprefix.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* setup for luaconf.h */
#define LUA_CORE
#define LUA_LIB
#define ltable_c
#define lvm_c
#include "luaconf.h"
/* do not export internal symbols */
#undef LUAI_FUNC
#undef LUAI_DDEC
#undef LUAI_DDEF
#define LUAI_FUNC static
#define LUAI_DDEC(def) /* empty */
#define LUAI_DDEF static
/* core -- used by all */
#include "lzio.c"
#include "lctype.c"
#include "lopcodes.c"
#include "lmem.c"
#include "lundump.c"
#include "ldump.c"
#include "lstate.c"
#include "lgc.c"
#include "llex.c"
#include "lcode.c"
#include "lparser.c"
#include "ldebug.c"
#include "lfunc.c"
#include "lobject.c"
#include "ltm.c"
#include "lstring.c"
#include "ltable.c"
#include "ldo.c"
#include "lvm.c"
#include "lapi.c"
/* auxiliary library -- used by all */
#include "lauxlib.c"
/* standard library -- not used by luac */
#ifndef MAKE_LUAC
#include "lbaselib.c"
#include "lcorolib.c"
#include "ldblib.c"
#include "liolib.c"
#include "lmathlib.c"
#include "loadlib.c"
#include "loslib.c"
#include "lstrlib.c"
#include "ltablib.c"
#include "lutf8lib.c"
#include "linit.c"
#endif
/* lua */
#ifdef MAKE_LUA
#include "lua.c"
#endif
/* luac */
#ifdef MAKE_LUAC
#include "luac.c"
#endif

321
testes/all.lua Normal file
View File

@@ -0,0 +1,321 @@
#!../lua
-- $Id: testes/all.lua $
-- See Copyright Notice at the end of this file
local version = "Lua 5.4"
if _VERSION ~= version then
io.stderr:write("This test suite is for ", version,
", not for ", _VERSION, "\nExiting tests")
return
end
_G.ARG = arg -- save arg for other tests
-- next variables control the execution of some tests
-- true means no test (so an undefined variable does not skip a test)
-- defaults are for Linux; test everything.
-- Make true to avoid long or memory consuming tests
_soft = rawget(_G, "_soft") or false
-- Make true to avoid non-portable tests
_port = rawget(_G, "_port") or false
-- Make true to avoid messages about tests not performed
_nomsg = rawget(_G, "_nomsg") or false
local usertests = rawget(_G, "_U")
if usertests then
-- tests for sissies ;) Avoid problems
_soft = true
_port = true
_nomsg = true
end
-- tests should require debug when needed
debug = nil
if usertests then
T = nil -- no "internal" tests for user tests
else
T = rawget(_G, "T") -- avoid problems with 'strict' module
end
--[=[
example of a long [comment],
[[spanning several [lines]]]
]=]
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")
local initclock = os.clock()
local lastclock = initclock
local walltime = os.time()
local collectgarbage = collectgarbage
do -- (
-- track messages for tests not performed
local msgs = {}
function Message (m)
if not _nomsg then
print(m)
msgs[#msgs+1] = string.sub(m, 3, -3)
end
end
assert(os.setlocale"C")
local T,print,format,write,assert,type,unpack,floor =
T,print,string.format,io.write,assert,type,table.unpack,math.floor
-- use K for 1000 and M for 1000000 (not 2^10 -- 2^20)
local function F (m)
local function round (m)
m = m + 0.04999
return format("%.1f", m) -- keep one decimal digit
end
if m < 1000 then return m
else
m = m / 1000
if m < 1000 then return round(m).."K"
else
return round(m/1000).."M"
end
end
end
local Cstacklevel
local showmem
if not T then
local max = 0
showmem = function ()
local m = collectgarbage("count") * 1024
max = (m > max) and m or max
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()
local total, numblocks, maxmem = T.totalmem()
local count = collectgarbage("count")
print(format(
"\n ---- total memory: %s (%.0fK), max use: %s, blocks: %d\n",
F(total), count, F(maxmem), numblocks))
print(format("\t(strings: %d, tables: %d, functions: %d, "..
"\n\tudata: %d, threads: %d)",
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
--
local function report (n) print("\n***** FILE '"..n.."'*****") end
local olddofile = dofile
local dofile = function (n, strip)
showmem()
local c = os.clock()
print(string.format("time: %g (+%g)", c - initclock, c - lastclock))
lastclock = c
report(n)
local f = assert(loadfile(n))
local b = string.dump(f, strip)
f = assert(load(b))
return f()
end
dofile('main.lua')
do
local next, setmetatable, stderr = next, setmetatable, io.stderr
-- track collections
local mt = {}
-- each time a table is collected, remark it for finalization
-- on next cycle
mt.__gc = function (o)
stderr:write'.' -- mark progress
local n = setmetatable(o, mt) -- remark it
end
local n = setmetatable({}, mt) -- create object
end
report"gc.lua"
local f = assert(loadfile('gc.lua'))
f()
dofile('db.lua')
assert(dofile('calls.lua') == deep and deep)
olddofile('strings.lua')
olddofile('literals.lua')
dofile('tpack.lua')
assert(dofile('attrib.lua') == 27)
dofile('gengc.lua')
assert(dofile('locals.lua') == 5)
dofile('constructs.lua')
dofile('code.lua', true)
if not _G._soft then
report('big.lua')
local f = coroutine.wrap(assert(loadfile('big.lua')))
assert(f() == 'b')
assert(f() == 'a')
end
dofile('cstack.lua')
dofile('nextvar.lua')
dofile('pm.lua')
dofile('utf8.lua')
dofile('api.lua')
assert(dofile('events.lua') == 12)
dofile('vararg.lua')
dofile('closure.lua')
dofile('coroutine.lua')
dofile('goto.lua', true)
dofile('errors.lua')
dofile('math.lua')
dofile('sort.lua', true)
dofile('bitwise.lua')
assert(dofile('verybig.lua', true) == 10); collectgarbage()
dofile('files.lua')
if #msgs > 0 then
local m = table.concat(msgs, "\n ")
warn("#tests not performed:\n ", m, "\n")
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'
assert(debug == nil)
local debug = require "debug"
print(string.format("%d-bit integers, %d-bit floats",
string.packsize("j") * 8, string.packsize("n") * 8))
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,
assert, open, warn =
_G, showmem, print, string.format, os.clock, os.time, os.difftime,
assert, io.open, warn
-- file with time of last performed test
local fname = T and "time-debug.txt" or "time.txt"
local lasttime
if not usertests then
-- open file with time of last performed test
local f = io.open(fname)
if f then
lasttime = assert(tonumber(f:read'a'))
f:close();
else -- no such file; assume it is recording time for first time
lasttime = nil
end
end
-- erase (almost) all globals
print('cleaning all!!!!')
for n in pairs(_G) do
if not ({___Glob = 1, tostring = 1})[n] then
_G[n] = undef
end
end
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage();showmem()
local clocktime = clock() - initclock
walltime = difftime(time(), walltime)
print(format("\n\ntotal time: %.2fs (wall time: %gs)\n", clocktime, walltime))
if not usertests then
lasttime = lasttime or clocktime -- if no last time, ignore difference
-- check whether current test time differs more than 5% from last time
local diff = (clocktime - lasttime) / lasttime
local tolerance = 0.05 -- 5%
if (diff >= tolerance or diff <= -tolerance) then
warn(format("#time difference from previous test: %+.1f%%",
diff * 100))
end
assert(open(fname, "w")):write(clocktime):close()
end
print("final OK !!!")
--[[
*****************************************************************************
* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************************
]]

1460
testes/api.lua Normal file

File diff suppressed because it is too large Load Diff

492
testes/attrib.lua Normal file
View File

@@ -0,0 +1,492 @@
-- $Id: testes/attrib.lua $
-- See Copyright Notice in file all.lua
print "testing require"
assert(require"string" == string)
assert(require"math" == math)
assert(require"table" == table)
assert(require"io" == io)
assert(require"os" == os)
assert(require"coroutine" == coroutine)
assert(type(package.path) == "string")
assert(type(package.cpath) == "string")
assert(type(package.loaded) == "table")
assert(type(package.preload) == "table")
assert(type(package.config) == "string")
print("package config: "..string.gsub(package.config, "\n", "|"))
do
-- create a path with 'max' templates,
-- each with 1-10 repetitions of '?'
local max = _soft and 100 or 2000
local t = {}
for i = 1,max do t[i] = string.rep("?", i%10 + 1) end
t[#t + 1] = ";" -- empty template
local path = table.concat(t, ";")
-- use that path in a search
local s, err = package.searchpath("xuxu", path)
-- search fails; check that message has an occurence of
-- '??????????' with ? replaced by xuxu and at least 'max' lines
assert(not s and
string.find(err, string.rep("xuxu", 10)) and
#string.gsub(err, "[^\n]", "") >= max)
-- path with one very long template
local path = string.rep("?", max)
local s, err = package.searchpath("xuxu", path)
assert(not s and string.find(err, string.rep('xuxu', max)))
end
do
local oldpath = package.path
package.path = {}
local s, err = pcall(require, "no-such-file")
assert(not s and string.find(err, "package.path"))
package.path = oldpath
end
print('+')
-- The next tests for 'require' assume some specific directories and
-- libraries.
if not _port then --[
local dirsep = string.match(package.config, "^([^\n]+)\n")
-- auxiliary directory with C modules and temporary files
local DIR = "libs" .. dirsep
-- prepend DIR to a name and correct directory separators
local function D (x)
x = string.gsub(x, "/", dirsep)
return DIR .. x
end
-- prepend DIR and pospend proper C lib. extension to a name
local function DC (x)
local ext = (dirsep == '\\') and ".dll" or ".so"
return D(x .. ext)
end
local function createfiles (files, preextras, posextras)
for n,c in pairs(files) do
io.output(D(n))
io.write(string.format(preextras, n))
io.write(c)
io.write(string.format(posextras, n))
io.close(io.output())
end
end
function removefiles (files)
for n in pairs(files) do
os.remove(D(n))
end
end
local files = {
["names.lua"] = "do return {...} end\n",
["err.lua"] = "B = 15; a = a + 1;",
["synerr.lua"] = "B =",
["A.lua"] = "",
["B.lua"] = "assert(...=='B');require 'A'",
["A.lc"] = "",
["A"] = "",
["L"] = "",
["XXxX"] = "",
["C.lua"] = "package.loaded[...] = 25; require'C'",
}
AA = nil
local extras = [[
NAME = '%s'
REQUIRED = ...
return AA]]
createfiles(files, "", extras)
-- testing explicit "dir" separator in 'searchpath'
assert(package.searchpath("C.lua", D"?", "", "") == D"C.lua")
assert(package.searchpath("C.lua", D"?", ".", ".") == D"C.lua")
assert(package.searchpath("--x-", D"?", "-", "X") == D"XXxX")
assert(package.searchpath("---xX", D"?", "---", "XX") == D"XXxX")
assert(package.searchpath(D"C.lua", "?", dirsep) == D"C.lua")
assert(package.searchpath(".\\C.lua", D"?", "\\") == D"./C.lua")
local oldpath = package.path
package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR)
local try = function (p, n, r, ext)
NAME = nil
local rr, x = require(p)
assert(NAME == n)
assert(REQUIRED == p)
assert(rr == r)
assert(ext == x)
end
a = require"names"
assert(a[1] == "names" and a[2] == D"names.lua")
_G.a = nil
local st, msg = pcall(require, "err")
assert(not st and string.find(msg, "arithmetic") and B == 15)
st, msg = pcall(require, "synerr")
assert(not st and string.find(msg, "error loading module"))
assert(package.searchpath("C", package.path) == D"C.lua")
assert(require"C" == 25)
assert(require"C" == 25)
AA = nil
try('B', 'B.lua', true, "libs/B.lua")
assert(package.loaded.B)
assert(require"B" == true)
assert(package.loaded.A)
assert(require"C" == 25)
package.loaded.A = nil
try('B', nil, true, nil) -- should not reload package
try('A', 'A.lua', true, "libs/A.lua")
package.loaded.A = nil
os.remove(D'A.lua')
AA = {}
try('A', 'A.lc', AA, "libs/A.lc") -- now must find second option
assert(package.searchpath("A", package.path) == D"A.lc")
assert(require("A") == AA)
AA = false
try('K', 'L', false, "libs/L") -- default option
try('K', 'L', false, "libs/L") -- default option (should reload it)
assert(rawget(_G, "_REQUIREDNAME") == nil)
AA = "x"
try("X", "XXxX", AA, "libs/XXxX")
removefiles(files)
-- testing require of sub-packages
local _G = _G
package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR)
files = {
["P1/init.lua"] = "AA = 10",
["P1/xuxu.lua"] = "AA = 20",
}
createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n")
AA = 0
local m, ext = assert(require"P1")
assert(ext == "libs/P1/init.lua")
assert(AA == 0 and m.AA == 10)
assert(require"P1" == m)
assert(require"P1" == m)
assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua")
m.xuxu, ext = assert(require"P1.xuxu")
assert(AA == 0 and m.xuxu.AA == 20)
assert(ext == "libs/P1/xuxu.lua")
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1" == m and m.AA == 10)
removefiles(files)
package.path = ""
assert(not pcall(require, "file_does_not_exist"))
package.path = "??\0?"
assert(not pcall(require, "file_does_not_exist1"))
package.path = oldpath
-- check 'require' error message
local fname = "file_does_not_exist2"
local m, err = pcall(require, fname)
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
t = string.gsub(t, "?", fname)
assert(string.find(err, t, 1, true))
end
do -- testing 'package.searchers' not being a table
local searchers = package.searchers
package.searchers = 3
local st, msg = pcall(require, 'a')
assert(not st and string.find(msg, "must be a table"))
package.searchers = searchers
end
local function import(...)
local f = {...}
return function (m)
for i=1, #f do m[f[i]] = _G[f[i]] end
end
end
-- cannot change environment of a C function
assert(not pcall(module, 'XUXU'))
-- testing require of C libraries
local p = "" -- On Mac OS X, redefine this to "_"
-- check whether loadlib works in this system
local st, err, when = package.loadlib(DC"lib1", "*")
if not st then
local f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "absent")
;(Message or print)('\n >>> cannot load dynamic library <<<\n')
print(err, when)
else
-- tests for loadlib
local f = assert(package.loadlib(DC"lib1", p.."onefunction"))
local a, b = f(15, 25)
assert(a == 25 and b == 15)
f = assert(package.loadlib(DC"lib1", p.."anotherfunc"))
assert(f(10, 20) == "10%20\n")
-- check error messages
local f, err, when = package.loadlib(DC"lib1", p.."xuxu")
assert(not f and type(err) == "string" and when == "init")
f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "open")
-- symbols from 'lib1' must be visible to other libraries
f = assert(package.loadlib(DC"lib11", p.."luaopen_lib11"))
assert(f() == "exported")
-- test C modules with prefixes in names
package.cpath = DC"?"
local lib2, ext = require"lib2-v2"
assert(string.find(ext, "libs/lib2-v2", 1, true))
-- check correct access to global environment and correct
-- parameters
assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2")
assert(lib2.id("x") == true) -- a different "id" implementation
-- test C submodules
local fs, ext = require"lib1.sub"
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
assert(string.find(ext, "libs/lib1", 1, true))
assert(fs.id(45) == 45)
end
_ENV = _G
-- testing preload
do
local p = package
package = {}
p.preload.pl = function (...)
local _ENV = {...}
function xuxu (x) return x+20 end
return _ENV
end
local pl, ext = require"pl"
assert(require"pl" == pl)
assert(pl.xuxu(10) == 30)
assert(pl[1] == "pl" and pl[2] == ":preload:" and ext == ":preload:")
package = p
assert(type(package.path) == "string")
end
print('+')
end --]
print("testing assignments, logical operators, and constructors")
local res, res2 = 27
a, b = 1, 2+3
assert(a==1 and b==5)
a={}
function f() return 10, 11, 12 end
a.x, b, a[1] = 1, 2, f()
assert(a.x==1 and b==2 and a[1]==10)
a[f()], b, a[f()+3] = f(), a, 'x'
assert(a[10] == 10 and b == a and a[13] == 'x')
do
local f = function (n) local x = {}; for i=1,n do x[i]=i end;
return table.unpack(x) end;
local a,b,c
a,b = 0, f(1)
assert(a == 0 and b == 1)
A,b = 0, f(1)
assert(A == 0 and b == 1)
a,b,c = 0,5,f(4)
assert(a==0 and b==5 and c==1)
a,b,c = 0,5,f(0)
assert(a==0 and b==5 and c==nil)
end
a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
assert(not a and b and c and d==6)
d = 20
a, b, c, d = f()
assert(a==10 and b==11 and c==12 and d==nil)
a,b = f(), 1, 2, 3, f()
assert(a==10 and b==1)
assert(a<b == false and a>b == true)
assert((10 and 2) == 2)
assert((10 or 2) == 10)
assert((10 or assert(nil)) == 10)
assert(not (nil and assert(nil)))
assert((nil or "alo") == "alo")
assert((nil and 10) == nil)
assert((false and 10) == false)
assert((true or 10) == true)
assert((false or 10) == 10)
assert(false ~= nil)
assert(nil ~= false)
assert(not nil == true)
assert(not not nil == false)
assert(not not 1 == true)
assert(not not a == true)
assert(not not (6 or nil) == true)
assert(not not (nil and 56) == false)
assert(not not (nil and true) == false)
assert(not 10 == false)
assert(not {} == false)
assert(not 0.5 == false)
assert(not "x" == false)
assert({} ~= {})
print('+')
a = {}
a[true] = 20
a[false] = 10
assert(a[1<2] == 20 and a[1>2] == 10)
function f(a) return a end
local a = {}
for i=3000,-3000,-1 do a[i + 0.0] = i; end
a[10e30] = "alo"; a[true] = 10; a[false] = 20
assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
for i=3000,-3000,-1 do assert(a[i] == i); end
a[print] = assert
a[f] = print
a[a] = a
assert(a[a][a][a][a][print] == assert)
a[print](a[a[f]] == a[print])
assert(not pcall(function () local a = {}; a[nil] = 10 end))
assert(not pcall(function () local a = {[nil] = 10} end))
assert(a[nil] == undef)
a = nil
a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
a, a.x, a.y = a, a[-3]
assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
a[1].alo(a[2]==10 and b==10 and c==print)
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 = 10
local function foo ()
return a.aVeryLongName012345678901234567890123456789012345678901234567890123456789
end
assert(foo() == 10 and
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 ==
10)
-- test of large float/integer indices
-- compute maximum integer where all bits fit in a float
local maxint = math.maxinteger
-- trim (if needed) to fit in a float
while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do
maxint = maxint // 2
end
maxintF = maxint + 0.0 -- float version
assert(maxintF == maxint and math.type(maxintF) == "float" and
maxintF >= 2.0^14)
-- floats and integers must index the same places
a[maxintF] = 10; a[maxintF - 1.0] = 11;
a[-maxintF] = 12; a[-maxintF + 1.0] = 13;
assert(a[maxint] == 10 and a[maxint - 1] == 11 and
a[-maxint] == 12 and a[-maxint + 1] == 13)
a[maxint] = 20
a[-maxint] = 22
assert(a[maxintF] == 20 and a[maxintF - 1.0] == 11 and
a[-maxintF] == 22 and a[-maxintF + 1.0] == 13)
a = nil
-- test conflicts in multiple assignment
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
a = {}
local function foo () -- assigining to upvalues
b, a.x, a = a, 10, 20
end
foo()
assert(a == 20 and b.x == 10)
end
-- repeat test with upvalues
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
local function foo ()
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
end
foo()
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
local t = {}
(function (a) t[a], a = 10, 20 end)(1);
assert(t[1] == 10)
end
-- bug in 5.2 beta
local function foo ()
local a
return function ()
local b
a, b = 3, 14 -- local and upvalue have same index
return a, b
end
end
local a, b = foo()()
assert(a == 3 and b == 14)
print('OK')
return res

82
testes/big.lua Normal file
View File

@@ -0,0 +1,82 @@
-- $Id: testes/big.lua $
-- See Copyright Notice in file all.lua
if _soft then
return 'a'
end
print "testing large tables"
local debug = require"debug"
local lim = 2^18 + 1000
local prog = { "local y = {0" }
for i = 1, lim do prog[#prog + 1] = i end
prog[#prog + 1] = "}\n"
prog[#prog + 1] = "X = y\n"
prog[#prog + 1] = ("assert(X[%d] == %d)"):format(lim - 1, lim - 2)
prog[#prog + 1] = "return 0"
prog = table.concat(prog, ";")
local env = {string = string, assert = assert}
local f = assert(load(prog, nil, nil, env))
f()
assert(env.X[lim] == lim - 1 and env.X[lim + 1] == lim)
for k in pairs(env) do env[k] = undef end
-- yields during accesses larger than K (in RK)
setmetatable(env, {
__index = function (t, n) coroutine.yield('g'); return _G[n] end,
__newindex = function (t, n, v) coroutine.yield('s'); _G[n] = v end,
})
X = nil
co = coroutine.wrap(f)
assert(co() == 's')
assert(co() == 'g')
assert(co() == 'g')
assert(co() == 0)
assert(X[lim] == lim - 1 and X[lim + 1] == lim)
-- errors in accesses larger than K (in RK)
getmetatable(env).__index = function () end
getmetatable(env).__newindex = function () end
local e, m = pcall(f)
assert(not e and m:find("global 'X'"))
-- errors in metamethods
getmetatable(env).__newindex = function () error("hi") end
local e, m = xpcall(f, debug.traceback)
assert(not e and m:find("'newindex'"))
f, X = nil
coroutine.yield'b'
if 2^32 == 0 then -- (small integers) {
print "testing string length overflow"
local repstrings = 192 -- number of strings to be concatenated
local ssize = math.ceil(2.0^32 / repstrings) + 1 -- size of each string
assert(repstrings * ssize > 2.0^32) -- it should be larger than maximum size
local longs = string.rep("\0", ssize) -- create one long string
-- create function to concatentate 'repstrings' copies of its argument
local rep = assert(load(
"local a = ...; return " .. string.rep("a", repstrings, "..")))
local a, b = pcall(rep, longs) -- call that function
-- it should fail without creating string (result would be too large)
assert(not a and string.find(b, "overflow"))
end -- }
print'OK'
return 'a'

346
testes/bitwise.lua Normal file
View File

@@ -0,0 +1,346 @@
-- $Id: testes/bitwise.lua $
-- See Copyright Notice in file all.lua
print("testing bitwise operations")
require "bwcoercion"
local numbits = string.packsize('j') * 8
assert(~0 == -1)
assert((1 << (numbits - 1)) == math.mininteger)
-- basic tests for bitwise operators;
-- use variables to avoid constant folding
local a, b, c, d
a = 0xFFFFFFFFFFFFFFFF
assert(a == -1 and a & -1 == a and a & 35 == 35)
a = 0xF0F0F0F0F0F0F0F0
assert(a | -1 == -1)
assert(a ~ a == 0 and a ~ 0 == a and a ~ ~a == -1)
assert(a >> 4 == ~a)
a = 0xF0; b = 0xCC; c = 0xAA; d = 0xFD
assert(a | b ~ c & d == 0xF4)
a = 0xF0.0; b = 0xCC.0; c = "0xAA.0"; d = "0xFD.0"
assert(a | b ~ c & d == 0xF4)
a = 0xF0000000; b = 0xCC000000;
c = 0xAA000000; d = 0xFD000000
assert(a | b ~ c & d == 0xF4000000)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
a = a << 32
b = b << 32
c = c << 32
d = d << 32
assert(a | b ~ c & d == 0xF4000000 << 32)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
assert(-1 >> (numbits - 1) == 1)
assert(-1 >> numbits == 0 and
-1 >> -numbits == 0 and
-1 << numbits == 0 and
-1 << -numbits == 0)
assert((2^30 - 1) << 2^30 == 0)
assert((2^30 - 1) >> 2^30 == 0)
assert(1 >> -3 == 1 << 3 and 1000 >> 5 == 1000 << -5)
-- coercion from strings to integers
assert("0xffffffffffffffff" | 0 == -1)
assert("0xfffffffffffffffe" & "-1" == -2)
assert(" \t-0xfffffffffffffffe\n\t" & "-1" == 2)
assert(" \n -45 \t " >> " -2 " == -45 * 4)
assert("1234.0" << "5.0" == 1234 * 32)
assert("0xffff.0" ~ "0xAAAA" == 0x5555)
assert(~"0x0.000p4" == -1)
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)
assert(string.find(msg, "'band'"))
local st, msg = pcall(function () return ~"a" end)
assert(string.find(msg, "'bnot'"))
end
-- out of range number
assert(not pcall(function () return "0xffffffffffffffff.0" | 0 end))
-- embedded zeros
assert(not pcall(function () return "0xffffffffffffffff\0" | 0 end))
print'+'
package.preload.bit32 = function () --{
-- no built-in 'bit32' library: implement it using bitwise operators
local bit = {}
function bit.bnot (a)
return ~a & 0xFFFFFFFF
end
--
-- in all vararg functions, avoid creating 'arg' table when there are
-- only 2 (or less) parameters, as 2 parameters is the common case
--
function bit.band (x, y, z, ...)
if not z then
return ((x or -1) & (y or -1)) & 0xFFFFFFFF
else
local arg = {...}
local res = x & y & z
for i = 1, #arg do res = res & arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bor (x, y, z, ...)
if not z then
return ((x or 0) | (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x | y | z
for i = 1, #arg do res = res | arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bxor (x, y, z, ...)
if not z then
return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x ~ y ~ z
for i = 1, #arg do res = res ~ arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.btest (...)
return bit.band(...) ~= 0
end
function bit.lshift (a, b)
return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
end
function bit.rshift (a, b)
return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
end
function bit.arshift (a, b)
a = a & 0xFFFFFFFF
if b <= 0 or (a & 0x80000000) == 0 then
return (a >> b) & 0xFFFFFFFF
else
return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
end
end
function bit.lrotate (a ,b)
b = b & 31
a = a & 0xFFFFFFFF
a = (a << b) | (a >> (32 - b))
return a & 0xFFFFFFFF
end
function bit.rrotate (a, b)
return bit.lrotate(a, -b)
end
local function checkfield (f, w)
w = w or 1
assert(f >= 0, "field cannot be negative")
assert(w > 0, "width must be positive")
assert(f + w <= 32, "trying to access non-existent bits")
return f, ~(-1 << w)
end
function bit.extract (a, f, w)
local f, mask = checkfield(f, w)
return (a >> f) & mask
end
function bit.replace (a, v, f, w)
local f, mask = checkfield(f, w)
v = v & mask
a = (a & ~(mask << f)) | (v << f)
return a & 0xFFFFFFFF
end
return bit
end --}
print("testing bitwise library")
local bit32 = require'bit32'
assert(bit32.band() == bit32.bnot(0))
assert(bit32.btest() == true)
assert(bit32.bor() == 0)
assert(bit32.bxor() == 0)
assert(bit32.band() == bit32.band(0xffffffff))
assert(bit32.band(1,2) == 0)
-- out-of-range numbers
assert(bit32.band(-1) == 0xffffffff)
assert(bit32.band((1 << 33) - 1) == 0xffffffff)
assert(bit32.band(-(1 << 33) - 1) == 0xffffffff)
assert(bit32.band((1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 40)) == 0)
assert(bit32.band(1 << 40) == 0)
assert(bit32.band(-(1 << 40) - 2) == 0xfffffffe)
assert(bit32.band((1 << 40) - 4) == 0xfffffffc)
assert(bit32.lrotate(0, -1) == 0)
assert(bit32.lrotate(0, 7) == 0)
assert(bit32.lrotate(0x12345678, 0) == 0x12345678)
assert(bit32.lrotate(0x12345678, 32) == 0x12345678)
assert(bit32.lrotate(0x12345678, 4) == 0x23456781)
assert(bit32.rrotate(0x12345678, -4) == 0x23456781)
assert(bit32.lrotate(0x12345678, -8) == 0x78123456)
assert(bit32.rrotate(0x12345678, 8) == 0x78123456)
assert(bit32.lrotate(0xaaaaaaaa, 2) == 0xaaaaaaaa)
assert(bit32.lrotate(0xaaaaaaaa, -2) == 0xaaaaaaaa)
for i = -50, 50 do
assert(bit32.lrotate(0x89abcdef, i) == bit32.lrotate(0x89abcdef, i%32))
end
assert(bit32.lshift(0x12345678, 4) == 0x23456780)
assert(bit32.lshift(0x12345678, 8) == 0x34567800)
assert(bit32.lshift(0x12345678, -4) == 0x01234567)
assert(bit32.lshift(0x12345678, -8) == 0x00123456)
assert(bit32.lshift(0x12345678, 32) == 0)
assert(bit32.lshift(0x12345678, -32) == 0)
assert(bit32.rshift(0x12345678, 4) == 0x01234567)
assert(bit32.rshift(0x12345678, 8) == 0x00123456)
assert(bit32.rshift(0x12345678, 32) == 0)
assert(bit32.rshift(0x12345678, -32) == 0)
assert(bit32.arshift(0x12345678, 0) == 0x12345678)
assert(bit32.arshift(0x12345678, 1) == 0x12345678 // 2)
assert(bit32.arshift(0x12345678, -1) == 0x12345678 * 2)
assert(bit32.arshift(-1, 1) == 0xffffffff)
assert(bit32.arshift(-1, 24) == 0xffffffff)
assert(bit32.arshift(-1, 32) == 0xffffffff)
assert(bit32.arshift(-1, -1) == bit32.band(-1 * 2, 0xffffffff))
assert(0x12345678 << 4 == 0x123456780)
assert(0x12345678 << 8 == 0x1234567800)
assert(0x12345678 << -4 == 0x01234567)
assert(0x12345678 << -8 == 0x00123456)
assert(0x12345678 << 32 == 0x1234567800000000)
assert(0x12345678 << -32 == 0)
assert(0x12345678 >> 4 == 0x01234567)
assert(0x12345678 >> 8 == 0x00123456)
assert(0x12345678 >> 32 == 0)
assert(0x12345678 >> -32 == 0x1234567800000000)
print("+")
-- some special cases
local c = {0, 1, 2, 3, 10, 0x80000000, 0xaaaaaaaa, 0x55555555,
0xffffffff, 0x7fffffff}
for _, b in pairs(c) do
assert(bit32.band(b) == b)
assert(bit32.band(b, b) == b)
assert(bit32.band(b, b, b, b) == b)
assert(bit32.btest(b, b) == (b ~= 0))
assert(bit32.band(b, b, b) == b)
assert(bit32.band(b, b, b, ~b) == 0)
assert(bit32.btest(b, b, b) == (b ~= 0))
assert(bit32.band(b, bit32.bnot(b)) == 0)
assert(bit32.bor(b, bit32.bnot(b)) == bit32.bnot(0))
assert(bit32.bor(b) == b)
assert(bit32.bor(b, b) == b)
assert(bit32.bor(b, b, b) == b)
assert(bit32.bor(b, b, 0, ~b) == 0xffffffff)
assert(bit32.bxor(b) == b)
assert(bit32.bxor(b, b) == 0)
assert(bit32.bxor(b, b, b) == b)
assert(bit32.bxor(b, b, b, b) == 0)
assert(bit32.bxor(b, 0) == b)
assert(bit32.bnot(b) ~= b)
assert(bit32.bnot(bit32.bnot(b)) == b)
assert(bit32.bnot(b) == (1 << 32) - 1 - b)
assert(bit32.lrotate(b, 32) == b)
assert(bit32.rrotate(b, 32) == b)
assert(bit32.lshift(bit32.lshift(b, -4), 4) == bit32.band(b, bit32.bnot(0xf)))
assert(bit32.rshift(bit32.rshift(b, 4), -4) == bit32.band(b, bit32.bnot(0xf)))
end
-- for this test, use at most 24 bits (mantissa of a single float)
c = {0, 1, 2, 3, 10, 0x800000, 0xaaaaaa, 0x555555, 0xffffff, 0x7fffff}
for _, b in pairs(c) do
for i = -40, 40 do
local x = bit32.lshift(b, i)
local y = math.floor(math.fmod(b * 2.0^i, 2.0^32))
assert(math.fmod(x - y, 2.0^32) == 0)
end
end
assert(not pcall(bit32.band, {}))
assert(not pcall(bit32.bnot, "a"))
assert(not pcall(bit32.lshift, 45))
assert(not pcall(bit32.lshift, 45, print))
assert(not pcall(bit32.rshift, 45, print))
print("+")
-- testing extract/replace
assert(bit32.extract(0x12345678, 0, 4) == 8)
assert(bit32.extract(0x12345678, 4, 4) == 7)
assert(bit32.extract(0xa0001111, 28, 4) == 0xa)
assert(bit32.extract(0xa0001111, 31, 1) == 1)
assert(bit32.extract(0x50000111, 31, 1) == 0)
assert(bit32.extract(0xf2345679, 0, 32) == 0xf2345679)
assert(not pcall(bit32.extract, 0, -1))
assert(not pcall(bit32.extract, 0, 32))
assert(not pcall(bit32.extract, 0, 0, 33))
assert(not pcall(bit32.extract, 0, 31, 2))
assert(bit32.replace(0x12345678, 5, 28, 4) == 0x52345678)
assert(bit32.replace(0x12345678, 0x87654321, 0, 32) == 0x87654321)
assert(bit32.replace(0, 1, 2) == 2^2)
assert(bit32.replace(0, -1, 4) == 2^4)
assert(bit32.replace(-1, 0, 31) == (1 << 31) - 1)
assert(bit32.replace(-1, 0, 1, 2) == (1 << 32) - 7)
-- testing conversion of floats
assert(bit32.bor(3.0) == 3)
assert(bit32.bor(-4.0) == 0xfffffffc)
-- large floats and large-enough integers?
if 2.0^50 < 2.0^50 + 1.0 and 2.0^50 < (-1 >> 1) then
assert(bit32.bor(2.0^32 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^32 - 6.0) == 0xfffffffa)
assert(bit32.bor(2.0^48 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^48 - 6.0) == 0xfffffffa)
end
print'OK'

78
testes/bwcoercion.lua Normal file
View File

@@ -0,0 +1,78 @@
local tonumber, tointeger = tonumber, math.tointeger
local type, getmetatable, rawget, error = type, getmetatable, rawget, error
local strsub = string.sub
local print = print
_ENV = nil
-- Try to convert a value to an integer, without assuming any coercion.
local function toint (x)
x = tonumber(x) -- handle numerical strings
if not x then
return false -- not coercible to a number
end
return tointeger(x)
end
-- If operation fails, maybe second operand has a metamethod that should
-- have been called if not for this string metamethod, so try to
-- call it.
local function trymt (x, y, mtname)
if type(y) ~= "string" then -- avoid recalling original metamethod
local mt = getmetatable(y)
local mm = mt and rawget(mt, mtname)
if mm then
return mm(x, y)
end
end
-- if any test fails, there is no other metamethod to be called
error("attempt to '" .. strsub(mtname, 3) ..
"' a " .. type(x) .. " with a " .. type(y), 4)
end
local function checkargs (x, y, mtname)
local xi = toint(x)
local yi = toint(y)
if xi and yi then
return xi, yi
else
return trymt(x, y, mtname), nil
end
end
local smt = getmetatable("")
smt.__band = function (x, y)
local x, y = checkargs(x, y, "__band")
return y and x & y or x
end
smt.__bor = function (x, y)
local x, y = checkargs(x, y, "__bor")
return y and x | y or x
end
smt.__bxor = function (x, y)
local x, y = checkargs(x, y, "__bxor")
return y and x ~ y or x
end
smt.__shl = function (x, y)
local x, y = checkargs(x, y, "__shl")
return y and x << y or x
end
smt.__shr = function (x, y)
local x, y = checkargs(x, y, "__shr")
return y and x >> y or x
end
smt.__bnot = function (x)
local x, y = checkargs(x, x, "__bnot")
return y and ~x or x
end

435
testes/calls.lua Normal file
View File

@@ -0,0 +1,435 @@
-- $Id: testes/calls.lua $
-- See Copyright Notice in file all.lua
print("testing functions and calls")
local debug = require "debug"
-- get the opportunity to test 'type' too ;)
assert(type(1<2) == 'boolean')
assert(type(true) == 'boolean' and type(false) == 'boolean')
assert(type(nil) == 'nil'
and type(-3) == 'number'
and type'x' == 'string'
and type{} == 'table'
and type(type) == 'function')
assert(type(assert) == type(print))
function f (x) return a:x (x) end
assert(type(f) == 'function')
assert(not pcall(type))
-- testing local-function recursion
fact = false
do
local res = 1
local function fact (n)
if n==0 then return res
else return n*fact(n-1)
end
end
assert(fact(5) == 120)
end
assert(fact == false)
-- testing declarations
a = {i = 10}
self = 20
function a:x (x) return x+self.i end
function a.y (x) return x+self end
assert(a:x(1)+10 == a.y(1))
a.t = {i=-100}
a["t"].x = function (self, a,b) return self.i+a+b end
assert(a.t:x(2,3) == -95)
do
local a = {x=0}
function a:add (x) self.x, a.y = self.x+x, 20; return self end
assert(a:add(10):add(20):add(30).x == 60 and a.y == 20)
end
local a = {b={c={}}}
function a.b.c.f1 (x) return x+1 end
function a.b.c:f2 (x,y) self[x] = y end
assert(a.b.c.f1(4) == 5)
a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+')
t = nil -- 'declare' t
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f( -- this line change must be valid
1,2)
assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
f(1,2, -- this one too
3,4)
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
function fat(x)
if x <= 1 then return 1
else return x*load("return fat(" .. x-1 .. ")", "")()
end
end
assert(load "load 'assert(fat(6)==720)' () ")()
a = load('return fat(5), 3')
a,b = a()
assert(a == 120 and b == 3)
print('+')
function err_on_n (n)
if n==0 then error(); exit(1);
else err_on_n (n-1); exit(1);
end
end
do
function dummy (n)
if n > 0 then
assert(not pcall(err_on_n, n))
dummy(n-1)
end
end
end
dummy(10)
function deep (n)
if n>0 then deep(n-1) end
end
deep(10)
deep(180)
-- testing tail calls
function deep (n) if n>0 then return deep(n-1) else return 101 end end
assert(deep(30000) == 101)
a = {}
function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end
assert(a:deep(30000) == 101)
do -- tail calls x varargs
local function foo (x, ...) local a = {...}; return x, a[1], a[2] end
local function foo1 (x) return foo(10, x, x + 1) end
local a, b, c = foo1(-2)
assert(a == 10 and b == -2 and c == -1)
-- tail calls x metamethods
local t = setmetatable({}, {__call = foo})
local function foo2 (x) return t(10, x) end
a, b, c = foo2(100)
assert(a == t and b == 10 and c == 100)
a, b = (function () return foo() end)()
assert(a == nil and b == nil)
local X, Y, A
local function foo (x, y, ...) X = x; Y = y; A = {...} end
local function foo1 (...) return foo(...) end
local a, b, c = foo1()
assert(X == nil and Y == nil and #A == 0)
a, b, c = foo1(10)
assert(X == 10 and Y == nil and #A == 0)
a, b, c = foo1(10, 20)
assert(X == 10 and Y == 20 and #A == 0)
a, b, c = foo1(10, 20, 30)
assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30)
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)
-- testing closures
-- fixed-point operator
Z = function (le)
local function a (f)
return le(function (x) return f(f)(x) end)
end
return a(a)
end
-- non-recursive factorial
F = function (f)
return function (n)
if n == 0 then return 1
else return n*f(n-1) end
end
end
fat = Z(F)
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
local function g (z)
local function f (a,b,c,d)
return function (x,y) return a+b+c+d+a+x+y+z end
end
return f(z,z+1,z+2,z+3)
end
f = g(10)
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
Z, F, f = nil
print('+')
-- testing multiple returns
function unlpack (t, i)
i = i or 1
if (i <= #t) then
return t[i], unlpack(t, i+1)
end
end
function equaltab (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
local pack = function (...) return (table.pack(...)) end
function f() return 1,2,30,4 end
function ret2 (a,b) return a,b end
local a,b,c,d = unlpack{1,2,3}
assert(a==1 and b==2 and c==3 and d==nil)
a = {1,2,3,4,false,10,'alo',false,assert}
equaltab(pack(unlpack(a)), a)
equaltab(pack(unlpack(a), -1), {1,-1})
a,b,c,d = ret2(f()), ret2(f())
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), ret2(f())))
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), (ret2(f()))))
assert(a==1 and b==1 and c==nil and d==nil)
a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}}
assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b")
-- testing calls with 'incorrect' arguments
rawget({}, "x", 1)
rawset({}, "x", 1, 2)
assert(math.sin(1,2) == math.sin(1))
table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
-- test for generic load
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
local a = function () x = 'hi' end; \
return '\0'"
function read1 (x)
local i = 0
return function ()
collectgarbage()
i=i+1
return string.sub(x, i, i)
end
end
function cannotload (msg, a,b)
assert(not a and string.find(b, msg))
end
a = assert(load(read1(x), "modname", "t", _G))
assert(a() == "\0" and _G.x == 33)
assert(debug.getinfo(a).source == "modname")
-- cannot read text in binary mode
cannotload("attempt to load a text chunk", load(read1(x), "modname", "b", {}))
cannotload("attempt to load a text chunk", load(x, "modname", "b"))
a = assert(load(function () return nil end))
a() -- empty chunk
assert(not load(function () return true end))
-- small bug
local t = {nil, "return ", "3"}
f, msg = load(function () return table.remove(t, 1) end)
assert(f() == nil) -- should read the empty chunk
-- another small bug (in 5.2.1)
f = load(string.dump(function () return 1 end), nil, "b", {})
assert(type(f) == "function" and f() == 1)
x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1)
cannotload("attempt to load a binary chunk", load(read1(x), nil, "t"))
cannotload("attempt to load a binary chunk", load(x, nil, "t"))
assert(not pcall(string.dump, print)) -- no dump of C functions
cannotload("unexpected symbol", load(read1("*a = 123")))
cannotload("unexpected symbol", load("*a = 123"))
cannotload("hhi", load(function () error("hhi") end))
-- any value is valid for _ENV
assert(load("return _ENV", nil, nil, 123)() == 123)
-- load when _ENV is not first upvalue
local x; XX = 123
local function h ()
local y=x -- use 'x', so that it becomes 1st upvalue
return XX -- global name
end
local d = string.dump(h)
x = load(d, "", "b")
assert(debug.getupvalue(x, 2) == '_ENV')
debug.setupvalue(x, 2, _G)
assert(x() == 123)
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
-- test generic load with nested functions
x = [[
return function (x)
return function (y)
return function (z)
return x+y+z
end
end
end
]]
a = assert(load(read1(x)))
assert(a()(2)(3)(10) == 15)
-- test for dump/undump with upvalues
local a, b = 20, 30
x = load(string.dump(function (x)
if x == "set" then a = 10+b; b = b+1 else
return a
end
end), "", "b", nil)
assert(x() == nil)
assert(debug.setupvalue(x, 1, "hi") == "a")
assert(x() == "hi")
assert(debug.setupvalue(x, 2, 13) == "b")
assert(not debug.setupvalue(x, 3, 10)) -- only 2 upvalues
x("set")
assert(x() == 23)
x("set")
assert(x() == 24)
-- test for dump/undump with many upvalues
do
local nup = 200 -- maximum number of local variables
local prog = {"local a1"}
for i = 2, nup do prog[#prog + 1] = ", a" .. i end
prog[#prog + 1] = " = 1"
for i = 2, nup do prog[#prog + 1] = ", " .. i end
local sum = 1
prog[#prog + 1] = "; return function () return a1"
for i = 2, nup do prog[#prog + 1] = " + a" .. i; sum = sum + i end
prog[#prog + 1] = " end"
prog = table.concat(prog)
local f = assert(load(prog))()
assert(f() == sum)
f = load(string.dump(f)) -- main chunk now has many upvalues
local a = 10
local h = function () return a end
for i = 1, nup do
debug.upvaluejoin(f, i, h, 1)
end
assert(f() == 10 * nup)
end
-- test for long method names
do
local t = {x = 1}
function t:_012345678901234567890123456789012345678901234567890123456789 ()
return self.x
end
assert(t:_012345678901234567890123456789012345678901234567890123456789() == 1)
end
-- test for bug in parameter adjustment
assert((function () return nil end)(4) == nil)
assert((function () local a; return a end)(4) == nil)
assert((function (a) return a end)() == nil)
print("testing binary chunks")
do
local header = string.pack("c4BBBc6BBBj",
"\27Lua", -- signature
(504 >> 7) & 0x7f, (504 & 0x7f) | 0x80, -- version 5.4 (504)
0, -- format
"\x19\x93\r\n\x1a\n", -- data
4, -- size of instruction
string.packsize("j"), -- sizeof(lua integer)
string.packsize("n"), -- sizeof(lua number)
0x5678 -- LUAC_INT
-- LUAC_NUM may not have a unique binary representation (padding...)
)
local c = string.dump(function () local a = 1; local b = 3; return a+b*3 end)
assert(string.sub(c, 1, #header) == header)
-- corrupted header
for i = 1, #header do
local s = string.sub(c, 1, i - 1) ..
string.char(string.byte(string.sub(c, i, i)) + 1) ..
string.sub(c, i + 1, -1)
assert(#s == #c)
assert(not load(s))
end
-- loading truncated binary chunks
for i = 1, #c - 1 do
local st, msg = load(string.sub(c, 1, i))
assert(not st and string.find(msg, "truncated"))
end
assert(assert(load(c))() == 10)
end
print('OK')
return deep

270
testes/closure.lua Normal file
View File

@@ -0,0 +1,270 @@
-- $Id: testes/closure.lua $
-- See Copyright Notice in file all.lua
print "testing closures"
local A,B = 0,{g=10}
function f(x)
local a = {}
for i=1,1000 do
local y = 0
do
a[i] = function () B.g = B.g+1; y = y+x; return y+A end
end
end
local dummy = function () return a[A] end
collectgarbage()
A = 1; assert(dummy() == a[1]); A = 0;
assert(a[1]() == x)
assert(a[3]() == x)
collectgarbage()
assert(B.g == 12)
return a
end
local a = f(10)
-- force a GC in this level
local x = {[1] = {}} -- to detect a GC
setmetatable(x, {__mode = 'kv'})
while x[1] do -- repeat until GC
local a = A..A..A..A -- create garbage
A = A+1
end
assert(a[1]() == 20+A)
assert(a[1]() == 30+A)
assert(a[2]() == 10+A)
collectgarbage()
assert(a[2]() == 20+A)
assert(a[2]() == 30+A)
assert(a[3]() == 20+A)
assert(a[8]() == 10+A)
assert(getmetatable(x).__mode == 'kv')
assert(B.g == 19)
-- testing equality
a = {}
for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
assert(a[3] ~= a[4] and a[4] ~= a[5])
do
local a = function (x) return math.sin(_ENV[x]) end
local function f()
return a
end
assert(f() == f())
end
-- testing closures with 'for' control variable
a = {}
for i=1,10 do
a[i] = {set = function(x) i=x end, get = function () return i end}
if i == 3 then break end
end
assert(a[4] == undef)
a[1].set(10)
assert(a[2].get() == 2)
a[2].set('a')
assert(a[3].get() == 3)
assert(a[2].get() == 'a')
a = {}
local t = {"a", "b"}
for i = 1, #t do
local k = t[i]
a[i] = {set = function(x, y) i=x; k=y end,
get = function () return i, k end}
if i == 2 then break end
end
a[1].set(10, 20)
local r,s = a[2].get()
assert(r == 2 and s == 'b')
r,s = a[1].get()
assert(r == 10 and s == 20)
a[2].set('a', 'b')
r,s = a[2].get()
assert(r == "a" and s == "b")
-- testing closures with 'for' control variable x break
for i=1,3 do
f = function () return i end
break
end
assert(f() == 1)
for k = 1, #t do
local v = t[k]
f = function () return k, v end
break
end
assert(({f()})[1] == 1)
assert(({f()})[2] == "a")
-- testing closure x break x return x errors
local b
function f(x)
local first = 1
while 1 do
if x == 3 and not first then return end
local a = 'xuxu'
b = function (op, y)
if op == 'set' then
a = x+y
else
return a
end
end
if x == 1 then do break end
elseif x == 2 then return
else if x ~= 3 then error() end
end
first = nil
end
end
for i=1,3 do
f(i)
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 10+i)
b = nil
end
pcall(f, 4);
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 14)
local w
-- testing multi-level closure
function f(x)
return function (y)
return function (z) return w+x+y+z end
end
end
y = f(10)
w = 1.345
assert(y(20)(30) == 60+w)
-- testing closures x break
do
local X, Y
local a = math.sin(0)
while a do
local b = 10
X = function () return b end -- closure with upvalue
if a then break end
end
do
local b = 20
Y = function () return b end -- closure with upvalue
end
-- upvalues must be different
assert(X() == 10 and Y() == 20)
end
-- testing closures x repeat-until
local a = {}
local i = 1
repeat
local x = i
a[i] = function () i = x+1; return x end
until i > 10 or a[i]() ~= x
assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
-- testing closures created in 'then' and 'else' parts of 'if's
a = {}
for i = 1, 10 do
if i % 3 == 0 then
local y = 0
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 1 then
goto L1
error'not here'
::L1::
local y = 1
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 2 then
local t
goto l4
::l4a:: a[i] = t; goto l4b
error("should never be here!")
::l4::
local y = 2
t = function (x) local t = y; y = x; return t end
goto l4a
error("should never be here!")
::l4b::
end
end
for i = 1, 10 do
assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
end
print'+'
-- test for correctly closing upvalues in tail calls of vararg functions
local function t ()
local function c(a,b) assert(a=="test" and b=="OK") end
local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
local x = 1
return v(function() return x end)
end
t()
-- test for debug manipulation of upvalues
local debug = require'debug'
do
local a , b, c = 3, 5, 7
foo1 = function () return a+b end;
foo2 = function () return b+a end;
do
local a = 10
foo3 = function () return a+b end;
end
end
assert(debug.upvalueid(foo1, 1))
assert(debug.upvalueid(foo1, 2))
assert(not pcall(debug.upvalueid, foo1, 3))
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
assert(debug.upvalueid(foo3, 1))
assert(debug.upvalueid(foo1, 1) ~= debug.upvalueid(foo3, 1))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo3, 2))
assert(debug.upvalueid(string.gmatch("x", "x"), 1) ~= nil)
assert(foo1() == 3 + 5 and foo2() == 5 + 3)
debug.upvaluejoin(foo1, 2, foo2, 2)
assert(foo1() == 3 + 3 and foo2() == 5 + 3)
assert(foo3() == 10 + 5)
debug.upvaluejoin(foo3, 2, foo2, 1)
assert(foo3() == 10 + 5)
debug.upvaluejoin(foo3, 2, foo2, 2)
assert(foo3() == 10 + 3)
assert(not pcall(debug.upvaluejoin, foo1, 3, foo2, 1))
assert(not pcall(debug.upvaluejoin, foo1, 1, foo2, 3))
assert(not pcall(debug.upvaluejoin, foo1, 0, foo2, 1))
assert(not pcall(debug.upvaluejoin, print, 1, foo2, 1))
assert(not pcall(debug.upvaluejoin, {}, 1, foo2, 1))
assert(not pcall(debug.upvaluejoin, foo1, 1, print, 1))
print'OK'

443
testes/code.lua Normal file
View File

@@ -0,0 +1,443 @@
-- $Id: testes/code.lua $
-- See Copyright Notice in file all.lua
if T==nil then
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
return
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
local function f (a)
for k,v,w in a do end
end
end
-- testing reuse in constant table
local function checkKlist (func, list)
local k = T.listk(func)
assert(#k == #list)
for i = 1, #k do
assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
end
end
local function foo ()
local a
a = k3;
a = 0; a = 0.0; a = -7 + 7
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 = k3; a = 3.0; a = 3; a = 3.0
end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
-- testing opcodes
-- check that 'f' opcodes match '...'
function check (f, ...)
local arg = {...}
local c = T.listcode(f)
for i=1, #arg do
local opcode = string.match(c[i], "%u%w+")
-- print(arg[i], opcode)
assert(arg[i] == opcode)
end
assert(c[#arg+2] == undef)
end
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
function checkR (f, p, r, ...)
local r1 = f(p)
assert(r == r1 and math.type(r) == math.type(r1))
check(f, ...)
end
-- check that 'a' and 'b' has the same opcodes
function checkequal (a, b)
a = T.listcode(a)
b = T.listcode(b)
assert(#a == #b)
for i = 1, #a do
a[i] = string.gsub(a[i], '%b()', '') -- remove line number
b[i] = string.gsub(b[i], '%b()', '') -- remove line number
assert(a[i] == b[i])
end
end
-- some basic instructions
check(function () -- function does not create upvalues
(function () end){f()}
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
'SETLIST', 'CALL', 'RETURN0')
check(function (x) -- function creates upvalues
(function () return x end){f()}
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=kNil; c=nil;
end, 'LOADNIL', 'RETURN0')
check(function ()
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
end, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0')
do
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
assert(a == nil and b == nil and c == nil and d == nil)
end
-- single return
check (function (a,b,c) return a end, 'RETURN1')
-- infinite loops
check(function () while kTrue do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () while 1 do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () repeat local x = 1 until true end,
'LOADI', 'RETURN0')
-- concat optimization
check(function (a,b,c,d) return a..b..c..d end,
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
-- not
check(function () return not not nil 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 k3 end, 'LOADBOOL', 'RETURN1')
-- direct access to locals
check(function ()
local a,b,c,d
a = b*a
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
end,
'LOADNIL',
'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
local c = kNil
a[kx] = 3.2
a.x = b
a[b] = 'x'
end,
'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
-- "get/set table" with numeric indices
check(function (a)
local k255 <const> = 255
a[1] = a[100]
a[k255] = a[256]
a[256] = 5
end,
'GETI', 'SETI',
'LOADI', 'GETTABLE', 'SETI',
'LOADI', 'SETTABLE', 'RETURN0')
check(function ()
local a,b
a = a - a
b = a/a
b = 5-4
end,
'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
check(function ()
local a,b
a[kTrue] = false
end,
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
-- equalities
checkR(function (a) if a == 1 then return 2 end end, 1, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
-- comparisons
checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
'LTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
'GTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 10 < a then return 2 end end, 11, 2,
'GTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 129 < a then return 2 end end, 130, 2,
'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
-- constant folding
local function checkK (func, val)
check(func, 'LOADK', 'RETURN1')
checkKlist(func, {val})
assert(func() == val)
end
local function checkI (func, val)
check(func, 'LOADI', 'RETURN1')
checkKlist(func, {})
assert(func() == val)
end
local function checkF (func, val)
check(func, 'LOADF', 'RETURN1')
checkKlist(func, {})
assert(func() == val)
end
checkF(function () return 0.0 end, 0.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 (-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 ~(~kFF0 | kFF0) end, 0)
checkI(function () return ~~-1024.0 end, -1024)
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
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))
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 + 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', '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 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,
'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
check(function () for i = 0xfffffff, 10.0, 1 do end end,
'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
-- bug in constant folding for 5.1
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
check(function ()
local a,b,c
b[c], a = c, b
b[a], a = c, b
a, b = c, a
a = a
end,
'LOADNIL',
'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE',
-- no code for a = a
'RETURN0')
-- x == nil , x ~= nil
-- checkequal(function (b) if (a==nil) then a=1 end; if a~=nil then a=1 end end,
-- function () if (a==9) then a=1 end; if a~=9 then a=1 end end)
-- check(function () if a==nil then a='a' end end,
-- 'GETTABUP', 'EQ', 'JMP', 'SETTABUP', 'RETURN')
do -- tests for table access in upvalues
local t
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')
end
-- de morgan
checkequal(function () local a; if not (a or b) then b=a end end,
function () local a; if (not a and not b) then b=a end end)
checkequal(function (l) local a; return 0 <= a and a <= l end,
function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
-- if-break optimizations
check(function (a, b)
while a do
if b then break else a = a + 1 end
end
end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a)
::loop::
if not (a < 10) then goto exit end
a = a + 1
goto loop
::exit::
end
)
checkequal(
function (a) repeat local x = a + 1; a = x until a > 0 end,
function (a)
::loop:: do
local x = a + 1
a = x
end
if not (a > 0) then goto loop end
end
)
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)
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'

377
testes/constructs.lua Normal file
View File

@@ -0,0 +1,377 @@
-- $Id: testes/constructs.lua $
-- See Copyright Notice in file all.lua
;;print "testing syntax";;
local debug = require "debug"
local function checkload (s, msg)
assert(string.find(select(2, load(s)), msg))
end
-- testing semicollons
do ;;; end
; do ; a = 3; assert(a == 3) end;
;
-- invalid operations should not raise errors when not executed
if false then a = 3 // 0; a = 0 % 0 end
-- testing priorities
assert(2^3^2 == 2^(3^2));
assert(2^3*4 == (2^3)*4);
assert(2.0^-2 == 1/4 and -2^- -2 == - - -4);
assert(not nil and 2 and not(2>3 or 3<2));
assert(-3-1-5 == 0+0-9);
assert(-2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0);
assert(-3%5 == 2 and -3+5 == 2)
assert(2*1+3/3 == 3 and 1+2 .. 3*1 == "33");
assert(not(2+1 > 3*1) and "a".."b" > "a");
assert(0xF0 | 0xCC ~ 0xAA & 0xFD == 0xF4)
assert(0xFD & 0xAA ~ 0xCC | 0xF0 == 0xF4)
assert(0xF0 & 0x0F + 1 == 0x10)
assert(3^4//2^3//5 == 2)
assert(-3+4*5//2^3^2//9+4%10/3 == (-3)+(((4*5)//(2^(3^2)))//9)+((4%10)/3))
assert(not ((true or false) and nil))
assert( true or false and nil)
-- old bug
assert((((1 or false) and true) or false) == true)
assert((((nil and true) or false) and true) == false)
local a,b = 1,nil;
assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75);
x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x);
x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x);
x,y=1,2;
assert((x>y) and x or y == 2);
x,y=2,1;
assert((x>y) and x or y == 2);
assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891)
do -- testing operators with diffent kinds of constants
-- operands to consider:
-- * fit in register
-- * constant doesn't fit in register
-- * floats with integral values
local operand = {3, 100, 5.0, -10, -5.0, 10000, -10000}
local operator = {"+", "-", "*", "/", "//", "%", "^",
"&", "|", "^", "<<", ">>",
"==", "~=", "<", ">", "<=", ">=",}
for _, op in ipairs(operator) do
local f = assert(load(string.format([[return function (x,y)
return x %s y
end]], op)))();
for _, o1 in ipairs(operand) do
for _, o2 in ipairs(operand) do
local gab = f(o1, o2)
_ENV.XX = o1
code = string.format("return XX %s %s", op, o2)
res = assert(load(code))()
assert(res == gab)
_ENV.XX = o2
local code = string.format("return (%s) %s XX", o1, op)
local res = assert(load(code))()
assert(res == gab)
code = string.format("return (%s) %s %s", o1, op, o2)
res = assert(load(code))()
assert(res == gab)
end
end
end
end
-- silly loops
repeat until 1; repeat until true;
while false do end; while nil do end;
do -- test old bug (first name could not be an `upvalue')
local a; function f(x) x={a=1}; x={x=1}; x={G=1} end
end
function f (i)
if type(i) ~= 'number' then return i,'jojo'; end;
if i > 0 then return i, f(i-1); end;
end
x = {f(3), f(5), f(10);};
assert(x[1] == 3 and x[2] == 5 and x[3] == 10 and x[4] == 9 and x[12] == 1);
assert(x[nil] == nil)
x = {f'alo', f'xixi', nil};
assert(x[1] == 'alo' and x[2] == 'xixi' and x[3] == nil);
x = {f'alo'..'xixi'};
assert(x[1] == 'aloxixi')
x = {f{}}
assert(x[2] == 'jojo' and type(x[1]) == 'table')
local f = function (i)
if i < 10 then return 'a';
elseif i < 20 then return 'b';
elseif i < 30 then return 'c';
end;
end
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil)
for i=1,1000 do break; end;
n=100;
i=3;
t = {};
a=nil
while not a do
a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end;
end
assert(a == n*(n+1)/2 and i==3);
assert(t[1] and t[n] and not t[0] and not t[n+1])
function f(b)
local x = 1;
repeat
local a;
if b==1 then local b=1; x=10; break
elseif b==2 then x=20; break;
elseif b==3 then x=30;
else local a,b,c,d=math.sin(1); x=x+1;
end
until x>=12;
return x;
end;
assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12)
local f = function (i)
if i < 10 then return 'a'
elseif i < 20 then return 'b'
elseif i < 30 then return 'c'
else return 8
end
end
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8)
local a, b = nil, 23
x = {f(100)*2+3 or a, a or b+2}
assert(x[1] == 19 and x[2] == 25)
x = {f=2+3 or a, a = b+2}
assert(x.f == 5 and x.a == 25)
a={y=1}
x = {a.y}
assert(x[1] == 1)
function f(i)
while 1 do
if i>0 then i=i-1;
else return; end;
end;
end;
function g(i)
while 1 do
if i>0 then i=i-1
else return end
end
end
f(10); g(10);
do
function f () return 1,2,3; end
local a, b, c = f();
assert(a==1 and b==2 and c==3)
a, b, c = (f());
assert(a==1 and b==nil and c==nil)
end
local a,b = 3 and f();
assert(a==1 and b==nil)
function g() f(); return; end;
assert(g() == nil)
function g() return nil or f() end
a,b = g()
assert(a==1 and b==nil)
print'+';
do -- testing constants
local prog <const> = [[local x <XXX> = 10]]
checkload(prog, "unknown attribute 'XXX'")
checkload([[local xxx <const> = 20; xxx = 10]],
":1: attempt to assign to const variable 'xxx'")
checkload([[
local xx;
local xxx <const> = 20;
local yyy;
local function foo ()
local abc = xx + yyy + xxx;
return function () return function () xxx = yyy end end
end
]], ":6: attempt to assign to const variable 'xxx'")
checkload([[
local x <close> = nil
x = io.open()
]], ":2: attempt to assign to const variable 'x'")
end
f = [[
return function ( a , b , c , d , e )
local x = a >= b or c or ( d and e ) or nil
return x
end , { a = 1 , b = 2 >= 1 , } or { 1 };
]]
f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes
f,a = load(f)();
assert(a.a == 1 and a.b)
function g (a,b,c,d,e)
if not (a>=b or c or d and e or nil) then return 0; else return 1; end;
end
function h (a,b,c,d,e)
while (a>=b or c or (d and e) or nil) do return 1; end;
return 0;
end;
assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1)
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
assert(f(1,2,'a')
~= -- force SETLINE before nil
nil, "")
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and
h(1,2,nil,1,'x') == 1)
assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and
h(1,2,nil,nil,'x') == 0)
assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and
h(1,2,nil,1,nil) == 0)
assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true)
x = 2<3 and not 3; assert(x==false)
x = 2<1 or (2>1 and 'a'); assert(x=='a')
do
local a; if nil then a=1; else a=2; end; -- this nil comes as PUSHNIL 2
assert(a==2)
end
function F(a)
assert(debug.getinfo(1, "n").name == 'F')
return a,2,3
end
a,b = F(1)~=nil; assert(a == true and b == nil);
a,b = F(nil)==nil; assert(a == true and b == nil)
----------------------------------------------------------------
------------------------------------------------------------------
-- sometimes will be 0, sometimes will not...
_ENV.GLOB1 = math.random(0, 1)
-- basic expressions with their respective values
local basiccases = {
{"nil", nil},
{"false", false},
{"true", true},
{"10", 10},
{"(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 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 cases <const> = {}
-- creates all combinations of '(cases[i] op cases[n-i])' plus
-- 'not(cases[i] op cases[n-i])' (syntax + value)
local function createcases (n)
local res = {}
for i = 1, n - 1 do
for _, v1 in ipairs(cases[i]) do
for _, v2 in ipairs(cases[n - i]) do
for _, op in ipairs(binops) do
local t = {
"(" .. v1[1] .. op[1] .. v2[1] .. ")",
op[2](v1[2], v2[2])
}
res[#res + 1] = t
res[#res + 1] = {"not" .. t[1], not t[2]}
end
end
end
end
return res
end
-- do not do too many combinations for soft tests
local level = _soft and 3 or 4
cases[1] = basiccases
for i = 2, level do cases[i] = createcases(i) end
print("+")
local i = 0
for n = 1, level do
for _, v in pairs(cases[n]) do
local s = v[1]
local p = load(string.format(prog, s, s), "")
IX = false
assert(p() == v[2] and IX == not not v[2])
i = i + 1
if i % 60000 == 0 then print('+') end
end
end
------------------------------------------------------------------
-- testing some syntax errors (chosen through 'gcov')
checkload("for x do", "expected")
checkload("x:call", "expected")
print'OK'

1015
testes/coroutine.lua Normal file

File diff suppressed because it is too large Load Diff

144
testes/cstack.lua Normal file
View File

@@ -0,0 +1,144 @@
-- $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, 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, ...)
assert(not s and string.find(err, msg))
end
local count
local back = string.rep("\b", 8)
local function progress ()
count = count + 1
local n = string.format("%-8d", count)
io.stderr:write(back, n)
end
do print("testing simple recursion:")
count = 0
local function foo ()
progress()
foo()
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
end
do print("testing stack overflow in message handling")
count = 0
local function loop (x, y, z)
progress()
return 1 + loop(x, y, z)
end
local res, msg = xpcall(loop, loop)
assert(msg == "error in error handling")
print("\tfinal count: ", count)
end
-- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
do print("testing recursion inside pattern matching")
local function f (size)
local s = string.rep("a", size)
local p = string.rep(".?", size)
return string.match(s, p)
end
local m = f(80)
assert(#m == 80)
checkerror("too complex", f, 200000)
end
do print("testing stack-overflow in recursive 'gsub'")
count = 0
local function foo ()
progress()
string.gsub("a", ".", foo)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
print("testing stack-overflow in recursive 'gsub' with metatables")
count = 0
local t = setmetatable({}, {__index = foo})
foo = function ()
count = count + 1
progress(count)
string.gsub("a", ".", t)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
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'

958
testes/db.lua Normal file
View File

@@ -0,0 +1,958 @@
-- $Id: testes/db.lua $
-- See Copyright Notice in file all.lua
-- testing debug library
local debug = require "debug"
local function dostring(s) return assert(load(s))() end
print"testing debug library and debug information"
do
local a=1
end
assert(not debug.gethook())
local testline = 19 -- line where 'test' is defined
function test (s, l, p) -- this must be line 19
collectgarbage() -- avoid gc during trace
local function f (event, line)
assert(event == 'line')
local l = table.remove(l, 1)
if p then print(l, line) end
assert(l == line, "wrong trace!!")
end
debug.sethook(f,"l"); load(s)(); debug.sethook()
assert(#l == 0)
end
do
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
assert(not debug.getinfo(1000)) -- out of range level
assert(not debug.getinfo(-1)) -- out of range level
local a = debug.getinfo(print)
assert(a.what == "C" and a.short_src == "[C]")
a = debug.getinfo(print, "L")
assert(a.activelines == nil)
local b = debug.getinfo(test, "SfL")
assert(b.name == nil and b.what == "Lua" and b.linedefined == testline and
b.lastlinedefined == b.linedefined + 10 and
b.func == test and not string.find(b.short_src, "%["))
assert(b.activelines[b.linedefined + 1] and
b.activelines[b.lastlinedefined])
assert(not b.activelines[b.linedefined] and
not b.activelines[b.lastlinedefined + 1])
end
-- test file and string names truncation
a = "function f () end"
local function dostring (s, x) return load(s, x)() end
dostring(a)
assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a))
dostring(a..string.format("; %s\n=1", string.rep('p', 400)))
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
dostring(a..string.format("; %s=1", string.rep('p', 400)))
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
dostring("\n"..a)
assert(debug.getinfo(f).short_src == '[string "..."]')
dostring(a, "")
assert(debug.getinfo(f).short_src == '[string ""]')
dostring(a, "@xuxu")
assert(debug.getinfo(f).short_src == "xuxu")
dostring(a, "@"..string.rep('p', 1000)..'t')
assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$"))
dostring(a, "=xuxu")
assert(debug.getinfo(f).short_src == "xuxu")
dostring(a, string.format("=%s", string.rep('x', 500)))
assert(string.find(debug.getinfo(f).short_src, "^x*$"))
dostring(a, "=")
assert(debug.getinfo(f).short_src == "")
a = nil; f = nil;
repeat
local g = {x = function ()
local a = debug.getinfo(2)
assert(a.name == 'f' and a.namewhat == 'local')
a = debug.getinfo(1)
assert(a.name == 'x' and a.namewhat == 'field')
return 'xixi'
end}
local f = function () return 1+1 and (not 1 or g.x()) end
assert(f() == 'xixi')
g = debug.getinfo(f)
assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name)
function f (x, name) -- local!
name = name or 'f'
local a = debug.getinfo(1)
assert(a.name == name and a.namewhat == 'local')
return x
end
-- breaks in different conditions
if 3>4 then break end; f()
if 3<4 then a=1 else break end; f()
while 1 do local x=10; break end; f()
local b = 1
if 3>4 then return math.sin(1) end; f()
a = 3<4; f()
a = 3<4 or 1; f()
repeat local x=20; if 4>3 then f() else break end; f() until 1
g = {}
f(g).x = f(2) and f(10)+f(9)
assert(g.x == f(19))
function g(x) if not x then return 3 end return (x('a', 'x')) end
assert(g(f) == 'a')
until 1
test([[if
math.sin(1)
then
a=1
else
a=2
end
]], {2,3,4,7})
test([[--
if nil then
a=1
else
a=2
end
]], {2,5,6})
test([[a=1
repeat
a=a+1
until a==3
]], {1,3,4,3,4})
test([[ do
return
end
]], {2})
test([[local a
a=1
while a<=3 do
a=a+1
end
]], {1,2,3,4,3,4,3,4,3,5})
test([[while math.sin(1) do
if math.sin(1)
then break
end
end
a=1]], {1,2,3,6})
test([[for i=1,3 do
a=i
end
]], {1,2,1,2,1,2,1,3})
test([[for i,v in pairs{'a','b'} do
a=tostring(i) .. v
end
]], {1,2,1,2,1,3})
test([[for i=1,4 do a=1 end]], {1,1,1,1})
do -- testing line info/trace with large gaps in source
local a = {1, 2, 3, 10, 124, 125, 126, 127, 128, 129, 130,
255, 256, 257, 500, 1000}
local s = [[
local b = {10}
a = b[1] X + Y b[1]
b = 4
]]
for _, i in ipairs(a) do
local subs = {X = string.rep("\n", i)}
for _, j in ipairs(a) do
subs.Y = string.rep("\n", j)
local s = string.gsub(s, "[XY]", subs)
test(s, {1, 2 + i, 2 + i + j, 2 + i, 2 + i + j, 3 + i + j})
end
end
end
print'+'
-- invalid levels in [gs]etlocal
assert(not pcall(debug.getlocal, 20, 1))
assert(not pcall(debug.setlocal, -1, 1, 10))
-- parameter names
local function foo (a,b,...) local d, e end
local co = coroutine.create(foo)
assert(debug.getlocal(foo, 1) == 'a')
assert(debug.getlocal(foo, 2) == 'b')
assert(not debug.getlocal(foo, 3))
assert(debug.getlocal(co, foo, 1) == 'a')
assert(debug.getlocal(co, foo, 2) == 'b')
assert(not debug.getlocal(co, foo, 3))
assert(not debug.getlocal(print, 1))
local function foo () return (debug.getlocal(1, -1)) end
assert(not foo(10))
-- varargs
local function foo (a, ...)
local t = table.pack(...)
for i = 1, t.n do
local n, v = debug.getlocal(1, -i)
assert(n == "(vararg)" and v == t[i])
end
assert(not debug.getlocal(1, -(t.n + 1)))
assert(not debug.setlocal(1, -(t.n + 1), 30))
if t.n > 0 then
(function (x)
assert(debug.setlocal(2, -1, x) == "(vararg)")
assert(debug.setlocal(2, -t.n, x) == "(vararg)")
end)(430)
assert(... == 430)
end
end
foo()
foo(print)
foo(200, 3, 4)
local a = {}
for i = 1, (_soft and 100 or 1000) do a[i] = i end
foo(table.unpack(a))
a = nil
do -- test hook presence in debug info
assert(not debug.gethook())
local count = 0
local function f ()
assert(debug.getinfo(1).namewhat == "hook")
local sndline = string.match(debug.traceback(), "\n(.-)\n")
assert(string.find(sndline, "hook"))
count = count + 1
end
debug.sethook(f, "l")
local a = 0
_ENV.a = a
a = 1
debug.sethook()
assert(count == 4)
end
-- hook table has weak keys
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
a = {}; L = nil
local glob = 1
local oldglob = glob
debug.sethook(function (e,l)
collectgarbage() -- force GC during a hook
local f, m, c = debug.gethook()
assert(m == 'crl' and c == 0)
if e == "line" then
if glob ~= oldglob then
L = l-1 -- get the first line where "glob" has changed
oldglob = glob
end
elseif e == "call" then
local f = debug.getinfo(2, "f").func
a[f] = 1
else assert(e == "return")
end
end, "crl")
function f(a,b)
collectgarbage()
local _, x = debug.getlocal(1, 1)
local _, y = debug.getlocal(1, 2)
assert(x == a and y == b)
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
assert(debug.setlocal(2, 4, "maçã") == "B")
x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
glob = glob+1
assert(debug.getinfo(1, "l").currentline == L+1)
assert(debug.getinfo(1, "l").currentline == L+2)
end
function foo()
glob = glob+1
assert(debug.getinfo(1, "l").currentline == L+1)
end; foo() -- set L
-- check line counting inside strings and empty lines
_ = 'alo\
alo' .. [[
]]
--[[
]]
assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines
function g (...)
local arg = {...}
do local a,b,c; a=math.sin(40); end
local feijao
local AAAA,B = "xuxu", "mamão"
f(AAAA,B)
assert(AAAA == "pera" and B == "maçã")
do
local B = 13
local x,y = debug.getlocal(1,5)
assert(x == 'B' and y == 13)
end
end
g()
assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print])
-- tests for manipulating non-registered locals (C and Lua temporaries)
local n, v = debug.getlocal(0, 1)
assert(v == 0 and n == "(C temporary)")
local n, v = debug.getlocal(0, 2)
assert(v == 2 and n == "(C temporary)")
assert(not debug.getlocal(0, 3))
assert(not debug.getlocal(0, 0))
function f()
assert(select(2, debug.getlocal(2,3)) == 1)
assert(not debug.getlocal(2,4))
debug.setlocal(2, 3, 10)
return 20
end
function g(a,b) return (a+1) + f() end
assert(g(0,0) == 30)
debug.sethook(nil);
assert(not debug.gethook())
-- minimal tests for setuservalue/getuservalue
do
assert(not debug.setuservalue(io.stdin, 10))
local a, b = debug.getuservalue(io.stdin, 10)
assert(a == nil and not b)
end
-- testing iteraction between multiple values x hooks
do
local function f(...) return 3, ... end
local count = 0
local a = {}
for i = 1, 100 do a[i] = i end
debug.sethook(function () count = count + 1 end, "", 1)
local t = {table.unpack(a)}
assert(#t == 100)
t = {table.unpack(a, 1, 3)}
assert(#t == 3)
t = {f(table.unpack(a, 1, 30))}
assert(#t == 31)
end
-- testing access to function arguments
local function collectlocals (level)
local tab = {}
for i = 1, math.huge do
local n, v = debug.getlocal(level + 1, i)
if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
break -- consider only real variables
end
tab[n] = v
end
return tab
end
X = nil
a = {}
function a:f (a, b, ...) local arg = {...}; local c = 13 end
debug.sethook(function (e)
assert(e == "call")
dostring("XX = 12") -- test dostring inside hooks
-- testing errors inside hooks
assert(not pcall(load("a='joao'+1")))
debug.sethook(function (e, l)
assert(debug.getinfo(2, "l").currentline == l)
local f,m,c = debug.gethook()
assert(e == "line")
assert(m == 'l' and c == 0)
debug.sethook(nil) -- hook is called only once
assert(not X) -- check that
X = collectlocals(2)
end, "l")
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(not debug.gethook())
-- testing access to local variables in return hook (bug in 5.2)
do
local X = false
local function foo (a, b, ...)
do local x,y,z end
local c, d = 10, 20
return
end
local function aux ()
if debug.getinfo(2).name == "foo" then
X = true -- to signal that it found 'foo'
local tab = {a = 100, b = 200, c = 10, d = 20}
for n, v in pairs(collectlocals(2)) do
assert(tab[n] == v)
tab[n] = undef
end
assert(next(tab) == nil) -- 'tab' must be empty
end
end
debug.sethook(aux, "r"); foo(100, 200); debug.sethook()
assert(X)
end
local function eqseq (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
do print("testing inspection of parameters/returned values")
local on = false
local inp, out
local function hook (event)
if not on then return end
local ar = debug.getinfo(2, "ruS")
local t = {}
for i = ar.ftransfer, ar.ftransfer + ar.ntransfer - 1 do
local _, v = debug.getlocal(2, i)
t[#t + 1] = v
end
if event == "return" then
out = t
else
inp = t
end
end
debug.sethook(hook, "cr")
on = true; math.sin(3); on = false
eqseq(inp, {3}); eqseq(out, {math.sin(3)})
on = true; select(2, 10, 20, 30, 40); on = false
eqseq(inp, {2, 10, 20, 30, 40}); eqseq(out, {20, 30, 40})
local function foo (a, ...) return ... end
local function foo1 () on = not on; return foo(20, 10, 0) end
foo1(); on = false
eqseq(inp, {20}); eqseq(out, {10, 0})
debug.sethook()
end
-- testing upvalue access
local function getupvalues (f)
local t = {}
local i = 1
while true do
local name, value = debug.getupvalue(f, i)
if not name then break end
assert(not t[name])
t[name] = value
i = i + 1
end
return t
end
local a,b,c = 1,2,3
local function foo1 (a) b = a; return c end
local function foo2 (x) a = x; return c+b end
assert(not debug.getupvalue(foo1, 3))
assert(not debug.getupvalue(foo1, 0))
assert(not debug.setupvalue(foo1, 3, "xuxu"))
local t = getupvalues(foo1)
assert(t.a == nil and t.b == 2 and t.c == 3)
t = getupvalues(foo2)
assert(t.a == 1 and t.b == 2 and t.c == 3)
assert(debug.setupvalue(foo1, 1, "xuxu") == "b")
assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu")
-- upvalues of C functions are allways "called" "" (the empty string)
assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "")
-- testing count hooks
local a=0
debug.sethook(function (e) a=a+1 end, "", 1)
a=0; for i=1,1000 do end; assert(1000 < a and a < 1012)
debug.sethook(function (e) a=a+1 end, "", 4)
a=0; for i=1,1000 do end; assert(250 < a and a < 255)
local f,m,c = debug.gethook()
assert(m == "" and c == 4)
debug.sethook(function (e) a=a+1 end, "", 4000)
a=0; for i=1,1000 do end; assert(a == 0)
do
debug.sethook(print, "", 2^24 - 1) -- count upperbound
local f,m,c = debug.gethook()
assert(({debug.gethook()})[3] == 2^24 - 1)
end
debug.sethook()
-- tests for tail calls
local function f (x)
if x then
assert(debug.getinfo(1, "S").what == "Lua")
assert(debug.getinfo(1, "t").istailcall == true)
local tail = debug.getinfo(2)
assert(tail.func == g1 and tail.istailcall == true)
assert(debug.getinfo(3, "S").what == "main")
print"+"
end
end
function g(x) return f(x) end
function g1(x) g(x) end
local function h (x) local f=g1; return f(x) end
h(true)
local b = {}
debug.sethook(function (e) table.insert(b, e) end, "cr")
h(false)
debug.sethook()
local res = {"return", -- first return (from sethook)
"call", "tail call", "call", "tail call",
"return", "return",
"call", -- last call (to sethook)
}
for i = 1, #res do assert(res[i] == table.remove(b, 1)) end
b = 0
debug.sethook(function (e)
if e == "tail call" then
b = b + 1
assert(debug.getinfo(2, "t").istailcall == true)
else
assert(debug.getinfo(2, "t").istailcall == false)
end
end, "c")
h(false)
debug.sethook()
assert(b == 2) -- two tail calls
lim = _soft and 3000 or 30000
local function foo (x)
if x==0 then
assert(debug.getinfo(2).what == "main")
local info = debug.getinfo(1)
assert(info.istailcall == true and info.func == foo)
else return foo(x-1)
end
end
foo(lim)
print"+"
-- testing local function information
co = load[[
local A = function ()
return x
end
return
]]
local a = 0
-- 'A' should be visible to debugger only after its complete definition
debug.sethook(function (e, l)
if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(temporary)")
elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A")
end
end, "l")
co() -- run local function definition
debug.sethook() -- turn off hook
assert(a == 2) -- ensure all two lines where hooked
-- testing traceback
assert(debug.traceback(print) == print)
assert(debug.traceback(print, 4) == print)
assert(string.find(debug.traceback("hi", 4), "^hi\n"))
assert(string.find(debug.traceback("hi"), "^hi\n"))
assert(not string.find(debug.traceback("hi"), "'debug.traceback'"))
assert(string.find(debug.traceback("hi", 0), "'debug.traceback'"))
assert(string.find(debug.traceback(), "^stack traceback:\n"))
do -- C-function names in traceback
local st, msg = (function () return pcall end)()(debug.traceback)
assert(st == true and string.find(msg, "pcall"))
end
-- testing nparams, nups e isvararg
local t = debug.getinfo(print, "u")
assert(t.isvararg == true and t.nparams == 0 and t.nups == 0)
t = debug.getinfo(function (a,b,c) end, "u")
assert(t.isvararg == false and t.nparams == 3 and t.nups == 0)
t = debug.getinfo(function (a,b,...) return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1)
t = debug.getinfo(1) -- main
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
debug.getupvalue(t.func, 1) == "_ENV")
-- testing debugging of coroutines
local function checktraceback (co, p, level)
local tb = debug.traceback(co, nil, level)
local i = 0
for l in string.gmatch(tb, "[^\n]+\n?") do
assert(i == 0 or string.find(l, p[i]))
i = i+1
end
assert(p[i] == undef)
end
local function f (n)
if n > 0 then f(n-1)
else coroutine.yield() end
end
local co = coroutine.create(f)
coroutine.resume(co, 3)
checktraceback(co, {"yield", "db.lua", "db.lua", "db.lua", "db.lua"})
checktraceback(co, {"db.lua", "db.lua", "db.lua", "db.lua"}, 1)
checktraceback(co, {"db.lua", "db.lua", "db.lua"}, 2)
checktraceback(co, {"db.lua"}, 4)
checktraceback(co, {}, 40)
co = coroutine.create(function (x)
local a = 1
coroutine.yield(debug.getinfo(1, "l"))
coroutine.yield(debug.getinfo(1, "l").currentline)
return a
end)
local tr = {}
local foo = function (e, l) if l then table.insert(tr, l) end end
debug.sethook(co, foo, "lcr")
local _, l = coroutine.resume(co, 10)
local x = debug.getinfo(co, 1, "lfLS")
assert(x.currentline == l.currentline and x.activelines[x.currentline])
assert(type(x.func) == "function")
for i=x.linedefined + 1, x.lastlinedefined do
assert(x.activelines[i])
x.activelines[i] = undef
end
assert(next(x.activelines) == nil) -- no 'extra' elements
assert(not debug.getinfo(co, 2))
local a,b = debug.getlocal(co, 1, 1)
assert(a == "x" and b == 10)
a,b = debug.getlocal(co, 1, 2)
assert(a == "a" and b == 1)
debug.setlocal(co, 1, 2, "hi")
assert(debug.gethook(co) == foo)
assert(#tr == 2 and
tr[1] == l.currentline-1 and tr[2] == l.currentline)
a,b,c = pcall(coroutine.resume, co)
assert(a and b and c == l.currentline+1)
checktraceback(co, {"yield", "in function <"})
a,b = coroutine.resume(co)
assert(a and b == "hi")
assert(#tr == 4 and tr[4] == l.currentline+2)
assert(debug.gethook(co) == foo)
assert(not debug.gethook())
checktraceback(co, {})
-- check get/setlocal in coroutines
co = coroutine.create(function (x)
local a, b = coroutine.yield(x)
assert(a == 100 and b == nil)
return x
end)
a, b = coroutine.resume(co, 10)
assert(a and b == 10)
a, b = debug.getlocal(co, 1, 1)
assert(a == "x" and b == 10)
assert(not debug.getlocal(co, 1, 5))
assert(debug.setlocal(co, 1, 1, 30) == "x")
assert(not debug.setlocal(co, 1, 5, 40))
a, b = coroutine.resume(co, 100)
assert(a and b == 30)
-- check traceback of suspended (or dead with error) coroutines
function f(i)
if i == 0 then error(i)
else coroutine.yield(); f(i-1)
end
end
co = coroutine.create(function (x) f(x) end)
a, b = coroutine.resume(co, 3)
t = {"'coroutine.yield'", "'f'", "in function <"}
while coroutine.status(co) == "suspended" do
checktraceback(co, t)
a, b = coroutine.resume(co)
table.insert(t, 2, "'f'") -- one more recursive call to 'f'
end
t[1] = "'error'"
checktraceback(co, t)
-- test acessing line numbers of a coroutine from a resume inside
-- a C function (this is a known bug in Lua 5.0)
local function g(x)
coroutine.yield(x)
end
local function f (i)
debug.sethook(function () end, "l")
for j=1,1000 do
g(i+j)
end
end
local co = coroutine.wrap(f)
co(10)
pcall(co)
pcall(co)
assert(type(debug.getregistry()) == "table")
-- test tagmethod information
local a = {}
local function f (t)
local info = debug.getinfo(1);
assert(info.namewhat == "metamethod")
a.op = info.name
return info.name
end
setmetatable(a, {
__index = f; __add = f; __div = f; __mod = f; __concat = f; __pow = f;
__mul = f; __idiv = f; __unm = f; __len = f; __sub = f;
__shl = f; __shr = f; __bor = f; __bxor = f;
__eq = f; __le = f; __lt = f; __unm = f; __len = f; __band = f;
__bnot = f;
})
local b = setmetatable({}, getmetatable(a))
assert(a[3] == "index" and a^3 == "pow" and a..a == "concat")
assert(a/3 == "div" and 3%a == "mod")
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 == "shl" and a>>1 == "shr")
assert (a==b and a.op == "eq")
assert (a>=b and a.op == "order")
assert (a>b and a.op == "order")
assert(~a == "bnot")
do -- testing for-iterator name
local function f()
assert(debug.getinfo(1).name == "for iterator")
end
for i in f do end
end
do -- testing debug info for finalizers
local name = nil
-- create a piece of garbage with a finalizer
setmetatable({}, {__gc = function ()
local t = debug.getinfo(2) -- get callee information
assert(t.namewhat == "metamethod")
name = t.name
end})
-- repeat until previous finalizer runs (setting 'name')
repeat local a = {} until name
assert(name == "__gc")
end
do
print("testing traceback sizes")
local function countlines (s)
return select(2, string.gsub(s, "\n", ""))
end
local function deep (lvl, n)
if lvl == 0 then
return (debug.traceback("message", n))
else
return (deep(lvl-1, n))
end
end
local function checkdeep (total, start)
local s = deep(total, start)
local rest = string.match(s, "^message\nstack traceback:\n(.*)$")
local cl = countlines(rest)
-- at most 10 lines in first part, 11 in second, plus '...'
assert(cl <= 10 + 11 + 1)
local brk = string.find(rest, "%.%.%.")
if brk then -- does message have '...'?
local rest1 = string.sub(rest, 1, brk)
local rest2 = string.sub(rest, brk, #rest)
assert(countlines(rest1) == 10 and countlines(rest2) == 11)
else
assert(cl == total - start + 2)
end
end
for d = 1, 51, 10 do
for l = 1, d do
-- use coroutines to ensure complete control of the stack
coroutine.wrap(checkdeep)(d, l)
end
end
end
print("testing debug functions on chunk without debug info")
prog = [[-- program to be loaded without debug information
local debug = require'debug'
local a = 12 -- a local variable
local n, v = debug.getlocal(1, 1)
assert(n == "(temporary)" and v == debug) -- unkown name but known value
n, v = debug.getlocal(1, 2)
assert(n == "(temporary)" and v == 12) -- unkown name but known value
-- a function with an upvalue
local f = function () local x; return a end
n, v = debug.getupvalue(f, 1)
assert(n == "(no name)" and v == 12)
assert(debug.setupvalue(f, 1, 13) == "(no name)")
assert(a == 13)
local t = debug.getinfo(f)
assert(t.name == nil and t.linedefined > 0 and
t.lastlinedefined == t.linedefined and
t.short_src == "?")
assert(debug.getinfo(1).currentline == -1)
t = debug.getinfo(f, "L").activelines
assert(next(t) == nil) -- active lines are empty
-- dump/load a function without debug info
f = load(string.dump(f))
t = debug.getinfo(f)
assert(t.name == nil and t.linedefined > 0 and
t.lastlinedefined == t.linedefined and
t.short_src == "?")
assert(debug.getinfo(1).currentline == -1)
return a
]]
-- load 'prog' without debug info
local f = assert(load(string.dump(load(prog), true)))
assert(f() == 13)
do -- tests for 'source' in binary dumps
local prog = [[
return function (x)
return function (y)
return x + y
end
end
]]
local name = string.rep("x", 1000)
local p = assert(load(prog, name))
-- load 'p' as a binary chunk with debug information
local c = string.dump(p)
assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump
local f = assert(load(c))
local g = f()
local h = g(3)
assert(h(5) == 8)
assert(debug.getinfo(f).source == name and -- all functions have 'source'
debug.getinfo(g).source == name and
debug.getinfo(h).source == name)
-- again, without debug info
local c = string.dump(p, true)
assert(#c < 500) -- no 'source' in dump
local f = assert(load(c))
local g = f()
local h = g(30)
assert(h(50) == 80)
assert(debug.getinfo(f).source == '=?' and -- no function has 'source'
debug.getinfo(g).source == '=?' and
debug.getinfo(h).source == '=?')
end
print"OK"

589
testes/errors.lua Normal file
View File

@@ -0,0 +1,589 @@
-- $Id: testes/errors.lua $
-- See Copyright Notice in file all.lua
print("testing errors")
local debug = require"debug"
-- avoid problems with 'strict' module (which may generate other error messages)
local mt = getmetatable(_G) or {}
local oldmm = mt.__index
mt.__index = nil
local function checkerr (msg, f, ...)
local st, err = pcall(f, ...)
assert(not st and string.find(err, msg))
end
local function doit (s)
local f, msg = load(s)
if not f then return msg end
local cond, msg = pcall(f)
return (not cond) and msg
end
local function checkmessage (prog, msg)
local m = doit(prog)
assert(string.find(m, msg, 1, true))
end
local function checksyntax (prog, extra, token, line)
local msg = doit(prog)
if not string.find(token, "^<%a") and not string.find(token, "^char%(")
then token = "'"..token.."'" end
token = string.gsub(token, "(%p)", "%%%1")
local pt = string.format([[^%%[string ".*"%%]:%d: .- near %s$]],
line, token)
assert(string.find(msg, pt))
assert(string.find(msg, msg, 1, true))
end
-- test error message with no extra info
assert(doit("error('hi', 0)") == 'hi')
-- test error message with no info
assert(doit("error()") == nil)
-- test common errors/errors that crashed in the past
assert(doit("table.unpack({}, 1, n=2^30)"))
assert(doit("a=math.sin()"))
assert(not doit("tostring(1)") and doit("tostring()"))
assert(doit"tonumber()")
assert(doit"repeat until 1; a")
assert(doit"return;;")
assert(doit"assert(false)")
assert(doit"assert(nil)")
assert(doit("function a (... , ...) end"))
assert(doit("function a (, ...) end"))
assert(doit("local t={}; t = t[#t] + 1"))
checksyntax([[
local a = {4
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
if not T then
(Message or print)
('\n >>> testC not active: skipping memory message test <<<\n')
else
print "testing memory error message"
local a = {}
for i = 1, 10000 do a[i] = true end -- preallocate array
collectgarbage()
T.totalmem(T.totalmem() + 10000)
-- force a memory error (by a small margin)
local st, msg = pcall(function()
for i = 1, 100000 do a[i] = tostring(i) end
end)
T.totalmem(0)
assert(not st and msg == "not enough" .. " memory")
end
-- tests for better error messages
checkmessage("a = {} + 1", "arithmetic")
checkmessage("a = {} | 1", "bitwise operation")
checkmessage("a = {} < 1", "attempt to compare")
checkmessage("a = {} <= 1", "attempt to compare")
checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'")
checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'")
checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'")
assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
checkmessage("a=(1)..{}", "a table value")
-- tail calls
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
checkmessage("a = #print", "length of a function value")
checkmessage("a = #3", "length of a number value")
aaa = nil
checkmessage("aaa.bbb:ddd(9)", "global 'aaa'")
checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'")
checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'")
checkmessage("local a,b,c; (function () a = b+1.1 end)()", "upvalue 'b'")
assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)")
-- upvalues being indexed do not go to the stack
checkmessage("local a,b,cc; (function () a = cc[1] end)()", "upvalue 'cc'")
checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'")
checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'")
checkmessage("b=1; local aaa={}; x=aaa+b", "local 'aaa'")
checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'")
checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'")
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
-- short circuit
checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
"local 'bbbb'")
checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'")
checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'")
checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value")
assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'"))
assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'"))
checkmessage("print(print < 10)", "function with number")
checkmessage("print(print < print)", "two function values")
checkmessage("print('10' < 10)", "string with number")
checkmessage("print(10 < '23')", "number with string")
-- float->integer conversions
checkmessage("local a = 2.0^100; x = a << 2", "local a")
checkmessage("local a = 1 >> 2.0^100", "has no integer representation")
checkmessage("local a = 10.1 << 2.0^100", "has no integer representation")
checkmessage("local a = 2.0^100 & 1", "has no integer representation")
checkmessage("local a = 2.0^100 & 1e100", "has no integer representation")
checkmessage("local a = 2.0 | 1e40", "has no integer representation")
checkmessage("local a = 2e100 ~ 1", "has no integer representation")
checkmessage("string.sub('a', 2.0^100)", "has no integer representation")
checkmessage("string.rep('a', 3.3)", "has no integer representation")
checkmessage("return 6e40 & 7", "has no integer representation")
checkmessage("return 34 << 7e30", "has no integer representation")
checkmessage("return ~-3e40", "has no integer representation")
checkmessage("return ~-3.009", "has no integer representation")
checkmessage("return 3.009 & 1", "has no integer representation")
checkmessage("return 34 >> {}", "table value")
checkmessage("a = 24 // 0", "divide by zero")
checkmessage("a = 1 % 0", "'n%0'")
-- numeric for loops
checkmessage("for i = {}, 10 do end", "table")
checkmessage("for i = io.stdin, 10 do end", "FILE")
checkmessage("for i = {}, 10 do end", "initial value")
checkmessage("for i = 1, 'x', 10 do end", "string")
checkmessage("for i = 1, {}, 10 do end", "limit")
checkmessage("for i = 1, {} do end", "limit")
checkmessage("for i = 1, 10, print do end", "step")
checkmessage("for i = 1, 10, print do end", "function")
-- passing light userdata instead of full userdata
_G.D = debug
checkmessage([[
-- create light udata
local x = D.upvalueid(function () return debug end, 1)
D.setuservalue(x, {})
]], "light userdata")
_G.D = nil
do -- named objects (field '__name')
checkmessage("math.sin(io.input())", "(number expected, got FILE*)")
_G.XX = setmetatable({}, {__name = "My Type"})
assert(string.find(tostring(XX), "^My Type"))
checkmessage("io.input(XX)", "(FILE* expected, got My Type)")
checkmessage("return XX + 1", "on a My Type value")
checkmessage("return ~io.stdin", "on a FILE* value")
checkmessage("return XX < XX", "two My Type values")
checkmessage("return {} < XX", "table with My Type")
checkmessage("return XX < io.stdin", "My Type with FILE*")
_G.XX = nil
end
-- global functions
checkmessage("(io.write or print){}", "io.write")
checkmessage("(collectgarbage or print){}", "collectgarbage")
-- errors in functions without debug info
do
local f = function (a) return a + 1 end
f = assert(load(string.dump(f, true)))
assert(f(3) == 4)
checkerr("^%?:%-1:", f, {})
-- code with a move to a local var ('OP_MOV A B' with A<B)
f = function () local a; a = {}; return a + 2 end
-- no debug info (so that 'a' is unknown)
f = assert(load(string.dump(f, true)))
-- symbolic execution should not get lost
checkerr("^%?:%-1:.*table value", f)
end
-- tests for field accesses after RK limit
local t = {}
for i = 1, 1000 do
t[i] = "a = x" .. i
end
local s = table.concat(t, "; ")
t = nil
checkmessage(s.."; a = bbb + 1", "global 'bbb'")
checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'")
checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'")
checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'")
checkmessage([[aaa=9
repeat until 3==3
local x=math.sin(math.cos(3))
if math.sin(1) == x then return math.sin(1) end -- tail call
local a,b = 1, {
{x='a'..'b'..'c', y='b', z=x},
{1,2,3,4,5} or 3+3<=3+3,
3+1>3+1,
{d = x and aaa[x or y]}}
]], "global 'aaa'")
checkmessage([[
local x,y = {},1
if math.sin(1) == 0 then return 3 end -- return
x.a()]], "field 'a'")
checkmessage([[
prefix = nil
insert = nil
while 1 do
local a
if nil then break end
insert(prefix, a)
end]], "global 'insert'")
checkmessage([[ -- tail call
return math.sin("a")
]], "'sin'")
checkmessage([[collectgarbage("nooption")]], "invalid option")
checkmessage([[x = print .. "a"]], "concatenate")
checkmessage([[x = "a" .. false]], "concatenate")
checkmessage([[x = {} .. 2]], "concatenate")
checkmessage("getmetatable(io.stdin).__gc()", "no value")
checkmessage([[
local Var
local function main()
NoSuchName (function() Var=0 end)
end
main()
]], "global 'NoSuchName'")
print'+'
a = {}; setmetatable(a, {__index = string})
checkmessage("a:sub()", "bad self")
checkmessage("string.sub('a', {})", "#2")
checkmessage("('a'):sub{}", "#1")
checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'")
checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'")
-- tests for errors in coroutines
local function f (n)
local c = coroutine.create(f)
local a,b = coroutine.resume(c)
return b
end
assert(string.find(f(), "C stack overflow"))
checkmessage("coroutine.yield()", "outside a coroutine")
f = coroutine.wrap(function () table.sort({1,2,3}, coroutine.yield) end)
checkerr("yield across", f)
-- testing size of 'source' info; size of buffer for that info is
-- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'.
idsize = 60 - 1
local function checksize (source)
-- syntax error
local _, msg = load("x", source)
msg = string.match(msg, "^([^:]*):") -- get source (1st part before ':')
assert(msg:len() <= idsize)
end
for i = 60 - 10, 60 + 10 do -- check border cases around 60
checksize("@" .. string.rep("x", i)) -- file names
checksize(string.rep("x", i - 10)) -- string sources
checksize("=" .. string.rep("x", i)) -- exact sources
end
-- testing line error
local function lineerror (s, l)
local err,msg = pcall(load(s))
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)
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3)
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4)
lineerror("function a.x.y ()\na=a+1\nend", 1)
lineerror("a = \na\n+\n{}", 3)
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6)
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3)
lineerror("a\n=\n-\n\nprint\n;", 3)
lineerror([[
a
(
23)
]], 1)
lineerror([[
local a = {x = 13}
a
.
x
(
23
)
]], 2)
lineerror([[
local a = {x = 13}
a
.
x
(
23 + a
)
]], 6)
local p = [[
function g() f() end
function f(x) error('a', X) end
g()
]]
X=3;lineerror((p), 3)
X=0;lineerror((p), false)
X=1;lineerror((p), 2)
X=2;lineerror((p), 1)
lineerror([[
local b = false
if not b then
error 'test'
end]], 3)
lineerror([[
local b = false
if not b then
if not b then
if not b then
error 'test'
end
end
end]], 5)
if not _soft then
-- several tests that exaust the Lua stack
collectgarbage()
print"testing stack overflow"
C = 0
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
local function checkstackmessage (m)
return (string.find(m, "stack overflow"))
end
-- repeated stack overflows (to check stack recovery)
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
-- error lines in stack overflow
C = 0
local l1
local function g(x)
l1 = debug.getinfo(x, "l").currentline; y()
end
local _, stackmsg = xpcall(g, debug.traceback, 1)
print('+')
local stack = {}
for line in string.gmatch(stackmsg, "[^\n]*") do
local curr = string.match(line, ":(%d+):")
if curr then table.insert(stack, tonumber(curr)) end
end
local i=1
while stack[i] ~= l1 do
assert(stack[i] == l)
i = i+1
end
assert(i > 15)
-- error in error handling
local res, msg = xpcall(error, error)
assert(not res and type(msg) == 'string')
print('+')
local function f (x)
if x==0 then error('a\n')
else
local aux = function () return f(x-1) end
local a,b = xpcall(aux, aux)
return a,b
end
end
f(3)
local function loop (x,y,z) return 1 + loop(x, y, z) end
local res, msg = xpcall(loop, function (m)
assert(string.find(m, "stack overflow"))
checkerr("error handling", loop)
assert(math.sin(0) == 0)
return 15
end)
assert(msg == 15)
local f = function ()
for i = 999900, 1000000, 1 do table.unpack({}, 1, i) end
end
checkerr("too many results", f)
end
do
-- non string messages
local t = {}
local res, msg = pcall(function () error(t) end)
assert(not res and msg == t)
res, msg = pcall(function () error(nil) end)
assert(not res and msg == nil)
local function f() error{msg='x'} end
res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end)
assert(msg.msg == 'xy')
-- 'assert' with extra arguments
res, msg = pcall(assert, false, "X", t)
assert(not res and msg == "X")
-- 'assert' with no message
res, msg = pcall(function () assert(false) end)
local line = string.match(msg, "%w+%.lua:(%d+): assertion failed!$")
assert(tonumber(line) == debug.getinfo(1, "l").currentline - 2)
-- 'assert' with non-string messages
res, msg = pcall(assert, false, t)
assert(not res and msg == t)
res, msg = pcall(assert, nil, nil)
assert(not res and msg == nil)
-- 'assert' without arguments
res, msg = pcall(assert)
assert(not res and string.find(msg, "value expected"))
end
-- xpcall with arguments
a, b, c = xpcall(string.find, error, "alo", "al")
assert(a and b == 1 and c == 2)
a, b, c = xpcall(string.find, function (x) return {} end, true, "al")
assert(not a and type(b) == "table" and c == nil)
print("testing tokens in error messages")
checksyntax("syntax error", "", "error", 1)
checksyntax("1.000", "", "1.000", 1)
checksyntax("[[a]]", "", "[[a]]", 1)
checksyntax("'aa'", "", "'aa'", 1)
checksyntax("while << do end", "", "<<", 1)
checksyntax("for >> do end", "", ">>", 1)
-- test invalid non-printable char in a chunk
checksyntax("a\1a = 1", "", "<\\1>", 1)
-- test 255 as first char in a chunk
checksyntax("\255a = 1", "", "<\\255>", 1)
doit('I = load("a=9+"); a=3')
assert(a==3 and not I)
print('+')
lim = 1000
if _soft then lim = 100 end
for i=1,lim do
doit('a = ')
doit('a = 4+nil')
end
-- testing syntax limits
local function testrep (init, rep, close, repc, finalresult)
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
local res, msg = load(s)
assert(res) -- 100 levels is OK
if (finalresult) then
assert(res() == finalresult)
end
s = init .. string.rep(rep, 10000)
local res, msg = load(s) -- 10000 levels not ok
assert(not res and (string.find(msg, "too many registers") or
string.find(msg, "stack overflow")))
end
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}")
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 = ''; return ", "a..", "'a'", "", "a")
testrep("local a = 1; return ", "a^", "a", "", 1)
checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
-- testing other limits
-- upvalues
local lim = 127
local s = "local function fooA ()\n local "
for j = 1,lim do
s = s.."a"..j..", "
end
s = s.."b,c\n"
s = s.."local function fooB ()\n local "
for j = 1,lim do
s = s.."b"..j..", "
end
s = s.."b\n"
s = s.."function fooC () return b+c"
local c = 1+2
for j = 1,lim do
s = s.."+a"..j.."+b"..j
c = c + 2
end
s = s.."\nend end end"
local a,b = load(s)
assert(c > 255 and string.find(b, "too many upvalues") and
string.find(b, "line 5"))
-- local variables
s = "\nfunction foo ()\n local "
for j = 1,300 do
s = s.."a"..j..", "
end
s = s.."b\n"
local a,b = load(s)
assert(string.find(b, "line 2") and string.find(b, "too many local variables"))
mt.__index = oldmm
print('OK')

476
testes/events.lua Normal file
View File

@@ -0,0 +1,476 @@
-- $Id: testes/events.lua $
-- See Copyright Notice in file all.lua
print('testing metatables')
local debug = require'debug'
X = 20; B = 30
_ENV = setmetatable({}, {__index=_G})
collectgarbage()
X = X+10
assert(X == 30 and _G.X == 20)
B = false
assert(B == false)
_ENV["B"] = undef
assert(B == 30)
assert(getmetatable{} == nil)
assert(getmetatable(4) == nil)
assert(getmetatable(nil) == nil)
a={name = "NAME"}; setmetatable(a, {__metatable = "xuxu",
__tostring=function(x) return x.name end})
assert(getmetatable(a) == "xuxu")
assert(tostring(a) == "NAME")
-- cannot change a protected metatable
assert(pcall(setmetatable, a, {}) == false)
a.name = "gororoba"
assert(tostring(a) == "gororoba")
local a, t = {10,20,30; x="10", y="20"}, {}
assert(setmetatable(a,t) == a)
assert(getmetatable(a) == t)
assert(setmetatable(a,nil) == a)
assert(getmetatable(a) == nil)
assert(setmetatable(a,t) == a)
function f (t, i, e)
assert(not e)
local p = rawget(t, "parent")
return (p and p[i]+3), "dummy return"
end
t.__index = f
a.parent = {z=25, x=12, [4] = 24}
assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10")
collectgarbage()
a = setmetatable({}, t)
function f(t, i, v) rawset(t, i, v-3) end
setmetatable(t, t) -- causes a bug in 5.1 !
t.__newindex = f
a[1] = 30; a.x = "101"; a[5] = 200
assert(a[1] == 27 and a.x == 98 and a[5] == 197)
do -- bug in Lua 5.3.2
local mt = {}
mt.__newindex = mt
local t = setmetatable({}, mt)
t[1] = 10 -- will segfault on some machines
assert(mt[1] == 10)
end
local c = {}
a = setmetatable({}, t)
t.__newindex = c
t.__index = c
a[1] = 10; a[2] = 20; a[3] = 90;
for i = 4, 20 do a[i] = i * 10 end
assert(a[1] == 10 and a[2] == 20 and a[3] == 90)
for i = 4, 20 do assert(a[i] == i * 10) end
assert(next(a) == nil)
do
local a;
a = setmetatable({}, {__index = setmetatable({},
{__index = setmetatable({},
{__index = function (_,n) return a[n-3]+4, "lixo" end})})})
a[0] = 20
for i=0,10 do
assert(a[i*3] == 20 + i*4)
end
end
do -- newindex
local foi
local a = {}
for i=1,10 do a[i] = 0; a['a'..i] = 0; end
setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end})
foi = false; a[1]=0; assert(not foi)
foi = false; a['a1']=0; assert(not foi)
foi = false; a['a11']=0; assert(foi)
foi = false; a[11]=0; assert(foi)
foi = false; a[1]=undef; assert(not foi)
a[1] = undef
foi = false; a[1]=nil; assert(foi)
end
setmetatable(t, nil)
function f (t, ...) return t, {...} end
t.__call = f
do
local x,y = a(table.unpack{'a', 1})
assert(x==a and y[1]=='a' and y[2]==1 and y[3]==undef)
x,y = a()
assert(x==a and y[1]==undef)
end
local b = setmetatable({}, t)
setmetatable(b,t)
function f(op)
return function (...) cap = {[0] = op, ...} ; return (...) end
end
t.__add = f("add")
t.__sub = f("sub")
t.__mul = f("mul")
t.__div = f("div")
t.__idiv = f("idiv")
t.__mod = f("mod")
t.__unm = f("unm")
t.__pow = f("pow")
t.__len = f("len")
t.__band = f("band")
t.__bor = f("bor")
t.__bxor = f("bxor")
t.__shl = f("shl")
t.__shr = f("shr")
t.__bnot = f("bnot")
t.__lt = f("lt")
t.__le = f("le")
local function checkcap (t)
assert(#cap + 1 == #t)
for i = 1, #t do
assert(cap[i - 1] == t[i])
assert(math.type(cap[i - 1]) == math.type(t[i]))
end
end
-- Some tests are done inside small anonymous functions to ensure
-- that constants go to constant table even in debug compilation,
-- when the constant table is very small.
assert(b+5 == b); checkcap{"add", b, 5}
assert(5.2 + b == 5.2); checkcap{"add", 5.2, b}
assert(b+'5' == b); checkcap{"add", b, '5'}
assert(5+b == 5); checkcap{"add", 5, b}
assert('5'+b == '5'); checkcap{"add", '5', b}
b=b-3; assert(getmetatable(b) == t); checkcap{"sub", b, 3}
assert(5-a == 5); checkcap{"sub", 5, a}
assert('5'-a == '5'); checkcap{"sub", '5', a}
assert(a*a == a); checkcap{"mul", a, a}
assert(a/0 == a); checkcap{"div", a, 0}
assert(a/0.0 == a); checkcap{"div", a, 0.0}
assert(a%2 == a); checkcap{"mod", a, 2}
assert(a // (1/0) == a); checkcap{"idiv", a, 1/0}
;(function () assert(a & "hi" == a) end)(); checkcap{"band", a, "hi"}
;(function () assert(10 & a == 10) end)(); checkcap{"band", 10, a}
;(function () assert(a | 10 == a) end)(); checkcap{"bor", a, 10}
assert(a | "hi" == a); checkcap{"bor", a, "hi"}
assert("hi" ~ a == "hi"); checkcap{"bxor", "hi", a}
;(function () assert(10 ~ a == 10) end)(); checkcap{"bxor", 10, a}
assert(-a == a); checkcap{"unm", a, a}
assert(a^4.0 == a); checkcap{"pow", a, 4.0}
assert(a^'4' == a); checkcap{"pow", a, '4'}
assert(4^a == 4); checkcap{"pow", 4, a}
assert('4'^a == '4'); checkcap{"pow", '4', a}
assert(#a == a); checkcap{"len", a, a}
assert(~a == a); checkcap{"bnot", a, a}
assert(a << 3 == a); checkcap{"shl", a, 3}
assert(1.5 >> a == 1.5); checkcap{"shr", 1.5, a}
-- for comparsion operators, all results are true
assert(5.0 > a); checkcap{"lt", a, 5.0}
assert(a >= 10); checkcap{"le", 10, a}
assert(a <= -10.0); checkcap{"le", a, -10.0}
assert(a < -10); checkcap{"lt", a, -10}
-- test for rawlen
t = setmetatable({1,2,3}, {__len = function () return 10 end})
assert(#t == 10 and rawlen(t) == 3)
assert(rawlen"abc" == 3)
assert(not pcall(rawlen, io.stdin))
assert(not pcall(rawlen, 34))
assert(not pcall(rawlen))
-- rawlen for long strings
assert(rawlen(string.rep('a', 1000)) == 1000)
t = {}
t.__lt = function (a,b,c)
collectgarbage()
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
t.__le = 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
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 (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')))
assert(not('a' < Op('a')) and (Op('a') < 'b') and not(Op('b') < Op('a')))
assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
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(Op(1), Op(2), Op(3))
-- test `partial order'
local function rawSet(x)
local y = {}
for _,k in pairs(x) do y[k] = 1 end
return y
end
local function Set(x)
return setmetatable(rawSet(x), t)
end
t.__lt = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = undef
end
return next(b) ~= nil
end
t.__le = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
assert(Set{1,2,3} < Set{1,2,3,4})
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
assert(not (Set{1,3} <= Set{3,5}))
assert(not(Set{1,3} <= Set{3,5}))
assert(not(Set{1,3} >= Set{3,5}))
t.__eq = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = undef
end
return next(b) == nil
end
local s = Set{1,3,5}
assert(s == Set{3,5,1})
assert(not rawequal(s, Set{3,5,1}))
assert(rawequal(s, s))
assert(Set{1,3,5,1} == rawSet{3,5,1})
assert(rawSet{1,3,5,1} == Set{3,5,1})
assert(Set{1,3,5} ~= Set{3,5,1,6})
-- '__eq' is not used for table accesses
t[Set{1,3,5}] = 1
assert(t[Set{1,3,5}] == undef)
if not T then
(Message or print)('\n >>> testC not active: skipping tests for \z
userdata <<<\n')
else
local u1 = T.newuserdata(0, 1)
local u2 = T.newuserdata(0, 1)
local u3 = T.newuserdata(0, 1)
assert(u1 ~= u2 and u1 ~= u3)
debug.setuservalue(u1, 1);
debug.setuservalue(u2, 2);
debug.setuservalue(u3, 1);
debug.setmetatable(u1, {__eq = function (a, b)
return debug.getuservalue(a) == debug.getuservalue(b)
end})
debug.setmetatable(u2, {__eq = function (a, b)
return true
end})
assert(u1 == u3 and u3 == u1 and u1 ~= u2)
assert(u2 == u1 and u2 == u3 and u3 == u2)
assert(u2 ~= {}) -- different types cannot be equal
local mirror = {}
debug.setmetatable(u3, {__index = mirror, __newindex = mirror})
for i = 1, 10 do u3[i] = i end
for i = 1, 10 do assert(u3[i] == i) end
end
t.__concat = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.val end
if type(b) == 'table' then b = b.val end
if A then return a..b
else
return setmetatable({val=a..b}, t)
end
end
c = {val="c"}; setmetatable(c, t)
d = {val="d"}; setmetatable(d, t)
A = true
assert(c..d == 'cd')
assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g")
A = false
assert((c..d..c..d).val == 'cdcd')
x = c..d
assert(getmetatable(x) == t and x.val == 'cd')
x = 0 .."a".."b"..c..d.."e".."f".."g"
assert(x.val == "0abcdefg")
-- concat metamethod x numbers (bug in 5.1.1)
c = {}
local x
setmetatable(c, {__concat = function (a,b)
assert(type(a) == "number" and b == c or type(b) == "number" and a == c)
return c
end})
assert(c..5 == c and 5 .. c == c)
assert(4 .. c .. 5 == c and 4 .. 5 .. 6 .. 7 .. c == c)
-- test comparison compatibilities
local t1, t2, c, d
t1 = {}; c = {}; setmetatable(c, t1)
d = {}
t1.__eq = function () return true end
t1.__lt = function () return true end
t1.__le = function () return false end
setmetatable(d, t1)
assert(c == d and c < d and not(d <= c))
t2 = {}
t2.__eq = t1.__eq
t2.__lt = t1.__lt
setmetatable(d, t2)
assert(c == d and c < d and not(d <= c))
-- test for several levels of calls
local i
local tt = {
__call = function (t, ...)
i = i+1
if t.f then return t.f(...)
else return {...}
end
end
}
local a = setmetatable({}, tt)
local b = setmetatable({f=a}, tt)
local c = setmetatable({f=b}, tt)
i = 0
x = c(3,4,5)
assert(i == 3 and x[1] == 3 and x[3] == 5)
assert(_G.X == 20)
print'+'
local _g = _G
_ENV = setmetatable({}, {__index=function (_,k) return _g[k] end})
a = {}
rawset(a, "x", 1, 2, 3)
assert(a.x == 1 and rawget(a, "x", 3) == 1)
print '+'
-- testing metatables for basic types
mt = {__index = function (a,b) return a+b end,
__len = function (x) return math.floor(x) end}
debug.setmetatable(10, mt)
assert(getmetatable(-2) == mt)
assert((10)[3] == 13)
assert((10)["3"] == 13)
assert(#3.45 == 3)
debug.setmetatable(23, nil)
assert(getmetatable(-2) == nil)
debug.setmetatable(true, mt)
assert(getmetatable(false) == mt)
mt.__index = function (a,b) return a or b end
assert((true)[false] == true)
assert((false)[false] == false)
debug.setmetatable(false, nil)
assert(getmetatable(true) == nil)
debug.setmetatable(nil, mt)
assert(getmetatable(nil) == mt)
mt.__add = function (a,b) return (a or 1) + (b or 2) end
assert(10 + nil == 12)
assert(nil + 23 == 24)
assert(nil + nil == 3)
debug.setmetatable(nil, nil)
assert(getmetatable(nil) == nil)
debug.setmetatable(nil, {})
-- loops in delegation
a = {}; setmetatable(a, a); a.__index = a; a.__newindex = a
assert(not pcall(function (a,b) return a[b] end, a, 10))
assert(not pcall(function (a,b,c) a[b] = c end, a, 10, true))
-- bug in 5.1
T, K, V = nil
grandparent = {}
grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end
parent = {}
parent.__newindex = parent
setmetatable(parent, grandparent)
child = setmetatable({}, parent)
child.foo = 10 --> CRASH (on some machines)
assert(T == parent and K == "foo" and V == 10)
print 'OK'
return 12

925
testes/files.lua Normal file
View File

@@ -0,0 +1,925 @@
-- $Id: testes/files.lua $
-- See Copyright Notice in file all.lua
local debug = require "debug"
local maxint = math.maxinteger
assert(type(os.getenv"PATH") == "string")
assert(io.input(io.stdin) == io.stdin)
assert(not pcall(io.input, "non-existent-file"))
assert(io.output(io.stdout) == io.stdout)
local function testerr (msg, f, ...)
local stat, err = pcall(f, ...)
return (not stat and string.find(err, msg, 1, true))
end
local function checkerr (msg, f, ...)
assert(testerr(msg, f, ...))
end
-- cannot close standard files
assert(not io.close(io.stdin) and
not io.stdout:close() and
not io.stderr:close())
-- cannot call close method without an argument (new in 5.3.5)
checkerr("got no value", io.stdin.close)
assert(type(io.input()) == "userdata" and io.type(io.output()) == "file")
assert(type(io.stdin) == "userdata" and io.type(io.stderr) == "file")
assert(not io.type(8))
local a = {}; setmetatable(a, {})
assert(not io.type(a))
assert(getmetatable(io.input()).__name == "FILE*")
local a,b,c = io.open('xuxu_nao_existe')
assert(not a and type(b) == "string" and type(c) == "number")
a,b,c = io.open('/a/b/c/d', 'w')
assert(not a and type(b) == "string" and type(c) == "number")
local file = os.tmpname()
local f, msg = io.open(file, "w")
if not f then
(Message or print)("'os.tmpname' file cannot be open; skipping file tests")
else --{ most tests here need tmpname
f:close()
print('testing i/o')
local otherfile = os.tmpname()
checkerr("invalid mode", io.open, file, "rw")
checkerr("invalid mode", io.open, file, "rb+")
checkerr("invalid mode", io.open, file, "r+bk")
checkerr("invalid mode", io.open, file, "")
checkerr("invalid mode", io.open, file, "+")
checkerr("invalid mode", io.open, file, "b")
assert(io.open(file, "r+b")):close()
assert(io.open(file, "r+")):close()
assert(io.open(file, "rb")):close()
assert(os.setlocale('C', 'all'))
io.input(io.stdin); io.output(io.stdout);
os.remove(file)
assert(not loadfile(file))
checkerr("", dofile, file)
assert(not io.open(file))
io.output(file)
assert(io.output() ~= io.stdout)
if not _port then -- invalid seek
local status, msg, code = io.stdin:seek("set", 1000)
assert(not status and type(msg) == "string" and type(code) == "number")
end
assert(io.output():seek() == 0)
assert(io.write("alo alo"):seek() == string.len("alo alo"))
assert(io.output():seek("cur", -3) == string.len("alo alo")-3)
assert(io.write("joao"))
assert(io.output():seek("end") == string.len("alo joao"))
assert(io.output():seek("set") == 0)
assert(io.write('"álo"', "{a}\n", "second line\n", "third line \n"))
assert(io.write('çfourth_line'))
io.output(io.stdout)
collectgarbage() -- file should be closed by GC
assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
print('+')
-- test GC for files
collectgarbage()
for i=1,120 do
for i=1,5 do
io.input(file)
assert(io.open(file, 'r'))
io.lines(file)
end
collectgarbage()
end
io.input():close()
io.close()
assert(os.rename(file, otherfile))
assert(not os.rename(file, otherfile))
io.output(io.open(otherfile, "ab"))
assert(io.write("\n\n\t\t ", 3450, "\n"));
io.close()
do
-- closing file by scope
local F = nil
do
local f <close> = assert(io.open(file, "w"))
F = f
end
assert(tostring(F) == "file (closed)")
end
assert(os.remove(file))
do
-- test writing/reading numbers
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')
f:write(0, '\n')
f:write(-maxint, '\n')
f:write(string.format("0x%X\n", -maxint))
f:write("-0xABCp-3", '\n')
assert(f:close())
local f <close> = assert(io.open(file, "r"))
assert(f:read("n") == maxint)
assert(f:read("n") == maxint)
assert(f:read("n") == 0xABCp-3)
assert(f:read("n") == 0)
assert(f:read("*n") == -maxint) -- test old format (with '*')
assert(f:read("n") == -maxint)
assert(f:read("*n") == -0xABCp-3) -- test old format (with '*')
end
assert(os.remove(file))
-- testing multiple arguments to io.read
do
local f <close> = assert(io.open(file, "w"))
f:write[[
a line
another line
1234
3.45
one
two
three
]]
local l1, l2, l3, l4, n1, n2, c, dummy
assert(f:close())
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 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 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 not n1)
end
assert(os.remove(file))
-- test yielding during 'dofile'
f = assert(io.open(file, "w"))
f:write[[
local x, z = coroutine.yield(10)
local y = coroutine.yield(20)
return x + y * z
]]
assert(f:close())
f = coroutine.wrap(dofile)
assert(f(file) == 10)
assert(f(100, 101) == 20)
assert(f(200) == 100 + 200 * 101)
assert(os.remove(file))
f = assert(io.open(file, "w"))
-- test number termination
f:write[[
-12.3- -0xffff+ .3|5.E-3X +234e+13E 0xDEADBEEFDEADBEEFx
0x1.13Ap+3e
]]
-- very long number
f:write("1234"); for i = 1, 1000 do f:write("0") end; f:write("\n")
-- invalid sequences (must read and discard valid prefixes)
f:write[[
.e+ 0.e; --; 0xX;
]]
assert(f:close())
f = assert(io.open(file, "r"))
assert(f:read("n") == -12.3); assert(f:read(1) == "-")
assert(f:read("n") == -0xffff); assert(f:read(2) == "+ ")
assert(f:read("n") == 0.3); assert(f:read(1) == "|")
assert(f:read("n") == 5e-3); assert(f:read(1) == "X")
assert(f:read("n") == 234e13); assert(f:read(1) == "E")
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(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
assert(not f:read("n")); assert(f:read(2) == "e+")
assert(not f:read("n")); assert(f:read(1) == ";")
assert(not f:read("n")); assert(f:read(2) == "-;")
assert(not f:read("n")); assert(f:read(1) == "X")
assert(not f:read("n")); assert(f:read(1) == ";")
assert(not f:read("n")); assert(not f:read(0)) -- end of file
assert(f:close())
assert(os.remove(file))
-- test line generators
assert(not pcall(io.lines, "non-existent-file"))
assert(os.rename(otherfile, file))
io.output(otherfile)
local n = 0
local f = io.lines(file)
while f() do n = n + 1 end;
assert(n == 6) -- number of lines in the file
checkerr("file is already closed", f)
checkerr("file is already closed", f)
-- copy from file to otherfile
n = 0
for l in io.lines(file) do io.write(l, "\n"); n = n + 1 end
io.close()
assert(n == 6)
-- copy from otherfile back to file
local f = assert(io.open(otherfile))
assert(io.type(f) == "file")
io.output(file)
assert(not io.output():read())
n = 0
for l in f:lines() do io.write(l, "\n"); n = n + 1 end
assert(tostring(f):sub(1, 5) == "file ")
assert(f:close()); io.close()
assert(n == 6)
checkerr("closed file", io.close, f)
assert(tostring(f) == "file (closed)")
assert(io.type(f) == "closed file")
io.input(file)
f = io.open(otherfile):lines()
n = 0
for l in io.lines() do assert(l == f()); n = n + 1 end
f = nil; collectgarbage()
assert(n == 6)
assert(os.remove(otherfile))
do -- bug in 5.3.1
io.output(otherfile)
io.write(string.rep("a", 300), "\n")
io.close()
local t ={}; for i = 1, 250 do t[i] = 1 end
t = {io.lines(otherfile, table.unpack(t))()}
-- everything ok here
assert(#t == 250 and t[1] == 'a' and t[#t] == 'a')
t[#t + 1] = 1 -- one too many
checkerr("too many arguments", io.lines, otherfile, table.unpack(t))
collectgarbage() -- ensure 'otherfile' is closed
assert(os.remove(otherfile))
end
io.input(file)
do -- test error returns
local a,b,c = io.input():write("xuxu")
assert(not a and type(b) == "string" and type(c) == "number")
end
checkerr("invalid format", io.read, "x")
assert(io.read(0) == "") -- not eof
assert(io.read(5, 'l') == '"álo"')
assert(io.read(0) == "")
assert(io.read() == "second line")
local x = io.input():seek()
assert(io.read() == "third line ")
assert(io.input():seek("set", x))
assert(io.read('L') == "third line \n")
assert(io.read(1) == "ç")
assert(io.read(string.len"fourth_line") == "fourth_line")
assert(io.input():seek("cur", -string.len"fourth_line"))
assert(io.read() == "fourth_line")
assert(io.read() == "") -- empty line
assert(io.read('n') == 3450)
assert(io.read(1) == '\n')
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(not io.read()) -- end of file
assert(({io.read()})[2] == undef)
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')
collectgarbage()
print('+')
io.close(io.input())
checkerr(" input file is closed", io.read)
assert(os.remove(file))
local t = '0123456789'
for i=1,10 do t = t..t; end
assert(string.len(t) == 10*2^10)
io.output(file)
io.write("alo"):write("\n")
io.close()
checkerr(" output file is closed", io.write)
local f = io.open(file, "a+b")
io.output(f)
collectgarbage()
assert(io.write(' ' .. t .. ' '))
assert(io.write(';', 'end of file\n'))
f:flush(); io.flush()
f:close()
print('+')
io.input(file)
assert(io.read() == "alo")
assert(io.read(1) == ' ')
assert(io.read(string.len(t)) == t)
assert(io.read(1) == ' ')
assert(io.read(0))
assert(io.read('a') == ';end of file\n')
assert(not io.read(0))
assert(io.close(io.input()))
-- test errors in read/write
do
local function ismsg (m)
-- error message is not a code number
return (type(m) == "string" and not tonumber(m))
end
-- read
local f = io.open(file, "w")
local r, m, c = f:read()
assert(not r and ismsg(m) and type(c) == "number")
assert(f:close())
-- write
f = io.open(file, "r")
r, m, c = f:write("whatever")
assert(not r and ismsg(m) and type(c) == "number")
assert(f:close())
-- lines
f = io.open(file, "w")
r, m = pcall(f:lines())
assert(r == false and ismsg(m))
assert(f:close())
end
assert(os.remove(file))
-- test for L format
io.output(file); io.write"\n\nline\nother":close()
io.input(file)
assert(io.read"L" == "\n")
assert(io.read"L" == "\n")
assert(io.read"L" == "line\n")
assert(io.read"L" == "other")
assert(not io.read"L")
io.input():close()
local f = assert(io.open(file))
local s = ""
for l in f:lines("L") do s = s .. l end
assert(s == "\n\nline\nother")
f:close()
io.input(file)
s = ""
for l in io.lines(nil, "L") do s = s .. l end
assert(s == "\n\nline\nother")
io.input():close()
s = ""
for l in io.lines(file, "L") do s = s .. l end
assert(s == "\n\nline\nother")
s = ""
for l in io.lines(file, "l") do s = s .. l end
assert(s == "lineother")
io.output(file); io.write"a = 10 + 34\na = 2*a\na = -a\n":close()
local t = {}
assert(load(io.lines(file, "L"), nil, nil, t))()
assert(t.a == -((10 + 34) * 2))
do -- testing closing file in line iteration
-- get the to-be-closed variable from a loop
local function gettoclose (lv)
lv = lv + 1
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 state)" then
stvar = stvar + 1
if stvar == 4 then return v end
end
end
end
local f
for l in io.lines(file) do
f = gettoclose(1)
assert(io.type(f) == "file")
break
end
assert(io.type(f) == "closed file")
f = nil
local function foo (name)
for l in io.lines(name) do
f = gettoclose(1)
assert(io.type(f) == "file")
error(f) -- exit loop with an error
end
end
local st, msg = pcall(foo, file)
assert(st == false and io.type(msg) == "closed file")
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(not b)
else assert(tonumber(a) == tonumber(b) - 1)
end
end
for a,b,c in io.lines(file, 1, 2, "a") do
assert(a == "0" and b == "12" and c == "3456789\n")
end
for a,b,c in io.lines(file, "a", 0, 1) do
if a == "" then break end
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(not b)
else assert(a == b - 10)
end
end
-- test load x lines
io.output(file);
io.write[[
local y
= X
X =
X *
2 +
X;
X =
X
- y;
]]:close()
_G.X = 1
assert(not load((io.lines(file))))
collectgarbage() -- to close file in previous iteration
load((io.lines(file, "L")))()
assert(_G.X == 2)
load((io.lines(file, 1)))()
assert(_G.X == 4)
load((io.lines(file, 3)))()
assert(_G.X == 8)
print('+')
local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'"
io.output(file)
assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1)))
io.close()
assert(loadfile(file))()
assert(x1 == x2)
print('+')
assert(os.remove(file))
assert(not os.remove(file))
assert(not os.remove(otherfile))
-- testing loadfile
local function testloadfile (s, expres)
io.output(file)
if s then io.write(s) end
io.close()
local res = assert(loadfile(file))()
assert(os.remove(file))
assert(res == expres)
end
-- loading empty file
testloadfile(nil, nil)
-- loading file with initial comment without end of line
testloadfile("# a non-ending comment", nil)
-- checking Unicode BOM in files
testloadfile("\xEF\xBB\xBF# some comment\nreturn 234", 234)
testloadfile("\xEF\xBB\xBFreturn 239", 239)
testloadfile("\xEF\xBB\xBF", nil) -- empty file with a BOM
-- checking line numbers in files with initial comments
testloadfile("# a comment\nreturn require'debug'.getinfo(1).currentline", 2)
-- loading binary file
io.output(io.open(file, "wb"))
assert(io.write(string.dump(function () return 10, '\0alo\255', 'hi' end)))
io.close()
a, b, c = assert(loadfile(file))()
assert(a == 10 and b == "\0alo\255" and c == "hi")
assert(os.remove(file))
-- bug in 5.2.1
do
io.output(io.open(file, "wb"))
-- save function with no upvalues
assert(io.write(string.dump(function () return 1 end)))
io.close()
f = assert(loadfile(file, "b", {}))
assert(type(f) == "function" and f() == 1)
assert(os.remove(file))
end
-- loading binary file with initial comment
io.output(io.open(file, "wb"))
assert(io.write("#this is a comment for a binary file\0\n",
string.dump(function () return 20, '\0\0\0' end)))
io.close()
a, b, c = assert(loadfile(file))()
assert(a == 20 and b == "\0\0\0" and c == nil)
assert(os.remove(file))
-- 'loadfile' with 'env'
do
local f = io.open(file, 'w')
f:write[[
if (...) then a = 15; return b, c, d
else return _ENV
end
]]
f:close()
local t = {b = 12, c = "xuxu", d = print}
local f = assert(loadfile(file, 't', t))
local b, c, d = f(1)
assert(t.a == 15 and b == 12 and c == t.c and d == print)
assert(f() == t)
f = assert(loadfile(file, 't', nil))
assert(f() == nil)
f = assert(loadfile(file))
assert(f() == _G)
assert(os.remove(file))
end
-- 'loadfile' x modes
do
io.open(file, 'w'):write("return 10"):close()
local s, m = loadfile(file, 'b')
assert(not s and string.find(m, "a text chunk"))
io.open(file, 'w'):write("\27 return 10"):close()
local s, m = loadfile(file, 't')
assert(not s and string.find(m, "a binary chunk"))
assert(os.remove(file))
end
io.output(file)
assert(io.write("qualquer coisa\n"))
assert(io.write("mais qualquer coisa"))
io.close()
assert(io.output(assert(io.open(otherfile, 'wb')))
:write("outra coisa\0\1\3\0\0\0\0\255\0")
:close())
local filehandle = assert(io.open(file, 'r+'))
local otherfilehandle = assert(io.open(otherfile, 'rb'))
assert(filehandle ~= otherfilehandle)
assert(type(filehandle) == "userdata")
assert(filehandle:read('l') == "qualquer coisa")
io.input(otherfilehandle)
assert(io.read(string.len"outra coisa") == "outra coisa")
assert(filehandle:read('l') == "mais qualquer coisa")
filehandle:close();
assert(type(filehandle) == "userdata")
io.input(otherfilehandle)
assert(io.read(4) == "\0\1\3\0")
assert(io.read(3) == "\0\0\0")
assert(io.read(0) == "") -- 255 is not eof
assert(io.read(1) == "\255")
assert(io.read('a') == "\0")
assert(not io.read(0))
assert(otherfilehandle == io.input())
otherfilehandle:close()
assert(os.remove(file))
assert(os.remove(otherfile))
collectgarbage()
io.output(file)
:write[[
123.4 -56e-2 not a number
second line
third line
and the rest of the file
]]
:close()
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 not __)
assert(type(a) == 'number' and a==123.4 and b==-56e-2)
assert(d=='second line' and e=='third line')
assert(h==[[
and the rest of the file
]])
assert(os.remove(file))
collectgarbage()
-- testing buffers
do
local f = assert(io.open(file, "w"))
local fr = assert(io.open(file, "r"))
assert(f:setvbuf("full", 2000))
f:write("x")
assert(fr:read("all") == "") -- full buffer; output not written yet
f:close()
fr:seek("set")
assert(fr:read("all") == "x") -- `close' flushes it
f = assert(io.open(file), "w")
assert(f:setvbuf("no"))
f:write("x")
fr:seek("set")
assert(fr:read("all") == "x") -- no buffer; output is ready
f:close()
f = assert(io.open(file, "a"))
assert(f:setvbuf("line"))
f:write("x")
fr:seek("set", 1)
assert(fr:read("all") == "") -- line buffer; no output without `\n'
f:write("a\n"):seek("set", 1)
assert(fr:read("all") == "xa\n") -- now we have a whole line
f:close(); fr:close()
assert(os.remove(file))
end
if not _soft then
print("testing large files (> BUFSIZ)")
io.output(file)
for i=1,5001 do io.write('0123456789123') end
io.write('\n12346'):close()
io.input(file)
local x = io.read('a')
io.input():seek('set', 0)
local y = io.read(30001)..io.read(1005)..io.read(0)..
io.read(1)..io.read(100003)
assert(x == y and string.len(x) == 5001*13 + 6)
io.input():seek('set', 0)
y = io.read() -- huge line
assert(x == y..'\n'..io.read())
assert(not io.read())
io.close(io.input())
assert(os.remove(file))
x = nil; y = nil
end
if not _port then
local progname
do -- get name of running executable
local arg = arg or ARG
local i = 0
while arg[i] do i = i - 1 end
progname = '"' .. arg[i + 1] .. '"'
end
print("testing popen/pclose and execute")
local tests = {
-- command, what, code
{"ls > /dev/null", "ok"},
{"not-to-be-found-command", "exit"},
{"exit 3", "exit", 3},
{"exit 129", "exit", 129},
{"kill -s HUP $$", "signal", 1},
{"kill -s KILL $$", "signal", 9},
{"sh -c 'kill -s HUP $$'", "exit"},
{progname .. ' -e " "', "ok"},
{progname .. ' -e "os.exit(0, true)"', "ok"},
{progname .. ' -e "os.exit(20, true)"', "exit", 20},
}
print("\n(some error messages are expected now)")
for _, v in ipairs(tests) do
local x, y, z = io.popen(v[1]):close()
local x1, y1, z1 = os.execute(v[1])
assert(x == x1 and y == y1 and z == z1)
if v[2] == "ok" then
assert(x and y == 'exit' and z == 0)
else
assert(not x and y == v[2]) -- correct status and 'what'
-- correct code if known (but always different from 0)
assert((v[3] == nil and z > 0) or v[3] == z)
end
end
end
-- testing tmpfile
f = io.tmpfile()
assert(io.type(f) == "file")
f:write("alo")
f:seek("set")
assert(f:read"a" == "alo")
end --}
print'+'
print("testing date/time")
assert(os.date("") == "")
assert(os.date("!") == "")
assert(os.date("\0\0") == "\0\0")
assert(os.date("!\0\0") == "\0\0")
local x = string.rep("a", 10000)
assert(os.date(x) == x)
local t = os.time()
D = os.date("*t", t)
assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
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")
checkerr("invalid conversion specifier", os.date, "%")
checkerr("invalid conversion specifier", os.date, "%O")
checkerr("invalid conversion specifier", os.date, "%E")
checkerr("invalid conversion specifier", os.date, "%Ea")
checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour='x'})
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
-- no arith overflows
checkerr("out-of-bound", os.time, {year = -maxint, month = 1, day = 1})
if string.packsize("i") == 4 then -- 4-byte ints
if testerr("out-of-bound", os.date, "%Y", 2^40) then
-- time_t has 4 bytes and therefore cannot represent year 4000
print(" 4-byte time_t")
checkerr("cannot be represented", os.time, {year=4000, month=1, day=1})
else
-- 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)
-- 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
do
local D = os.date("*t")
local t = os.time(D)
if D.isdst == nil then
print("no daylight saving information")
else
assert(type(D.isdst) == 'boolean')
end
D.isdst = nil
local t1 = os.time(D)
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)
-- allow for leap years
assert(math.abs(os.difftime(t,t1)/(24*3600) - 365) < 2)
-- should not take more than 1 second to execute these two lines
t = os.time()
t1 = os.time(os.date("*t"))
local diff = os.difftime(t1,t)
assert(0 <= diff and diff <= 1)
diff = os.difftime(t,t1)
assert(-1 <= diff and diff <= 0)
local t1 = os.time{year=2000, month=10, day=1, hour=23, min=12}
local t2 = os.time{year=2000, month=10, day=1, hour=23, min=10, sec=19}
assert(os.difftime(t1,t2) == 60*2-19)
-- since 5.3.3, 'os.time' normalizes table fields
t1 = {year = 2005, month = 1, day = 1, hour = 1, min = 0, sec = -3602}
os.time(t1)
assert(t1.day == 31 and t1.month == 12 and t1.year == 2004 and
t1.hour == 23 and t1.min == 59 and t1.sec == 58 and
t1.yday == 366)
io.output(io.stdout)
local t = os.date('%d %m %Y %H %M %S')
local d, m, a, h, min, s = string.match(t,
"(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)")
d = tonumber(d)
m = tonumber(m)
a = tonumber(a)
h = tonumber(h)
min = tonumber(min)
s = tonumber(s)
io.write(string.format('test done on %2.2d/%2.2d/%d', d, m, a))
io.write(string.format(', at %2.2d:%2.2d:%2.2d\n', h, min, s))
io.write(string.format('%s\n', _VERSION))

681
testes/gc.lua Normal file
View File

@@ -0,0 +1,681 @@
-- $Id: testes/gc.lua $
-- See Copyright Notice in file all.lua
print('testing incremental garbage collection')
local debug = require"debug"
assert(collectgarbage("isrunning"))
collectgarbage()
local oldmode = collectgarbage("incremental")
-- changing modes should return previous mode
assert(collectgarbage("generational") == "incremental")
assert(collectgarbage("generational") == "generational")
assert(collectgarbage("incremental") == "generational")
assert(collectgarbage("incremental") == "incremental")
local function nop () end
local function gcinfo ()
return collectgarbage"count" * 1024
end
-- test weird parameters to 'collectgarbage'
do
-- save original parameters
local a = collectgarbage("setpause", 200)
local b = collectgarbage("setstepmul", 200)
local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
for i = 1, #t do
local p = t[i]
for j = 1, #t do
local m = t[j]
collectgarbage("setpause", p)
collectgarbage("setstepmul", m)
collectgarbage("step", 0)
collectgarbage("step", 10000)
end
end
-- restore original parameters
collectgarbage("setpause", a)
collectgarbage("setstepmul", b)
collectgarbage()
end
_G["while"] = 234
--
-- tests for GC activation when creating different kinds of objects
--
local function GC1 ()
local u
local b -- (above 'u' it in the stack)
local finish = false
u = setmetatable({}, {__gc = function () finish = true end})
b = {34}
repeat u = {} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false; local i = 1
u = setmetatable({}, {__gc = function () finish = true end})
repeat i = i + 1; u = tostring(i) .. tostring(i) until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false
u = setmetatable({}, {__gc = function () finish = true end})
repeat local i; u = function () return i end until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
end
local function GC2 ()
local u
local finish = false
u = {setmetatable({}, {__gc = function () finish = true end})}
local b = {34}
repeat u = {{}} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false; local i = 1
u = {setmetatable({}, {__gc = function () finish = true end})}
repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false
u = {setmetatable({}, {__gc = function () finish = true end})}
repeat local i; u = {function () return i end} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
end
local function GC() GC1(); GC2() end
do
print("creating many objects")
local limit = 5000
for i = 1, limit do
local a = {}; a = nil
end
local a = "a"
for i = 1, limit do
a = i .. "b";
a = string.gsub(a, '(%d%d*)', "%1 %1")
a = "a"
end
a = {}
function a:test ()
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
a:test()
end
-- collection of functions without locals, globals, etc.
do local f = function () end end
print("functions with errors")
prog = [[
do
a = 10;
function foo(x,y)
a = sin(a+0.456-0.23e-12);
return function (z) return sin(%x+z) end
end
local x = function (w) a=a+w; end
end
]]
do
local step = 1
if _soft then step = 13 end
for i=1, string.len(prog), step do
for j=i, string.len(prog), step do
pcall(load(string.sub(prog, i, j), ""))
end
end
end
foo = nil
print('long strings')
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
assert(string.len(x)==80)
s = ''
k = math.min(300, (math.maxinteger // 80) // 2)
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)', '')
assert(i==10000 // 4)
s = nil
x = nil
assert(_G["while"] == 234)
--
-- test the "size" of basic GC steps (whatever they mean...)
--
do
print("steps")
print("steps (2)")
local function dosteps (siz)
collectgarbage()
local a = {}
for i=1,100 do a[i] = {{}}; local b = {} end
local x = gcinfo()
local i = 0
repeat -- do steps until it completes a collection cycle
i = i+1
until collectgarbage("step", siz)
assert(gcinfo() < x)
return i -- number of steps
end
collectgarbage"stop"
if not _port then
assert(dosteps(10) < dosteps(2))
end
-- collector should do a full collection with so many steps
assert(dosteps(20000) == 1)
assert(collectgarbage("step", 20000) == true)
assert(collectgarbage("step", 20000) == true)
assert(not collectgarbage("isrunning"))
collectgarbage"restart"
assert(collectgarbage("isrunning"))
end
if not _port then
-- test the pace of the collector
collectgarbage(); collectgarbage()
local x = gcinfo()
collectgarbage"stop"
repeat
local a = {}
until gcinfo() > 3 * x
collectgarbage"restart"
assert(collectgarbage("isrunning"))
repeat
local a = {}
until gcinfo() <= x * 2
end
print("clearing tables")
lim = 15
a = {}
-- fill a with `collectable' indices
for i=1,lim do a[{}] = i end
b = {}
for k,v in pairs(a) do b[k]=v end
-- remove all indices and collect them
for n in pairs(b) do
a[n] = undef
assert(type(n) == 'table' and next(n) == nil)
collectgarbage()
end
b = nil
collectgarbage()
for n in pairs(a) do error'cannot be here' end
for i=1,lim do a[i] = i end
for i=1,lim do assert(a[i] == i) end
print('weak tables')
a = {}; setmetatable(a, {__mode = 'k'});
-- fill a with some `collectable' indices
for i=1,lim do a[{}] = i end
-- and some non-collectable ones
for i=1,lim do a[i] = i end
for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end
collectgarbage()
local i = 0
for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end
assert(i == 2*lim)
a = {}; setmetatable(a, {__mode = 'v'});
a[1] = string.rep('b', 21)
collectgarbage()
assert(a[1]) -- strings are *values*
a[1] = undef
-- fill a with some `collectable' values (in both parts of the table)
for i=1,lim do a[i] = {} end
for i=1,lim do a[i..'x'] = {} end
-- and some non-collectable ones
for i=1,lim do local t={}; a[t]=t end
for i=1,lim do a[i+lim]=i..'x' end
collectgarbage()
local i = 0
for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end
assert(i == 2*lim)
a = {}; setmetatable(a, {__mode = 'kv'});
local x, y, z = {}, {}, {}
-- keep only some items
a[1], a[2], a[3] = x, y, z
a[string.rep('$', 11)] = string.rep('$', 11)
-- fill a with some `collectable' values
for i=4,lim do a[i] = {} end
for i=1,lim do a[{}] = i end
for i=1,lim do local t={}; a[t]=t end
collectgarbage()
assert(next(a) ~= nil)
local i = 0
for k,v in pairs(a) do
assert((k == 1 and v == x) or
(k == 2 and v == y) or
(k == 3 and v == z) or k==v);
i = i+1
end
assert(i == 4)
x,y,z=nil
collectgarbage()
assert(next(a) == string.rep('$', 11))
-- 'bug' in 5.1
a = {}
local t = {x = 10}
local C = setmetatable({key = t}, {__mode = 'v'})
local C1 = setmetatable({[t] = 1}, {__mode = 'k'})
a.x = t -- this should not prevent 't' from being removed from
-- weak table 'C' by the time 'a' is finalized
setmetatable(a, {__gc = function (u)
assert(C.key == nil)
assert(type(next(C1)) == 'table')
end})
a, t = nil
collectgarbage()
collectgarbage()
assert(next(C) == nil and next(C1) == nil)
C, C1 = nil
-- ephemerons
local mt = {__mode = 'k'}
a = {{10},{20},{30},{40}}; setmetatable(a, mt)
x = nil
for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end
GC()
local n = x
local i = 0
while n do n = a[n].k[1]; i = i + 1 end
assert(i == 100)
x = nil
GC()
for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = undef end
assert(next(a) == nil)
local K = {}
a[K] = {}
for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end
x = nil
local k = 1
for j = 1,100 do
local n = {}; local nk = k%10 + 1
a[a[K][nk]][n] = {x, k = k}; x = n; k = nk
end
GC()
local n = x
local i = 0
while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end
assert(i == 100)
K = nil
GC()
-- assert(next(a) == nil)
-- testing errors during GC
if T then
collectgarbage("stop") -- stop collection
local u = {}
local s = {}; setmetatable(s, {__mode = 'k'})
setmetatable(u, {__gc = function (o)
local i = s[o]
s[i] = true
assert(not s[i - 1]) -- check proper finalization order
if i == 8 then error("@expected@") end -- error during GC
end})
for i = 6, 10 do
local n = setmetatable({}, getmetatable(u))
s[n] = i
end
warn("@on"); warn("@store")
collectgarbage()
assert(string.find(_WARN, "error in __gc metamethod"))
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = nil
for i = 8, 10 do assert(s[i]) end
for i = 1, 5 do
local n = setmetatable({}, getmetatable(u))
s[n] = i
end
collectgarbage()
for i = 1, 10 do assert(s[i]) end
getmetatable(u).__gc = nil
warn("@normal")
end
print '+'
-- testing userdata
if T==nil then
(Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n')
else
local function newproxy(u)
return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u))
end
collectgarbage("stop") -- stop collection
local u = newproxy(nil)
debug.setmetatable(u, {__gc = true})
local s = 0
local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'})
for i=1,10 do a[newproxy(u)] = i end
for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end
local a1 = {}; for k,v in pairs(a) do a1[k] = v end
for k,v in pairs(a1) do a[v] = k end
for i =1,10 do assert(a[i]) end
getmetatable(u).a = a1
getmetatable(u).u = u
do
local u = u
getmetatable(u).__gc = function (o)
assert(a[o] == 10-s)
assert(a[10-s] == undef) -- udata already removed from weak table
assert(getmetatable(o) == getmetatable(u))
assert(getmetatable(o).a[o] == 10-s)
s=s+1
end
end
a1, u = nil
assert(next(a) ~= nil)
collectgarbage()
assert(s==11)
collectgarbage()
assert(next(a) == nil) -- finalized keys are removed in two cycles
end
-- __gc x weak tables
local u = setmetatable({}, {__gc = true})
-- __gc metamethod should be collected before running
setmetatable(getmetatable(u), {__mode = "v"})
getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen
u = nil
collectgarbage()
local u = setmetatable({}, {__gc = true})
local m = getmetatable(u)
m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"});
m.__gc = function (o)
assert(next(getmetatable(o).x) == nil)
m = 10
end
u, m = nil
collectgarbage()
assert(m==10)
do -- tests for string keys in weak tables
collectgarbage(); collectgarbage()
local m = collectgarbage("count") -- current memory
local a = setmetatable({}, {__mode = "kv"})
a[string.rep("a", 2^22)] = 25 -- long string key -> number value
a[string.rep("b", 2^22)] = {} -- long string key -> colectable value
a[{}] = 14 -- colectable key
assert(collectgarbage("count") > m + 2^13) -- 2^13 == 2 * 2^22 in KB
collectgarbage()
assert(collectgarbage("count") >= m + 2^12 and
collectgarbage("count") < m + 2^13) -- one key was collected
local k, v = next(a) -- string key with number value preserved
assert(k == string.rep("a", 2^22) and v == 25)
assert(next(a, k) == nil) -- everything else cleared
assert(a[string.rep("b", 2^22)] == undef)
a[k] = undef -- erase this last entry
k = nil
collectgarbage()
assert(next(a) == nil)
-- make sure will not try to compare with dead key
assert(a[string.rep("b", 100)] == undef)
assert(collectgarbage("count") <= m + 1) -- eveything collected
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
if not _soft then
print("long list")
local a = {}
for i = 1,200000 do
a = {next = a}
end
a = nil
collectgarbage()
end
-- create many threads with self-references and open upvalues
print("self-referenced threads")
local thread_id = 0
local threads = {}
local function fn (thread)
local x = {}
threads[thread_id] = function()
thread = x
end
coroutine.yield()
end
while thread_id < 1000 do
local thread = coroutine.create(fn)
coroutine.resume(thread, thread)
thread_id = thread_id + 1
end
-- Create a closure (function inside 'f') with an upvalue ('param') that
-- points (through a table) to the closure itself and to the thread
-- ('co' and the initial value of 'param') where closure is running.
-- Then, assert that table (and therefore everything else) will be
-- collected.
do
local collected = false -- to detect collection
collectgarbage(); collectgarbage("stop")
do
local function f (param)
;(function ()
assert(type(f) == 'function' and type(param) == 'thread')
param = {param, f}
setmetatable(param, {__gc = function () collected = true end})
coroutine.yield(100)
end)()
end
local co = coroutine.create(f)
assert(coroutine.resume(co, co))
end
-- Now, thread and closure are not reacheable any more.
collectgarbage()
assert(collected)
collectgarbage("restart")
end
do
collectgarbage()
collectgarbage"stop"
collectgarbage("step", 0) -- steps should not unblock the collector
local x = gcinfo()
repeat
for i=1,1000 do _ENV.a = {} end -- no collection during the loop
until gcinfo() > 2 * x
collectgarbage"restart"
end
if T then -- tests for weird cases collecting upvalues
local function foo ()
local a = {x = 20}
coroutine.yield(function () return a.x end) -- will run collector
assert(a.x == 20) -- 'a' is 'ok'
a = {x = 30} -- create a new object
assert(T.gccolor(a) == "white") -- of course it is new...
coroutine.yield(100) -- 'a' is still local to this thread
end
local t = setmetatable({}, {__mode = "kv"})
collectgarbage(); collectgarbage('stop')
-- create coroutine in a weak table, so it will never be marked
t.co = coroutine.wrap(foo)
local f = t.co() -- create function to access local 'a'
T.gcstate("atomic") -- ensure all objects are traversed
assert(T.gcstate() == "atomic")
assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
assert(T.gccolor(t.co) == "white") -- thread was not traversed
T.gcstate("pause") -- collect thread, but should mark 'a' before that
assert(t.co == nil and f() == 30) -- ensure correct access to 'a'
collectgarbage("restart")
-- test barrier in sweep phase (backing userdata to gray)
local u = T.newuserdata(0, 1) -- create a userdata
collectgarbage()
collectgarbage"stop"
local a = {} -- avoid 'u' as first element in 'allgc'
T.gcstate"atomic"
T.gcstate"sweepallgc"
local x = {}
assert(T.gccolor(u) == "black") -- userdata is "old" (black)
assert(T.gccolor(x) == "white") -- table is "new" (white)
debug.setuservalue(u, x) -- trigger barrier
assert(T.gccolor(u) == "gray") -- userdata changed back to gray
collectgarbage"restart"
print"+"
end
if T then
local debug = require "debug"
collectgarbage("stop")
local x = T.newuserdata(0)
local y = T.newuserdata(0)
debug.setmetatable(y, {__gc = nop}) -- bless the new udata before...
debug.setmetatable(x, {__gc = nop}) -- ...the old one
assert(T.gccolor(y) == "white")
T.checkmemory()
collectgarbage("restart")
end
if T then
print("emergency collections")
collectgarbage()
collectgarbage()
T.totalmem(T.totalmem() + 200)
for i=1,200 do local a = {} end
T.totalmem(0)
collectgarbage()
local t = T.totalmem("table")
local a = {{}, {}, {}} -- create 4 new tables
assert(T.totalmem("table") == t + 4)
t = T.totalmem("function")
a = function () end -- create 1 new closure
assert(T.totalmem("function") == t + 1)
t = T.totalmem("thread")
a = coroutine.create(function () end) -- create 1 new coroutine
assert(T.totalmem("thread") == t + 1)
end
-- create an object to be collected when state is closed
do
local setmetatable,assert,type,print,getmetatable =
setmetatable,assert,type,print,getmetatable
local tt = {}
tt.__gc = function (o)
assert(getmetatable(o) == tt)
-- create new objects during GC
local a = 'xuxu'..(10+3)..'joao', {}
___Glob = o -- ressurect object!
setmetatable({}, tt) -- creates a new one with same metatable
print(">>> closing state " .. "<<<\n")
end
local u = setmetatable({}, tt)
___Glob = {u} -- avoid object being collected before program end
end
-- create several objects to raise errors when collected while closing state
if T then
local error, assert, find, warn = error, assert, string.find, warn
local n = 0
local lastmsg
local mt = {__gc = function (o)
n = n + 1
assert(n == o[1])
if n == 1 then
_WARN = nil
elseif n == 2 then
assert(find(_WARN, "@expected warning"))
lastmsg = _WARN -- get message from previous error (first 'o')
else
assert(lastmsg == _WARN) -- subsequent error messages are equal
end
warn("@store"); _WARN = nil
error"@expected warning"
end}
for i = 10, 1, -1 do
-- create object and preserve it until the end
table.insert(___Glob, setmetatable({i}, mt))
end
end
-- just to make sure
assert(collectgarbage'isrunning')
collectgarbage(oldmode)
print('OK')

83
testes/gengc.lua Normal file
View File

@@ -0,0 +1,83 @@
-- $Id: testes/gengc.lua $
-- See Copyright Notice in file all.lua
print('testing generational garbage collection')
local debug = require"debug"
assert(collectgarbage("isrunning"))
collectgarbage()
local oldmode = collectgarbage("generational")
-- ensure that table barrier evolves correctly
do
local U = {}
-- full collection makes 'U' old
collectgarbage()
assert(not T or T.gcage(U) == "old")
-- U refers to a new table, so it becomes 'touched1'
U[1] = {x = {234}}
assert(not T or (T.gcage(U) == "touched1" and T.gcage(U[1]) == "new"))
-- both U and the table survive one more collection
collectgarbage("step", 0)
assert(not T or (T.gcage(U) == "touched2" and T.gcage(U[1]) == "survival"))
-- both U and the table survive yet another collection
-- now everything is old
collectgarbage("step", 0)
assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
-- data was not corrupted
assert(U[1].x[1] == 234)
end
if T == nil then
(Message or print)('\n >>> testC not active: \z
skipping some generational tests <<<\n')
print 'OK'
return
end
-- ensure that userdata barrier evolves correctly
do
local U = T.newuserdata(0, 1)
-- full collection makes 'U' old
collectgarbage()
assert(T.gcage(U) == "old")
-- U refers to a new table, so it becomes 'touched1'
debug.setuservalue(U, {x = {234}})
assert(T.gcage(U) == "touched1" and
T.gcage(debug.getuservalue(U)) == "new")
-- both U and the table survive one more collection
collectgarbage("step", 0)
assert(T.gcage(U) == "touched2" and
T.gcage(debug.getuservalue(U)) == "survival")
-- both U and the table survive yet another collection
-- now everything is old
collectgarbage("step", 0)
assert(T.gcage(U) == "old" and
T.gcage(debug.getuservalue(U)) == "old1")
-- data was not corrupted
assert(debug.getuservalue(U).x[1] == 234)
end
-- just to make sure
assert(collectgarbage'isrunning')
collectgarbage(oldmode)
print('OK')

271
testes/goto.lua Normal file
View File

@@ -0,0 +1,271 @@
-- $Id: testes/goto.lua $
-- See Copyright Notice in file all.lua
collectgarbage()
local function errmsg (code, m)
local st, msg = load(code)
assert(not st and string.find(msg, m))
end
-- cannot see label inside block
errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'")
errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
-- repeated label
errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
errmsg([[ ::l1:: do ::l1:: end]], "label 'l1'")
-- undefined label
errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'")
-- jumping over variable definition
errmsg([[
do local bb, cc; goto l1; end
local aa
::l1:: print(3)
]], "local 'aa'")
-- jumping into a block
errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'")
-- cannot continue a repeat-until with variables
errmsg([[
repeat
if x then goto cont end
local xuxu = 10
::cont::
until xuxu < x
]], "local 'xuxu'")
-- simple gotos
local x
do
local y = 12
goto l1
::l2:: x = x + 1; goto l3
::l1:: x = y; goto l2
end
::l3:: ::l3_1:: assert(x == 13)
-- long labels
do
local prog = [[
do
local a = 1
goto l%sa; a = a + 1
::l%sa:: a = a + 10
goto l%sb; a = a + 2
::l%sb:: a = a + 20
return a
end
]]
local label = string.rep("0123456789", 40)
prog = string.format(prog, label, label, label, label)
assert(assert(load(prog))() == 31)
end
-- ok to jump over local dec. to end of block
do
goto l1
local a = 23
x = a
::l1::;
end
while true do
goto l4
goto l1 -- ok to jump over local dec. to end of block
goto l1 -- multiple uses of same label
local x = 45
::l1:: ;;;
end
::l4:: assert(x == 13)
if print then
goto l1 -- ok to jump over local dec. to end of block
error("should not be here")
goto l2 -- ok to jump over local dec. to end of block
local x
::l1:: ; ::l2:: ;;
else end
-- to repeat a label in a different function is OK
local function foo ()
local a = {}
goto l3
::l1:: a[#a + 1] = 1; goto l2;
::l2:: a[#a + 1] = 2; goto l5;
::l3::
::l3a:: a[#a + 1] = 3; goto l1;
::l4:: a[#a + 1] = 4; goto l6;
::l5:: a[#a + 1] = 5; goto l4;
::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and
a[4] == 5 and a[5] == 4)
if not a[6] then a[6] = true; goto l3a end -- do it twice
end
::l6:: foo()
do -- bug in 5.2 -> 5.3.2
local x
::L1::
local y -- cannot join this SETNIL with previous one
assert(y == nil)
y = true
if x == nil then
x = 1
goto L1
else
x = x + 1
end
assert(x == 2 and y == true)
end
-- bug in 5.3
do
local first = true
local a = false
if true then
goto LBL
::loop::
a = true
::LBL::
if first then
first = false
goto loop
end
end
assert(a)
end
do -- compiling infinite loops
goto escape -- do not run the infinite loops
::a:: goto a
::b:: goto c
::c:: goto b
end
::escape::
--------------------------------------------------------------------------------
-- testing closing of upvalues
local debug = require 'debug'
local function foo ()
local t = {}
do
local i = 1
local a, b, c, d
t[1] = function () return a, b, c, d end
::l1::
local b
do
local c
t[#t + 1] = function () return a, b, c, d end -- t[2], t[4], t[6]
if i > 2 then goto l2 end
do
local d
t[#t + 1] = function () return a, b, c, d end -- t[3], t[5]
i = i + 1
local a
goto l1
end
end
end
::l2:: return t
end
local a = foo()
assert(#a == 6)
-- all functions share same 'a'
for i = 2, 6 do
assert(debug.upvalueid(a[1], 1) == debug.upvalueid(a[i], 1))
end
-- 'b' and 'c' are shared among some of them
for i = 2, 6 do
-- only a[1] uses external 'b'/'b'
assert(debug.upvalueid(a[1], 2) ~= debug.upvalueid(a[i], 2))
assert(debug.upvalueid(a[1], 3) ~= debug.upvalueid(a[i], 3))
end
for i = 3, 5, 2 do
-- inner functions share 'b'/'c' with previous ones
assert(debug.upvalueid(a[i], 2) == debug.upvalueid(a[i - 1], 2))
assert(debug.upvalueid(a[i], 3) == debug.upvalueid(a[i - 1], 3))
-- but not with next ones
assert(debug.upvalueid(a[i], 2) ~= debug.upvalueid(a[i + 1], 2))
assert(debug.upvalueid(a[i], 3) ~= debug.upvalueid(a[i + 1], 3))
end
-- only external 'd' is shared
for i = 2, 6, 2 do
assert(debug.upvalueid(a[1], 4) == debug.upvalueid(a[i], 4))
end
-- internal 'd's are all different
for i = 3, 5, 2 do
for j = 1, 6 do
assert((debug.upvalueid(a[i], 4) == debug.upvalueid(a[j], 4))
== (i == j))
end
end
--------------------------------------------------------------------------------
-- testing if x goto optimizations
local function testG (a)
if a == 1 then
goto l1
error("should never be here!")
elseif a == 2 then goto l2
elseif a == 3 then goto l3
elseif a == 4 then
goto l1 -- go to inside the block
error("should never be here!")
::l1:: a = a + 1 -- must go to 'if' end
else
goto l4
::l4a:: a = a * 2; goto l4b
error("should never be here!")
::l4:: goto l4a
error("should never be here!")
::l4b::
end
do return a end
::l2:: do return "2" end
::l3:: do return "3" end
::l1:: return "1"
end
assert(testG(1) == "1")
assert(testG(2) == "2")
assert(testG(3) == "3")
assert(testG(4) == 5)
assert(testG(5) == 10)
do
-- if x back goto out of scope of upvalue
local X
goto L1
::L2:: goto L3
::L1:: do
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
::L3:: assert(X == true) -- checks that 'a' was correctly closed
end
--------------------------------------------------------------------------------
print'OK'

173
testes/heavy.lua Normal file
View File

@@ -0,0 +1,173 @@
-- $Id: heavy.lua,v 1.7 2017/12/29 15:42:15 roberto Exp $
-- See Copyright Notice in file all.lua
local function teststring ()
print("creating a string too long")
do
local a = "x"
local st, msg = pcall(function ()
while true do
a = a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
.. a .. a.. a.. a.. a.. a.. a.. a.. a.. a
print(string.format("string with %d bytes", #a))
end
end)
assert(not st and
(string.find(msg, "string length overflow") or
string.find(msg, "not enough memory")))
print("string length overflow with " .. #a * 100)
end
print('+')
end
local function loadrep (x, what)
local p = 1<<20
local s = string.rep(x, p)
local count = 0
local function f()
count = count + p
if count % (0x80*p) == 0 then
io.stderr:write("(", count // 2^20, " M)")
end
return s
end
local st, msg = load(f, "=big")
print("\nmemory: ", collectgarbage'count' * 1024)
msg = string.match(msg, "^[^\n]+") -- get only first line
print(string.format("total: 0x%x %s ('%s')", count, what, msg))
return st, msg
end
function controlstruct ()
print("control structure too long")
local lim = ((1 << 24) - 2) // 3
local s = string.rep("a = a + 1\n", lim)
s = "while true do " .. s .. "end"
assert(load(s))
print("ok with " .. lim .. " lines")
lim = lim + 3
s = string.rep("a = a + 1\n", lim)
s = "while true do " .. s .. "end"
local st, msg = load(s)
assert(not st and string.find(msg, "too long"))
print(msg)
end
function manylines ()
print("loading chunk with too many lines")
local st, msg = loadrep("\n", "lines")
assert(not st and string.find(msg, "too many lines"))
print('+')
end
function hugeid ()
print("loading chunk with huge identifier")
local st, msg = loadrep("a", "chars")
assert(not st and
(string.find(msg, "lexical element too long") or
string.find(msg, "not enough memory")))
print('+')
end
function toomanyinst ()
print("loading chunk with too many instructions")
local st, msg = loadrep("a = 10; ", "instructions")
print('+')
end
local function loadrepfunc (prefix, f)
local count = -1
local function aux ()
count = count + 1
if count == 0 then
return prefix
else
if count % (0x100000) == 0 then
io.stderr:write("(", count // 2^20, " M)")
end
return f(count)
end
end
local st, msg = load(aux, "k")
print("\nmemory: ", collectgarbage'count' * 1024)
msg = string.match(msg, "^[^\n]+") -- get only first line
print("expected error: ", msg)
end
function toomanyconst ()
print("loading function with too many constants")
loadrepfunc("function foo () return {0,",
function (n)
-- convert 'n' to a string in the format [["...",]],
-- where '...' is a kind of number in base 128
-- (in a range that does not include either the double quote
-- and the escape.)
return string.char(34,
((n // 128^0) & 127) + 128,
((n // 128^1) & 127) + 128,
((n // 128^2) & 127) + 128,
((n // 128^3) & 127) + 128,
((n // 128^4) & 127) + 128,
34, 44)
end)
end
function toomanystr ()
local a = {}
local st, msg = pcall(function ()
for i = 1, math.huge do
if i % (0x100000) == 0 then
io.stderr:write("(", i // 2^20, " M)")
end
a[i] = string.pack("I", i)
end
end)
local size = #a
a = collectgarbage'count'
print("\nmemory:", a * 1024)
print("expected error:", msg)
print("size:", size)
end
function toomanyidx ()
local a = {}
local st, msg = pcall(function ()
for i = 1, math.huge do
if i % (0x100000) == 0 then
io.stderr:write("(", i // 2^20, " M)")
end
a[i] = i
end
end)
print("\nmemory: ", collectgarbage'count' * 1024)
print("expected error: ", msg)
print("size:", #a)
end
-- teststring()
-- controlstruct()
-- manylines()
-- hugeid()
-- toomanyinst()
-- toomanyconst()
-- toomanystr()
toomanyidx()
print "OK"

2
testes/libs/P1/dummy Normal file
View File

@@ -0,0 +1,2 @@
# This is a dummy file just to make git keep the otherwise empty
# directory 'P1' in the repository.

44
testes/libs/lib1.c Normal file
View File

@@ -0,0 +1,44 @@
#include "lua.h"
#include "lauxlib.h"
static int id (lua_State *L) {
return lua_gettop(L);
}
static const struct luaL_Reg funcs[] = {
{"id", id},
{NULL, NULL}
};
/* function used by lib11.c */
LUAMOD_API int lib1_export (lua_State *L) {
lua_pushstring(L, "exported");
return 1;
}
LUAMOD_API int onefunction (lua_State *L) {
luaL_checkversion(L);
lua_settop(L, 2);
lua_pushvalue(L, 1);
return 2;
}
LUAMOD_API int anotherfunc (lua_State *L) {
luaL_checkversion(L);
lua_pushfstring(L, "%d%%%d\n", (int)lua_tointeger(L, 1),
(int)lua_tointeger(L, 2));
return 1;
}
LUAMOD_API int luaopen_lib1_sub (lua_State *L) {
lua_setglobal(L, "y"); /* 2nd arg: extra value (file name) */
lua_setglobal(L, "x"); /* 1st arg: module name */
luaL_newlib(L, funcs);
return 1;
}

10
testes/libs/lib11.c Normal file
View File

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

23
testes/libs/lib2.c Normal file
View File

@@ -0,0 +1,23 @@
#include "lua.h"
#include "lauxlib.h"
static int id (lua_State *L) {
return lua_gettop(L);
}
static const struct luaL_Reg funcs[] = {
{"id", id},
{NULL, NULL}
};
LUAMOD_API int luaopen_lib2 (lua_State *L) {
lua_settop(L, 2);
lua_setglobal(L, "y"); /* y gets 2nd parameter */
lua_setglobal(L, "x"); /* x gets 1st parameter */
luaL_newlib(L, funcs);
return 1;
}

10
testes/libs/lib21.c Normal file
View File

@@ -0,0 +1,10 @@
#include "lua.h"
int luaopen_lib2 (lua_State *L);
LUAMOD_API int luaopen_lib21 (lua_State *L) {
return luaopen_lib2(L);
}

25
testes/libs/lib22.c Normal file
View File

@@ -0,0 +1,25 @@
#include "lua.h"
#include "lauxlib.h"
static int id (lua_State *L) {
lua_pushboolean(L, 1);
lua_insert(L, 1);
return lua_gettop(L);
}
static const struct luaL_Reg funcs[] = {
{"id", id},
{NULL, NULL}
};
LUAMOD_API int luaopen_lib2 (lua_State *L) {
lua_settop(L, 2);
lua_setglobal(L, "y"); /* y gets 2nd parameter */
lua_setglobal(L, "x"); /* x gets 1st parameter */
luaL_newlib(L, funcs);
return 1;
}

27
testes/libs/makefile Normal file
View File

@@ -0,0 +1,27 @@
# change this variable to point to the directory with Lua headers
# of the version being tested
LUA_DIR = ../../
CC = gcc
# compilation should generate Dynamic-Link Libraries
CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
# libraries used by the tests
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
touch all
lib1.so: lib1.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib1.so lib1.c
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib11.so lib11.c
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib2.so lib2.c
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib21.so lib21.c
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c

318
testes/literals.lua Normal file
View File

@@ -0,0 +1,318 @@
-- $Id: testes/literals.lua $
-- See Copyright Notice in file all.lua
print('testing scanner')
local debug = require "debug"
local function dostring (x) return assert(load(x), "")() end
dostring("x \v\f = \t\r 'a\0a' \v\f\f")
assert(x == 'a\0a' and string.len(x) == 3)
-- escape sequences
assert('\n\"\'\\' == [[
"'\]])
assert(string.find("\a\b\f\n\r\t\v", "^%c%c%c%c%c%c%c$"))
-- assume ASCII just for tests:
assert("\09912" == 'c12')
assert("\99ab" == 'cab')
assert("\099" == '\99')
assert("\099\n" == 'c\10')
assert('\0\0\0alo' == '\0' .. '\0\0' .. 'alo')
assert(010 .. 020 .. -030 == "1020-30")
-- hexadecimal escapes
assert("\x00\x05\x10\x1f\x3C\xfF\xe8" == "\0\5\16\31\60\255\232")
local function lexstring (x, y, n)
local f = assert(load('return ' .. x ..
', require"debug".getinfo(1).currentline', ''))
local s, l = f()
assert(s == y and l == n)
end
lexstring("'abc\\z \n efg'", "abcefg", 2)
lexstring("'abc\\z \n\n\n'", "abc", 4)
lexstring("'\\z \n\t\f\v\n'", "", 3)
lexstring("[[\nalo\nalo\n\n]]", "alo\nalo\n\n", 5)
lexstring("[[\nalo\ralo\n\n]]", "alo\nalo\n\n", 5)
lexstring("[[\nalo\ralo\r\n]]", "alo\nalo\n", 4)
lexstring("[[\ralo\n\ralo\r\n]]", "alo\nalo\n", 4)
lexstring("[[alo]\n]alo]]", "alo]\n]alo", 2)
assert("abc\z
def\z
ghi\z
" == 'abcdefghi')
-- UTF-8 sequences
assert("\u{0}\u{00000000}\x00\0" == string.char(0, 0, 0, 0))
-- limits for 1-byte sequences
assert("\u{0}\u{7F}" == "\x00\x7F")
-- limits for 2-byte sequences
assert("\u{80}\u{7FF}" == "\xC2\x80\xDF\xBF")
-- limits for 3-byte sequences
assert("\u{800}\u{FFFF}" == "\xE0\xA0\x80\xEF\xBF\xBF")
-- limits for 4-byte sequences
assert("\u{10000}\u{1FFFFF}" == "\xF0\x90\x80\x80\xF7\xBF\xBF\xBF")
-- limits for 5-byte sequences
assert("\u{200000}\u{3FFFFFF}" == "\xF8\x88\x80\x80\x80\xFB\xBF\xBF\xBF\xBF")
-- limits for 6-byte sequences
assert("\u{4000000}\u{7FFFFFFF}" ==
"\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF")
-- Error in escape sequences
local function lexerror (s, err)
local st, msg = load('return ' .. s, '')
if err ~= '<eof>' then err = err .. "'" end
assert(not st and string.find(msg, "near .-" .. err))
end
lexerror([["abc\x"]], [[\x"]])
lexerror([["abc\x]], [[\x]])
lexerror([["\x]], [[\x]])
lexerror([["\x5"]], [[\x5"]])
lexerror([["\x5]], [[\x5]])
lexerror([["\xr"]], [[\xr]])
lexerror([["\xr]], [[\xr]])
lexerror([["\x.]], [[\x.]])
lexerror([["\x8%"]], [[\x8%%]])
lexerror([["\xAG]], [[\xAG]])
lexerror([["\g"]], [[\g]])
lexerror([["\g]], [[\g]])
lexerror([["\."]], [[\%.]])
lexerror([["\999"]], [[\999"]])
lexerror([["xyz\300"]], [[\300"]])
lexerror([[" \256"]], [[\256"]])
-- errors in UTF-8 sequences
lexerror([["abc\u{100000000}"]], [[abc\u{100000000]]) -- too large
lexerror([["abc\u11r"]], [[abc\u1]]) -- missing '{'
lexerror([["abc\u"]], [[abc\u"]]) -- missing '{'
lexerror([["abc\u{11r"]], [[abc\u{11r]]) -- missing '}'
lexerror([["abc\u{11"]], [[abc\u{11"]]) -- missing '}'
lexerror([["abc\u{11]], [[abc\u{11]]) -- missing '}'
lexerror([["abc\u{r"]], [[abc\u{r]]) -- no digits
-- unfinished strings
lexerror("[=[alo]]", "<eof>")
lexerror("[=[alo]=", "<eof>")
lexerror("[=[alo]", "<eof>")
lexerror("'alo", "<eof>")
lexerror("'alo \\z \n\n", "<eof>")
lexerror("'alo \\z", "<eof>")
lexerror([['alo \98]], "<eof>")
-- valid characters in variable names
for i = 0, 255 do
local s = string.char(i)
assert(not string.find(s, "[a-zA-Z_]") == not load(s .. "=1", ""))
assert(not string.find(s, "[a-zA-Z_0-9]") ==
not load("a" .. s .. "1 = 1", ""))
end
-- long variable names
var1 = string.rep('a', 15000) .. '1'
var2 = string.rep('a', 15000) .. '2'
prog = string.format([[
%s = 5
%s = %s + 1
return function () return %s - %s end
]], var1, var2, var1, var1, var2)
local f = dostring(prog)
assert(_G[var1] == 5 and _G[var2] == 6 and f() == -1)
var1, var2, f = nil
print('+')
-- escapes --
assert("\n\t" == [[
]])
assert([[
$debug]] == "\n $debug")
assert([[ [ ]] ~= [[ ] ]])
-- long strings --
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
assert(string.len(b) == 960)
prog = [=[
print('+')
a1 = [["this is a 'string' with several 'quotes'"]]
a2 = "'quotes'"
assert(string.find(a1, a2) == 34)
print('+')
a1 = [==[temp = [[an arbitrary value]]; ]==]
assert(load(a1))()
assert(temp == 'an arbitrary value')
-- long strings --
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
assert(string.len(b) == 960)
print('+')
a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
]]
assert(string.len(a) == 1863)
assert(string.sub(a, 1, 40) == string.sub(b, 1, 40))
x = 1
]=]
print('+')
x = nil
dostring(prog)
assert(x)
prog = nil
a = nil
b = nil
-- testing line ends
prog = [[
a = 1 -- a comment
b = 2
x = [=[
hi
]=]
y = "\
hello\r\n\
"
return require"debug".getinfo(1).currentline
]]
for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do
local prog, nn = string.gsub(prog, "\n", n)
assert(dostring(prog) == nn)
assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n")
end
-- testing comments and strings with long brackets
a = [==[]=]==]
assert(a == "]=")
a = [==[[===[[=[]]=][====[]]===]===]==]
assert(a == "[===[[=[]]=][====[]]===]===")
a = [====[[===[[=[]]=][====[]]===]===]====]
assert(a == "[===[[=[]]=][====[]]===]===")
a = [=[]]]]]]]]]=]
assert(a == "]]]]]]]]")
--[===[
x y z [==[ blu foo
]==
]
]=]==]
error error]=]===]
-- generate all strings of four of these chars
local x = {"=", "[", "]", "\n"}
local len = 4
local function gen (c, n)
if n==0 then coroutine.yield(c)
else
for _, a in pairs(x) do
gen(c..a, n-1)
end
end
end
for s in coroutine.wrap(function () gen("", len) end) do
assert(s == load("return [====[\n"..s.."]====]", "")())
end
-- testing decimal point locale
if os.setlocale("pt_BR") or os.setlocale("ptb") then
assert(tonumber("3,4") == 3.4 and tonumber"3.4" == 3.4)
assert(tonumber(" -.4 ") == -0.4)
assert(tonumber(" +0x.41 ") == 0X0.41)
assert(not load("a = (3,4)"))
assert(assert(load("return 3.4"))() == 3.4)
assert(assert(load("return .4,3"))() == .4)
assert(assert(load("return 4."))() == 4.)
assert(assert(load("return 4.+.5"))() == 4.5)
assert(" 0x.1 " + " 0x,1" + "-0X.1\t" == 0x0.1)
assert(not tonumber"inf" and not tonumber"NAN")
assert(assert(load(string.format("return %q", 4.51)))() == 4.51)
local a,b = load("return 4.5.")
assert(string.find(b, "'4%.5%.'"))
assert(os.setlocale("C"))
else
(Message or print)(
'\n >>> pt_BR locale not available: skipping decimal point tests <<<\n')
end
-- testing %q x line ends
local s = "a string with \r and \n and \r\n and \n\r"
local c = string.format("return %q", s)
assert(assert(load(c))() == s)
-- testing errors
assert(not load"a = 'non-ending string")
assert(not load"a = 'non-ending string\n'")
assert(not load"a = '\\345'")
assert(not load"a = [=x]")
local function malformednum (n, exp)
local s, msg = load("return " .. n)
assert(not s and string.find(msg, exp))
end
malformednum("0xe-", "near <eof>")
malformednum("0xep-p", "malformed number")
malformednum("1print()", "malformed number")
print('OK')

699
testes/locals.lua Normal file
View File

@@ -0,0 +1,699 @@
-- $Id: testes/locals.lua $
-- See Copyright Notice in file all.lua
print('testing local variables and environments')
local debug = require"debug"
-- bug in 5.1:
local function f(x) x = nil; return x end
assert(f(10) == nil)
local function f() local x; return x end
assert(f(10) == nil)
local function f(x) x = nil; local y; return x, y end
assert(f(10) == nil and select(2, f(20)) == nil)
do
local i = 10
do local i = 100; assert(i==100) end
do local i = 1000; assert(i==1000) end
assert(i == 10)
if i ~= 10 then
local i = 20
else
local i = 30
assert(i == 30)
end
end
f = nil
local f
x = 1
a = nil
load('local a = {}')()
assert(a == nil)
function f (a)
local _1, _2, _3, _4, _5
local _6, _7, _8, _9, _10
local x = 3
local b = a
local c,d = a,b
if (d == b) then
local x = 'q'
x = b
assert(x == 2)
else
assert(nil)
end
assert(x == 3)
local f = 10
end
local b=10
local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3
assert(x == 1)
f(2)
assert(type(f) == 'function')
local function getenv (f)
local a,b = debug.getupvalue(f, 1)
assert(a == '_ENV')
return b
end
-- test for global table of loaded chunks
assert(getenv(load"a=3") == _G)
local c = {}; local f = load("a = 3", nil, nil, c)
assert(getenv(f) == c)
assert(c.a == nil)
f()
assert(c.a == 3)
-- old test for limits for special instructions
do
local i = 2
local p = 4 -- p == 2^i
repeat
for j=-3,3 do
assert(load(string.format([[local a=%s;
a=a+%s;
assert(a ==2^%s)]], j, p-j, i), '')) ()
assert(load(string.format([[local a=%s;
a=a-%s;
assert(a==-2^%s)]], -j, p-j, i), '')) ()
assert(load(string.format([[local a,b=0,%s;
a=b-%s;
assert(a==-2^%s)]], -j, p-j, i), '')) ()
end
p = 2 * p; i = i + 1
until p <= 0
end
print'+'
if rawget(_G, "T") then
-- testing clearing of dead elements from tables
collectgarbage("stop") -- stop GC
local a = {[{}] = 4, [3] = 0, alo = 1,
a1234567890123456789012345678901234567890 = 10}
local t = T.querytab(a)
for k,_ in pairs(a) do a[k] = undef end
collectgarbage() -- restore GC and collect dead fiels in `a'
for i=0,t-1 do
local k = querytab(a, i)
assert(k == nil or type(k) == 'number' or k == 'alo')
end
-- testing allocation errors during table insertions
local a = {}
local function additems ()
a.x = true; a.y = true; a.z = true
a[1] = true
a[2] = true
end
for i = 1, math.huge do
T.alloccount(i)
local st, msg = pcall(additems)
T.alloccount()
local count = 0
for k, v in pairs(a) do
assert(a[k] == v)
count = count + 1
end
if st then assert(count == 5); break end
end
end
-- testing lexical environments
assert(_ENV == _G)
do
local dummy
local _ENV = (function (...) return ... end)(_G, dummy) -- {
do local _ENV = {assert=assert}; assert(true) end
mt = {_G = _G}
local foo,x
A = false -- "declare" A
do local _ENV = mt
function foo (x)
A = x
do local _ENV = _G; A = 1000 end
return function (x) return A .. x end
end
end
assert(getenv(foo) == mt)
x = foo('hi'); assert(mt.A == 'hi' and A == 1000)
assert(x('*') == mt.A .. '*')
do local _ENV = {assert=assert, A=10};
do local _ENV = {assert=assert, A=20};
assert(A==20);x=A
end
assert(A==10 and x==20)
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, x, y)
local obj = setmetatable({}, {__close = f})
if x then
return x, obj, y
else
return obj
end
end
do
local a = {}
do
local b <close> = false -- not to be closed
local x <close> = setmetatable({"x"}, {__close = function (self)
a[#a + 1] = self[1] 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")
end
do
local X = false
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 _ <close> = closescope
return x, X, 23
end
local a, b, c = foo(1.5)
assert(a == 1.5 and b == false and c == 23 and X == true)
X = false
foo = function (x)
local _<close> = closescope
local y = 15
return y
end
assert(foo() == 15 and X == true)
X = false
foo = function ()
local x <close> = closescope
return x
end
assert(foo() == closescope and X == true)
end
do
-- calls cannot be tail in the scope of to-be-closed variables
local X, Y
local function foo ()
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 _ <close> = func2close(function () X = false end)
X = true
do
return foo() -- not a tail call!
end
end
local a, b, c, d = bar()
assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil)
end
-- auxiliary functions for testing warnings in '__close'
local function prepwarn ()
if not T then -- no test library?
warn("@off") -- do not show (lots of) warnings
else
warn("@store") -- to test the warnings
end
end
local function endwarn ()
if not T then
warn("@on") -- back to normal
else
assert(_WARN == nil)
warn("@normal")
end
end
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 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' got a non%-closable value"))
local function foo ()
local xyz <close> = setmetatable({}, {__close = print})
getmetatable(xyz).__close = nil -- remove metamethod
end
local stat, msg = pcall(foo)
assert(not stat and
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
end
if rawget(_G, "T") then
warn("@off")
-- memory error inside closing function
local function foo ()
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
end
stack(5) -- ensure a minimal number of CI structures
-- despite memory error, 'y' will be executed and
-- memory limit will be lifted
local _, msg = pcall(foo)
assert(msg == 1000)
local close = func2close(function (self, msg)
T.alloccount()
assert(msg == "not enough memory")
end)
-- set a memory limit and return a closing object to remove the limit
local function enter (count)
stack(10) -- reserve some stack space
T.alloccount(count)
return close
end
local function test ()
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
assert(false) -- should not run
end
local _, msg = pcall(test)
assert(msg == "not enough memory")
-- now use metamethod for closing
close = setmetatable({}, {__close = function ()
T.alloccount()
end})
-- repeat test with extra closing upvalues
local function test ()
local xxx <close> = func2close(function (self, msg)
assert(msg == "not enough memory");
error(1000) -- raise another error
end)
local xx <close> = func2close(function (self, msg)
assert(msg == "not enough memory");
end)
local x <close> = enter(0) -- set a memory limit
-- creation of previous upvalue will raise a memory error
os.exit(false) -- should not run
end
local _, msg = pcall(test)
assert(msg == "not enough memory") -- reported error is the first one
do -- testing 'toclose' in C string buffer
collectgarbage()
local s = string.rep('a', 10000) -- large string
local m = T.totalmem()
collectgarbage("stop")
s = string.upper(s) -- allocate buffer + new string (10K each)
-- ensure buffer was deallocated
assert(T.totalmem() - m <= 11000)
collectgarbage("restart")
end
do -- now some tests for freeing buffer in case of errors
local lim = 10000 -- some size larger than the static buffer
local extra = 2000 -- some extra memory (for callinfo, etc.)
local s = string.rep("a", lim)
-- concat this table needs two buffer resizes (one for each 's')
local a = {s, s}
collectgarbage()
m = T.totalmem()
collectgarbage("stop")
-- error in the first buffer allocation
T. totalmem(m + extra)
assert(not pcall(table.concat, a))
-- first buffer was not even allocated
assert(T.totalmem() - m <= extra)
-- error in the second buffer allocation
T. totalmem(m + lim + extra)
assert(not pcall(table.concat, a))
-- first buffer was released by 'toclose'
assert(T.totalmem() - m <= extra)
-- error in creation of final string
T.totalmem(m + 2 * lim + extra)
assert(not pcall(table.concat, a))
-- second buffer was released by 'toclose'
assert(T.totalmem() - m <= extra)
-- userdata, upvalue, buffer, buffer, final string
T.totalmem(m + 4*lim + extra)
assert(#table.concat(a) == 2*lim)
T.totalmem(0) -- remove memory limit
collectgarbage("restart")
print'+'
end
warn("@on")
end
print "to-be-closed variables in coroutines"
do
-- an error in a wrapped coroutine closes variables
local x = false
local y = false
local co = coroutine.wrap(function ()
local xv <close> = func2close(function () x = true end)
do
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
error(23) -- error does
end)
local b = co()
assert(b == 100 and not x and not y)
b = co()
assert(b == 200 and not x and y)
local a, b = pcall(co)
assert(not a and b == 23 and x and y)
end
do
prepwarn()
-- error in a wrapped coroutine raising errors when closing a variable
local x = 0
local co = coroutine.wrap(function ()
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
local 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")
local x = 0
local y = 0
co = coroutine.wrap(function ()
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
coroutine.yield(100)
return 200
end)
assert(co() == 100); assert(x == 0)
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()
-- should not run
local x <close> = func2close(function () os.exit(false) end)
co = nil
coroutine.yield()
end)
co() -- start coroutine
assert(co == nil) -- eventually it will be collected
collectgarbage()
-- to-be-closed variables in generic for loops
do
local numopen = 0
local function open (x)
numopen = numopen + 1
return
function () -- iteraction function
x = x - 1
if x > 0 then return x end
end,
nil, -- state
nil, -- control variable
func2close(function () numopen = numopen - 1 end) -- closing function
end
local s = 0
for i in open(10) do
s = s + i
end
assert(s == 45 and numopen == 0)
local s = 0
for i in open(10) do
if i < 5 then break end
s = s + i
end
assert(s == 35 and numopen == 0)
local s = 0
for i in open(10) do
for j in open(10) do
if i + j < 5 then goto endloop end
s = s + i
end
end
::endloop::
assert(s == 375 and numopen == 0)
end
print('OK')
return 5,f
end -- }

468
testes/main.lua Normal file
View File

@@ -0,0 +1,468 @@
# testing special comment on first line
-- $Id: testes/main.lua $
-- See Copyright Notice in file all.lua
-- most (all?) tests here assume a reasonable "Unix-like" shell
if _port then return end
-- use only "double quotes" inside shell scripts (better change to
-- run on Windows)
print ("testing stand-alone interpreter")
assert(os.execute()) -- machine has a system command
local arg = arg or ARG
local prog = os.tmpname()
local otherprog = os.tmpname()
local out = os.tmpname()
local progname
do
local i = 0
while arg[i] do i=i-1 end
progname = arg[i+1]
end
print("progname: "..progname)
local prepfile = function (s, p)
p = p or prog
io.output(p)
io.write(s)
assert(io.close())
end
local function getoutput ()
io.input(out)
local t = io.read("a")
io.input():close()
assert(os.remove(out))
return t
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))
end
end
local function checkout (s)
local t = getoutput()
if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end
assert(s == t)
return t
end
local function RUN (p, ...)
p = string.gsub(p, "lua", '"'..progname..'"', 1)
local s = string.format(p, ...)
assert(os.execute(s))
end
local function NoRun (msg, p, ...)
p = string.gsub(p, "lua", '"'..progname..'"', 1)
local s = string.format(p, ...)
s = string.format("%s 2> %s", s, out) -- will send error to 'out'
assert(not os.execute(s))
assert(string.find(getoutput(), msg, 1, true)) -- check error message
end
RUN('lua -v')
print(string.format("(temporary program file used in these tests: %s)", prog))
-- running stdin as a file
prepfile""
RUN('lua - < %s > %s', prog, out)
checkout("")
prepfile[[
print(
1, a
)
]]
RUN('lua - < %s > %s', prog, out)
checkout("1\tnil\n")
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
checkout("10\n2\n")
-- test option '-'
RUN('echo "print(arg[1])" | lua - -h > %s', out)
checkout("-h\n")
-- test environment variables used by Lua
prepfile("print(package.path)")
-- test LUA_PATH
RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out)
checkout("x\n")
-- test LUA_PATH_version
RUN('env LUA_INIT= LUA_PATH_5_4=y LUA_PATH=x lua %s > %s', prog, out)
checkout("y\n")
-- test LUA_CPATH
prepfile("print(package.cpath)")
RUN('env LUA_INIT= LUA_CPATH=xuxu lua %s > %s', prog, out)
checkout("xuxu\n")
-- test LUA_CPATH_version
RUN('env LUA_INIT= LUA_CPATH_5_4=yacc LUA_CPATH=x lua %s > %s', prog, out)
checkout("yacc\n")
-- test LUA_INIT (and its access to 'arg' table)
prepfile("print(X)")
RUN('env LUA_INIT="X=tonumber(arg[1])" lua %s 3.2 > %s', prog, out)
checkout("3.2\n")
-- test LUA_INIT_version
prepfile("print(X)")
RUN('env LUA_INIT_5_4="X=10" LUA_INIT="X=3" lua %s > %s', prog, out)
checkout("10\n")
-- test LUA_INIT for files
prepfile("x = x or 10; print(x); x = x + 1")
RUN('env LUA_INIT="@%s" lua %s > %s', prog, prog, out)
checkout("10\n11\n")
-- test errors in LUA_INIT
NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
-- test option '-E'
local defaultpath, defaultCpath
do
prepfile("print(package.path, package.cpath)")
RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
prog, out)
local output = getoutput()
defaultpath = string.match(output, "^(.-)\t")
defaultCpath = string.match(output, "\t(.-)$")
-- running with an empty environment
RUN('env -i lua %s > %s', prog, out)
local out = getoutput()
assert(defaultpath == string.match(output, "^(.-)\t"))
assert(defaultCpath == string.match(output, "\t(.-)$"))
end
-- paths did not change
assert(not string.find(defaultpath, "xxx") and
string.find(defaultpath, "lua") and
not string.find(defaultCpath, "xxx") and
string.find(defaultCpath, "lua"))
-- test replacement of ';;' to default path
local function convert (p)
prepfile("print(package.path)")
RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
local expected = getoutput()
expected = string.sub(expected, 1, -2) -- cut final end of line
if string.find(p, ";;") then
p = string.gsub(p, ";;", ";"..defaultpath..";")
p = string.gsub(p, "^;", "") -- remove ';' at the beginning
p = string.gsub(p, ";$", "") -- remove ';' at the end
end
assert(p == expected)
end
convert(";")
convert(";;")
convert("a;;b")
convert(";;b")
convert("a;;")
convert("a;b;;c")
-- test -l over multiple libraries
prepfile("print(1); a=2; return {x=15}")
prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog)
RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out)
checkout("1\n2\n15\n2\n15\n")
-- test 'arg' table
local a = [[
assert(#arg == 3 and arg[1] == 'a' and
arg[2] == 'b' and arg[3] == 'c')
assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == '%s')
assert(arg[4] == undef and arg[-4] == undef)
local a, b, c = ...
assert(... == 'a' and a == 'a' and b == 'b' and c == 'c')
]]
a = string.format(a, progname)
prepfile(a)
RUN('lua "-e " -- %s a b c', prog) -- "-e " runs an empty command
-- test 'arg' availability in libraries
prepfile"assert(arg)"
prepfile("assert(arg)", otherprog)
RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
-- test messing up the 'arg' table
RUN('echo "print(...)" | lua -e "arg[1] = 100" - > %s', out)
checkout("100\n")
NoRun("'arg' is not a table", 'echo "" | lua -e "arg = 1" -')
-- test error in 'print'
RUN('echo 10 | lua -e "print=nil" -i > /dev/null 2> %s', out)
assert(string.find(getoutput(), "error calling 'print'"))
-- test 'debug.debug'
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)
checkout("a\n")
RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out)
checkout("1\n3\n")
-- test iteractive mode
prepfile[[
(6*2-6) -- ===
a =
10
print(a)
a]]
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("6\n10\n10\n\n")
prepfile("a = [[b\nc\nd\ne]]\n=a")
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("b\nc\nd\ne\n\n")
prompt = "alo"
prepfile[[ --
a = 2
]]
RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
local t = getoutput()
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- test for error objects
prepfile[[
debug = require "debug"
m = {x=0}
setmetatable(m, {__tostring = function(x)
return tostring(debug.getinfo(4).currentline + x.x)
end})
error(m)
]]
NoRun(progname .. ": 6\n", [[lua %s]], prog)
prepfile("error{}")
NoRun("error object is a table value", [[lua %s]], prog)
-- chunk broken in many lines
s = [=[ --
function f ( x )
local a = [[
xuxu
]]
local b = "\
xuxu\n"
if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]]
return x + 1
--\\
end
return( f( 100 ) )
assert( a == b )
do return f( 11 ) end ]=]
s = string.gsub(s, ' ', '\n\n') -- change all spaces for newlines
prepfile(s)
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("101\n13\t22\n\n")
prepfile[[#comment in 1st line without \n at the end]]
RUN('lua %s', prog)
prepfile[[#test line number when file starts with comment line
debug = require"debug"
print(debug.getinfo(1).currentline)
]]
RUN('lua %s > %s', prog, out)
checkprogout('3\n')
-- close Lua with an open file
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
RUN('lua %s', prog)
checkout('alo')
-- bug in 5.2 beta (extra \0 after version line)
RUN([[lua -v -e"print'hello'" > %s]], out)
t = getoutput()
assert(string.find(t, "PUC%-Rio\nhello"))
-- testing os.exit
prepfile("os.exit(nil, true)")
RUN('lua %s', prog)
prepfile("os.exit(0, true)")
RUN('lua %s', prog)
prepfile("os.exit(true, true)")
RUN('lua %s', prog)
prepfile("os.exit(1, true)")
NoRun("", "lua %s", prog) -- no message
prepfile("os.exit(false, true)")
NoRun("", "lua %s", prog) -- no message
-- to-be-closed variables in main chunk
prepfile[[
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("120\nOk\n")
-- remove temporary files
assert(os.remove(prog))
assert(os.remove(otherprog))
assert(not os.remove(out))
-- invalid options
NoRun("unrecognized option '-h'", "lua -h")
NoRun("unrecognized option '---'", "lua ---")
NoRun("unrecognized option '-Ex'", "lua -Ex")
NoRun("unrecognized option '-vv'", "lua -vv")
NoRun("unrecognized option '-iv'", "lua -iv")
NoRun("'-e' needs argument", "lua -e")
NoRun("syntax error", "lua -e a")
NoRun("'-l' needs argument", "lua -l")
if T then -- test library?
print("testing 'not enough memory' to create a state")
NoRun("not enough memory", "env MEMLIMIT=100 lua")
-- testing 'warn'
warn("@store")
warn("@123", "456", "789")
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
-- 'warn' must get at least one argument
local st, msg = pcall(warn)
assert(string.find(msg, "string expected"))
-- 'warn' does not leave unfinished warning in case of errors
-- (message would appear in next warning)
st, msg = pcall(warn, "SHOULD NOT APPEAR", {})
assert(string.find(msg, "string expected"))
end
print('+')
print('testing Ctrl C')
do
-- interrupt a script
local function kill (pid)
return os.execute(string.format('kill -INT %s 2> /dev/null', pid))
end
-- function to run a script in background, returning its output file
-- descriptor and its pid
local function runback (luaprg)
-- shell script to run 'luaprg' in background and echo its pid
local shellprg = string.format('%s -e "%s" & echo $!', progname, luaprg)
local f = io.popen(shellprg, "r") -- run shell script
local pid = f:read() -- get pid for Lua script
print("(if test fails now, it may leave a Lua script running in \z
background, pid " .. pid .. ")")
return f, pid
end
-- Lua script that runs protected infinite loop and then prints '42'
local f, pid = runback[[
pcall(function () print(12); while true do end end); print(42)]]
-- wait until script is inside 'pcall'
assert(f:read() == "12")
kill(pid) -- send INT signal to Lua script
-- check that 'pcall' captured the exception and script continued running
assert(f:read() == "42") -- expected output
assert(f:close())
print("done")
-- Lua script in a long unbreakable search
local f, pid = runback[[
print(15); string.find(string.rep('a', 100000), '.*b')]]
-- wait (so script can reach the loop)
assert(f:read() == "15")
assert(os.execute("sleep 1"))
-- must send at least two INT signals to stop this Lua script
local n = 100
for i = 0, 100 do -- keep sending signals
if not kill(pid) then -- until it fails
n = i -- number of non-failed kills
break
end
end
assert(f:close())
assert(n >= 2)
print(string.format("done (with %d kills)", n))
end
print("OK")

1019
testes/math.lua Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More